more work on route_message() logic
[oweals/gnunet.git] / src / json / json_mhd.c
1 /*
2   This file is part of GNUnet
3   Copyright (C) 2014, 2015, 2016 GNUnet e.V.
4
5   GNUnet is free software: you can redistribute it and/or modify it
6   under the terms of the GNU Affero General Public License as published
7   by the Free Software Foundation, either version 3 of the License,
8   or (at your option) any later version.
9
10   GNUnet is distributed in the hope that it will be useful, but
11   WITHOUT ANY WARRANTY; without even the implied warranty of
12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13   Affero General Public License for more details.
14  
15   You should have received a copy of the GNU Affero General Public License
16   along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
18      SPDX-License-Identifier: AGPL3.0-or-later
19 */
20 /**
21  * @file json/json_mhd.c
22  * @brief functions to parse JSON snippets we receive via MHD
23  * @author Florian Dold
24  * @author Benedikt Mueller
25  * @author Christian Grothoff
26  */
27 #include "platform.h"
28 #include "gnunet_json_lib.h"
29
30 /**
31  * Initial size for POST request buffers.  Should be big enough to
32  * usually not require a reallocation, but not so big that it hurts in
33  * terms of memory use.
34  */
35 #define REQUEST_BUFFER_INITIAL (2*1024)
36
37
38 /**
39  * Buffer for POST requests.
40  */
41 struct Buffer
42 {
43   /**
44    * Allocated memory
45    */
46   char *data;
47
48   /**
49    * Number of valid bytes in buffer.
50    */
51   size_t fill;
52
53   /**
54    * Number of allocated bytes in buffer.
55    */
56   size_t alloc;
57 };
58
59
60 /**
61  * Initialize a buffer.
62  *
63  * @param buf the buffer to initialize
64  * @param data the initial data
65  * @param data_size size of the initial data
66  * @param alloc_size size of the buffer
67  * @param max_size maximum size that the buffer can grow to
68  * @return a GNUnet result code
69  */
70 static int
71 buffer_init (struct Buffer *buf,
72              const void *data,
73              size_t data_size,
74              size_t alloc_size,
75              size_t max_size)
76 {
77   if ( (data_size > max_size) ||
78        (alloc_size > max_size) )
79     return GNUNET_SYSERR;
80   if (data_size > alloc_size)
81     alloc_size = data_size;
82   buf->data = GNUNET_malloc (alloc_size);
83   GNUNET_memcpy (buf->data, data, data_size);
84   return GNUNET_OK;
85 }
86
87
88 /**
89  * Free the data in a buffer.  Does *not* free
90  * the buffer object itself.
91  *
92  * @param buf buffer to de-initialize
93  */
94 static void
95 buffer_deinit (struct Buffer *buf)
96 {
97   GNUNET_free (buf->data);
98   buf->data = NULL;
99 }
100
101
102 /**
103  * Append data to a buffer, growing the buffer if necessary.
104  *
105  * @param buf the buffer to append to
106  * @param data the data to append
107  * @param data_size the size of @a data
108  * @param max_size maximum size that the buffer can grow to
109  * @return #GNUNET_OK on success,
110  *         #GNUNET_NO if the buffer can't accomodate for the new data
111  */
112 static int
113 buffer_append (struct Buffer *buf,
114                const void *data,
115                size_t data_size,
116                size_t max_size)
117 {
118   if (buf->fill + data_size > max_size)
119     return GNUNET_NO;
120   if (data_size + buf->fill > buf->alloc)
121   {
122     char *new_buf;
123     size_t new_size = buf->alloc;
124     while (new_size < buf->fill + data_size)
125       new_size += 2;
126     if (new_size > max_size)
127       return GNUNET_NO;
128     new_buf = GNUNET_malloc (new_size);
129     GNUNET_memcpy (new_buf, buf->data, buf->fill);
130     GNUNET_free (buf->data);
131     buf->data = new_buf;
132     buf->alloc = new_size;
133   }
134   GNUNET_memcpy (buf->data + buf->fill, data, data_size);
135   buf->fill += data_size;
136   return GNUNET_OK;
137 }
138
139
140 /**
141  * Process a POST request containing a JSON object.  This function
142  * realizes an MHD POST processor that will (incrementally) process
143  * JSON data uploaded to the HTTP server.  It will store the required
144  * state in the @a con_cls, which must be cleaned up using
145  * #GNUNET_JSON_post_parser_callback().
146  *
147  * @param buffer_max maximum allowed size for the buffer
148  * @param con_cls the closure (will point to a `struct Buffer *`)
149  * @param upload_data the POST data
150  * @param upload_data_size number of bytes in @a upload_data
151  * @param json the JSON object for a completed request
152  * @return result code indicating the status of the operation
153  */
154 enum GNUNET_JSON_PostResult
155 GNUNET_JSON_post_parser (size_t buffer_max,
156                          void **con_cls,
157                          const char *upload_data,
158                          size_t *upload_data_size,
159                          json_t **json)
160 {
161   struct Buffer *r = *con_cls;
162
163   *json = NULL;
164   if (NULL == *con_cls)
165   {
166     /* We are seeing a fresh POST request. */
167     r = GNUNET_new (struct Buffer);
168     if (GNUNET_OK !=
169         buffer_init (r,
170                      upload_data,
171                      *upload_data_size,
172                      REQUEST_BUFFER_INITIAL,
173                      buffer_max))
174     {
175       *con_cls = NULL;
176       buffer_deinit (r);
177       GNUNET_free (r);
178       return GNUNET_JSON_PR_OUT_OF_MEMORY;
179     }
180     /* everything OK, wait for more POST data */
181     *upload_data_size = 0;
182     *con_cls = r;
183     return GNUNET_JSON_PR_CONTINUE;
184   }
185   if (0 != *upload_data_size)
186   {
187     /* We are seeing an old request with more data available. */
188
189     if (GNUNET_OK !=
190         buffer_append (r,
191                        upload_data,
192                        *upload_data_size,
193                        buffer_max))
194     {
195       /* Request too long */
196       *con_cls = NULL;
197       buffer_deinit (r);
198       GNUNET_free (r);
199       return GNUNET_JSON_PR_REQUEST_TOO_LARGE;
200     }
201     /* everything OK, wait for more POST data */
202     *upload_data_size = 0;
203     return GNUNET_JSON_PR_CONTINUE;
204   }
205
206   /* We have seen the whole request. */
207
208   *json = json_loadb (r->data,
209                       r->fill,
210                       0,
211                       NULL);
212   if (NULL == *json)
213   {
214     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
215                 "Failed to parse JSON request body\n");
216     return GNUNET_JSON_PR_JSON_INVALID;
217   }
218   buffer_deinit (r);
219   GNUNET_free (r);
220   *con_cls = NULL;
221
222   return GNUNET_JSON_PR_SUCCESS;
223 }
224
225
226 /**
227  * Function called whenever we are done with a request
228  * to clean up our state.
229  *
230  * @param con_cls value as it was left by
231  *        #GNUNET_JSON_post_parser(), to be cleaned up
232  */
233 void
234 GNUNET_JSON_post_parser_cleanup (void *con_cls)
235 {
236   struct Buffer *r = con_cls;
237
238   if (NULL != r)
239   {
240     buffer_deinit (r);
241     GNUNET_free (r);
242   }
243 }
244
245 /* end of mhd_json.c */