Merge in additional test for increment with namespace key.
[awesomized/libmemcached] / libmemcached / protocol / pedantic.c
1 /* -*- Mode: C; tab-width: 2; c-basic-offset: 2; indent-tabs-mode: nil -*- */
2 #include "libmemcached/protocol/common.h"
3
4 #include <sys/types.h>
5 #include <inttypes.h>
6
7 #define ensure(a) if (!(a)) { return false; }
8
9 bool memcached_binary_protocol_pedantic_check_request(const protocol_binary_request_header *request)
10 {
11 ensure(request->request.magic == PROTOCOL_BINARY_REQ);
12 ensure(request->request.datatype == PROTOCOL_BINARY_RAW_BYTES);
13
14 ensure(request->bytes[6] == 0);
15 ensure(request->bytes[7] == 0);
16
17 uint8_t opcode= request->request.opcode;
18 uint16_t keylen= ntohs(request->request.keylen);
19 uint8_t extlen= request->request.extlen;
20 uint32_t bodylen= ntohl(request->request.bodylen);
21
22 ensure(bodylen >= (keylen + extlen));
23
24 switch (opcode) {
25 case PROTOCOL_BINARY_CMD_GET:
26 case PROTOCOL_BINARY_CMD_GETK:
27 case PROTOCOL_BINARY_CMD_GETKQ:
28 case PROTOCOL_BINARY_CMD_GETQ:
29 ensure(extlen == 0);
30 ensure(keylen > 0);
31 ensure(keylen == bodylen);
32 ensure(request->request.cas == 0);
33 break;
34
35 case PROTOCOL_BINARY_CMD_ADD:
36 case PROTOCOL_BINARY_CMD_ADDQ:
37 /* it makes no sense to run add with a cas value */
38 ensure(request->request.cas == 0);
39 /* FALLTHROUGH */
40 case PROTOCOL_BINARY_CMD_SET:
41 case PROTOCOL_BINARY_CMD_SETQ:
42 case PROTOCOL_BINARY_CMD_REPLACE:
43 case PROTOCOL_BINARY_CMD_REPLACEQ:
44 ensure(keylen > 0);
45 ensure(extlen == 8);
46 break;
47
48 case PROTOCOL_BINARY_CMD_DELETE:
49 case PROTOCOL_BINARY_CMD_DELETEQ:
50 ensure(extlen == 0);
51 ensure(keylen > 0);
52 ensure(keylen == bodylen);
53 break;
54
55 case PROTOCOL_BINARY_CMD_INCREMENT:
56 case PROTOCOL_BINARY_CMD_INCREMENTQ:
57 case PROTOCOL_BINARY_CMD_DECREMENT:
58 case PROTOCOL_BINARY_CMD_DECREMENTQ:
59 ensure(extlen == 20);
60 ensure(keylen > 0);
61 ensure(keylen + extlen == bodylen);
62 break;
63
64 case PROTOCOL_BINARY_CMD_QUIT:
65 case PROTOCOL_BINARY_CMD_QUITQ:
66 case PROTOCOL_BINARY_CMD_NOOP:
67 case PROTOCOL_BINARY_CMD_VERSION:
68 ensure(extlen == 0);
69 ensure(keylen == 0);
70 ensure(bodylen == 0);
71 break;
72
73 case PROTOCOL_BINARY_CMD_FLUSH:
74 case PROTOCOL_BINARY_CMD_FLUSHQ:
75 ensure(extlen == 0 || extlen == 4);
76 ensure(keylen == 0);
77 ensure(bodylen == extlen);
78 break;
79
80 case PROTOCOL_BINARY_CMD_STAT:
81 ensure(extlen == 0);
82 /* May have key, but not value */
83 ensure(keylen == bodylen);
84 break;
85
86 case PROTOCOL_BINARY_CMD_APPEND:
87 case PROTOCOL_BINARY_CMD_APPENDQ:
88 case PROTOCOL_BINARY_CMD_PREPEND:
89 case PROTOCOL_BINARY_CMD_PREPENDQ:
90 ensure(extlen == 0);
91 ensure(keylen > 0);
92 break;
93 default:
94 /* Unknown command */
95 ;
96 }
97
98 return true;
99 }
100
101 bool memcached_binary_protocol_pedantic_check_response(const protocol_binary_request_header *request,
102 const protocol_binary_response_header *response)
103 {
104 ensure(response->response.magic == PROTOCOL_BINARY_RES);
105 ensure(response->response.datatype == PROTOCOL_BINARY_RAW_BYTES);
106 ensure(response->response.opaque == request->request.opaque);
107
108 uint16_t status= ntohs(response->response.status);
109 uint8_t opcode= response->response.opcode;
110
111 if (status == PROTOCOL_BINARY_RESPONSE_SUCCESS)
112 {
113 switch (opcode) {
114 case PROTOCOL_BINARY_CMD_ADDQ:
115 case PROTOCOL_BINARY_CMD_APPENDQ:
116 case PROTOCOL_BINARY_CMD_DECREMENTQ:
117 case PROTOCOL_BINARY_CMD_DELETEQ:
118 case PROTOCOL_BINARY_CMD_FLUSHQ:
119 case PROTOCOL_BINARY_CMD_INCREMENTQ:
120 case PROTOCOL_BINARY_CMD_PREPENDQ:
121 case PROTOCOL_BINARY_CMD_QUITQ:
122 case PROTOCOL_BINARY_CMD_REPLACEQ:
123 case PROTOCOL_BINARY_CMD_SETQ:
124 /* Quiet command shouldn't return on success */
125 return false;
126 default:
127 break;
128 }
129
130 switch (opcode) {
131 case PROTOCOL_BINARY_CMD_ADD:
132 case PROTOCOL_BINARY_CMD_REPLACE:
133 case PROTOCOL_BINARY_CMD_SET:
134 case PROTOCOL_BINARY_CMD_APPEND:
135 case PROTOCOL_BINARY_CMD_PREPEND:
136 ensure(response->response.keylen == 0);
137 ensure(response->response.extlen == 0);
138 ensure(response->response.bodylen == 0);
139 ensure(response->response.cas != 0);
140 break;
141 case PROTOCOL_BINARY_CMD_FLUSH:
142 case PROTOCOL_BINARY_CMD_NOOP:
143 case PROTOCOL_BINARY_CMD_QUIT:
144 case PROTOCOL_BINARY_CMD_DELETE:
145 ensure(response->response.keylen == 0);
146 ensure(response->response.extlen == 0);
147 ensure(response->response.bodylen == 0);
148 ensure(response->response.cas == 0);
149 break;
150
151 case PROTOCOL_BINARY_CMD_DECREMENT:
152 case PROTOCOL_BINARY_CMD_INCREMENT:
153 ensure(response->response.keylen == 0);
154 ensure(response->response.extlen == 0);
155 ensure(ntohl(response->response.bodylen) == 8);
156 ensure(response->response.cas != 0);
157 break;
158
159 case PROTOCOL_BINARY_CMD_STAT:
160 ensure(response->response.extlen == 0);
161 /* key and value exists in all packets except in the terminating */
162 ensure(response->response.cas == 0);
163 break;
164
165 case PROTOCOL_BINARY_CMD_VERSION:
166 ensure(response->response.keylen == 0);
167 ensure(response->response.extlen == 0);
168 ensure(response->response.bodylen != 0);
169 ensure(response->response.cas == 0);
170 break;
171
172 case PROTOCOL_BINARY_CMD_GET:
173 case PROTOCOL_BINARY_CMD_GETQ:
174 ensure(response->response.keylen == 0);
175 ensure(response->response.extlen == 4);
176 ensure(response->response.cas != 0);
177 break;
178
179 case PROTOCOL_BINARY_CMD_GETK:
180 case PROTOCOL_BINARY_CMD_GETKQ:
181 ensure(response->response.keylen != 0);
182 ensure(response->response.extlen == 4);
183 ensure(response->response.cas != 0);
184 break;
185
186 default:
187 /* Undefined command code */
188 break;
189 }
190 }
191 else
192 {
193 ensure(response->response.cas == 0);
194 ensure(response->response.extlen == 0);
195 if (opcode != PROTOCOL_BINARY_CMD_GETK)
196 {
197 ensure(response->response.keylen == 0);
198 }
199 }
200
201 return true;
202 }