1/*
2     This file is part of libmicrohttpd
3     Copyright (C) 2007, 2008 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 2, 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 daemontest_get_chunked.c
23 * @brief  Testcase for libmicrohttpd GET operations with chunked content encoding
24 * @author Christian Grothoff
25 */
26
27#include "MHD_config.h"
28#include "platform.h"
29#include <curl/curl.h>
30#include <microhttpd.h>
31#include <stdlib.h>
32#include <string.h>
33#include <time.h>
34
35#ifndef WINDOWS
36#include <unistd.h>
37#endif
38
39#include "socat.c"
40
41struct CBC
42{
43  char *buf;
44  size_t pos;
45  size_t size;
46};
47
48static size_t
49copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx)
50{
51  struct CBC *cbc = ctx;
52
53  if (cbc->pos + size * nmemb > cbc->size)
54    return 0;                   /* overflow */
55  memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb);
56  cbc->pos += size * nmemb;
57  return size * nmemb;
58}
59
60/**
61 * MHD content reader callback that returns
62 * data in chunks.
63 */
64static ssize_t
65crc (void *cls, uint64_t pos, char *buf, size_t max)
66{
67  struct MHD_Response **responseptr = cls;
68
69  if (pos == 128 * 10)
70    {
71      MHD_add_response_header (*responseptr, "Footer", "working");
72      return MHD_CONTENT_READER_END_OF_STREAM;
73    }
74  if (max < 128)
75    abort ();                   /* should not happen in this testcase... */
76  memset (buf, 'A' + (pos / 128), 128);
77  return 128;
78}
79
80/**
81 * Dummy function that does nothing.
82 */
83static void
84crcf (void *ptr)
85{
86  free (ptr);
87}
88
89static int
90ahc_echo (void *cls,
91          struct MHD_Connection *connection,
92          const char *url,
93          const char *method,
94          const char *version,
95          const char *upload_data, size_t *upload_data_size, void **ptr)
96{
97  static int aptr;
98  const char *me = cls;
99  struct MHD_Response *response;
100  struct MHD_Response **responseptr;
101  int ret;
102
103  if (0 != strcmp (me, method))
104    return MHD_NO;              /* unexpected method */
105  if (&aptr != *ptr)
106    {
107      /* do never respond on first call */
108      *ptr = &aptr;
109      return MHD_YES;
110    }
111  responseptr = malloc (sizeof (struct MHD_Response *));
112  response = MHD_create_response_from_callback (MHD_SIZE_UNKNOWN,
113                                                1024,
114                                                &crc, responseptr, &crcf);
115  *responseptr = response;
116  ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
117  MHD_destroy_response (response);
118  return ret;
119}
120
121static int
122testInternalGet ()
123{
124  struct MHD_Daemon *d;
125  CURL *c;
126  char buf[2048];
127  struct CBC cbc;
128  int i;
129
130  cbc.buf = buf;
131  cbc.size = 2048;
132  cbc.pos = 0;
133  d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY /* | MHD_USE_DEBUG */ ,
134                        11080, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END);
135  if (d == NULL)
136    return 1;
137  zzuf_socat_start ();
138  for (i = 0; i < LOOP_COUNT; i++)
139    {
140      fprintf (stderr, ".");
141      c = curl_easy_init ();
142      curl_easy_setopt (c, CURLOPT_URL, "http://localhost:11081/hello_world");
143      curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
144      curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
145      curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
146      curl_easy_setopt (c, CURLOPT_TIMEOUT_MS, CURL_TIMEOUT);
147      curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT_MS, CURL_TIMEOUT);
148      curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
149      // NOTE: use of CONNECTTIMEOUT without also
150      //   setting NOSIGNAL results in really weird
151      //   crashes on my system!
152      curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
153      curl_easy_perform (c);
154      curl_easy_cleanup (c);
155    }
156  fprintf (stderr, "\n");
157  zzuf_socat_stop ();
158  MHD_stop_daemon (d);
159  return 0;
160}
161
162static int
163testMultithreadedGet ()
164{
165  struct MHD_Daemon *d;
166  CURL *c;
167  char buf[2048];
168  struct CBC cbc;
169  int i;
170
171  cbc.buf = buf;
172  cbc.size = 2048;
173  cbc.pos = 0;
174  d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION /* | MHD_USE_DEBUG */ ,
175                        11080, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END);
176  if (d == NULL)
177    return 16;
178  zzuf_socat_start ();
179  for (i = 0; i < LOOP_COUNT; i++)
180    {
181      fprintf (stderr, ".");
182      c = curl_easy_init ();
183      curl_easy_setopt (c, CURLOPT_URL, "http://localhost:11081/hello_world");
184      curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
185      curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
186      curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
187      curl_easy_setopt (c, CURLOPT_TIMEOUT_MS, CURL_TIMEOUT);
188      curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
189      curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT_MS, CURL_TIMEOUT);
190      // NOTE: use of CONNECTTIMEOUT without also
191      //   setting NOSIGNAL results in really weird
192      //   crashes on my system!
193      curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
194      curl_easy_perform (c);
195      curl_easy_cleanup (c);
196    }
197  fprintf (stderr, "\n");
198  zzuf_socat_stop ();
199  MHD_stop_daemon (d);
200  return 0;
201}
202
203
204static int
205testExternalGet ()
206{
207  struct MHD_Daemon *d;
208  CURL *c;
209  char buf[2048];
210  struct CBC cbc;
211  CURLM *multi;
212  CURLMcode mret;
213  fd_set rs;
214  fd_set ws;
215  fd_set es;
216  int max;
217  int running;
218  time_t start;
219  struct timeval tv;
220  int i;
221
222  multi = NULL;
223  cbc.buf = buf;
224  cbc.size = 2048;
225  cbc.pos = 0;
226  d = MHD_start_daemon (MHD_NO_FLAG /* | MHD_USE_DEBUG */ ,
227                        11080, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END);
228  if (d == NULL)
229    return 256;
230  multi = curl_multi_init ();
231  if (multi == NULL)
232    {
233      MHD_stop_daemon (d);
234      return 512;
235    }
236  zzuf_socat_start ();
237  for (i = 0; i < LOOP_COUNT; i++)
238    {
239      fprintf (stderr, ".");
240      c = curl_easy_init ();
241      curl_easy_setopt (c, CURLOPT_URL, "http://localhost:11081/hello_world");
242      curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
243      curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
244      curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
245      curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
246      curl_easy_setopt (c, CURLOPT_TIMEOUT_MS, CURL_TIMEOUT);
247      curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT_MS, CURL_TIMEOUT);
248      // NOTE: use of CONNECTTIMEOUT without also
249      //   setting NOSIGNAL results in really weird
250      //   crashes on my system!
251      curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
252      mret = curl_multi_add_handle (multi, c);
253      if (mret != CURLM_OK)
254        {
255          curl_multi_cleanup (multi);
256          curl_easy_cleanup (c);
257          zzuf_socat_stop ();
258          MHD_stop_daemon (d);
259          return 1024;
260        }
261      start = time (NULL);
262      while ((time (NULL) - start < 5) && (c != NULL))
263        {
264          max = 0;
265          FD_ZERO (&rs);
266          FD_ZERO (&ws);
267          FD_ZERO (&es);
268          curl_multi_perform (multi, &running);
269          mret = curl_multi_fdset (multi, &rs, &ws, &es, &max);
270          if (mret != CURLM_OK)
271            {
272              curl_multi_remove_handle (multi, c);
273              curl_multi_cleanup (multi);
274              curl_easy_cleanup (c);
275              zzuf_socat_stop ();
276              MHD_stop_daemon (d);
277              return 2048;
278            }
279          if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &max))
280            {
281              curl_multi_remove_handle (multi, c);
282              curl_multi_cleanup (multi);
283              curl_easy_cleanup (c);
284              zzuf_socat_stop ();
285              MHD_stop_daemon (d);
286              return 4096;
287            }
288          tv.tv_sec = 0;
289          tv.tv_usec = 1000;
290          select (max + 1, &rs, &ws, &es, &tv);
291          curl_multi_perform (multi, &running);
292          if (running == 0)
293            {
294              curl_multi_info_read (multi, &running);
295              curl_multi_remove_handle (multi, c);
296              curl_easy_cleanup (c);
297              c = NULL;
298            }
299          MHD_run (d);
300        }
301      if (c != NULL)
302        {
303          curl_multi_remove_handle (multi, c);
304          curl_easy_cleanup (c);
305        }
306    }
307  fprintf (stderr, "\n");
308  curl_multi_cleanup (multi);
309  zzuf_socat_stop ();
310  MHD_stop_daemon (d);
311  return 0;
312}
313
314
315
316int
317main (int argc, char *const *argv)
318{
319  unsigned int errorCount = 0;
320
321  if (0 != curl_global_init (CURL_GLOBAL_WIN32))
322    return 2;
323  errorCount += testInternalGet ();
324  errorCount += testMultithreadedGet ();
325  errorCount += testExternalGet ();
326  if (errorCount != 0)
327    fprintf (stderr, "Error (code: %u)\n", errorCount);
328  curl_global_cleanup ();
329  return errorCount != 0;       /* 0 == pass */
330}
331