• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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, &copyBuffer);
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, &copyBuffer);
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, &copyBuffer);
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, &copyBuffer);
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