aboutsummaryrefslogtreecommitdiff
path: root/src/testcurl/test_get_chunked.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/testcurl/test_get_chunked.c')
-rw-r--r--src/testcurl/test_get_chunked.c407
1 files changed, 407 insertions, 0 deletions
diff --git a/src/testcurl/test_get_chunked.c b/src/testcurl/test_get_chunked.c
new file mode 100644
index 00000000..d80de8de
--- /dev/null
+++ b/src/testcurl/test_get_chunked.c
@@ -0,0 +1,407 @@
1/*
2 This file is part of libmicrohttpd
3 (C) 2007 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 * TODO:
25 * - how to test that chunking was actually used?
26 * - use CURLOPT_HEADERFUNCTION to validate
27 * footer was sent
28 * @author Christian Grothoff
29 */
30
31#include "MHD_config.h"
32#include "platform.h"
33#include <curl/curl.h>
34#include <microhttpd.h>
35#include <stdlib.h>
36#include <string.h>
37#include <time.h>
38
39#ifndef WINDOWS
40#include <unistd.h>
41#endif
42
43struct CBC
44{
45 char *buf;
46 size_t pos;
47 size_t size;
48};
49
50static size_t
51copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx)
52{
53 struct CBC *cbc = ctx;
54
55 if (cbc->pos + size * nmemb > cbc->size)
56 return 0; /* overflow */
57 memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb);
58 cbc->pos += size * nmemb;
59 return size * nmemb;
60}
61
62/**
63 * MHD content reader callback that returns
64 * data in chunks.
65 */
66static ssize_t
67crc (void *cls, uint64_t pos, char *buf, size_t max)
68{
69 struct MHD_Response **responseptr = cls;
70
71 if (pos == 128 * 10)
72 {
73 MHD_add_response_header (*responseptr, "Footer", "working");
74 return MHD_CONTENT_READER_END_OF_STREAM;
75 }
76 if (max < 128)
77 abort (); /* should not happen in this testcase... */
78 memset (buf, 'A' + (pos / 128), 128);
79 return 128;
80}
81
82/**
83 * Dummy function that does nothing.
84 */
85static void
86crcf (void *ptr)
87{
88 free (ptr);
89}
90
91static int
92ahc_echo (void *cls,
93 struct MHD_Connection *connection,
94 const char *url,
95 const char *method,
96 const char *version,
97 const char *upload_data, size_t *upload_data_size, void **ptr)
98{
99 static int aptr;
100 const char *me = cls;
101 struct MHD_Response *response;
102 struct MHD_Response **responseptr;
103 int ret;
104
105 if (0 != strcmp (me, method))
106 return MHD_NO; /* unexpected method */
107 if (&aptr != *ptr)
108 {
109 /* do never respond on first call */
110 *ptr = &aptr;
111 return MHD_YES;
112 }
113 responseptr = malloc (sizeof (struct MHD_Response *));
114 if (responseptr == NULL)
115 return MHD_NO;
116 response = MHD_create_response_from_callback (MHD_SIZE_UNKNOWN,
117 1024,
118 &crc, responseptr, &crcf);
119 *responseptr = response;
120 ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
121 MHD_destroy_response (response);
122 return ret;
123}
124
125static int
126validate (struct CBC cbc, int ebase)
127{
128 int i;
129 char buf[128];
130
131 if (cbc.pos != 128 * 10)
132 return ebase;
133
134 for (i = 0; i < 10; i++)
135 {
136 memset (buf, 'A' + i, 128);
137 if (0 != memcmp (buf, &cbc.buf[i * 128], 128))
138 {
139 fprintf (stderr,
140 "Got `%.*s'\nWant `%.*s'\n",
141 128, buf, 128, &cbc.buf[i * 128]);
142 return ebase * 2;
143 }
144 }
145 return 0;
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 1080, 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:1080/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 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
172 // NOTE: use of CONNECTTIMEOUT without also
173 // setting NOSIGNAL results in really weird
174 // crashes on my system!
175 curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
176 if (CURLE_OK != (errornum = curl_easy_perform (c)))
177 {
178 fprintf (stderr,
179 "curl_easy_perform failed: `%s'\n",
180 curl_easy_strerror (errornum));
181 curl_easy_cleanup (c);
182 MHD_stop_daemon (d);
183 return 2;
184 }
185 curl_easy_cleanup (c);
186 MHD_stop_daemon (d);
187 return validate (cbc, 4);
188}
189
190static int
191testMultithreadedGet ()
192{
193 struct MHD_Daemon *d;
194 CURL *c;
195 char buf[2048];
196 struct CBC cbc;
197 CURLcode errornum;
198
199 cbc.buf = buf;
200 cbc.size = 2048;
201 cbc.pos = 0;
202 d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION | MHD_USE_DEBUG,
203 1081, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END);
204 if (d == NULL)
205 return 16;
206 c = curl_easy_init ();
207 curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:1081/hello_world");
208 curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
209 curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
210 curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
211 curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
212 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
213 curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
214 // NOTE: use of CONNECTTIMEOUT without also
215 // setting NOSIGNAL results in really weird
216 // crashes on my system!
217 curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
218 if (CURLE_OK != (errornum = curl_easy_perform (c)))
219 {
220 fprintf (stderr,
221 "curl_easy_perform failed: `%s'\n",
222 curl_easy_strerror (errornum));
223 curl_easy_cleanup (c);
224 MHD_stop_daemon (d);
225 return 32;
226 }
227 curl_easy_cleanup (c);
228 MHD_stop_daemon (d);
229 return validate (cbc, 64);
230}
231
232static int
233testMultithreadedPoolGet ()
234{
235 struct MHD_Daemon *d;
236 CURL *c;
237 char buf[2048];
238 struct CBC cbc;
239 CURLcode errornum;
240
241 cbc.buf = buf;
242 cbc.size = 2048;
243 cbc.pos = 0;
244 d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG,
245 1081, NULL, NULL, &ahc_echo, "GET",
246 MHD_OPTION_THREAD_POOL_SIZE, 4, MHD_OPTION_END);
247 if (d == NULL)
248 return 16;
249 c = curl_easy_init ();
250 curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:1081/hello_world");
251 curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
252 curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
253 curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
254 curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
255 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
256 curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
257 // NOTE: use of CONNECTTIMEOUT without also
258 // setting NOSIGNAL results in really weird
259 // crashes on my system!
260 curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
261 if (CURLE_OK != (errornum = curl_easy_perform (c)))
262 {
263 fprintf (stderr,
264 "curl_easy_perform failed: `%s'\n",
265 curl_easy_strerror (errornum));
266 curl_easy_cleanup (c);
267 MHD_stop_daemon (d);
268 return 32;
269 }
270 curl_easy_cleanup (c);
271 MHD_stop_daemon (d);
272 return validate (cbc, 64);
273}
274
275static int
276testExternalGet ()
277{
278 struct MHD_Daemon *d;
279 CURL *c;
280 char buf[2048];
281 struct CBC cbc;
282 CURLM *multi;
283 CURLMcode mret;
284 fd_set rs;
285 fd_set ws;
286 fd_set es;
287 int max;
288 int running;
289 struct CURLMsg *msg;
290 time_t start;
291 struct timeval tv;
292
293 multi = NULL;
294 cbc.buf = buf;
295 cbc.size = 2048;
296 cbc.pos = 0;
297 d = MHD_start_daemon (MHD_USE_DEBUG,
298 1082, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END);
299 if (d == NULL)
300 return 256;
301 c = curl_easy_init ();
302 curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:1082/hello_world");
303 curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
304 curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
305 curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
306 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
307 curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
308 curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 5L);
309 // NOTE: use of CONNECTTIMEOUT without also
310 // setting NOSIGNAL results in really weird
311 // crashes on my system!
312 curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
313
314
315 multi = curl_multi_init ();
316 if (multi == NULL)
317 {
318 curl_easy_cleanup (c);
319 MHD_stop_daemon (d);
320 return 512;
321 }
322 mret = curl_multi_add_handle (multi, c);
323 if (mret != CURLM_OK)
324 {
325 curl_multi_cleanup (multi);
326 curl_easy_cleanup (c);
327 MHD_stop_daemon (d);
328 return 1024;
329 }
330 start = time (NULL);
331 while ((time (NULL) - start < 5) && (multi != NULL))
332 {
333 max = 0;
334 FD_ZERO (&rs);
335 FD_ZERO (&ws);
336 FD_ZERO (&es);
337 curl_multi_perform (multi, &running);
338 mret = curl_multi_fdset (multi, &rs, &ws, &es, &max);
339 if (mret != CURLM_OK)
340 {
341 curl_multi_remove_handle (multi, c);
342 curl_multi_cleanup (multi);
343 curl_easy_cleanup (c);
344 MHD_stop_daemon (d);
345 return 2048;
346 }
347 if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &max))
348 {
349 curl_multi_remove_handle (multi, c);
350 curl_multi_cleanup (multi);
351 curl_easy_cleanup (c);
352 MHD_stop_daemon (d);
353 return 4096;
354 }
355 tv.tv_sec = 0;
356 tv.tv_usec = 1000;
357 select (max + 1, &rs, &ws, &es, &tv);
358 curl_multi_perform (multi, &running);
359 if (running == 0)
360 {
361 msg = curl_multi_info_read (multi, &running);
362 if (msg == NULL)
363 break;
364 if (msg->msg == CURLMSG_DONE)
365 {
366 if (msg->data.result != CURLE_OK)
367 printf ("%s failed at %s:%d: `%s'\n",
368 "curl_multi_perform",
369 __FILE__,
370 __LINE__, curl_easy_strerror (msg->data.result));
371 curl_multi_remove_handle (multi, c);
372 curl_multi_cleanup (multi);
373 curl_easy_cleanup (c);
374 c = NULL;
375 multi = NULL;
376 }
377 }
378 MHD_run (d);
379 }
380 if (multi != NULL)
381 {
382 curl_multi_remove_handle (multi, c);
383 curl_easy_cleanup (c);
384 curl_multi_cleanup (multi);
385 }
386 MHD_stop_daemon (d);
387 return validate (cbc, 8192);
388}
389
390
391
392int
393main (int argc, char *const *argv)
394{
395 unsigned int errorCount = 0;
396
397 if (0 != curl_global_init (CURL_GLOBAL_WIN32))
398 return 2;
399 errorCount += testInternalGet ();
400 errorCount += testMultithreadedGet ();
401 errorCount += testMultithreadedPoolGet ();
402 errorCount += testExternalGet ();
403 if (errorCount != 0)
404 fprintf (stderr, "Error (code: %u)\n", errorCount);
405 curl_global_cleanup ();
406 return errorCount != 0; /* 0 == pass */
407}