1 /*
2 This file is part of libmicrohttpd
3 Copyright (C) 2007,2013 Christian Grothoff
4
5 libmicrohttpd is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 libmicrohttpd 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 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with libmicrohttpd; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21 /**
22 * @file test_postprocessor.c
23 * @brief Testcase for postprocessor
24 * @author Christian Grothoff
25 */
26
27 #include "platform.h"
28 #include "microhttpd.h"
29 #include "internal.h"
30 #include <stdlib.h>
31 #include <string.h>
32 #include <stdio.h>
33
34 #ifndef WINDOWS
35 #include <unistd.h>
36 #endif
37
38 /**
39 * Array of values that the value checker "wants".
40 * Each series of checks should be terminated by
41 * five NULL-entries.
42 */
43 const char *want[] = {
44 #define URL_DATA "abc=def&x=5"
45 #define URL_START 0
46 "abc", NULL, NULL, NULL, "def",
47 "x", NULL, NULL, NULL, "5",
48 #define URL_END (URL_START + 10)
49 NULL, NULL, NULL, NULL, NULL,
50 #define FORM_DATA "--AaB03x\r\ncontent-disposition: form-data; name=\"field1\"\r\n\r\nJoe Blow\r\n--AaB03x\r\ncontent-disposition: form-data; name=\"pics\"; filename=\"file1.txt\"\r\nContent-Type: text/plain\r\nContent-Transfer-Encoding: binary\r\n\r\nfiledata\r\n--AaB03x--\r\n"
51 #define FORM_START (URL_END + 5)
52 "field1", NULL, NULL, NULL, "Joe Blow",
53 "pics", "file1.txt", "text/plain", "binary", "filedata",
54 #define FORM_END (FORM_START + 10)
55 NULL, NULL, NULL, NULL, NULL,
56 #define FORM_NESTED_DATA "--AaB03x\r\ncontent-disposition: form-data; name=\"field1\"\r\n\r\nJane Blow\r\n--AaB03x\r\ncontent-disposition: form-data; name=\"pics\"\r\nContent-type: multipart/mixed, boundary=BbC04y\r\n\r\n--BbC04y\r\nContent-disposition: attachment; filename=\"file1.txt\"\r\nContent-Type: text/plain\r\n\r\nfiledata1\r\n--BbC04y\r\nContent-disposition: attachment; filename=\"file2.gif\"\r\nContent-type: image/gif\r\nContent-Transfer-Encoding: binary\r\n\r\nfiledata2\r\n--BbC04y--\r\n--AaB03x--"
57 #define FORM_NESTED_START (FORM_END + 5)
58 "field1", NULL, NULL, NULL, "Jane Blow",
59 "pics", "file1.txt", "text/plain", NULL, "filedata1",
60 "pics", "file2.gif", "image/gif", "binary", "filedata2",
61 #define FORM_NESTED_END (FORM_NESTED_START + 15)
62 NULL, NULL, NULL, NULL, NULL,
63 #define URL_EMPTY_VALUE_DATA "key1=value1&key2=&key3="
64 #define URL_EMPTY_VALUE_START (FORM_NESTED_END + 5)
65 "key1", NULL, NULL, NULL, "value1",
66 "key2", NULL, NULL, NULL, "",
67 "key3", NULL, NULL, NULL, "",
68 #define URL_EMPTY_VALUE_END (URL_EMPTY_VALUE_START + 15)
69 NULL, NULL, NULL, NULL, NULL
70 };
71
72 static int
mismatch(const char * a,const char * b)73 mismatch (const char *a, const char *b)
74 {
75 if (a == b)
76 return 0;
77 if ((a == NULL) || (b == NULL))
78 return 1;
79 return 0 != strcmp (a, b);
80 }
81
82 static int
value_checker(void * cls,enum MHD_ValueKind kind,const char * key,const char * filename,const char * content_type,const char * transfer_encoding,const char * data,uint64_t off,size_t size)83 value_checker (void *cls,
84 enum MHD_ValueKind kind,
85 const char *key,
86 const char *filename,
87 const char *content_type,
88 const char *transfer_encoding,
89 const char *data, uint64_t off, size_t size)
90 {
91 int *want_off = cls;
92 int idx = *want_off;
93
94 #if 0
95 fprintf (stderr,
96 "VC: `%s' `%s' `%s' `%s' `%.*s'\n",
97 key, filename, content_type, transfer_encoding,
98 (int) size,
99 data);
100 #endif
101 if ( (0 != off) && (0 == size) )
102 return MHD_YES;
103 if ((idx < 0) ||
104 (want[idx] == NULL) ||
105 (0 != strcmp (key, want[idx])) ||
106 (mismatch (filename, want[idx + 1])) ||
107 (mismatch (content_type, want[idx + 2])) ||
108 (mismatch (transfer_encoding, want[idx + 3])) ||
109 (0 != memcmp (data, &want[idx + 4][off], size)))
110 {
111 *want_off = -1;
112 return MHD_NO;
113 }
114 if (off + size == strlen (want[idx + 4]))
115 *want_off = idx + 5;
116 return MHD_YES;
117
118 }
119
120
121 static int
test_urlencoding()122 test_urlencoding ()
123 {
124 struct MHD_Connection connection;
125 struct MHD_HTTP_Header header;
126 struct MHD_PostProcessor *pp;
127 unsigned int want_off = URL_START;
128 int i;
129 int delta;
130 size_t size;
131
132 memset (&connection, 0, sizeof (struct MHD_Connection));
133 memset (&header, 0, sizeof (struct MHD_HTTP_Header));
134 connection.headers_received = &header;
135 header.header = MHD_HTTP_HEADER_CONTENT_TYPE;
136 header.value = MHD_HTTP_POST_ENCODING_FORM_URLENCODED;
137 header.kind = MHD_HEADER_KIND;
138 pp = MHD_create_post_processor (&connection,
139 1024, &value_checker, &want_off);
140 i = 0;
141 size = strlen (URL_DATA);
142 while (i < size)
143 {
144 delta = 1 + MHD_random_ () % (size - i);
145 MHD_post_process (pp, &URL_DATA[i], delta);
146 i += delta;
147 }
148 MHD_destroy_post_processor (pp);
149 if (want_off != URL_END)
150 return 1;
151 return 0;
152 }
153
154
155 static int
test_multipart_garbage()156 test_multipart_garbage ()
157 {
158 struct MHD_Connection connection;
159 struct MHD_HTTP_Header header;
160 struct MHD_PostProcessor *pp;
161 unsigned int want_off;
162 size_t size = strlen (FORM_DATA);
163 size_t splitpoint;
164 char xdata[size + 3];
165
166 /* fill in evil garbage at the beginning */
167 xdata[0] = '-';
168 xdata[1] = 'x';
169 xdata[2] = '\r';
170 memcpy (&xdata[3], FORM_DATA, size);
171 size += 3;
172
173 size = strlen (FORM_DATA);
174 for (splitpoint = 1; splitpoint < size; splitpoint++)
175 {
176 want_off = FORM_START;
177 memset (&connection, 0, sizeof (struct MHD_Connection));
178 memset (&header, 0, sizeof (struct MHD_HTTP_Header));
179 connection.headers_received = &header;
180 header.header = MHD_HTTP_HEADER_CONTENT_TYPE;
181 header.value =
182 MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA ", boundary=AaB03x";
183 header.kind = MHD_HEADER_KIND;
184 pp = MHD_create_post_processor (&connection,
185 1024, &value_checker, &want_off);
186 MHD_post_process (pp, xdata, splitpoint);
187 MHD_post_process (pp, &xdata[splitpoint], size - splitpoint);
188 MHD_destroy_post_processor (pp);
189 if (want_off != FORM_END)
190 return (int) splitpoint;
191 }
192 return 0;
193 }
194
195
196 static int
test_multipart_splits()197 test_multipart_splits ()
198 {
199 struct MHD_Connection connection;
200 struct MHD_HTTP_Header header;
201 struct MHD_PostProcessor *pp;
202 unsigned int want_off;
203 size_t size;
204 size_t splitpoint;
205
206 size = strlen (FORM_DATA);
207 for (splitpoint = 1; splitpoint < size; splitpoint++)
208 {
209 want_off = FORM_START;
210 memset (&connection, 0, sizeof (struct MHD_Connection));
211 memset (&header, 0, sizeof (struct MHD_HTTP_Header));
212 connection.headers_received = &header;
213 header.header = MHD_HTTP_HEADER_CONTENT_TYPE;
214 header.value =
215 MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA ", boundary=AaB03x";
216 header.kind = MHD_HEADER_KIND;
217 pp = MHD_create_post_processor (&connection,
218 1024, &value_checker, &want_off);
219 MHD_post_process (pp, FORM_DATA, splitpoint);
220 MHD_post_process (pp, &FORM_DATA[splitpoint], size - splitpoint);
221 MHD_destroy_post_processor (pp);
222 if (want_off != FORM_END)
223 return (int) splitpoint;
224 }
225 return 0;
226 }
227
228
229 static int
test_multipart()230 test_multipart ()
231 {
232 struct MHD_Connection connection;
233 struct MHD_HTTP_Header header;
234 struct MHD_PostProcessor *pp;
235 unsigned int want_off = FORM_START;
236 int i;
237 int delta;
238 size_t size;
239
240 memset (&connection, 0, sizeof (struct MHD_Connection));
241 memset (&header, 0, sizeof (struct MHD_HTTP_Header));
242 connection.headers_received = &header;
243 header.header = MHD_HTTP_HEADER_CONTENT_TYPE;
244 header.value =
245 MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA ", boundary=AaB03x";
246 header.kind = MHD_HEADER_KIND;
247 pp = MHD_create_post_processor (&connection,
248 1024, &value_checker, &want_off);
249 i = 0;
250 size = strlen (FORM_DATA);
251 while (i < size)
252 {
253 delta = 1 + MHD_random_ () % (size - i);
254 MHD_post_process (pp, &FORM_DATA[i], delta);
255 i += delta;
256 }
257 MHD_destroy_post_processor (pp);
258 if (want_off != FORM_END)
259 return 2;
260 return 0;
261 }
262
263
264 static int
test_nested_multipart()265 test_nested_multipart ()
266 {
267 struct MHD_Connection connection;
268 struct MHD_HTTP_Header header;
269 struct MHD_PostProcessor *pp;
270 unsigned int want_off = FORM_NESTED_START;
271 int i;
272 int delta;
273 size_t size;
274
275 memset (&connection, 0, sizeof (struct MHD_Connection));
276 memset (&header, 0, sizeof (struct MHD_HTTP_Header));
277 connection.headers_received = &header;
278 header.header = MHD_HTTP_HEADER_CONTENT_TYPE;
279 header.value =
280 MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA ", boundary=AaB03x";
281 header.kind = MHD_HEADER_KIND;
282 pp = MHD_create_post_processor (&connection,
283 1024, &value_checker, &want_off);
284 i = 0;
285 size = strlen (FORM_NESTED_DATA);
286 while (i < size)
287 {
288 delta = 1 + MHD_random_ () % (size - i);
289 MHD_post_process (pp, &FORM_NESTED_DATA[i], delta);
290 i += delta;
291 }
292 MHD_destroy_post_processor (pp);
293 if (want_off != FORM_NESTED_END)
294 return 4;
295 return 0;
296 }
297
298
299 static int
test_empty_value()300 test_empty_value ()
301 {
302 struct MHD_Connection connection;
303 struct MHD_HTTP_Header header;
304 struct MHD_PostProcessor *pp;
305 unsigned int want_off = URL_EMPTY_VALUE_START;
306 int i;
307 int delta;
308 size_t size;
309
310 memset (&connection, 0, sizeof (struct MHD_Connection));
311 memset (&header, 0, sizeof (struct MHD_HTTP_Header));
312 connection.headers_received = &header;
313 header.header = MHD_HTTP_HEADER_CONTENT_TYPE;
314 header.value = MHD_HTTP_POST_ENCODING_FORM_URLENCODED;
315 header.kind = MHD_HEADER_KIND;
316 pp = MHD_create_post_processor (&connection,
317 1024, &value_checker, &want_off);
318 i = 0;
319 size = strlen (URL_EMPTY_VALUE_DATA);
320 while (i < size)
321 {
322 delta = 1 + MHD_random_ () % (size - i);
323 MHD_post_process (pp, &URL_EMPTY_VALUE_DATA[i], delta);
324 i += delta;
325 }
326 MHD_destroy_post_processor (pp);
327 if (want_off != URL_EMPTY_VALUE_END)
328 return 8;
329 return 0;
330 }
331
332
333
334
335 int
main(int argc,char * const * argv)336 main (int argc, char *const *argv)
337 {
338 unsigned int errorCount = 0;
339
340 errorCount += test_multipart_splits ();
341 errorCount += test_multipart_garbage ();
342 errorCount += test_urlencoding ();
343 errorCount += test_multipart ();
344 errorCount += test_nested_multipart ();
345 errorCount += test_empty_value ();
346 if (errorCount != 0)
347 fprintf (stderr, "Error (code: %u)\n", errorCount);
348 return errorCount != 0; /* 0 == pass */
349 }
350