// Copyright 2011 Xing Xing . // All rights reserved. // Use of this source code is governed by a MIT // license that can be found in the LICENSE file. package client import ( "bytes" "encoding/binary" "fmt" "strconv" ) // Response handler type ResponseHandler func(*Response) // response type Response struct { DataType uint32 Data, UID []byte Handle string } // Extract the Response's result. // if data == nil, err != nil, then worker failing to execute job // if data != nil, err != nil, then worker has a exception // if data != nil, err == nil, then worker complate job // after calling this method, the Response.Handle will be filled func (resp *Response) Result() (data []byte, err error) { switch resp.DataType { case WORK_FAIL: resp.Handle = string(resp.Data) err = ErrWorkFail return case WORK_EXCEPTION: err = ErrWorkException fallthrough case WORK_COMPLETE: s := bytes.SplitN(resp.Data, []byte{'\x00'}, 2) if len(s) != 2 { err = fmt.Errorf("Invalid data: %V", resp.Data) return } resp.Handle = string(s[0]) data = s[1] default: err = ErrDataType } return } // Extract the job's update func (resp *Response) Update() (data []byte, err error) { if resp.DataType != WORK_DATA && resp.DataType != WORK_WARNING { err = ErrDataType return } s := bytes.SplitN(resp.Data, []byte{'\x00'}, 2) if len(s) != 2 { err = ErrInvalidData return } if resp.DataType == WORK_WARNING { err = ErrWorkWarning } resp.Handle = string(s[0]) data = s[1] return } // Decode a job from byte slice func decodeResponse(data []byte) (resp *Response, l int, err error) { if len(data) < MIN_PACKET_LEN { // valid package should not less 12 bytes err = fmt.Errorf("Invalid data: %V", data) return } dl := int(binary.BigEndian.Uint32(data[8:12])) dt := data[MIN_PACKET_LEN : dl+MIN_PACKET_LEN] if len(dt) != int(dl) { // length not equal err = fmt.Errorf("Invalid data: %V", data) return } resp = getResponse() resp.DataType = binary.BigEndian.Uint32(data[4:8]) switch resp.DataType { case JOB_CREATED: resp.Handle = string(dt) case STATUS_RES, WORK_DATA, WORK_WARNING, WORK_STATUS, WORK_COMPLETE, WORK_FAIL, WORK_EXCEPTION: s := bytes.SplitN(dt, []byte{'\x00'}, 2) if len(s) >= 2 { resp.Handle = string(s[0]) resp.Data = s[1] } else { err = fmt.Errorf("Invalid data: %V", data) return } case ECHO_RES: fallthrough default: resp.Data = dt } l = dl + MIN_PACKET_LEN return } // status handler func (resp *Response) Status() (status *Status, err error) { data := bytes.SplitN(resp.Data, []byte{'\x00'}, 4) if len(data) != 4 { err = fmt.Errorf("Invalid data: %V", resp.Data) return } status = &Status{} status.Handle = resp.Handle status.Known = (data[0][0] == '1') status.Running = (data[1][0] == '1') status.Numerator, err = strconv.ParseUint(string(data[2]), 10, 0) if err != nil { err = fmt.Errorf("Invalid Integer: %s", data[2]) return } status.Denominator, err = strconv.ParseUint(string(data[3]), 10, 0) if err != nil { err = fmt.Errorf("Invalid Integer: %s", data[3]) return } return } func getResponse() (resp *Response) { // TODO add a pool resp = &Response{} return }