1 /*  vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
2  *
3  *  Libmemcached library
4  *
5  *  Copyright (C) 2011 Data Differential, http://datadifferential.com/
6  *
7  *  Redistribution and use in source and binary forms, with or without
8  *  modification, are permitted provided that the following conditions are
9  *  met:
10  *
11  *      * Redistributions of source code must retain the above copyright
12  *  notice, this list of conditions and the following disclaimer.
13  *
14  *      * Redistributions in binary form must reproduce the above
15  *  copyright notice, this list of conditions and the following disclaimer
16  *  in the documentation and/or other materials provided with the
17  *  distribution.
18  *
19  *      * The names of its contributors may not be used to endorse or
20  *  promote products derived from this software without specific prior
21  *  written permission.
22  *
23  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24  *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25  *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
26  *  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
27  *  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28  *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
29  *  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
30  *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
31  *  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
32  *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
33  *  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34  *
35  */
36 
37 #pragma once
38 
39 #include "mem_config.h"
40 #include <assert.h>
41 
42 #include <libmemcachedprotocol-0.0/handler.h>
43 #include <libmemcachedprotocol/cache.h>
44 #include <libmemcached/byteorder.h>
45 #include <libmemcached/socket.hpp>
46 
47 /*
48  * I don't really need the following two functions as function pointers
49  * in the instance handle, but I don't want to put them in the global
50  * namespace for those linking statically (personally I don't like that,
51  * but some people still do). If it ever shows up as a performance thing
52  * I'll look into optimizing this ;-)
53  */
54 typedef bool (*drain_func)(memcached_protocol_client_st *client);
55 typedef protocol_binary_response_status (*spool_func)(memcached_protocol_client_st *client,
56                                                       const void *data,
57                                                       size_t length);
58 
59 /**
60  * Definition of the per instance structure.
61  */
62 struct memcached_protocol_st {
63   memcached_binary_protocol_callback_st *callback;
64   memcached_protocol_recv_func recv;
65   memcached_protocol_send_func send;
66 
67   /*
68    * I really don't need these as funciton pointers, but I don't want
69    * to clutter the namespace if someone links statically.
70    */
71   drain_func drain;
72   spool_func spool;
73 
74   /*
75    * To avoid keeping a buffer in each client all the time I have a
76    * bigger buffer in the instance that I read to initially, and then
77    * I try to parse and execute as much from the buffer. If I wasn't able
78    * to process all data I'll keep that in a per-connection buffer until
79    * the next time I can read from the socket.
80    */
81   uint8_t *input_buffer;
82   size_t input_buffer_size;
83 
84   bool pedantic;
85   /* @todo use multiple sized buffers */
86   cache_t *buffer_cache;
87 };
88 
89 struct chunk_st {
90   /* Pointer to the data */
91   char *data;
92   /* The offset to the first byte into the buffer that is used */
93   size_t offset;
94   /* The offset into the buffer for the first free byte */
95   size_t nbytes;
96   /* The number of bytes in the buffer */
97   size_t size;
98   /* Pointer to the next buffer in the chain */
99   struct chunk_st *next;
100 };
101 
102 #define CHUNK_BUFFERSIZE 2048
103 
104 typedef memcached_protocol_event_t (*process_data)(struct memcached_protocol_client_st *client, ssize_t *length, void **endptr);
105 
106 enum ascii_cmd {
107   GET_CMD,
108   GETS_CMD,
109   SET_CMD,
110   ADD_CMD,
111   REPLACE_CMD,
112   CAS_CMD,
113   APPEND_CMD,
114   PREPEND_CMD,
115   DELETE_CMD,
116   INCR_CMD,
117   DECR_CMD,
118   STATS_CMD,
119   FLUSH_ALL_CMD,
120   VERSION_CMD,
121   QUIT_CMD,
122   VERBOSITY_CMD,
123   UNKNOWN_CMD
124 };
125 
126 struct memcached_protocol_client_st {
127   bool is_verbose;
128   memcached_protocol_st *root;
129   memcached_socket_t sock;
130   int error;
131 
132   /* Linked list of data to send */
133   struct chunk_st *output;
134   struct chunk_st *output_tail;
135 
136   /*
137    * While we process input data, this is where we spool incomplete commands
138    * if we need to receive more data....
139    * @todo use the buffercace
140    */
141   uint8_t *input_buffer;
142   size_t input_buffer_size;
143   size_t input_buffer_offset;
144 
145   /* The callback to the protocol handler to use (ascii or binary) */
146   process_data work;
147 
148   /*
149    * Should the spool data discard the data to send or not? (aka noreply in
150    * the ascii protocol..
151    */
152   bool mute;
153 
154   /* Members used by the binary protocol */
155   protocol_binary_request_header *current_command;
156 
157   /* Members used by the ascii protocol */
158   enum ascii_cmd ascii_command;
159 };
160 
161 #include "ascii_handler.h"
162 #include "binary_handler.h"
163