1 /* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ 2 /* 3 * Copyright 2014 Couchbase, Inc. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 #ifndef LCB_PACKETUTILS_H 19 #define LCB_PACKETUTILS_H 20 21 #include "config.h" 22 23 #include <libcouchbase/couchbase.h> 24 #include <memcached/protocol_binary.h> 25 #include "rdb/rope.h" 26 27 #ifndef __cplusplus 28 typedef struct packet_info_st packet_info; 29 #else 30 #include "contrib/lcb-jsoncpp/lcb-jsoncpp.h" 31 #include <math.h> 32 namespace lcb { 33 class Server; 34 35 /** 36 * Response packet informational structure. 37 * 38 * This contains information regarding the response packet which is used by 39 * the response processors. 40 */ 41 class MemcachedResponse { 42 public: MemcachedResponse()43 MemcachedResponse() : payload(NULL), bufh(NULL) { 44 // Bodyless. Members are initialized via load! 45 } 46 MemcachedResponse(protocol_binary_command cmd,uint32_t opaque_,protocol_binary_response_status code)47 MemcachedResponse(protocol_binary_command cmd, uint32_t opaque_, 48 protocol_binary_response_status code) 49 : res(), payload(NULL), bufh(NULL) { 50 res.response.opcode = cmd; 51 res.response.opaque = opaque_; 52 res.response.status = htons(code); 53 } 54 /** 55 * Read from an 'IOR' structure to parse the packet information. This will 56 * always load a full packet. 57 * 58 * @param ior the rope structure to read from 59 * @param[out] required how much total bytes must remain in the buffer for the 60 * parse to complete. 61 * 62 * @return false if more data is needed, true otherwise 63 */ load(rdb_IOROPE * ior,unsigned * required)64 bool load(rdb_IOROPE *ior, unsigned *required) { 65 unsigned total = rdb_get_nused(ior); 66 unsigned wanted = sizeof(res.bytes); 67 68 if (total < wanted) { 69 *required = wanted; 70 return false; 71 } 72 73 rdb_copyread(ior, res.bytes, sizeof(res.bytes)); 74 if (!bodylen()) { 75 rdb_consumed(ior, sizeof(res.bytes)); 76 return true; 77 } 78 79 wanted += bodylen(); 80 if (total < wanted) { 81 *required = wanted; 82 return false; 83 } 84 85 rdb_consumed(ior, sizeof(res.bytes)); 86 payload = rdb_get_consolidated(ior, bodylen()); 87 return true; 88 } 89 90 template <typename T> load(T ctx,unsigned * required)91 bool load(T ctx, unsigned *required) { 92 return load(&ctx->ior, required); 93 } 94 release(rdb_IOROPE * ior)95 void release(rdb_IOROPE *ior) { 96 if (!bodylen()) { 97 return; 98 } 99 rdb_consumed(ior, bodylen()); 100 } 101 102 template <typename T> release(T ctx)103 void release(T ctx) { 104 release(&ctx->ior); 105 } 106 107 /** 108 * Gets the command for the packet 109 */ opcode()110 uint8_t opcode() const { 111 return res.response.opcode; 112 } 113 114 /** 115 * Gets the CAS for the packet 116 */ cas()117 uint64_t cas() const { 118 return lcb_ntohll(res.response.cas); 119 } 120 121 /** 122 * Gets the 'datatype' field for the packet. 123 */ datatype()124 uint8_t datatype() const { 125 return res.response.datatype; 126 } 127 128 #define FRAMING_EXTRAS_TRACING 0x00 129 #if defined(_MSC_VER) 130 #define __lcb_round(d) ((d) > 0.0) ? ((d) + 0.5) : ((d) - 0.5) 131 #else 132 #define __lcb_round(d) round(d) 133 #endif 134 duration()135 uint64_t duration() const { 136 if (ffextlen() == 0) { 137 return 0; 138 } 139 140 const char *end, *ptr; 141 ptr = ffext(); 142 end = ptr + ffextlen(); 143 144 for (; ptr < end;) { 145 uint8_t control = *ptr; 146 uint8_t id = control & 0xF0; 147 uint8_t len = control & 0x0F; 148 ptr++; 149 if (id == FRAMING_EXTRAS_TRACING && len == sizeof(uint16_t)) { 150 uint16_t encoded; 151 memcpy(&encoded, ptr, sizeof(uint16_t)); 152 encoded = ntohs(encoded); 153 return (uint64_t)__lcb_round(pow(encoded, 1.74) / 2); 154 } 155 ptr += len; 156 } 157 return 0; 158 } 159 160 #undef __lcb_round 161 162 /** 163 * Gets a pointer starting at the packet's flexible framing ext field 164 */ ffext()165 const char *ffext() const { 166 return body<const char*>(); 167 } 168 169 /** 170 * Gets a pointer starting at the packet's ext field. 171 */ ext()172 const char *ext() const { 173 return body<const char*>() + ffextlen(); 174 } 175 176 /** 177 * Gets a pointer starting at the packet's key field. Only use if NKEY is 0 178 */ key()179 const char *key() const { 180 return body<const char*>() + extlen() + ffextlen(); 181 } 182 183 /** 184 * Gets a pointer starting at the packet's value field. Only use if NVALUE is 0 185 */ value()186 const char *value() const { 187 return body<const char*>() + keylen() + extlen() + ffextlen(); 188 } 189 190 /** 191 * Gets the size of the packet value. The value is the part of the payload 192 * which is after the key (if applicable) and extras (if applicable). 193 */ vallen()194 uint32_t vallen() const { 195 return bodylen() - (keylen() + extlen() + ffextlen()); 196 } 197 198 199 /** 200 * Gets the status of the packet 201 */ status()202 uint16_t status() const { 203 return ntohs(res.response.status); 204 } 205 206 /** 207 * Gets the payload 208 */ 209 template <typename T> body()210 const T body() const { 211 return reinterpret_cast<const T>(payload); 212 } 213 214 /** 215 * Map a command 'subclass' so that its body field starts at the payload. Note 216 * that the return value is actually an ephemeral pointer starting 24 bytes 217 * _before_ the actual memory block, so only use the non-header part. 218 */ ephemeral_start()219 const char *ephemeral_start() const { 220 return body<const char*>() - 24; 221 } 222 223 /** 224 * Gets the size of the _total_ non-header part of the packet. This data is 225 * also featured inside the payload field itself. 226 */ bodylen()227 uint32_t bodylen() const { 228 return ntohl(res.response.bodylen); 229 } 230 231 /** 232 * Gets the key size, if included in the packet. 233 */ keylen()234 uint16_t keylen() const { 235 if (res.response.magic == PROTOCOL_BINARY_ARES) { 236 return (res.response.keylen >> 8) & 0xff; 237 } else { 238 return ntohs(res.response.keylen); 239 } 240 } 241 242 /** 243 * Gets the length of the 'extras' in the body 244 */ extlen()245 uint8_t extlen() const { 246 return (res.response.extlen); 247 } 248 249 /** 250 * Gets flexible framing extras length 251 */ ffextlen()252 uint8_t ffextlen() const { 253 if (res.response.magic == PROTOCOL_BINARY_ARES) { 254 return res.response.keylen & 0xff; 255 } else { 256 return 0; 257 } 258 } 259 260 /** 261 * Gets the raw unconverted 'opaque' 32 bit field 262 */ opaque()263 uint32_t opaque() const { 264 return (res.response.opaque); 265 } 266 hdrsize()267 size_t hdrsize() const { 268 return sizeof (res.bytes); 269 } 270 hdrbytes()271 uint8_t *hdrbytes() { 272 return res.bytes; 273 } 274 bufseg()275 void *bufseg() const { 276 return bufh; 277 } 278 279 static lcb_error_t parse_enhanced_error(const char * value,lcb_SIZE nvalue,char ** err_ref,char ** err_ctx)280 parse_enhanced_error(const char *value, lcb_SIZE nvalue, char **err_ref, char **err_ctx) 281 { 282 if (value == NULL || nvalue == 0) { 283 return LCB_EINVAL; 284 } 285 Json::Value jval; 286 if (!Json::Reader().parse(value, value + nvalue, jval)) { 287 return LCB_EINVAL; 288 } 289 if (jval.empty()) { 290 return LCB_EINVAL; 291 } 292 Json::Value jerr = jval["error"]; 293 if (jerr.empty()) { 294 return LCB_EINVAL; 295 } 296 std::string emsg; 297 if (!jerr["ref"].empty()) { 298 *err_ref = strdup(jerr["ref"].asString().c_str()); 299 } 300 if (!jerr["context"].empty()) { 301 *err_ctx = strdup(jerr["context"].asString().c_str()); 302 } 303 return LCB_SUCCESS; 304 } 305 306 protected: 307 /** The response header */ 308 protocol_binary_response_header res; 309 /** The payload of the response. This should only be used if there is a body */ 310 void *payload; 311 /** Segment for payload */ 312 void *bufh; 313 314 friend class lcb::Server; 315 }; 316 317 #define PACKET_REQUEST(pkt) \ 318 ( (protocol_binary_request_header *) &(pkt)->res) 319 320 #define PACKET_REQ_VBID(pkt) \ 321 (ntohs(PACKET_REQUEST(pkt)->request.vbucket)) 322 323 class MemcachedRequest { 324 public: 325 /** 326 * Declare the extras, key, and value size for the packet 327 * @param extlen Length of extras 328 * @param keylen Length of key 329 * @param valuelen Length of value (i.e. minus extras and key) 330 */ sizes(uint8_t extlen,uint16_t keylen,uint32_t valuelen)331 void sizes(uint8_t extlen, uint16_t keylen, uint32_t valuelen) { 332 hdr.request.bodylen = htonl(extlen + keylen + valuelen); 333 hdr.request.keylen = htons(keylen); 334 hdr.request.extlen = extlen; 335 } 336 vbucket(uint16_t vb)337 void vbucket(uint16_t vb) { 338 hdr.request.vbucket = htons(vb); 339 } 340 opaque(uint32_t opaque_)341 void opaque(uint32_t opaque_) { 342 hdr.request.opaque = opaque_; 343 } 344 opaque()345 uint32_t opaque() const { 346 return hdr.request.opaque; 347 } 348 opcode()349 uint8_t opcode() const { 350 return hdr.request.opcode; 351 } 352 MemcachedRequest(uint8_t opcode_)353 MemcachedRequest(uint8_t opcode_) { 354 assign(opcode_); 355 } 356 MemcachedRequest(uint8_t opcode_,uint32_t opaque_)357 MemcachedRequest(uint8_t opcode_, uint32_t opaque_) { 358 assign(opcode_); 359 hdr.request.opaque = opaque_; 360 } 361 MemcachedRequest(const void * buf)362 MemcachedRequest(const void *buf) { 363 memcpy(hdr.bytes, buf, sizeof hdr.bytes); 364 } 365 data()366 const void *data() const { return hdr.bytes; } size()367 size_t size() const { return sizeof hdr.bytes; } 368 369 private: 370 protocol_binary_request_header hdr; 371 assign(uint8_t opcode_)372 void assign(uint8_t opcode_) { 373 hdr.request.opcode = opcode_; 374 hdr.request.magic = PROTOCOL_BINARY_REQ; 375 hdr.request.datatype = PROTOCOL_BINARY_RAW_BYTES; 376 hdr.request.cas = 0; 377 hdr.request.vbucket = 0; 378 hdr.request.opaque = 0; 379 hdr.request.bodylen = 0; 380 hdr.request.extlen = 0; 381 hdr.request.keylen = 0; 382 hdr.request.opaque = 0; 383 } 384 }; 385 } 386 typedef lcb::MemcachedResponse packet_info; 387 #endif 388 #endif 389