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