You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

138 lines
3.2 KiB

  1. // Copyright 2011 Xing Xing <mikespook@gmail.com>.
  2. // All rights reserved.
  3. // Use of this source code is governed by a MIT
  4. // license that can be found in the LICENSE file.
  5. package client
  6. import (
  7. "bytes"
  8. "encoding/binary"
  9. "fmt"
  10. "strconv"
  11. )
  12. // Response handler
  13. type ResponseHandler func(*Response)
  14. // response
  15. type Response struct {
  16. DataType uint32
  17. Data, UID []byte
  18. Handle string
  19. }
  20. // Extract the Response's result.
  21. // if data == nil, err != nil, then worker failing to execute job
  22. // if data != nil, err != nil, then worker has a exception
  23. // if data != nil, err == nil, then worker complate job
  24. // after calling this method, the Response.Handle will be filled
  25. func (resp *Response) Result() (data []byte, err error) {
  26. switch resp.DataType {
  27. case WORK_FAIL:
  28. resp.Handle = string(resp.Data)
  29. err = ErrWorkFail
  30. return
  31. case WORK_EXCEPTION:
  32. err = ErrWorkException
  33. fallthrough
  34. case WORK_COMPLETE:
  35. s := bytes.SplitN(resp.Data, []byte{'\x00'}, 2)
  36. if len(s) != 2 {
  37. err = fmt.Errorf("Invalid data: %V", resp.Data)
  38. return
  39. }
  40. resp.Handle = string(s[0])
  41. data = s[1]
  42. default:
  43. err = ErrDataType
  44. }
  45. return
  46. }
  47. // Extract the job's update
  48. func (resp *Response) Update() (data []byte, err error) {
  49. if resp.DataType != WORK_DATA &&
  50. resp.DataType != WORK_WARNING {
  51. err = ErrDataType
  52. return
  53. }
  54. s := bytes.SplitN(resp.Data, []byte{'\x00'}, 2)
  55. if len(s) != 2 {
  56. err = ErrInvalidData
  57. return
  58. }
  59. if resp.DataType == WORK_WARNING {
  60. err = ErrWorkWarning
  61. }
  62. resp.Handle = string(s[0])
  63. data = s[1]
  64. return
  65. }
  66. // Decode a job from byte slice
  67. func decodeResponse(data []byte) (resp *Response, l int, err error) {
  68. if len(data) < MIN_PACKET_LEN { // valid package should not less 12 bytes
  69. err = fmt.Errorf("Invalid data: %V", data)
  70. return
  71. }
  72. dl := int(binary.BigEndian.Uint32(data[8:12]))
  73. dt := data[MIN_PACKET_LEN : dl+MIN_PACKET_LEN]
  74. if len(dt) != int(dl) { // length not equal
  75. err = fmt.Errorf("Invalid data: %V", data)
  76. return
  77. }
  78. resp = getResponse()
  79. resp.DataType = binary.BigEndian.Uint32(data[4:8])
  80. switch resp.DataType {
  81. case JOB_CREATED:
  82. resp.Handle = string(dt)
  83. case STATUS_RES, WORK_DATA, WORK_WARNING, WORK_STATUS,
  84. WORK_COMPLETE, WORK_FAIL, WORK_EXCEPTION:
  85. s := bytes.SplitN(dt, []byte{'\x00'}, 2)
  86. if len(s) >= 2 {
  87. resp.Handle = string(s[0])
  88. resp.Data = s[1]
  89. } else {
  90. err = fmt.Errorf("Invalid data: %V", data)
  91. return
  92. }
  93. case ECHO_RES:
  94. fallthrough
  95. default:
  96. resp.Data = dt
  97. }
  98. l = dl + MIN_PACKET_LEN
  99. return
  100. }
  101. // status handler
  102. func (resp *Response) Status() (status *Status, err error) {
  103. data := bytes.SplitN(resp.Data, []byte{'\x00'}, 4)
  104. if len(data) != 4 {
  105. err = fmt.Errorf("Invalid data: %V", resp.Data)
  106. return
  107. }
  108. status = &Status{}
  109. status.Handle = resp.Handle
  110. status.Known = (data[0][0] == '1')
  111. status.Running = (data[1][0] == '1')
  112. status.Numerator, err = strconv.ParseUint(string(data[2]), 10, 0)
  113. if err != nil {
  114. err = fmt.Errorf("Invalid Integer: %s", data[2])
  115. return
  116. }
  117. status.Denominator, err = strconv.ParseUint(string(data[3]), 10, 0)
  118. if err != nil {
  119. err = fmt.Errorf("Invalid Integer: %s", data[3])
  120. return
  121. }
  122. return
  123. }
  124. func getResponse() (resp *Response) {
  125. // TODO add a pool
  126. resp = &Response{}
  127. return
  128. }