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