本站源代码
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.

198 lines
4.6KB

  1. package gomemcached
  2. import (
  3. "encoding/binary"
  4. "fmt"
  5. "io"
  6. )
  7. // The maximum reasonable body length to expect.
  8. // Anything larger than this will result in an error.
  9. // The current limit, 20MB, is the size limit supported by ep-engine.
  10. var MaxBodyLen = int(20 * 1024 * 1024)
  11. // MCRequest is memcached Request
  12. type MCRequest struct {
  13. // The command being issued
  14. Opcode CommandCode
  15. // The CAS (if applicable, or 0)
  16. Cas uint64
  17. // An opaque value to be returned with this request
  18. Opaque uint32
  19. // The vbucket to which this command belongs
  20. VBucket uint16
  21. // Command extras, key, and body
  22. Extras, Key, Body, ExtMeta []byte
  23. // Datatype identifier
  24. DataType uint8
  25. }
  26. // Size gives the number of bytes this request requires.
  27. func (req *MCRequest) Size() int {
  28. return HDR_LEN + len(req.Extras) + len(req.Key) + len(req.Body) + len(req.ExtMeta)
  29. }
  30. // A debugging string representation of this request
  31. func (req MCRequest) String() string {
  32. return fmt.Sprintf("{MCRequest opcode=%s, bodylen=%d, key='%s'}",
  33. req.Opcode, len(req.Body), req.Key)
  34. }
  35. func (req *MCRequest) fillHeaderBytes(data []byte) int {
  36. pos := 0
  37. data[pos] = REQ_MAGIC
  38. pos++
  39. data[pos] = byte(req.Opcode)
  40. pos++
  41. binary.BigEndian.PutUint16(data[pos:pos+2],
  42. uint16(len(req.Key)))
  43. pos += 2
  44. // 4
  45. data[pos] = byte(len(req.Extras))
  46. pos++
  47. // Data type
  48. if req.DataType != 0 {
  49. data[pos] = byte(req.DataType)
  50. }
  51. pos++
  52. binary.BigEndian.PutUint16(data[pos:pos+2], req.VBucket)
  53. pos += 2
  54. // 8
  55. binary.BigEndian.PutUint32(data[pos:pos+4],
  56. uint32(len(req.Body)+len(req.Key)+len(req.Extras)+len(req.ExtMeta)))
  57. pos += 4
  58. // 12
  59. binary.BigEndian.PutUint32(data[pos:pos+4], req.Opaque)
  60. pos += 4
  61. // 16
  62. if req.Cas != 0 {
  63. binary.BigEndian.PutUint64(data[pos:pos+8], req.Cas)
  64. }
  65. pos += 8
  66. if len(req.Extras) > 0 {
  67. copy(data[pos:pos+len(req.Extras)], req.Extras)
  68. pos += len(req.Extras)
  69. }
  70. if len(req.Key) > 0 {
  71. copy(data[pos:pos+len(req.Key)], req.Key)
  72. pos += len(req.Key)
  73. }
  74. return pos
  75. }
  76. // HeaderBytes will return the wire representation of the request header
  77. // (with the extras and key).
  78. func (req *MCRequest) HeaderBytes() []byte {
  79. data := make([]byte, HDR_LEN+len(req.Extras)+len(req.Key))
  80. req.fillHeaderBytes(data)
  81. return data
  82. }
  83. // Bytes will return the wire representation of this request.
  84. func (req *MCRequest) Bytes() []byte {
  85. data := make([]byte, req.Size())
  86. pos := req.fillHeaderBytes(data)
  87. if len(req.Body) > 0 {
  88. copy(data[pos:pos+len(req.Body)], req.Body)
  89. }
  90. if len(req.ExtMeta) > 0 {
  91. copy(data[pos+len(req.Body):pos+len(req.Body)+len(req.ExtMeta)], req.ExtMeta)
  92. }
  93. return data
  94. }
  95. // Transmit will send this request message across a writer.
  96. func (req *MCRequest) Transmit(w io.Writer) (n int, err error) {
  97. if len(req.Body) < 128 {
  98. n, err = w.Write(req.Bytes())
  99. } else {
  100. n, err = w.Write(req.HeaderBytes())
  101. if err == nil {
  102. m := 0
  103. m, err = w.Write(req.Body)
  104. n += m
  105. }
  106. }
  107. return
  108. }
  109. // Receive will fill this MCRequest with the data from a reader.
  110. func (req *MCRequest) Receive(r io.Reader, hdrBytes []byte) (int, error) {
  111. if len(hdrBytes) < HDR_LEN {
  112. hdrBytes = []byte{
  113. 0, 0, 0, 0, 0, 0, 0, 0,
  114. 0, 0, 0, 0, 0, 0, 0, 0,
  115. 0, 0, 0, 0, 0, 0, 0, 0}
  116. }
  117. n, err := io.ReadFull(r, hdrBytes)
  118. if err != nil {
  119. return n, err
  120. }
  121. if hdrBytes[0] != RES_MAGIC && hdrBytes[0] != REQ_MAGIC {
  122. return n, fmt.Errorf("bad magic: 0x%02x", hdrBytes[0])
  123. }
  124. klen := int(binary.BigEndian.Uint16(hdrBytes[2:]))
  125. elen := int(hdrBytes[4])
  126. // Data type at 5
  127. req.DataType = uint8(hdrBytes[5])
  128. req.Opcode = CommandCode(hdrBytes[1])
  129. // Vbucket at 6:7
  130. req.VBucket = binary.BigEndian.Uint16(hdrBytes[6:])
  131. totalBodyLen := int(binary.BigEndian.Uint32(hdrBytes[8:]))
  132. req.Opaque = binary.BigEndian.Uint32(hdrBytes[12:])
  133. req.Cas = binary.BigEndian.Uint64(hdrBytes[16:])
  134. if totalBodyLen > 0 {
  135. buf := make([]byte, totalBodyLen)
  136. m, err := io.ReadFull(r, buf)
  137. n += m
  138. if err == nil {
  139. if req.Opcode >= TAP_MUTATION &&
  140. req.Opcode <= TAP_CHECKPOINT_END &&
  141. len(buf) > 1 {
  142. // In these commands there is "engine private"
  143. // data at the end of the extras. The first 2
  144. // bytes of extra data give its length.
  145. elen += int(binary.BigEndian.Uint16(buf))
  146. }
  147. req.Extras = buf[0:elen]
  148. req.Key = buf[elen : klen+elen]
  149. // get the length of extended metadata
  150. extMetaLen := 0
  151. if elen > 29 {
  152. extMetaLen = int(binary.BigEndian.Uint16(req.Extras[28:30]))
  153. }
  154. bodyLen := totalBodyLen - klen - elen - extMetaLen
  155. if bodyLen > MaxBodyLen {
  156. return n, fmt.Errorf("%d is too big (max %d)",
  157. bodyLen, MaxBodyLen)
  158. }
  159. req.Body = buf[klen+elen : klen+elen+bodyLen]
  160. req.ExtMeta = buf[klen+elen+bodyLen:]
  161. }
  162. }
  163. return n, err
  164. }
上海开阖软件有限公司 沪ICP备12045867号-1