b412b9e9f9009aad375aa6aee628da63d642e099
[m6w6/libmemcached] / src / libmemcachedprotocol / handler.c
1 /*
2 +--------------------------------------------------------------------+
3 | libmemcached - C/C++ Client Library for memcached |
4 +--------------------------------------------------------------------+
5 | Redistribution and use in source and binary forms, with or without |
6 | modification, are permitted under the terms of the BSD license. |
7 | You should have received a copy of the license in a bundled file |
8 | named LICENSE; in case you did not receive a copy you can review |
9 | the terms online at: https://opensource.org/licenses/BSD-3-Clause |
10 +--------------------------------------------------------------------+
11 | Copyright (c) 2006-2014 Brian Aker https://datadifferential.com/ |
12 | Copyright (c) 2020 Michael Wallner <mike@php.net> |
13 +--------------------------------------------------------------------+
14 */
15
16 #include "libmemcachedprotocol/common.h"
17
18 #include <stdlib.h>
19 #include <sys/types.h>
20 #include <errno.h>
21 #include <stdbool.h>
22 #include <string.h>
23 #include <strings.h>
24 #include <ctype.h>
25 #include <stdio.h>
26
27 #include <sys/types.h>
28 #include <sys/socket.h>
29
30 /*
31 ** **********************************************************************
32 ** INTERNAL INTERFACE
33 ** **********************************************************************
34 */
35
36 /**
37 * The default function to receive data from the client. This function
38 * just wraps the recv function to receive from a socket.
39 * See man -s3socket recv for more information.
40 *
41 * @param cookie cookie indentifying a client, not used
42 * @param sock socket to read from
43 * @param buf the destination buffer
44 * @param nbytes the number of bytes to read
45 * @return the number of bytes transferred of -1 upon error
46 */
47 static ssize_t default_recv(const void *cookie, memcached_socket_t sock, void *buf, size_t nbytes) {
48 (void) cookie;
49 return recv(sock, buf, nbytes, 0);
50 }
51
52 /**
53 * The default function to send data to the server. This function
54 * just wraps the send function to send through a socket.
55 * See man -s3socket send for more information.
56 *
57 * @param cookie cookie indentifying a client, not used
58 * @param sock socket to send to
59 * @param buf the source buffer
60 * @param nbytes the number of bytes to send
61 * @return the number of bytes transferred of -1 upon error
62 */
63 static ssize_t default_send(const void *cookie, memcached_socket_t fd, const void *buf,
64 size_t nbytes) {
65 (void) cookie;
66 return send(fd, buf, nbytes, MSG_NOSIGNAL);
67 }
68
69 /**
70 * Try to drain the output buffers without blocking
71 *
72 * @param client the client to drain
73 * @return false if an error occured (connection should be shut down)
74 * true otherwise (please note that there may be more data to
75 * left in the buffer to send)
76 */
77 static bool drain_output(struct memcached_protocol_client_st *client) {
78 if (client->is_verbose) {
79 fprintf(stderr, "%s:%d %s mute:%d output:%s length:%d\n", __FILE__, __LINE__, __func__,
80 (int) client->mute, client->output ? "yes" : "no",
81 client->output ? (int) (client->output->nbytes - client->output->offset) : 0);
82 }
83
84 /* Do we have pending data to send? */
85 while (client->output) {
86 ssize_t len =
87 client->root->send(client, client->sock, client->output->data + client->output->offset,
88 client->output->nbytes - client->output->offset);
89
90 if (len == -1) {
91 if (get_socket_errno() == EWOULDBLOCK) {
92 return true;
93 } else if (get_socket_errno() != EINTR) {
94 client->error = get_socket_errno();
95 return false;
96 }
97 } else {
98 client->output->offset += (size_t) len;
99 if (client->output->offset == client->output->nbytes) {
100 /* This was the complete buffer */
101 struct chunk_st *old = client->output;
102 client->output = client->output->next;
103 if (client->output == NULL) {
104 client->output_tail = NULL;
105 }
106 cache_free(client->root->buffer_cache, old);
107 }
108 }
109 }
110
111 return true;
112 }
113
114 /**
115 * Allocate an output buffer and chain it into the output list
116 *
117 * @param client the client that needs the buffer
118 * @return pointer to the new chunk if the allocation succeeds, NULL otherwise
119 */
120 static struct chunk_st *allocate_output_chunk(struct memcached_protocol_client_st *client) {
121 struct chunk_st *ret = cache_alloc(client->root->buffer_cache);
122
123 if (ret == NULL) {
124 return NULL;
125 }
126
127 ret->offset = ret->nbytes = 0;
128 ret->next = NULL;
129 ret->size = CHUNK_BUFFERSIZE;
130 ret->data = (void *) (ret + 1);
131 if (client->output == NULL) {
132 client->output = client->output_tail = ret;
133 } else {
134 client->output_tail->next = ret;
135 client->output_tail = ret;
136 }
137
138 return ret;
139 }
140
141 /**
142 * Spool data into the send-buffer for a client.
143 *
144 * @param client the client to spool the data for
145 * @param data the data to spool
146 * @param length the number of bytes of data to spool
147 * @return PROTOCOL_BINARY_RESPONSE_SUCCESS if success,
148 * PROTOCOL_BINARY_RESPONSE_ENOMEM if we failed to allocate memory
149 */
150 static protocol_binary_response_status spool_output(struct memcached_protocol_client_st *client,
151 const void *data, size_t length) {
152 if (client->is_verbose) {
153 fprintf(stderr, "%s:%d %s mute:%d length:%d\n", __FILE__, __LINE__, __func__,
154 (int) client->mute, (int) length);
155 }
156
157 if (client->mute) {
158 return PROTOCOL_BINARY_RESPONSE_SUCCESS;
159 }
160
161 size_t offset = 0;
162
163 struct chunk_st *chunk = client->output;
164 while (offset < length) {
165 if (chunk == NULL || (chunk->size - chunk->nbytes) == 0) {
166 if ((chunk = allocate_output_chunk(client)) == NULL) {
167 return PROTOCOL_BINARY_RESPONSE_ENOMEM;
168 }
169 }
170
171 size_t bulk = length - offset;
172 if (bulk > chunk->size - chunk->nbytes) {
173 bulk = chunk->size - chunk->nbytes;
174 }
175
176 memcpy(chunk->data + chunk->nbytes, data, bulk);
177 chunk->nbytes += bulk;
178 offset += bulk;
179 }
180
181 return PROTOCOL_BINARY_RESPONSE_SUCCESS;
182 }
183
184 /**
185 * Try to determine the protocol used on this connection.
186 * If the first byte contains the magic byte PROTOCOL_BINARY_REQ we should
187 * be using the binary protocol on the connection. I implemented the support
188 * for the ASCII protocol by wrapping into the simple interface (aka v1),
189 * so the implementors needs to provide an implementation of that interface
190 *
191 */
192 static memcached_protocol_event_t determine_protocol(struct memcached_protocol_client_st *client,
193 ssize_t *length, void **endptr) {
194 if (*client->root->input_buffer == (uint8_t) PROTOCOL_BINARY_REQ) {
195 if (client->is_verbose) {
196 fprintf(stderr, "%s:%d PROTOCOL: memcached_binary_protocol_process_data\n", __FILE__,
197 __LINE__);
198 }
199 client->work = memcached_binary_protocol_process_data;
200 } else if (client->root->callback->interface_version == 1) {
201 if (client->is_verbose) {
202 fprintf(stderr, "%s:%d PROTOCOL: memcached_ascii_protocol_process_data\n", __FILE__,
203 __LINE__);
204 }
205
206 /*
207 * The ASCII protocol can only be used if the implementors provide
208 * an implementation for the version 1 of the interface..
209 *
210 * @todo I should allow the implementors to provide an implementation
211 * for version 0 and 1 at the same time and set the preferred
212 * interface to use...
213 */
214 client->work = memcached_ascii_protocol_process_data;
215 } else {
216 if (client->is_verbose) {
217 fprintf(stderr, "%s:%d PROTOCOL: Unsupported protocol\n", __FILE__, __LINE__);
218 }
219
220 /* Let's just output a warning the way it is supposed to look like
221 * in the ASCII protocol...
222 */
223 const char *err = "CLIENT_ERROR: Unsupported protocol\r\n";
224 client->root->spool(client, err, strlen(err));
225 client->root->drain(client);
226
227 return MEMCACHED_PROTOCOL_ERROR_EVENT; /* Unsupported protocol */
228 }
229
230 return client->work(client, length, endptr);
231 }
232
233 /*
234 ** **********************************************************************
235 ** * PUBLIC INTERFACE
236 ** * See protocol_handler.h for function description
237 ** **********************************************************************
238 */
239 struct memcached_protocol_st *memcached_protocol_create_instance(void) {
240 struct memcached_protocol_st *ret = calloc(1, sizeof(*ret));
241 if (ret) {
242 ret->recv = default_recv;
243 ret->send = default_send;
244 ret->drain = drain_output;
245 ret->spool = spool_output;
246 ret->input_buffer_size = 1 * 1024 * 1024;
247 ret->input_buffer = malloc(ret->input_buffer_size);
248 if (ret->input_buffer == NULL) {
249 free(ret);
250 ret = NULL;
251
252 return NULL;
253 }
254
255 ret->buffer_cache =
256 cache_create("protocol_handler", CHUNK_BUFFERSIZE + sizeof(struct chunk_st), 0, NULL, NULL);
257 if (ret->buffer_cache == NULL) {
258 free(ret->input_buffer);
259 free(ret);
260 ret = NULL;
261 }
262 }
263
264 return ret;
265 }
266
267 void memcached_protocol_destroy_instance(struct memcached_protocol_st *instance) {
268 cache_destroy(instance->buffer_cache);
269 free(instance->input_buffer);
270 free(instance);
271 }
272
273 struct memcached_protocol_client_st *
274 memcached_protocol_create_client(struct memcached_protocol_st *instance, memcached_socket_t sock) {
275 struct memcached_protocol_client_st *ret = calloc(1, sizeof(memcached_protocol_client_st));
276 if (ret) {
277 ret->root = instance;
278 ret->sock = sock;
279 ret->work = determine_protocol;
280 }
281
282 return ret;
283 }
284
285 void memcached_protocol_client_destroy(struct memcached_protocol_client_st *client) {
286 free(client);
287 }
288
289 void memcached_protocol_client_set_verbose(struct memcached_protocol_client_st *client, bool arg) {
290 if (client) {
291 client->is_verbose = arg;
292 }
293 }
294
295 memcached_protocol_event_t
296 memcached_protocol_client_work(struct memcached_protocol_client_st *client) {
297 /* Try to send data and read from the socket */
298 bool more_data = true;
299 do {
300 ssize_t len = client->root->recv(client, client->sock,
301 client->root->input_buffer + client->input_buffer_offset,
302 client->root->input_buffer_size - client->input_buffer_offset);
303
304 if (len > 0) {
305 /* Do we have the complete packet? */
306 if (client->input_buffer_offset > 0) {
307 memcpy(client->root->input_buffer, client->input_buffer, client->input_buffer_offset);
308 len += (ssize_t) client->input_buffer_offset;
309
310 /* @todo use buffer-cache! */
311 free(client->input_buffer);
312 client->input_buffer_offset = 0;
313 }
314
315 void *endptr;
316 memcached_protocol_event_t events = client->work(client, &len, &endptr);
317 if (events == MEMCACHED_PROTOCOL_ERROR_EVENT) {
318 return MEMCACHED_PROTOCOL_ERROR_EVENT;
319 }
320
321 if (len > 0) {
322 /* save the data for later on */
323 /* @todo use buffer-cache */
324 client->input_buffer = malloc((size_t) len);
325 if (client->input_buffer == NULL) {
326 client->error = ENOMEM;
327 return MEMCACHED_PROTOCOL_ERROR_EVENT;
328 }
329 memcpy(client->input_buffer, endptr, (size_t) len);
330 client->input_buffer_offset = (size_t) len;
331 more_data = false;
332 }
333 } else if (len == 0) {
334 /* Connection closed */
335 drain_output(client);
336 return MEMCACHED_PROTOCOL_ERROR_EVENT;
337 } else {
338 if (get_socket_errno() != EWOULDBLOCK) {
339 client->error = get_socket_errno();
340 /* mark this client as terminated! */
341 return MEMCACHED_PROTOCOL_ERROR_EVENT;
342 }
343 more_data = false;
344 }
345 } while (more_data);
346
347 if (!drain_output(client)) {
348 return MEMCACHED_PROTOCOL_ERROR_EVENT;
349 }
350
351 memcached_protocol_event_t ret = MEMCACHED_PROTOCOL_READ_EVENT;
352 if (client->output) {
353 ret |= MEMCACHED_PROTOCOL_READ_EVENT;
354 }
355
356 return ret;
357 }