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