1
2 /*
3 This file is part of libmicrohttpd
4 Copyright (C) 2007 Christian Grothoff
5
6 libmicrohttpd is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published
8 by the Free Software Foundation; either version 2, or (at your
9 option) any later version.
10
11 libmicrohttpd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with libmicrohttpd; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 Boston, MA 02111-1307, USA.
20 */
21
22 /**
23 * @file test_process_headers.c
24 * @brief Testcase for HTTP header access
25 * @author Christian Grothoff
26 */
27
28 #include "MHD_config.h"
29 #include "platform.h"
30 #include <curl/curl.h>
31 #include <microhttpd.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <time.h>
35
36 #ifndef WINDOWS
37 #include <unistd.h>
38 #endif
39
40 #if defined(CPU_COUNT) && (CPU_COUNT+0) < 2
41 #undef CPU_COUNT
42 #endif
43 #if !defined(CPU_COUNT)
44 #define CPU_COUNT 2
45 #endif
46
47 static int oneone;
48
49 struct CBC
50 {
51 char *buf;
52 size_t pos;
53 size_t size;
54 };
55
56 static size_t
copyBuffer(void * ptr,size_t size,size_t nmemb,void * ctx)57 copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx)
58 {
59 struct CBC *cbc = ctx;
60
61 if (cbc->pos + size * nmemb > cbc->size)
62 return 0; /* overflow */
63 memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb);
64 cbc->pos += size * nmemb;
65 return size * nmemb;
66 }
67
68 static int
kv_cb(void * cls,enum MHD_ValueKind kind,const char * key,const char * value)69 kv_cb (void *cls, enum MHD_ValueKind kind, const char *key, const char *value)
70 {
71 if ((0 == strcmp (key, MHD_HTTP_HEADER_HOST)) &&
72 (0 == strcmp (value, "127.0.0.1:21080")) && (kind == MHD_HEADER_KIND))
73 {
74 *((int *) cls) = 1;
75 return MHD_NO;
76 }
77 return MHD_YES;
78 }
79
80 static int
ahc_echo(void * cls,struct MHD_Connection * connection,const char * url,const char * method,const char * version,const char * upload_data,size_t * upload_data_size,void ** unused)81 ahc_echo (void *cls,
82 struct MHD_Connection *connection,
83 const char *url,
84 const char *method,
85 const char *version,
86 const char *upload_data, size_t *upload_data_size,
87 void **unused)
88 {
89 static int ptr;
90 const char *me = cls;
91 struct MHD_Response *response;
92 int ret;
93 const char *hdr;
94
95 if (0 != strcmp (me, method))
96 return MHD_NO; /* unexpected method */
97 if (&ptr != *unused)
98 {
99 *unused = &ptr;
100 return MHD_YES;
101 }
102 *unused = NULL;
103 ret = 0;
104 MHD_get_connection_values (connection, MHD_HEADER_KIND, &kv_cb, &ret);
105 if (ret != 1)
106 abort ();
107 hdr = MHD_lookup_connection_value (connection, MHD_HEADER_KIND, "NotFound");
108 if (hdr != NULL)
109 abort ();
110 hdr = MHD_lookup_connection_value (connection,
111 MHD_HEADER_KIND, MHD_HTTP_HEADER_ACCEPT);
112 if ((hdr == NULL) || (0 != strcmp (hdr, "*/*")))
113 abort ();
114 hdr = MHD_lookup_connection_value (connection,
115 MHD_HEADER_KIND, MHD_HTTP_HEADER_HOST);
116 if ((hdr == NULL) || (0 != strcmp (hdr, "127.0.0.1:21080")))
117 abort ();
118 MHD_set_connection_value (connection,
119 MHD_HEADER_KIND, "FakeHeader", "NowPresent");
120 hdr = MHD_lookup_connection_value (connection,
121 MHD_HEADER_KIND, "FakeHeader");
122 if ((hdr == NULL) || (0 != strcmp (hdr, "NowPresent")))
123 abort ();
124
125 response = MHD_create_response_from_buffer (strlen (url),
126 (void *) url,
127 MHD_RESPMEM_MUST_COPY);
128 MHD_add_response_header (response, "MyHeader", "MyValue");
129 hdr = MHD_get_response_header (response, "MyHeader");
130 if (0 != strcmp ("MyValue", hdr))
131 abort ();
132 MHD_add_response_header (response, "MyHeader", "MyValueToo");
133 if (MHD_YES != MHD_del_response_header (response, "MyHeader", "MyValue"))
134 abort ();
135 hdr = MHD_get_response_header (response, "MyHeader");
136 if (0 != strcmp ("MyValueToo", hdr))
137 abort ();
138 if (1 != MHD_get_response_headers (response, NULL, NULL))
139 abort ();
140 ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
141 MHD_destroy_response (response);
142 if (ret == MHD_NO)
143 abort ();
144 return ret;
145 }
146
147
148 static int
testInternalGet()149 testInternalGet ()
150 {
151 struct MHD_Daemon *d;
152 CURL *c;
153 char buf[2048];
154 struct CBC cbc;
155 CURLcode errornum;
156
157 cbc.buf = buf;
158 cbc.size = 2048;
159 cbc.pos = 0;
160 d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG,
161 21080, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END);
162 if (d == NULL)
163 return 1;
164 c = curl_easy_init ();
165 curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:21080/hello_world");
166 curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, ©Buffer);
167 curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
168 curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
169 curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
170 curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
171 if (oneone)
172 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
173 else
174 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
175 /* NOTE: use of CONNECTTIMEOUT without also
176 setting NOSIGNAL results in really weird
177 crashes on my system! */
178 curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
179 if (CURLE_OK != (errornum = curl_easy_perform (c)))
180 {
181 fprintf (stderr,
182 "curl_easy_perform failed: `%s'\n",
183 curl_easy_strerror (errornum));
184 curl_easy_cleanup (c);
185 MHD_stop_daemon (d);
186 return 2;
187 }
188 curl_easy_cleanup (c);
189 MHD_stop_daemon (d);
190 if (cbc.pos != strlen ("/hello_world"))
191 return 4;
192 if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
193 return 8;
194 return 0;
195 }
196
197 static int
testMultithreadedGet()198 testMultithreadedGet ()
199 {
200 struct MHD_Daemon *d;
201 CURL *c;
202 char buf[2048];
203 struct CBC cbc;
204 CURLcode errornum;
205
206 cbc.buf = buf;
207 cbc.size = 2048;
208 cbc.pos = 0;
209 d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION | MHD_USE_DEBUG,
210 21080, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END);
211 if (d == NULL)
212 return 16;
213 c = curl_easy_init ();
214 curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:21080/hello_world");
215 curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, ©Buffer);
216 curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
217 curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
218 curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
219 if (oneone)
220 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
221 else
222 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
223 curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
224 /* NOTE: use of CONNECTTIMEOUT without also
225 setting NOSIGNAL results in really weird
226 crashes on my system! */
227 curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
228 if (CURLE_OK != (errornum = curl_easy_perform (c)))
229 {
230 fprintf (stderr,
231 "curl_easy_perform failed: `%s'\n",
232 curl_easy_strerror (errornum));
233 curl_easy_cleanup (c);
234 MHD_stop_daemon (d);
235 return 32;
236 }
237 curl_easy_cleanup (c);
238 MHD_stop_daemon (d);
239 if (cbc.pos != strlen ("/hello_world"))
240 return 64;
241 if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
242 return 128;
243 return 0;
244 }
245
246 static int
testMultithreadedPoolGet()247 testMultithreadedPoolGet ()
248 {
249 struct MHD_Daemon *d;
250 CURL *c;
251 char buf[2048];
252 struct CBC cbc;
253 CURLcode errornum;
254
255 cbc.buf = buf;
256 cbc.size = 2048;
257 cbc.pos = 0;
258 d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG,
259 21080, NULL, NULL, &ahc_echo, "GET",
260 MHD_OPTION_THREAD_POOL_SIZE, CPU_COUNT, MHD_OPTION_END);
261 if (d == NULL)
262 return 16;
263 c = curl_easy_init ();
264 curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:21080/hello_world");
265 curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, ©Buffer);
266 curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
267 curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
268 curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
269 if (oneone)
270 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
271 else
272 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
273 curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
274 /* NOTE: use of CONNECTTIMEOUT without also
275 setting NOSIGNAL results in really weird
276 crashes on my system! */
277 curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
278 if (CURLE_OK != (errornum = curl_easy_perform (c)))
279 {
280 fprintf (stderr,
281 "curl_easy_perform failed: `%s'\n",
282 curl_easy_strerror (errornum));
283 curl_easy_cleanup (c);
284 MHD_stop_daemon (d);
285 return 32;
286 }
287 curl_easy_cleanup (c);
288 MHD_stop_daemon (d);
289 if (cbc.pos != strlen ("/hello_world"))
290 return 64;
291 if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
292 return 128;
293 return 0;
294 }
295
296 static int
testExternalGet()297 testExternalGet ()
298 {
299 struct MHD_Daemon *d;
300 CURL *c;
301 char buf[2048];
302 struct CBC cbc;
303 CURLM *multi;
304 CURLMcode mret;
305 fd_set rs;
306 fd_set ws;
307 fd_set es;
308 MHD_socket max;
309 int running;
310 struct CURLMsg *msg;
311 time_t start;
312 struct timeval tv;
313
314 multi = NULL;
315 cbc.buf = buf;
316 cbc.size = 2048;
317 cbc.pos = 0;
318 d = MHD_start_daemon (MHD_USE_DEBUG,
319 21080, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END);
320 if (d == NULL)
321 return 256;
322 c = curl_easy_init ();
323 curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:21080/hello_world");
324 curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, ©Buffer);
325 curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
326 curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
327 if (oneone)
328 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
329 else
330 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
331 curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
332 curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
333 /* NOTE: use of CONNECTTIMEOUT without also
334 setting NOSIGNAL results in really weird
335 crashes on my system! */
336 curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
337
338
339 multi = curl_multi_init ();
340 if (multi == NULL)
341 {
342 curl_easy_cleanup (c);
343 MHD_stop_daemon (d);
344 return 512;
345 }
346 mret = curl_multi_add_handle (multi, c);
347 if (mret != CURLM_OK)
348 {
349 curl_multi_cleanup (multi);
350 curl_easy_cleanup (c);
351 MHD_stop_daemon (d);
352 return 1024;
353 }
354 start = time (NULL);
355 while ((time (NULL) - start < 5) && (multi != NULL))
356 {
357 max = 0;
358 FD_ZERO (&rs);
359 FD_ZERO (&ws);
360 FD_ZERO (&es);
361 curl_multi_perform (multi, &running);
362 mret = curl_multi_fdset (multi, &rs, &ws, &es, &max);
363 if (mret != CURLM_OK)
364 {
365 curl_multi_remove_handle (multi, c);
366 curl_multi_cleanup (multi);
367 curl_easy_cleanup (c);
368 MHD_stop_daemon (d);
369 return 2048;
370 }
371 if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &max))
372 {
373 curl_multi_remove_handle (multi, c);
374 curl_multi_cleanup (multi);
375 curl_easy_cleanup (c);
376 MHD_stop_daemon (d);
377 return 4096;
378 }
379 tv.tv_sec = 0;
380 tv.tv_usec = 1000;
381 select (max + 1, &rs, &ws, &es, &tv);
382 curl_multi_perform (multi, &running);
383 if (running == 0)
384 {
385 msg = curl_multi_info_read (multi, &running);
386 if (msg == NULL)
387 break;
388 if (msg->msg == CURLMSG_DONE)
389 {
390 if (msg->data.result != CURLE_OK)
391 printf ("%s failed at %s:%d: `%s'\n",
392 "curl_multi_perform",
393 __FILE__,
394 __LINE__, curl_easy_strerror (msg->data.result));
395 curl_multi_remove_handle (multi, c);
396 curl_multi_cleanup (multi);
397 curl_easy_cleanup (c);
398 c = NULL;
399 multi = NULL;
400 }
401 }
402 MHD_run (d);
403 }
404 if (multi != NULL)
405 {
406 curl_multi_remove_handle (multi, c);
407 curl_easy_cleanup (c);
408 curl_multi_cleanup (multi);
409 }
410 MHD_stop_daemon (d);
411 if (cbc.pos != strlen ("/hello_world"))
412 return 8192;
413 if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
414 return 16384;
415 return 0;
416 }
417
418
419
420 int
main(int argc,char * const * argv)421 main (int argc, char *const *argv)
422 {
423 unsigned int errorCount = 0;
424
425 oneone = (NULL != strrchr (argv[0], (int) '/')) ?
426 (NULL != strstr (strrchr (argv[0], (int) '/'), "11")) : 0;
427 errorCount += testMultithreadedGet ();
428 errorCount += testMultithreadedPoolGet ();
429 errorCount += testExternalGet ();
430 if (errorCount != 0)
431 fprintf (stderr, "Error (code: %u)\n", errorCount);
432 curl_global_cleanup ();
433 return errorCount != 0; /* 0 == pass */
434 }
435