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
47static int oneone;
48
49struct CBC
50{
51  char *buf;
52  size_t pos;
53  size_t size;
54};
55
56static size_t
57copyBuffer (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
68static int
69kv_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
80static int
81ahc_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
148static int
149testInternalGet ()
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
197static int
198testMultithreadedGet ()
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
246static int
247testMultithreadedPoolGet ()
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
296static int
297testExternalGet ()
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
420int
421main (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