nutcracker 0.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.md +22 -0
- data/Rakefile +55 -0
- data/bin/nutcracker +2 -0
- data/ext/nutcracker/ChangeLog +66 -0
- data/ext/nutcracker/LICENSE +177 -0
- data/ext/nutcracker/Makefile.am +7 -0
- data/ext/nutcracker/Makefile.in +726 -0
- data/ext/nutcracker/NOTICE +124 -0
- data/ext/nutcracker/README.md +240 -0
- data/ext/nutcracker/aclocal.m4 +956 -0
- data/ext/nutcracker/conf/nutcracker.leaf.yml +10 -0
- data/ext/nutcracker/conf/nutcracker.root.yml +8 -0
- data/ext/nutcracker/conf/nutcracker.yml +67 -0
- data/ext/nutcracker/config.h.in +316 -0
- data/ext/nutcracker/config/config.guess +1561 -0
- data/ext/nutcracker/config/config.sub +1686 -0
- data/ext/nutcracker/config/depcomp +630 -0
- data/ext/nutcracker/config/install-sh +520 -0
- data/ext/nutcracker/config/ltmain.sh +8413 -0
- data/ext/nutcracker/config/missing +376 -0
- data/ext/nutcracker/configure +18862 -0
- data/ext/nutcracker/configure.ac +155 -0
- data/ext/nutcracker/contrib/Makefile.am +3 -0
- data/ext/nutcracker/contrib/Makefile.in +560 -0
- data/ext/nutcracker/contrib/yaml-0.1.4.tar.gz +0 -0
- data/ext/nutcracker/contrib/yaml-0.1.4/LICENSE +19 -0
- data/ext/nutcracker/contrib/yaml-0.1.4/Makefile.am +20 -0
- data/ext/nutcracker/contrib/yaml-0.1.4/Makefile.in +736 -0
- data/ext/nutcracker/contrib/yaml-0.1.4/README +27 -0
- data/ext/nutcracker/contrib/yaml-0.1.4/aclocal.m4 +956 -0
- data/ext/nutcracker/contrib/yaml-0.1.4/config.h.in +80 -0
- data/ext/nutcracker/contrib/yaml-0.1.4/config/config.guess +1561 -0
- data/ext/nutcracker/contrib/yaml-0.1.4/config/config.sub +1686 -0
- data/ext/nutcracker/contrib/yaml-0.1.4/config/depcomp +630 -0
- data/ext/nutcracker/contrib/yaml-0.1.4/config/install-sh +520 -0
- data/ext/nutcracker/contrib/yaml-0.1.4/config/ltmain.sh +8406 -0
- data/ext/nutcracker/contrib/yaml-0.1.4/config/missing +376 -0
- data/ext/nutcracker/contrib/yaml-0.1.4/configure +13085 -0
- data/ext/nutcracker/contrib/yaml-0.1.4/configure.ac +75 -0
- data/ext/nutcracker/contrib/yaml-0.1.4/doc/doxygen.cfg +222 -0
- data/ext/nutcracker/contrib/yaml-0.1.4/include/yaml.h +1971 -0
- data/ext/nutcracker/contrib/yaml-0.1.4/m4/libtool.m4 +7357 -0
- data/ext/nutcracker/contrib/yaml-0.1.4/m4/ltoptions.m4 +368 -0
- data/ext/nutcracker/contrib/yaml-0.1.4/m4/ltsugar.m4 +123 -0
- data/ext/nutcracker/contrib/yaml-0.1.4/m4/ltversion.m4 +23 -0
- data/ext/nutcracker/contrib/yaml-0.1.4/m4/lt~obsolete.m4 +92 -0
- data/ext/nutcracker/contrib/yaml-0.1.4/src/Makefile.am +4 -0
- data/ext/nutcracker/contrib/yaml-0.1.4/src/Makefile.in +484 -0
- data/ext/nutcracker/contrib/yaml-0.1.4/src/api.c +1392 -0
- data/ext/nutcracker/contrib/yaml-0.1.4/src/dumper.c +394 -0
- data/ext/nutcracker/contrib/yaml-0.1.4/src/emitter.c +2329 -0
- data/ext/nutcracker/contrib/yaml-0.1.4/src/loader.c +432 -0
- data/ext/nutcracker/contrib/yaml-0.1.4/src/parser.c +1374 -0
- data/ext/nutcracker/contrib/yaml-0.1.4/src/reader.c +465 -0
- data/ext/nutcracker/contrib/yaml-0.1.4/src/scanner.c +3570 -0
- data/ext/nutcracker/contrib/yaml-0.1.4/src/writer.c +141 -0
- data/ext/nutcracker/contrib/yaml-0.1.4/src/yaml_private.h +640 -0
- data/ext/nutcracker/contrib/yaml-0.1.4/tests/Makefile.am +8 -0
- data/ext/nutcracker/contrib/yaml-0.1.4/tests/Makefile.in +675 -0
- data/ext/nutcracker/contrib/yaml-0.1.4/tests/example-deconstructor-alt.c +800 -0
- data/ext/nutcracker/contrib/yaml-0.1.4/tests/example-deconstructor.c +1130 -0
- data/ext/nutcracker/contrib/yaml-0.1.4/tests/example-reformatter-alt.c +217 -0
- data/ext/nutcracker/contrib/yaml-0.1.4/tests/example-reformatter.c +202 -0
- data/ext/nutcracker/contrib/yaml-0.1.4/tests/run-dumper.c +311 -0
- data/ext/nutcracker/contrib/yaml-0.1.4/tests/run-emitter.c +327 -0
- data/ext/nutcracker/contrib/yaml-0.1.4/tests/run-loader.c +63 -0
- data/ext/nutcracker/contrib/yaml-0.1.4/tests/run-parser.c +63 -0
- data/ext/nutcracker/contrib/yaml-0.1.4/tests/run-scanner.c +63 -0
- data/ext/nutcracker/contrib/yaml-0.1.4/tests/test-reader.c +354 -0
- data/ext/nutcracker/contrib/yaml-0.1.4/tests/test-version.c +29 -0
- data/ext/nutcracker/extconf.rb +5 -0
- data/ext/nutcracker/m4/libtool.m4 +7376 -0
- data/ext/nutcracker/m4/ltoptions.m4 +368 -0
- data/ext/nutcracker/m4/ltsugar.m4 +123 -0
- data/ext/nutcracker/m4/ltversion.m4 +23 -0
- data/ext/nutcracker/m4/lt~obsolete.m4 +92 -0
- data/ext/nutcracker/notes/c-styleguide.txt +425 -0
- data/ext/nutcracker/notes/debug.txt +96 -0
- data/ext/nutcracker/notes/memcache.txt +123 -0
- data/ext/nutcracker/notes/recommendation.md +118 -0
- data/ext/nutcracker/notes/redis.md +415 -0
- data/ext/nutcracker/notes/socket.txt +131 -0
- data/ext/nutcracker/scripts/multi_get.sh +26 -0
- data/ext/nutcracker/scripts/nutcracker.init +73 -0
- data/ext/nutcracker/scripts/nutcracker.spec +52 -0
- data/ext/nutcracker/scripts/pipelined_read.sh +23 -0
- data/ext/nutcracker/scripts/pipelined_write.sh +29 -0
- data/ext/nutcracker/scripts/populate_memcached.sh +24 -0
- data/ext/nutcracker/scripts/redis-check.py +23 -0
- data/ext/nutcracker/scripts/redis-check.sh +564 -0
- data/ext/nutcracker/src/Makefile.am +46 -0
- data/ext/nutcracker/src/Makefile.in +726 -0
- data/ext/nutcracker/src/hashkit/Makefile.am +22 -0
- data/ext/nutcracker/src/hashkit/Makefile.in +501 -0
- data/ext/nutcracker/src/hashkit/nc_crc32.c +105 -0
- data/ext/nutcracker/src/hashkit/nc_fnv.c +82 -0
- data/ext/nutcracker/src/hashkit/nc_hashkit.h +74 -0
- data/ext/nutcracker/src/hashkit/nc_hsieh.c +93 -0
- data/ext/nutcracker/src/hashkit/nc_jenkins.c +230 -0
- data/ext/nutcracker/src/hashkit/nc_ketama.c +240 -0
- data/ext/nutcracker/src/hashkit/nc_md5.c +379 -0
- data/ext/nutcracker/src/hashkit/nc_modula.c +144 -0
- data/ext/nutcracker/src/hashkit/nc_murmur.c +99 -0
- data/ext/nutcracker/src/hashkit/nc_one_at_a_time.c +51 -0
- data/ext/nutcracker/src/hashkit/nc_random.c +146 -0
- data/ext/nutcracker/src/nc.c +573 -0
- data/ext/nutcracker/src/nc_array.c +204 -0
- data/ext/nutcracker/src/nc_array.h +73 -0
- data/ext/nutcracker/src/nc_client.c +189 -0
- data/ext/nutcracker/src/nc_client.h +28 -0
- data/ext/nutcracker/src/nc_conf.c +1766 -0
- data/ext/nutcracker/src/nc_conf.h +134 -0
- data/ext/nutcracker/src/nc_connection.c +392 -0
- data/ext/nutcracker/src/nc_connection.h +99 -0
- data/ext/nutcracker/src/nc_core.c +334 -0
- data/ext/nutcracker/src/nc_core.h +131 -0
- data/ext/nutcracker/src/nc_event.c +214 -0
- data/ext/nutcracker/src/nc_event.h +39 -0
- data/ext/nutcracker/src/nc_log.c +254 -0
- data/ext/nutcracker/src/nc_log.h +120 -0
- data/ext/nutcracker/src/nc_mbuf.c +285 -0
- data/ext/nutcracker/src/nc_mbuf.h +67 -0
- data/ext/nutcracker/src/nc_message.c +828 -0
- data/ext/nutcracker/src/nc_message.h +253 -0
- data/ext/nutcracker/src/nc_proxy.c +359 -0
- data/ext/nutcracker/src/nc_proxy.h +34 -0
- data/ext/nutcracker/src/nc_queue.h +788 -0
- data/ext/nutcracker/src/nc_rbtree.c +348 -0
- data/ext/nutcracker/src/nc_rbtree.h +47 -0
- data/ext/nutcracker/src/nc_request.c +588 -0
- data/ext/nutcracker/src/nc_response.c +332 -0
- data/ext/nutcracker/src/nc_server.c +841 -0
- data/ext/nutcracker/src/nc_server.h +143 -0
- data/ext/nutcracker/src/nc_signal.c +131 -0
- data/ext/nutcracker/src/nc_signal.h +34 -0
- data/ext/nutcracker/src/nc_stats.c +1188 -0
- data/ext/nutcracker/src/nc_stats.h +206 -0
- data/ext/nutcracker/src/nc_string.c +109 -0
- data/ext/nutcracker/src/nc_string.h +112 -0
- data/ext/nutcracker/src/nc_util.c +619 -0
- data/ext/nutcracker/src/nc_util.h +214 -0
- data/ext/nutcracker/src/proto/Makefile.am +14 -0
- data/ext/nutcracker/src/proto/Makefile.in +482 -0
- data/ext/nutcracker/src/proto/nc_memcache.c +1306 -0
- data/ext/nutcracker/src/proto/nc_proto.h +155 -0
- data/ext/nutcracker/src/proto/nc_redis.c +2102 -0
- data/lib/nutcracker.rb +7 -0
- data/lib/nutcracker/version.rb +3 -0
- metadata +194 -0
|
@@ -0,0 +1,1306 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* twemproxy - A fast and lightweight proxy for memcached protocol.
|
|
3
|
+
* Copyright (C) 2011 Twitter, 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
|
+
#include <ctype.h>
|
|
19
|
+
|
|
20
|
+
#include <nc_core.h>
|
|
21
|
+
#include <nc_proto.h>
|
|
22
|
+
|
|
23
|
+
/*
|
|
24
|
+
* From memcache protocol specification:
|
|
25
|
+
*
|
|
26
|
+
* Data stored by memcached is identified with the help of a key. A key
|
|
27
|
+
* is a text string which should uniquely identify the data for clients
|
|
28
|
+
* that are interested in storing and retrieving it. Currently the
|
|
29
|
+
* length limit of a key is set at 250 characters (of course, normally
|
|
30
|
+
* clients wouldn't need to use such long keys); the key must not include
|
|
31
|
+
* control characters or whitespace.
|
|
32
|
+
*/
|
|
33
|
+
#define MEMCACHE_MAX_KEY_LENGTH 250
|
|
34
|
+
|
|
35
|
+
/*
|
|
36
|
+
* Return true, if the memcache command is a storage command, otherwise
|
|
37
|
+
* return false
|
|
38
|
+
*/
|
|
39
|
+
static bool
|
|
40
|
+
memcache_storage(struct msg *r)
|
|
41
|
+
{
|
|
42
|
+
switch (r->type) {
|
|
43
|
+
case MSG_REQ_MC_SET:
|
|
44
|
+
case MSG_REQ_MC_CAS:
|
|
45
|
+
case MSG_REQ_MC_ADD:
|
|
46
|
+
case MSG_REQ_MC_REPLACE:
|
|
47
|
+
case MSG_REQ_MC_APPEND:
|
|
48
|
+
case MSG_REQ_MC_PREPEND:
|
|
49
|
+
return true;
|
|
50
|
+
|
|
51
|
+
default:
|
|
52
|
+
break;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/*
|
|
59
|
+
* Return true, if the memcache command is a cas command, otherwise
|
|
60
|
+
* return false
|
|
61
|
+
*/
|
|
62
|
+
static bool
|
|
63
|
+
memcache_cas(struct msg *r)
|
|
64
|
+
{
|
|
65
|
+
if (r->type == MSG_REQ_MC_CAS) {
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/*
|
|
73
|
+
* Return true, if the memcache command is a retrieval command, otherwise
|
|
74
|
+
* return false
|
|
75
|
+
*/
|
|
76
|
+
static bool
|
|
77
|
+
memcache_retrieval(struct msg *r)
|
|
78
|
+
{
|
|
79
|
+
switch (r->type) {
|
|
80
|
+
case MSG_REQ_MC_GET:
|
|
81
|
+
case MSG_REQ_MC_GETS:
|
|
82
|
+
return true;
|
|
83
|
+
|
|
84
|
+
default:
|
|
85
|
+
break;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/*
|
|
92
|
+
* Return true, if the memcache command is a arithmetic command, otherwise
|
|
93
|
+
* return false
|
|
94
|
+
*/
|
|
95
|
+
static bool
|
|
96
|
+
memcache_arithmetic(struct msg *r)
|
|
97
|
+
{
|
|
98
|
+
switch (r->type) {
|
|
99
|
+
case MSG_REQ_MC_INCR:
|
|
100
|
+
case MSG_REQ_MC_DECR:
|
|
101
|
+
return true;
|
|
102
|
+
|
|
103
|
+
default:
|
|
104
|
+
break;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/*
|
|
111
|
+
* Return true, if the memcache command is a delete command, otherwise
|
|
112
|
+
* return false
|
|
113
|
+
*/
|
|
114
|
+
static bool
|
|
115
|
+
memcache_delete(struct msg *r)
|
|
116
|
+
{
|
|
117
|
+
if (r->type == MSG_REQ_MC_DELETE) {
|
|
118
|
+
return true;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
void
|
|
125
|
+
memcache_parse_req(struct msg *r)
|
|
126
|
+
{
|
|
127
|
+
struct mbuf *b;
|
|
128
|
+
uint8_t *p, *m;
|
|
129
|
+
uint8_t ch;
|
|
130
|
+
enum {
|
|
131
|
+
SW_START,
|
|
132
|
+
SW_REQ_TYPE,
|
|
133
|
+
SW_SPACES_BEFORE_KEY,
|
|
134
|
+
SW_KEY,
|
|
135
|
+
SW_SPACES_BEFORE_KEYS,
|
|
136
|
+
SW_SPACES_BEFORE_FLAGS,
|
|
137
|
+
SW_FLAGS,
|
|
138
|
+
SW_SPACES_BEFORE_EXPIRY,
|
|
139
|
+
SW_EXPIRY,
|
|
140
|
+
SW_SPACES_BEFORE_VLEN,
|
|
141
|
+
SW_VLEN,
|
|
142
|
+
SW_SPACES_BEFORE_CAS,
|
|
143
|
+
SW_CAS,
|
|
144
|
+
SW_RUNTO_VAL,
|
|
145
|
+
SW_VAL,
|
|
146
|
+
SW_SPACES_BEFORE_NUM,
|
|
147
|
+
SW_NUM,
|
|
148
|
+
SW_RUNTO_CRLF,
|
|
149
|
+
SW_CRLF,
|
|
150
|
+
SW_NOREPLY,
|
|
151
|
+
SW_AFTER_NOREPLY,
|
|
152
|
+
SW_ALMOST_DONE,
|
|
153
|
+
SW_SENTINEL
|
|
154
|
+
} state;
|
|
155
|
+
|
|
156
|
+
state = r->state;
|
|
157
|
+
b = STAILQ_LAST(&r->mhdr, mbuf, next);
|
|
158
|
+
|
|
159
|
+
ASSERT(r->request);
|
|
160
|
+
ASSERT(!r->redis);
|
|
161
|
+
ASSERT(state >= SW_START && state < SW_SENTINEL);
|
|
162
|
+
ASSERT(b != NULL);
|
|
163
|
+
ASSERT(b->pos <= b->last);
|
|
164
|
+
|
|
165
|
+
/* validate the parsing maker */
|
|
166
|
+
ASSERT(r->pos != NULL);
|
|
167
|
+
ASSERT(r->pos >= b->pos && r->pos <= b->last);
|
|
168
|
+
|
|
169
|
+
for (p = r->pos; p < b->last; p++) {
|
|
170
|
+
ch = *p;
|
|
171
|
+
|
|
172
|
+
switch (state) {
|
|
173
|
+
|
|
174
|
+
case SW_START:
|
|
175
|
+
if (ch == ' ') {
|
|
176
|
+
break;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (!islower(ch)) {
|
|
180
|
+
goto error;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/* req_start <- p; type_start <- p */
|
|
184
|
+
r->token = p;
|
|
185
|
+
state = SW_REQ_TYPE;
|
|
186
|
+
|
|
187
|
+
break;
|
|
188
|
+
|
|
189
|
+
case SW_REQ_TYPE:
|
|
190
|
+
if (ch == ' ' || ch == CR) {
|
|
191
|
+
/* type_end = p - 1 */
|
|
192
|
+
m = r->token;
|
|
193
|
+
r->token = NULL;
|
|
194
|
+
r->type = MSG_UNKNOWN;
|
|
195
|
+
|
|
196
|
+
switch (p - m) {
|
|
197
|
+
|
|
198
|
+
case 3:
|
|
199
|
+
if (str4cmp(m, 'g', 'e', 't', ' ')) {
|
|
200
|
+
r->type = MSG_REQ_MC_GET;
|
|
201
|
+
break;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
if (str4cmp(m, 's', 'e', 't', ' ')) {
|
|
205
|
+
r->type = MSG_REQ_MC_SET;
|
|
206
|
+
break;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
if (str4cmp(m, 'a', 'd', 'd', ' ')) {
|
|
210
|
+
r->type = MSG_REQ_MC_ADD;
|
|
211
|
+
break;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
if (str4cmp(m, 'c', 'a', 's', ' ')) {
|
|
215
|
+
r->type = MSG_REQ_MC_CAS;
|
|
216
|
+
break;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
break;
|
|
220
|
+
|
|
221
|
+
case 4:
|
|
222
|
+
if (str4cmp(m, 'g', 'e', 't', 's')) {
|
|
223
|
+
r->type = MSG_REQ_MC_GETS;
|
|
224
|
+
break;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
if (str4cmp(m, 'i', 'n', 'c', 'r')) {
|
|
228
|
+
r->type = MSG_REQ_MC_INCR;
|
|
229
|
+
break;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
if (str4cmp(m, 'd', 'e', 'c', 'r')) {
|
|
233
|
+
r->type = MSG_REQ_MC_DECR;
|
|
234
|
+
break;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
if (str4cmp(m, 'q', 'u', 'i', 't')) {
|
|
238
|
+
r->type = MSG_REQ_MC_QUIT;
|
|
239
|
+
r->quit = 1;
|
|
240
|
+
break;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
break;
|
|
244
|
+
|
|
245
|
+
case 6:
|
|
246
|
+
if (str6cmp(m, 'a', 'p', 'p', 'e', 'n', 'd')) {
|
|
247
|
+
r->type = MSG_REQ_MC_APPEND;
|
|
248
|
+
break;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
if (str6cmp(m, 'd', 'e', 'l', 'e', 't', 'e')) {
|
|
252
|
+
r->type = MSG_REQ_MC_DELETE;
|
|
253
|
+
break;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
break;
|
|
257
|
+
|
|
258
|
+
case 7:
|
|
259
|
+
if (str7cmp(m, 'p', 'r', 'e', 'p', 'e', 'n', 'd')) {
|
|
260
|
+
r->type = MSG_REQ_MC_PREPEND;
|
|
261
|
+
break;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
if (str7cmp(m, 'r', 'e', 'p', 'l', 'a', 'c', 'e')) {
|
|
265
|
+
r->type = MSG_REQ_MC_REPLACE;
|
|
266
|
+
break;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
break;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
switch (r->type) {
|
|
273
|
+
case MSG_REQ_MC_GET:
|
|
274
|
+
case MSG_REQ_MC_GETS:
|
|
275
|
+
case MSG_REQ_MC_DELETE:
|
|
276
|
+
case MSG_REQ_MC_CAS:
|
|
277
|
+
case MSG_REQ_MC_SET:
|
|
278
|
+
case MSG_REQ_MC_ADD:
|
|
279
|
+
case MSG_REQ_MC_REPLACE:
|
|
280
|
+
case MSG_REQ_MC_APPEND:
|
|
281
|
+
case MSG_REQ_MC_PREPEND:
|
|
282
|
+
case MSG_REQ_MC_INCR:
|
|
283
|
+
case MSG_REQ_MC_DECR:
|
|
284
|
+
if (ch == CR) {
|
|
285
|
+
goto error;
|
|
286
|
+
}
|
|
287
|
+
state = SW_SPACES_BEFORE_KEY;
|
|
288
|
+
break;
|
|
289
|
+
|
|
290
|
+
case MSG_REQ_MC_QUIT:
|
|
291
|
+
p = p - 1; /* go back by 1 byte */
|
|
292
|
+
state = SW_CRLF;
|
|
293
|
+
break;
|
|
294
|
+
|
|
295
|
+
case MSG_UNKNOWN:
|
|
296
|
+
goto error;
|
|
297
|
+
|
|
298
|
+
default:
|
|
299
|
+
NOT_REACHED();
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
} else if (!islower(ch)) {
|
|
303
|
+
goto error;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
break;
|
|
307
|
+
|
|
308
|
+
case SW_SPACES_BEFORE_KEY:
|
|
309
|
+
if (ch != ' ') {
|
|
310
|
+
r->token = p;
|
|
311
|
+
r->key_start = p;
|
|
312
|
+
state = SW_KEY;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
break;
|
|
316
|
+
|
|
317
|
+
case SW_KEY:
|
|
318
|
+
if (ch == ' ' || ch == CR) {
|
|
319
|
+
if ((p - r->key_start) > MEMCACHE_MAX_KEY_LENGTH) {
|
|
320
|
+
log_error("parsed bad req %"PRIu64" of type %d with key "
|
|
321
|
+
"prefix '%.*s...' and length %d that exceeds "
|
|
322
|
+
"maximum key length", r->id, r->type, 16,
|
|
323
|
+
r->key_start, p - r->key_start);
|
|
324
|
+
goto error;
|
|
325
|
+
}
|
|
326
|
+
r->key_end = p;
|
|
327
|
+
r->token = NULL;
|
|
328
|
+
|
|
329
|
+
/* get next state */
|
|
330
|
+
if (memcache_storage(r)) {
|
|
331
|
+
state = SW_SPACES_BEFORE_FLAGS;
|
|
332
|
+
} else if (memcache_arithmetic(r)) {
|
|
333
|
+
state = SW_SPACES_BEFORE_NUM;
|
|
334
|
+
} else if (memcache_delete(r)) {
|
|
335
|
+
state = SW_RUNTO_CRLF;
|
|
336
|
+
} else if (memcache_retrieval(r)) {
|
|
337
|
+
state = SW_SPACES_BEFORE_KEYS;
|
|
338
|
+
} else {
|
|
339
|
+
state = SW_RUNTO_CRLF;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
if (ch == CR) {
|
|
343
|
+
if (memcache_storage(r) || memcache_arithmetic(r)) {
|
|
344
|
+
goto error;
|
|
345
|
+
}
|
|
346
|
+
p = p - 1; /* go back by 1 byte */
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
break;
|
|
351
|
+
|
|
352
|
+
case SW_SPACES_BEFORE_KEYS:
|
|
353
|
+
ASSERT(memcache_retrieval(r));
|
|
354
|
+
switch (ch) {
|
|
355
|
+
case ' ':
|
|
356
|
+
break;
|
|
357
|
+
|
|
358
|
+
case CR:
|
|
359
|
+
state = SW_ALMOST_DONE;
|
|
360
|
+
break;
|
|
361
|
+
|
|
362
|
+
default:
|
|
363
|
+
r->token = p;
|
|
364
|
+
goto fragment;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
break;
|
|
368
|
+
|
|
369
|
+
case SW_SPACES_BEFORE_FLAGS:
|
|
370
|
+
if (ch != ' ') {
|
|
371
|
+
if (!isdigit(ch)) {
|
|
372
|
+
goto error;
|
|
373
|
+
}
|
|
374
|
+
/* flags_start <- p; flags <- ch - '0' */
|
|
375
|
+
r->token = p;
|
|
376
|
+
state = SW_FLAGS;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
break;
|
|
380
|
+
|
|
381
|
+
case SW_FLAGS:
|
|
382
|
+
if (isdigit(ch)) {
|
|
383
|
+
/* flags <- flags * 10 + (ch - '0') */
|
|
384
|
+
;
|
|
385
|
+
} else if (ch == ' ') {
|
|
386
|
+
/* flags_end <- p - 1 */
|
|
387
|
+
r->token = NULL;
|
|
388
|
+
state = SW_SPACES_BEFORE_EXPIRY;
|
|
389
|
+
} else {
|
|
390
|
+
goto error;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
break;
|
|
394
|
+
|
|
395
|
+
case SW_SPACES_BEFORE_EXPIRY:
|
|
396
|
+
if (ch != ' ') {
|
|
397
|
+
if (!isdigit(ch)) {
|
|
398
|
+
goto error;
|
|
399
|
+
}
|
|
400
|
+
/* expiry_start <- p; expiry <- ch - '0' */
|
|
401
|
+
r->token = p;
|
|
402
|
+
state = SW_EXPIRY;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
break;
|
|
406
|
+
|
|
407
|
+
case SW_EXPIRY:
|
|
408
|
+
if (isdigit(ch)) {
|
|
409
|
+
/* expiry <- expiry * 10 + (ch - '0') */
|
|
410
|
+
;
|
|
411
|
+
} else if (ch == ' ') {
|
|
412
|
+
/* expiry_end <- p - 1 */
|
|
413
|
+
r->token = NULL;
|
|
414
|
+
state = SW_SPACES_BEFORE_VLEN;
|
|
415
|
+
} else {
|
|
416
|
+
goto error;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
break;
|
|
420
|
+
|
|
421
|
+
case SW_SPACES_BEFORE_VLEN:
|
|
422
|
+
if (ch != ' ') {
|
|
423
|
+
if (!isdigit(ch)) {
|
|
424
|
+
goto error;
|
|
425
|
+
}
|
|
426
|
+
/* vlen_start <- p */
|
|
427
|
+
r->token = p;
|
|
428
|
+
r->vlen = (uint32_t)(ch - '0');
|
|
429
|
+
state = SW_VLEN;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
break;
|
|
433
|
+
|
|
434
|
+
case SW_VLEN:
|
|
435
|
+
if (isdigit(ch)) {
|
|
436
|
+
r->vlen = r->vlen * 10 + (uint32_t)(ch - '0');
|
|
437
|
+
} else if (memcache_cas(r)) {
|
|
438
|
+
if (ch != ' ') {
|
|
439
|
+
goto error;
|
|
440
|
+
}
|
|
441
|
+
/* vlen_end <- p - 1 */
|
|
442
|
+
p = p - 1; /* go back by 1 byte */
|
|
443
|
+
r->token = NULL;
|
|
444
|
+
state = SW_SPACES_BEFORE_CAS;
|
|
445
|
+
} else if (ch == ' ' || ch == CR) {
|
|
446
|
+
/* vlen_end <- p - 1 */
|
|
447
|
+
p = p - 1; /* go back by 1 byte */
|
|
448
|
+
r->token = NULL;
|
|
449
|
+
state = SW_RUNTO_CRLF;
|
|
450
|
+
} else {
|
|
451
|
+
goto error;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
break;
|
|
455
|
+
|
|
456
|
+
case SW_SPACES_BEFORE_CAS:
|
|
457
|
+
if (ch != ' ') {
|
|
458
|
+
if (!isdigit(ch)) {
|
|
459
|
+
goto error;
|
|
460
|
+
}
|
|
461
|
+
/* cas_start <- p; cas <- ch - '0' */
|
|
462
|
+
r->token = p;
|
|
463
|
+
state = SW_CAS;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
break;
|
|
467
|
+
|
|
468
|
+
case SW_CAS:
|
|
469
|
+
if (isdigit(ch)) {
|
|
470
|
+
/* cas <- cas * 10 + (ch - '0') */
|
|
471
|
+
;
|
|
472
|
+
} else if (ch == ' ' || ch == CR) {
|
|
473
|
+
/* cas_end <- p - 1 */
|
|
474
|
+
p = p - 1; /* go back by 1 byte */
|
|
475
|
+
r->token = NULL;
|
|
476
|
+
state = SW_RUNTO_CRLF;
|
|
477
|
+
} else {
|
|
478
|
+
goto error;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
break;
|
|
482
|
+
|
|
483
|
+
|
|
484
|
+
case SW_RUNTO_VAL:
|
|
485
|
+
switch (ch) {
|
|
486
|
+
case LF:
|
|
487
|
+
/* val_start <- p + 1 */
|
|
488
|
+
state = SW_VAL;
|
|
489
|
+
break;
|
|
490
|
+
|
|
491
|
+
default:
|
|
492
|
+
goto error;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
break;
|
|
496
|
+
|
|
497
|
+
case SW_VAL:
|
|
498
|
+
m = p + r->vlen;
|
|
499
|
+
if (m >= b->last) {
|
|
500
|
+
ASSERT(r->vlen >= (uint32_t)(b->last - p));
|
|
501
|
+
r->vlen -= (uint32_t)(b->last - p);
|
|
502
|
+
m = b->last - 1;
|
|
503
|
+
p = m; /* move forward by vlen bytes */
|
|
504
|
+
break;
|
|
505
|
+
}
|
|
506
|
+
switch (*m) {
|
|
507
|
+
case CR:
|
|
508
|
+
/* val_end <- p - 1 */
|
|
509
|
+
p = m; /* move forward by vlen bytes */
|
|
510
|
+
state = SW_ALMOST_DONE;
|
|
511
|
+
break;
|
|
512
|
+
|
|
513
|
+
default:
|
|
514
|
+
goto error;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
break;
|
|
518
|
+
|
|
519
|
+
case SW_SPACES_BEFORE_NUM:
|
|
520
|
+
if (ch != ' ') {
|
|
521
|
+
if (!isdigit(ch)) {
|
|
522
|
+
goto error;
|
|
523
|
+
}
|
|
524
|
+
/* num_start <- p; num <- ch - '0' */
|
|
525
|
+
r->token = p;
|
|
526
|
+
state = SW_NUM;
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
break;
|
|
530
|
+
|
|
531
|
+
case SW_NUM:
|
|
532
|
+
if (isdigit(ch)) {
|
|
533
|
+
/* num <- num * 10 + (ch - '0') */
|
|
534
|
+
;
|
|
535
|
+
} else if (ch == ' ' || ch == CR) {
|
|
536
|
+
r->token = NULL;
|
|
537
|
+
/* num_end <- p - 1 */
|
|
538
|
+
p = p - 1; /* go back by 1 byte */
|
|
539
|
+
state = SW_RUNTO_CRLF;
|
|
540
|
+
} else {
|
|
541
|
+
goto error;
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
break;
|
|
545
|
+
|
|
546
|
+
case SW_RUNTO_CRLF:
|
|
547
|
+
switch (ch) {
|
|
548
|
+
case ' ':
|
|
549
|
+
break;
|
|
550
|
+
|
|
551
|
+
case 'n':
|
|
552
|
+
if (memcache_storage(r) || memcache_arithmetic(r) || memcache_delete(r)) {
|
|
553
|
+
/* noreply_start <- p */
|
|
554
|
+
r->token = p;
|
|
555
|
+
state = SW_NOREPLY;
|
|
556
|
+
} else {
|
|
557
|
+
goto error;
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
break;
|
|
561
|
+
|
|
562
|
+
case CR:
|
|
563
|
+
if (memcache_storage(r)) {
|
|
564
|
+
state = SW_RUNTO_VAL;
|
|
565
|
+
} else {
|
|
566
|
+
state = SW_ALMOST_DONE;
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
break;
|
|
570
|
+
|
|
571
|
+
default:
|
|
572
|
+
goto error;
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
break;
|
|
576
|
+
|
|
577
|
+
case SW_NOREPLY:
|
|
578
|
+
switch (ch) {
|
|
579
|
+
case ' ':
|
|
580
|
+
case CR:
|
|
581
|
+
m = r->token;
|
|
582
|
+
if (((p - m) == 7) && str7cmp(m, 'n', 'o', 'r', 'e', 'p', 'l', 'y')) {
|
|
583
|
+
ASSERT(memcache_storage(r) || memcache_arithmetic(r) || memcache_delete(r));
|
|
584
|
+
r->token = NULL;
|
|
585
|
+
/* noreply_end <- p - 1 */
|
|
586
|
+
r->noreply = 1;
|
|
587
|
+
state = SW_AFTER_NOREPLY;
|
|
588
|
+
p = p - 1; /* go back by 1 byte */
|
|
589
|
+
} else {
|
|
590
|
+
goto error;
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
break;
|
|
595
|
+
|
|
596
|
+
case SW_AFTER_NOREPLY:
|
|
597
|
+
switch (ch) {
|
|
598
|
+
case ' ':
|
|
599
|
+
break;
|
|
600
|
+
|
|
601
|
+
case CR:
|
|
602
|
+
if (memcache_storage(r)) {
|
|
603
|
+
state = SW_RUNTO_VAL;
|
|
604
|
+
} else {
|
|
605
|
+
state = SW_ALMOST_DONE;
|
|
606
|
+
}
|
|
607
|
+
break;
|
|
608
|
+
|
|
609
|
+
default:
|
|
610
|
+
goto error;
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
break;
|
|
614
|
+
|
|
615
|
+
case SW_CRLF:
|
|
616
|
+
switch (ch) {
|
|
617
|
+
case ' ':
|
|
618
|
+
break;
|
|
619
|
+
|
|
620
|
+
case CR:
|
|
621
|
+
state = SW_ALMOST_DONE;
|
|
622
|
+
break;
|
|
623
|
+
|
|
624
|
+
default:
|
|
625
|
+
goto error;
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
break;
|
|
629
|
+
|
|
630
|
+
case SW_ALMOST_DONE:
|
|
631
|
+
switch (ch) {
|
|
632
|
+
case LF:
|
|
633
|
+
/* req_end <- p */
|
|
634
|
+
goto done;
|
|
635
|
+
|
|
636
|
+
default:
|
|
637
|
+
goto error;
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
break;
|
|
641
|
+
|
|
642
|
+
case SW_SENTINEL:
|
|
643
|
+
default:
|
|
644
|
+
NOT_REACHED();
|
|
645
|
+
break;
|
|
646
|
+
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
/*
|
|
651
|
+
* At this point, buffer from b->pos to b->last has been parsed completely
|
|
652
|
+
* but we haven't been able to reach to any conclusion. Normally, this
|
|
653
|
+
* means that we have to parse again starting from the state we are in
|
|
654
|
+
* after more data has been read. The newly read data is either read into
|
|
655
|
+
* a new mbuf, if existing mbuf is full (b->last == b->end) or into the
|
|
656
|
+
* existing mbuf.
|
|
657
|
+
*
|
|
658
|
+
* The only exception to this is when the existing mbuf is full (b->last
|
|
659
|
+
* is at b->end) and token marker is set, which means that we have to
|
|
660
|
+
* copy the partial token into a new mbuf and parse again with more data
|
|
661
|
+
* read into new mbuf.
|
|
662
|
+
*/
|
|
663
|
+
ASSERT(p == b->last);
|
|
664
|
+
r->pos = p;
|
|
665
|
+
r->state = state;
|
|
666
|
+
|
|
667
|
+
if (b->last == b->end && r->token != NULL) {
|
|
668
|
+
r->pos = r->token;
|
|
669
|
+
r->token = NULL;
|
|
670
|
+
r->result = MSG_PARSE_REPAIR;
|
|
671
|
+
} else {
|
|
672
|
+
r->result = MSG_PARSE_AGAIN;
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
log_hexdump(LOG_VERB, b->pos, mbuf_length(b), "parsed req %"PRIu64" res %d "
|
|
676
|
+
"type %d state %d rpos %d of %d", r->id, r->result, r->type,
|
|
677
|
+
r->state, r->pos - b->pos, b->last - b->pos);
|
|
678
|
+
return;
|
|
679
|
+
|
|
680
|
+
fragment:
|
|
681
|
+
ASSERT(p != b->last);
|
|
682
|
+
ASSERT(r->token != NULL);
|
|
683
|
+
r->pos = r->token;
|
|
684
|
+
r->token = NULL;
|
|
685
|
+
r->state = state;
|
|
686
|
+
r->result = MSG_PARSE_FRAGMENT;
|
|
687
|
+
|
|
688
|
+
log_hexdump(LOG_VERB, b->pos, mbuf_length(b), "parsed req %"PRIu64" res %d "
|
|
689
|
+
"type %d state %d rpos %d of %d", r->id, r->result, r->type,
|
|
690
|
+
r->state, r->pos - b->pos, b->last - b->pos);
|
|
691
|
+
return;
|
|
692
|
+
|
|
693
|
+
done:
|
|
694
|
+
ASSERT(r->type > MSG_UNKNOWN && r->type < MSG_SENTINEL);
|
|
695
|
+
r->pos = p + 1;
|
|
696
|
+
ASSERT(r->pos <= b->last);
|
|
697
|
+
r->state = SW_START;
|
|
698
|
+
r->result = MSG_PARSE_OK;
|
|
699
|
+
|
|
700
|
+
log_hexdump(LOG_VERB, b->pos, mbuf_length(b), "parsed req %"PRIu64" res %d "
|
|
701
|
+
"type %d state %d rpos %d of %d", r->id, r->result, r->type,
|
|
702
|
+
r->state, r->pos - b->pos, b->last - b->pos);
|
|
703
|
+
return;
|
|
704
|
+
|
|
705
|
+
error:
|
|
706
|
+
r->result = MSG_PARSE_ERROR;
|
|
707
|
+
r->state = state;
|
|
708
|
+
errno = EINVAL;
|
|
709
|
+
|
|
710
|
+
log_hexdump(LOG_INFO, b->pos, mbuf_length(b), "parsed bad req %"PRIu64" "
|
|
711
|
+
"res %d type %d state %d", r->id, r->result, r->type,
|
|
712
|
+
r->state);
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
void
|
|
716
|
+
memcache_parse_rsp(struct msg *r)
|
|
717
|
+
{
|
|
718
|
+
struct mbuf *b;
|
|
719
|
+
uint8_t *p, *m;
|
|
720
|
+
uint8_t ch;
|
|
721
|
+
enum {
|
|
722
|
+
SW_START,
|
|
723
|
+
SW_RSP_NUM,
|
|
724
|
+
SW_RSP_STR,
|
|
725
|
+
SW_SPACES_BEFORE_KEY,
|
|
726
|
+
SW_KEY,
|
|
727
|
+
SW_SPACES_BEFORE_FLAGS,
|
|
728
|
+
SW_FLAGS,
|
|
729
|
+
SW_SPACES_BEFORE_VLEN,
|
|
730
|
+
SW_VLEN,
|
|
731
|
+
SW_RUNTO_VAL,
|
|
732
|
+
SW_VAL,
|
|
733
|
+
SW_VAL_LF,
|
|
734
|
+
SW_END,
|
|
735
|
+
SW_RUNTO_CRLF,
|
|
736
|
+
SW_CRLF,
|
|
737
|
+
SW_ALMOST_DONE,
|
|
738
|
+
SW_SENTINEL
|
|
739
|
+
} state;
|
|
740
|
+
|
|
741
|
+
state = r->state;
|
|
742
|
+
b = STAILQ_LAST(&r->mhdr, mbuf, next);
|
|
743
|
+
|
|
744
|
+
ASSERT(!r->request);
|
|
745
|
+
ASSERT(!r->redis);
|
|
746
|
+
ASSERT(state >= SW_START && state < SW_SENTINEL);
|
|
747
|
+
ASSERT(b != NULL);
|
|
748
|
+
ASSERT(b->pos <= b->last);
|
|
749
|
+
|
|
750
|
+
/* validate the parsing marker */
|
|
751
|
+
ASSERT(r->pos != NULL);
|
|
752
|
+
ASSERT(r->pos >= b->pos && r->pos <= b->last);
|
|
753
|
+
|
|
754
|
+
for (p = r->pos; p < b->last; p++) {
|
|
755
|
+
ch = *p;
|
|
756
|
+
|
|
757
|
+
switch (state) {
|
|
758
|
+
case SW_START:
|
|
759
|
+
if (isdigit(ch)) {
|
|
760
|
+
state = SW_RSP_NUM;
|
|
761
|
+
} else {
|
|
762
|
+
state = SW_RSP_STR;
|
|
763
|
+
}
|
|
764
|
+
p = p - 1; /* go back by 1 byte */
|
|
765
|
+
|
|
766
|
+
break;
|
|
767
|
+
|
|
768
|
+
case SW_RSP_NUM:
|
|
769
|
+
if (r->token == NULL) {
|
|
770
|
+
/* rsp_start <- p; type_start <- p */
|
|
771
|
+
r->token = p;
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
if (isdigit(ch)) {
|
|
775
|
+
/* num <- num * 10 + (ch - '0') */
|
|
776
|
+
;
|
|
777
|
+
} else if (ch == ' ' || ch == CR) {
|
|
778
|
+
/* type_end <- p - 1 */
|
|
779
|
+
r->token = NULL;
|
|
780
|
+
r->type = MSG_RSP_MC_NUM;
|
|
781
|
+
p = p - 1; /* go back by 1 byte */
|
|
782
|
+
state = SW_CRLF;
|
|
783
|
+
} else {
|
|
784
|
+
goto error;
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
break;
|
|
788
|
+
|
|
789
|
+
case SW_RSP_STR:
|
|
790
|
+
if (r->token == NULL) {
|
|
791
|
+
/* rsp_start <- p; type_start <- p */
|
|
792
|
+
r->token = p;
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
if (ch == ' ' || ch == CR) {
|
|
796
|
+
/* type_end <- p - 1 */
|
|
797
|
+
m = r->token;
|
|
798
|
+
r->token = NULL;
|
|
799
|
+
r->type = MSG_UNKNOWN;
|
|
800
|
+
|
|
801
|
+
switch (p - m) {
|
|
802
|
+
case 3:
|
|
803
|
+
if (str4cmp(m, 'E', 'N', 'D', '\r')) {
|
|
804
|
+
r->type = MSG_RSP_MC_END;
|
|
805
|
+
/* end_start <- m; end_end <- p - 1*/
|
|
806
|
+
r->end = m;
|
|
807
|
+
break;
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
break;
|
|
811
|
+
|
|
812
|
+
case 5:
|
|
813
|
+
if (str5cmp(m, 'V', 'A', 'L', 'U', 'E')) {
|
|
814
|
+
/*
|
|
815
|
+
* Encompasses responses for 'get', 'gets' and
|
|
816
|
+
* 'cas' command.
|
|
817
|
+
*/
|
|
818
|
+
r->type = MSG_RSP_MC_VALUE;
|
|
819
|
+
break;
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
if (str5cmp(m, 'E', 'R', 'R', 'O', 'R')) {
|
|
823
|
+
r->type = MSG_RSP_MC_ERROR;
|
|
824
|
+
break;
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
break;
|
|
828
|
+
|
|
829
|
+
case 6:
|
|
830
|
+
if (str6cmp(m, 'S', 'T', 'O', 'R', 'E', 'D')) {
|
|
831
|
+
r->type = MSG_RSP_MC_STORED;
|
|
832
|
+
break;
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
if (str6cmp(m, 'E', 'X', 'I', 'S', 'T', 'S')) {
|
|
836
|
+
r->type = MSG_RSP_MC_EXISTS;
|
|
837
|
+
break;
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
break;
|
|
841
|
+
|
|
842
|
+
case 7:
|
|
843
|
+
if (str7cmp(m, 'D', 'E', 'L', 'E', 'T', 'E', 'D')) {
|
|
844
|
+
r->type = MSG_RSP_MC_DELETED;
|
|
845
|
+
break;
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
break;
|
|
849
|
+
|
|
850
|
+
case 9:
|
|
851
|
+
if (str9cmp(m, 'N', 'O', 'T', '_', 'F', 'O', 'U', 'N', 'D')) {
|
|
852
|
+
r->type = MSG_RSP_MC_NOT_FOUND;
|
|
853
|
+
break;
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
break;
|
|
857
|
+
|
|
858
|
+
case 10:
|
|
859
|
+
if (str10cmp(m, 'N', 'O', 'T', '_', 'S', 'T', 'O', 'R', 'E', 'D')) {
|
|
860
|
+
r->type = MSG_RSP_MC_NOT_STORED;
|
|
861
|
+
break;
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
break;
|
|
865
|
+
|
|
866
|
+
case 12:
|
|
867
|
+
if (str12cmp(m, 'C', 'L', 'I', 'E', 'N', 'T', '_', 'E', 'R', 'R', 'O', 'R')) {
|
|
868
|
+
r->type = MSG_RSP_MC_CLIENT_ERROR;
|
|
869
|
+
break;
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
if (str12cmp(m, 'S', 'E', 'R', 'V', 'E', 'R', '_', 'E', 'R', 'R', 'O', 'R')) {
|
|
873
|
+
r->type = MSG_RSP_MC_SERVER_ERROR;
|
|
874
|
+
break;
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
break;
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
switch (r->type) {
|
|
881
|
+
case MSG_UNKNOWN:
|
|
882
|
+
goto error;
|
|
883
|
+
|
|
884
|
+
case MSG_RSP_MC_STORED:
|
|
885
|
+
case MSG_RSP_MC_NOT_STORED:
|
|
886
|
+
case MSG_RSP_MC_EXISTS:
|
|
887
|
+
case MSG_RSP_MC_NOT_FOUND:
|
|
888
|
+
case MSG_RSP_MC_DELETED:
|
|
889
|
+
state = SW_CRLF;
|
|
890
|
+
break;
|
|
891
|
+
|
|
892
|
+
case MSG_RSP_MC_END:
|
|
893
|
+
state = SW_CRLF;
|
|
894
|
+
break;
|
|
895
|
+
|
|
896
|
+
case MSG_RSP_MC_VALUE:
|
|
897
|
+
state = SW_SPACES_BEFORE_KEY;
|
|
898
|
+
break;
|
|
899
|
+
|
|
900
|
+
case MSG_RSP_MC_ERROR:
|
|
901
|
+
state = SW_CRLF;
|
|
902
|
+
break;
|
|
903
|
+
|
|
904
|
+
case MSG_RSP_MC_CLIENT_ERROR:
|
|
905
|
+
case MSG_RSP_MC_SERVER_ERROR:
|
|
906
|
+
state = SW_RUNTO_CRLF;
|
|
907
|
+
break;
|
|
908
|
+
|
|
909
|
+
default:
|
|
910
|
+
NOT_REACHED();
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
p = p - 1; /* go back by 1 byte */
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
break;
|
|
917
|
+
|
|
918
|
+
case SW_SPACES_BEFORE_KEY:
|
|
919
|
+
if (ch != ' ') {
|
|
920
|
+
state = SW_KEY;
|
|
921
|
+
p = p - 1; /* go back by 1 byte */
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
break;
|
|
925
|
+
|
|
926
|
+
case SW_KEY:
|
|
927
|
+
if (r->token == NULL) {
|
|
928
|
+
r->token = p;
|
|
929
|
+
r->key_start = p;
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
if (ch == ' ') {
|
|
933
|
+
if ((p - r->key_start) > MEMCACHE_MAX_KEY_LENGTH) {
|
|
934
|
+
log_error("parsed bad req %"PRIu64" of type %d with key "
|
|
935
|
+
"prefix '%.*s...' and length %d that exceeds "
|
|
936
|
+
"maximum key length", r->id, r->type, 16,
|
|
937
|
+
r->key_start, p - r->key_start);
|
|
938
|
+
goto error;
|
|
939
|
+
}
|
|
940
|
+
r->key_end = p;
|
|
941
|
+
r->token = NULL;
|
|
942
|
+
state = SW_SPACES_BEFORE_FLAGS;
|
|
943
|
+
}
|
|
944
|
+
|
|
945
|
+
break;
|
|
946
|
+
|
|
947
|
+
case SW_SPACES_BEFORE_FLAGS:
|
|
948
|
+
if (ch != ' ') {
|
|
949
|
+
if (!isdigit(ch)) {
|
|
950
|
+
goto error;
|
|
951
|
+
}
|
|
952
|
+
state = SW_FLAGS;
|
|
953
|
+
p = p - 1; /* go back by 1 byte */
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
break;
|
|
957
|
+
|
|
958
|
+
case SW_FLAGS:
|
|
959
|
+
if (r->token == NULL) {
|
|
960
|
+
/* flags_start <- p */
|
|
961
|
+
r->token = p;
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
if (isdigit(ch)) {
|
|
965
|
+
/* flags <- flags * 10 + (ch - '0') */
|
|
966
|
+
;
|
|
967
|
+
} else if (ch == ' ') {
|
|
968
|
+
/* flags_end <- p - 1 */
|
|
969
|
+
r->token = NULL;
|
|
970
|
+
state = SW_SPACES_BEFORE_VLEN;
|
|
971
|
+
} else {
|
|
972
|
+
goto error;
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
break;
|
|
976
|
+
|
|
977
|
+
case SW_SPACES_BEFORE_VLEN:
|
|
978
|
+
if (ch != ' ') {
|
|
979
|
+
if (!isdigit(ch)) {
|
|
980
|
+
goto error;
|
|
981
|
+
}
|
|
982
|
+
p = p - 1; /* go back by 1 byte */
|
|
983
|
+
state = SW_VLEN;
|
|
984
|
+
}
|
|
985
|
+
|
|
986
|
+
break;
|
|
987
|
+
|
|
988
|
+
case SW_VLEN:
|
|
989
|
+
if (r->token == NULL) {
|
|
990
|
+
/* vlen_start <- p */
|
|
991
|
+
r->token = p;
|
|
992
|
+
r->vlen = (uint32_t)(ch - '0');
|
|
993
|
+
} else if (isdigit(ch)) {
|
|
994
|
+
r->vlen = r->vlen * 10 + (uint32_t)(ch - '0');
|
|
995
|
+
} else if (ch == ' ' || ch == CR) {
|
|
996
|
+
/* vlen_end <- p - 1 */
|
|
997
|
+
p = p - 1; /* go back by 1 byte */
|
|
998
|
+
r->token = NULL;
|
|
999
|
+
state = SW_RUNTO_CRLF;
|
|
1000
|
+
} else {
|
|
1001
|
+
goto error;
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
break;
|
|
1005
|
+
|
|
1006
|
+
case SW_RUNTO_VAL:
|
|
1007
|
+
switch (ch) {
|
|
1008
|
+
case LF:
|
|
1009
|
+
/* val_start <- p + 1 */
|
|
1010
|
+
state = SW_VAL;
|
|
1011
|
+
break;
|
|
1012
|
+
|
|
1013
|
+
default:
|
|
1014
|
+
goto error;
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
break;
|
|
1018
|
+
|
|
1019
|
+
case SW_VAL:
|
|
1020
|
+
m = p + r->vlen;
|
|
1021
|
+
if (m >= b->last) {
|
|
1022
|
+
ASSERT(r->vlen >= (uint32_t)(b->last - p));
|
|
1023
|
+
r->vlen -= (uint32_t)(b->last - p);
|
|
1024
|
+
m = b->last - 1;
|
|
1025
|
+
p = m; /* move forward by vlen bytes */
|
|
1026
|
+
break;
|
|
1027
|
+
}
|
|
1028
|
+
switch (*m) {
|
|
1029
|
+
case CR:
|
|
1030
|
+
/* val_end <- p - 1 */
|
|
1031
|
+
p = m; /* move forward by vlen bytes */
|
|
1032
|
+
state = SW_VAL_LF;
|
|
1033
|
+
break;
|
|
1034
|
+
|
|
1035
|
+
default:
|
|
1036
|
+
goto error;
|
|
1037
|
+
}
|
|
1038
|
+
|
|
1039
|
+
break;
|
|
1040
|
+
|
|
1041
|
+
case SW_VAL_LF:
|
|
1042
|
+
switch (ch) {
|
|
1043
|
+
case LF:
|
|
1044
|
+
state = SW_END;
|
|
1045
|
+
break;
|
|
1046
|
+
|
|
1047
|
+
default:
|
|
1048
|
+
goto error;
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1051
|
+
break;
|
|
1052
|
+
|
|
1053
|
+
case SW_END:
|
|
1054
|
+
if (r->token == NULL) {
|
|
1055
|
+
if (ch != 'E') {
|
|
1056
|
+
goto error;
|
|
1057
|
+
}
|
|
1058
|
+
/* end_start <- p */
|
|
1059
|
+
r->token = p;
|
|
1060
|
+
} else if (ch == CR) {
|
|
1061
|
+
/* end_end <- p */
|
|
1062
|
+
m = r->token;
|
|
1063
|
+
r->token = NULL;
|
|
1064
|
+
|
|
1065
|
+
switch (p - m) {
|
|
1066
|
+
case 3:
|
|
1067
|
+
if (str4cmp(m, 'E', 'N', 'D', '\r')) {
|
|
1068
|
+
r->end = m;
|
|
1069
|
+
state = SW_ALMOST_DONE;
|
|
1070
|
+
}
|
|
1071
|
+
break;
|
|
1072
|
+
|
|
1073
|
+
default:
|
|
1074
|
+
goto error;
|
|
1075
|
+
}
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1078
|
+
break;
|
|
1079
|
+
|
|
1080
|
+
case SW_RUNTO_CRLF:
|
|
1081
|
+
switch (ch) {
|
|
1082
|
+
case CR:
|
|
1083
|
+
if (r->type == MSG_RSP_MC_VALUE) {
|
|
1084
|
+
state = SW_RUNTO_VAL;
|
|
1085
|
+
} else {
|
|
1086
|
+
state = SW_ALMOST_DONE;
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1089
|
+
break;
|
|
1090
|
+
|
|
1091
|
+
default:
|
|
1092
|
+
break;
|
|
1093
|
+
}
|
|
1094
|
+
|
|
1095
|
+
break;
|
|
1096
|
+
|
|
1097
|
+
case SW_CRLF:
|
|
1098
|
+
switch (ch) {
|
|
1099
|
+
case ' ':
|
|
1100
|
+
break;
|
|
1101
|
+
|
|
1102
|
+
case CR:
|
|
1103
|
+
state = SW_ALMOST_DONE;
|
|
1104
|
+
break;
|
|
1105
|
+
|
|
1106
|
+
default:
|
|
1107
|
+
goto error;
|
|
1108
|
+
}
|
|
1109
|
+
|
|
1110
|
+
break;
|
|
1111
|
+
|
|
1112
|
+
case SW_ALMOST_DONE:
|
|
1113
|
+
switch (ch) {
|
|
1114
|
+
case LF:
|
|
1115
|
+
/* rsp_end <- p */
|
|
1116
|
+
goto done;
|
|
1117
|
+
|
|
1118
|
+
default:
|
|
1119
|
+
goto error;
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1122
|
+
break;
|
|
1123
|
+
|
|
1124
|
+
case SW_SENTINEL:
|
|
1125
|
+
default:
|
|
1126
|
+
NOT_REACHED();
|
|
1127
|
+
break;
|
|
1128
|
+
|
|
1129
|
+
}
|
|
1130
|
+
}
|
|
1131
|
+
|
|
1132
|
+
ASSERT(p == b->last);
|
|
1133
|
+
r->pos = p;
|
|
1134
|
+
r->state = state;
|
|
1135
|
+
|
|
1136
|
+
if (b->last == b->end && r->token != NULL) {
|
|
1137
|
+
r->pos = r->token;
|
|
1138
|
+
r->token = NULL;
|
|
1139
|
+
r->result = MSG_PARSE_REPAIR;
|
|
1140
|
+
} else {
|
|
1141
|
+
r->result = MSG_PARSE_AGAIN;
|
|
1142
|
+
}
|
|
1143
|
+
|
|
1144
|
+
log_hexdump(LOG_VERB, b->pos, mbuf_length(b), "parsed rsp %"PRIu64" res %d "
|
|
1145
|
+
"type %d state %d rpos %d of %d", r->id, r->result, r->type,
|
|
1146
|
+
r->state, r->pos - b->pos, b->last - b->pos);
|
|
1147
|
+
return;
|
|
1148
|
+
|
|
1149
|
+
done:
|
|
1150
|
+
ASSERT(r->type > MSG_UNKNOWN && r->type < MSG_SENTINEL);
|
|
1151
|
+
r->pos = p + 1;
|
|
1152
|
+
ASSERT(r->pos <= b->last);
|
|
1153
|
+
r->state = SW_START;
|
|
1154
|
+
r->token = NULL;
|
|
1155
|
+
r->result = MSG_PARSE_OK;
|
|
1156
|
+
|
|
1157
|
+
log_hexdump(LOG_VERB, b->pos, mbuf_length(b), "parsed rsp %"PRIu64" res %d "
|
|
1158
|
+
"type %d state %d rpos %d of %d", r->id, r->result, r->type,
|
|
1159
|
+
r->state, r->pos - b->pos, b->last - b->pos);
|
|
1160
|
+
return;
|
|
1161
|
+
|
|
1162
|
+
error:
|
|
1163
|
+
r->result = MSG_PARSE_ERROR;
|
|
1164
|
+
r->state = state;
|
|
1165
|
+
errno = EINVAL;
|
|
1166
|
+
|
|
1167
|
+
log_hexdump(LOG_INFO, b->pos, mbuf_length(b), "parsed bad rsp %"PRIu64" "
|
|
1168
|
+
"res %d type %d state %d", r->id, r->result, r->type,
|
|
1169
|
+
r->state);
|
|
1170
|
+
}
|
|
1171
|
+
|
|
1172
|
+
/*
|
|
1173
|
+
* Pre-split copy handler invoked when the request is a multi vector -
|
|
1174
|
+
* 'get' or 'gets' request and is about to be split into two requests
|
|
1175
|
+
*/
|
|
1176
|
+
void
|
|
1177
|
+
memcache_pre_splitcopy(struct mbuf *mbuf, void *arg)
|
|
1178
|
+
{
|
|
1179
|
+
struct msg *r = arg; /* request vector */
|
|
1180
|
+
struct string get = string("get "); /* 'get ' string */
|
|
1181
|
+
struct string gets = string("gets "); /* 'gets ' string */
|
|
1182
|
+
|
|
1183
|
+
ASSERT(r->request);
|
|
1184
|
+
ASSERT(!r->redis);
|
|
1185
|
+
ASSERT(mbuf_empty(mbuf));
|
|
1186
|
+
|
|
1187
|
+
switch (r->type) {
|
|
1188
|
+
case MSG_REQ_MC_GET:
|
|
1189
|
+
mbuf_copy(mbuf, get.data, get.len);
|
|
1190
|
+
break;
|
|
1191
|
+
|
|
1192
|
+
case MSG_REQ_MC_GETS:
|
|
1193
|
+
mbuf_copy(mbuf, gets.data, gets.len);
|
|
1194
|
+
break;
|
|
1195
|
+
|
|
1196
|
+
default:
|
|
1197
|
+
NOT_REACHED();
|
|
1198
|
+
}
|
|
1199
|
+
}
|
|
1200
|
+
|
|
1201
|
+
/*
|
|
1202
|
+
* Post-split copy handler invoked when the request is a multi vector -
|
|
1203
|
+
* 'get' or 'gets' request and has already been split into two requests
|
|
1204
|
+
*/
|
|
1205
|
+
rstatus_t
|
|
1206
|
+
memcache_post_splitcopy(struct msg *r)
|
|
1207
|
+
{
|
|
1208
|
+
struct mbuf *mbuf;
|
|
1209
|
+
struct string crlf = string(CRLF);
|
|
1210
|
+
|
|
1211
|
+
ASSERT(r->request);
|
|
1212
|
+
ASSERT(!r->redis);
|
|
1213
|
+
ASSERT(!STAILQ_EMPTY(&r->mhdr));
|
|
1214
|
+
|
|
1215
|
+
mbuf = STAILQ_LAST(&r->mhdr, mbuf, next);
|
|
1216
|
+
mbuf_copy(mbuf, crlf.data, crlf.len);
|
|
1217
|
+
|
|
1218
|
+
return NC_OK;
|
|
1219
|
+
}
|
|
1220
|
+
|
|
1221
|
+
/*
|
|
1222
|
+
* Pre-coalesce handler is invoked when the message is a response to
|
|
1223
|
+
* the fragmented multi vector request - 'get' or 'gets' and all the
|
|
1224
|
+
* responses to the fragmented request vector hasn't been received
|
|
1225
|
+
*/
|
|
1226
|
+
void
|
|
1227
|
+
memcache_pre_coalesce(struct msg *r)
|
|
1228
|
+
{
|
|
1229
|
+
struct msg *pr = r->peer; /* peer request */
|
|
1230
|
+
struct mbuf *mbuf;
|
|
1231
|
+
|
|
1232
|
+
ASSERT(!r->request);
|
|
1233
|
+
ASSERT(pr->request);
|
|
1234
|
+
|
|
1235
|
+
if (pr->frag_id == 0) {
|
|
1236
|
+
/* do nothing, if not a response to a fragmented request */
|
|
1237
|
+
return;
|
|
1238
|
+
}
|
|
1239
|
+
|
|
1240
|
+
switch (r->type) {
|
|
1241
|
+
|
|
1242
|
+
case MSG_RSP_MC_VALUE:
|
|
1243
|
+
case MSG_RSP_MC_END:
|
|
1244
|
+
|
|
1245
|
+
/*
|
|
1246
|
+
* Readjust responses of the fragmented message vector by not
|
|
1247
|
+
* including the end marker for all but the last response
|
|
1248
|
+
*/
|
|
1249
|
+
|
|
1250
|
+
if (pr->last_fragment) {
|
|
1251
|
+
break;
|
|
1252
|
+
}
|
|
1253
|
+
|
|
1254
|
+
ASSERT(r->end != NULL);
|
|
1255
|
+
|
|
1256
|
+
for (;;) {
|
|
1257
|
+
mbuf = STAILQ_LAST(&r->mhdr, mbuf, next);
|
|
1258
|
+
ASSERT(mbuf != NULL);
|
|
1259
|
+
|
|
1260
|
+
/*
|
|
1261
|
+
* We cannot assert that end marker points to the last mbuf
|
|
1262
|
+
* Consider a scenario where end marker points to the
|
|
1263
|
+
* penultimate mbuf and the last mbuf only contains spaces
|
|
1264
|
+
* and CRLF: mhdr -> [...END] -> [\r\n]
|
|
1265
|
+
*/
|
|
1266
|
+
|
|
1267
|
+
if (r->end >= mbuf->pos && r->end < mbuf->last) {
|
|
1268
|
+
/* end marker is within this mbuf */
|
|
1269
|
+
r->mlen -= (uint32_t)(mbuf->last - r->end);
|
|
1270
|
+
mbuf->last = r->end;
|
|
1271
|
+
break;
|
|
1272
|
+
}
|
|
1273
|
+
|
|
1274
|
+
/* end marker is not in this mbuf */
|
|
1275
|
+
r->mlen -= mbuf_length(mbuf);
|
|
1276
|
+
mbuf_remove(&r->mhdr, mbuf);
|
|
1277
|
+
mbuf_put(mbuf);
|
|
1278
|
+
}
|
|
1279
|
+
|
|
1280
|
+
break;
|
|
1281
|
+
|
|
1282
|
+
default:
|
|
1283
|
+
/*
|
|
1284
|
+
* Valid responses for a fragmented requests are MSG_RSP_MC_VALUE or,
|
|
1285
|
+
* MSG_RSP_MC_END. For an invalid response, we send out SERVER_ERRROR
|
|
1286
|
+
* with EINVAL errno
|
|
1287
|
+
*/
|
|
1288
|
+
mbuf = STAILQ_FIRST(&r->mhdr);
|
|
1289
|
+
log_hexdump(LOG_ERR, mbuf->pos, mbuf_length(mbuf), "rsp fragment "
|
|
1290
|
+
"with unknown type %d", r->type);
|
|
1291
|
+
pr->error = 1;
|
|
1292
|
+
pr->err = EINVAL;
|
|
1293
|
+
break;
|
|
1294
|
+
}
|
|
1295
|
+
}
|
|
1296
|
+
|
|
1297
|
+
/*
|
|
1298
|
+
* Post-coalesce handler is invoked when the message is a response to
|
|
1299
|
+
* the fragmented multi vector request - 'get' or 'gets' and all the
|
|
1300
|
+
* responses to the fragmented request vector has been received and
|
|
1301
|
+
* the fragmented request is consider to be done
|
|
1302
|
+
*/
|
|
1303
|
+
void
|
|
1304
|
+
memcache_post_coalesce(struct msg *r)
|
|
1305
|
+
{
|
|
1306
|
+
}
|