module EventMachine::Protocols::Couchbase::Packet

Constants

CMD_ADD
CMD_ADDQ
CMD_APPEND
CMD_APPENDQ
CMD_DECREMENT
CMD_DECREMENTQ
CMD_DELETE
CMD_DELETEQ
CMD_FLUSH
CMD_FLUSHQ
CMD_GAT
CMD_GATQ
CMD_GET
CMD_GETK
CMD_GETKQ
CMD_GETQ
CMD_INCREMENT
CMD_INCREMENTQ
CMD_NOOP
CMD_PREPEND
CMD_PREPENDQ
CMD_QUIT
CMD_QUITQ
CMD_REPLACE
CMD_REPLACEQ
CMD_SASL_AUTH
CMD_SASL_LIST_MECHS
CMD_SASL_STEP
CMD_SET
CMD_SETQ
CMD_STAT
CMD_TOUCH
CMD_VERBOSITY
CMD_VERSION
REQUEST_HEADER_FMT

uint8_t magic uint8_t opcode uint16_t keylen uint8_t extlen uint8_t datatype uint16_t vbucket uint32_t bodylen uint32_t opaque uint64_t cas

REQUEST_HEADER_SIZE
RESPONSE_HEADER_FMT

Sum of lengths of fields below

uint8_t   magic
uint8_t   opcode
uint16_t  keylen
uint8_t   extlen
uint8_t   datatype
uint16_t  status
uint32_t  bodylen
uint32_t  opaque
uint64_t  cas
RESPONSE_HEADER_SIZE

Public Class Methods

build(opaque, vbucket, opcode, *args) click to toggle source
# File lib/em-couchbase/packet.rb, line 85
def self.build(opaque, vbucket, opcode, *args)
  case opcode
  when :set
    key, value, flags, expiration, cas = args.shift(5)
    bodylen = key.size + value.size + 8
    [
      0x80,             # uint8_t   magic
      CMD_SET,          # uint8_t   opcode
      key.size,         # uint16_t  keylen
      8,                # uint8_t   extlen (flags + expiration)
      0,                # uint8_t   datatype
      vbucket,          # uint16_t  vbucket
      bodylen,          # uint32_t  bodylen
      opaque || 0,      # uint32_t  opaque
      cas || 0,         # uint64_t  cas
      flags || 0,       # uint32_t  flags
      expiration || 0,  # uint32_t  expiration
      key,
      value
    ].pack("#{REQUEST_HEADER_FMT}NNa*a*")
  when :get
    key = args.shift
    bodylen = key.size
    [
      0x80,             # uint8_t   magic
      CMD_GET,          # uint8_t   opcode
      key.size,         # uint16_t  keylen
      0,                # uint8_t   extlen
      0,                # uint8_t   datatype
      vbucket,          # uint16_t  vbucket
      bodylen,          # uint32_t  bodylen
      opaque || 0,      # uint32_t  opaque
      0,                # uint64_t  cas
      key
    ].pack("#{REQUEST_HEADER_FMT}a*")
  when :incr, :decr
    cmd_id = opcode == :incr ? CMD_INCREMENT : CMD_DECREMENT
    key, delta, initial, expiration, cas = args.shift(5)
    delta ||= 1
    initial ||= 0
    bodylen = key.size + 20
    [
      0x80,                   # uint8_t   magic
      cmd_id,                 # uint8_t   opcode
      key.size,               # uint16_t  keylen
      20,                     # uint8_t   extlen (delta + initial + expiration)
      0,                      # uint8_t   datatype
      vbucket,                # uint16_t  vbucket
      bodylen,                # uint32_t  bodylen
      opaque || 0,            # uint32_t  opaque
      cas || 0,               # uint64_t  cas
      delta >> 32,            # uint64_t
      delta & 0xffffffff,     #
      initial >> 32,          # uint64_t
      initial & 0xffffffff,   #
      expiration || 0,        # uint32_t
      key
    ].pack("#{REQUEST_HEADER_FMT}NNNNNa*")
  when :sasl_auth
    mech, username, password = args.shift(3)
    value = "\0#{username}\0#{password}"
    bodylen = mech.size + value.size
    [
      0x80,             # uint8_t   magic
      CMD_SASL_AUTH,    # uint8_t   opcode
      mech.size,        # uint16_t  keylen
      0,                # uint8_t   extlen
      0,                # uint8_t   datatype
      0,                # uint16_t  vbucket
      bodylen,          # uint32_t  bodylen
      0,                # uint32_t  opaque
      0,                # uint64_t  cas
      mech,
      value
    ].pack("#{REQUEST_HEADER_FMT}a*a*")
  else
    raise Couchbase::Error::UnknownCommand, [opcode, *args].inspect
  end
end
parse(data) { |operation, opaque, result| ... } click to toggle source
# File lib/em-couchbase/packet.rb, line 165
def self.parse(data)
  while data.length >= RESPONSE_HEADER_SIZE
    header = data[0...RESPONSE_HEADER_SIZE]
    ( magic,
      opcode,
      keylen,
      extlen,
      datatype,
      status,
      bodylen,
      opaque,
      cas ) = header.unpack(RESPONSE_HEADER_FMT)

    if magic != 0x81
      fail Couchbase::Error::Protocol.new "Broken packet: #{header.inspect}"
    end

    if data.size < bodylen + RESPONSE_HEADER_SIZE
      return  # need moar data
    else
      data[0...RESPONSE_HEADER_SIZE] = ""
    end

    ext = data[0...extlen]
    data[0...extlen] = ""

    key = data[0...keylen]
    data[0...keylen] = ""

    vallen = bodylen - extlen - keylen
    body = data[0...vallen]
    data[0...vallen] = ""

    result = Result.new(:key => key,
                        :value => body,
                        :status => status,
                        :cas => cas)

    case opcode
    when CMD_SET
      result.operation = :set
    when CMD_SASL_AUTH
      result.operation = :sasl_auth
    when CMD_GET
      result.operation = :get
      result.flags, _ = ext.unpack("N")
    when CMD_INCREMENT
      result.operation = :incr
      hi, lo = result.value.unpack("NN")
      result.value = hi << 32 | lo
    when CMD_DECREMENT
      result.operation = :decr
      hi, lo = result.value.unpack("NN")
      result.value = hi << 32 | lo
    else
      raise Couchbase::Error::UnknownCommand, header.inspect
    end

    if error_class = Couchbase::Error.map_error_code(status)
      result.error = error_class.new(body)
      result.error.error = status
      result.error.key = key
      result.error.cas = cas
      result.error.operation = result.operation
    end
    yield(result.operation, opaque, result)
  end
end