• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
41 struct CBC
42 {
43   char *buf;
44   size_t pos;
45   size_t size;
46 };
47 
48 static size_t
copyBuffer(void * ptr,size_t size,size_t nmemb,void * ctx)49 copyBuffer (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  */
64 static ssize_t
crc(void * cls,uint64_t pos,char * buf,size_t max)65 crc (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  */
83 static void
crcf(void * ptr)84 crcf (void *ptr)
85 {
86   free (ptr);
87 }
88 
89 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 ** ptr)90 ahc_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 
121 static int
testInternalGet()122 testInternalGet ()
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 
162 static int
testMultithreadedGet()163 testMultithreadedGet ()
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 
204 static int
testExternalGet()205 testExternalGet ()
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 
316 int
main(int argc,char * const * argv)317 main (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