1 /*
2 This file is part of libmicrohttpd
3 Copyright (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_put_chunked.c
23 * @brief Testcase for libmicrohttpd PUT operations with chunked encoding
24 * for the upload data
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 struct CBC
48 {
49 char *buf;
50 size_t pos;
51 size_t size;
52 };
53
54 static size_t
putBuffer(void * stream,size_t size,size_t nmemb,void * ptr)55 putBuffer (void *stream, size_t size, size_t nmemb, void *ptr)
56 {
57 unsigned int *pos = ptr;
58 unsigned int wrt;
59
60 wrt = size * nmemb;
61 if (wrt > 8 - (*pos))
62 wrt = 8 - (*pos);
63 if (wrt > 4)
64 wrt = 4; /* only send half at first => force multiple chunks! */
65 memcpy (stream, &("Hello123"[*pos]), wrt);
66 (*pos) += wrt;
67 return wrt;
68 }
69
70 static size_t
copyBuffer(void * ptr,size_t size,size_t nmemb,void * ctx)71 copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx)
72 {
73 struct CBC *cbc = ctx;
74
75 if (cbc->pos + size * nmemb > cbc->size)
76 return 0; /* overflow */
77 memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb);
78 cbc->pos += size * nmemb;
79 return size * nmemb;
80 }
81
82 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)83 ahc_echo (void *cls,
84 struct MHD_Connection *connection,
85 const char *url,
86 const char *method,
87 const char *version,
88 const char *upload_data, size_t *upload_data_size,
89 void **unused)
90 {
91 int *done = cls;
92 struct MHD_Response *response;
93 int ret;
94 int have;
95
96 if (0 != strcmp ("PUT", method))
97 return MHD_NO; /* unexpected method */
98 if ((*done) < 8)
99 {
100 have = *upload_data_size;
101 if (have + *done > 8)
102 {
103 printf ("Invalid upload data `%8s'!\n", upload_data);
104 return MHD_NO;
105 }
106 if (0 == memcmp (upload_data, &"Hello123"[*done], have))
107 {
108 *done += have;
109 *upload_data_size = 0;
110 }
111 else
112 {
113 printf ("Invalid upload data `%8s'!\n", upload_data);
114 return MHD_NO;
115 }
116 #if 0
117 fprintf (stderr, "Not ready for response: %u/%u\n", *done, 8);
118 #endif
119 return MHD_YES;
120 }
121 response = MHD_create_response_from_buffer (strlen (url),
122 (void *) url,
123 MHD_RESPMEM_MUST_COPY);
124 ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
125 MHD_destroy_response (response);
126 return ret;
127 }
128
129
130 static int
testInternalPut()131 testInternalPut ()
132 {
133 struct MHD_Daemon *d;
134 CURL *c;
135 char buf[2048];
136 struct CBC cbc;
137 unsigned int pos = 0;
138 int done_flag = 0;
139 CURLcode errornum;
140
141 cbc.buf = buf;
142 cbc.size = 2048;
143 cbc.pos = 0;
144 d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG,
145 11080,
146 NULL, NULL, &ahc_echo, &done_flag, MHD_OPTION_END);
147 if (d == NULL)
148 return 1;
149 c = curl_easy_init ();
150 curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:11080/hello_world");
151 curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, ©Buffer);
152 curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
153 curl_easy_setopt (c, CURLOPT_READFUNCTION, &putBuffer);
154 curl_easy_setopt (c, CURLOPT_READDATA, &pos);
155 curl_easy_setopt (c, CURLOPT_UPLOAD, 1L);
156 /*
157 // by not giving the file size, we force chunking!
158 curl_easy_setopt (c, CURLOPT_INFILESIZE_LARGE, (curl_off_t) 8L);
159 */
160 curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
161 curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
162 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
163 curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
164 // NOTE: use of CONNECTTIMEOUT without also
165 // setting NOSIGNAL results in really weird
166 // crashes on my system!
167 curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
168 if (CURLE_OK != (errornum = curl_easy_perform (c)))
169 {
170 fprintf (stderr,
171 "curl_easy_perform failed: `%s'\n",
172 curl_easy_strerror (errornum));
173 curl_easy_cleanup (c);
174 MHD_stop_daemon (d);
175 return 2;
176 }
177 curl_easy_cleanup (c);
178 MHD_stop_daemon (d);
179 if (cbc.pos != strlen ("/hello_world"))
180 return 4;
181 if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
182 return 8;
183 return 0;
184 }
185
186 static int
testMultithreadedPut()187 testMultithreadedPut ()
188 {
189 struct MHD_Daemon *d;
190 CURL *c;
191 char buf[2048];
192 struct CBC cbc;
193 unsigned int pos = 0;
194 int done_flag = 0;
195 CURLcode errornum;
196
197 cbc.buf = buf;
198 cbc.size = 2048;
199 cbc.pos = 0;
200 d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION | MHD_USE_DEBUG,
201 11081,
202 NULL, NULL, &ahc_echo, &done_flag, MHD_OPTION_END);
203 if (d == NULL)
204 return 16;
205 c = curl_easy_init ();
206 curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:11081/hello_world");
207 curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, ©Buffer);
208 curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
209 curl_easy_setopt (c, CURLOPT_READFUNCTION, &putBuffer);
210 curl_easy_setopt (c, CURLOPT_READDATA, &pos);
211 curl_easy_setopt (c, CURLOPT_UPLOAD, 1L);
212 /*
213 // by not giving the file size, we force chunking!
214 curl_easy_setopt (c, CURLOPT_INFILESIZE_LARGE, (curl_off_t) 8L);
215 */
216 curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
217 curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
218 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
219 curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
220 // NOTE: use of CONNECTTIMEOUT without also
221 // setting NOSIGNAL results in really weird
222 // crashes on my system!
223 curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
224 if (CURLE_OK != (errornum = curl_easy_perform (c)))
225 {
226 fprintf (stderr,
227 "curl_easy_perform failed: `%s'\n",
228 curl_easy_strerror (errornum));
229 curl_easy_cleanup (c);
230 MHD_stop_daemon (d);
231 return 32;
232 }
233 curl_easy_cleanup (c);
234 MHD_stop_daemon (d);
235 if (cbc.pos != strlen ("/hello_world"))
236 return 64;
237 if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
238 return 128;
239
240 return 0;
241 }
242
243 static int
testMultithreadedPoolPut()244 testMultithreadedPoolPut ()
245 {
246 struct MHD_Daemon *d;
247 CURL *c;
248 char buf[2048];
249 struct CBC cbc;
250 unsigned int pos = 0;
251 int done_flag = 0;
252 CURLcode errornum;
253
254 cbc.buf = buf;
255 cbc.size = 2048;
256 cbc.pos = 0;
257 d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG,
258 11081,
259 NULL, NULL, &ahc_echo, &done_flag,
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:11081/hello_world");
265 curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, ©Buffer);
266 curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
267 curl_easy_setopt (c, CURLOPT_READFUNCTION, &putBuffer);
268 curl_easy_setopt (c, CURLOPT_READDATA, &pos);
269 curl_easy_setopt (c, CURLOPT_UPLOAD, 1L);
270 /*
271 // by not giving the file size, we force chunking!
272 curl_easy_setopt (c, CURLOPT_INFILESIZE_LARGE, (curl_off_t) 8L);
273 */
274 curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
275 curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
276 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
277 curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
278 // NOTE: use of CONNECTTIMEOUT without also
279 // setting NOSIGNAL results in really weird
280 // crashes on my system!
281 curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
282 if (CURLE_OK != (errornum = curl_easy_perform (c)))
283 {
284 fprintf (stderr,
285 "curl_easy_perform failed: `%s'\n",
286 curl_easy_strerror (errornum));
287 curl_easy_cleanup (c);
288 MHD_stop_daemon (d);
289 return 32;
290 }
291 curl_easy_cleanup (c);
292 MHD_stop_daemon (d);
293 if (cbc.pos != strlen ("/hello_world"))
294 return 64;
295 if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
296 return 128;
297
298 return 0;
299 }
300
301
302 static int
testExternalPut()303 testExternalPut ()
304 {
305 struct MHD_Daemon *d;
306 CURL *c;
307 char buf[2048];
308 struct CBC cbc;
309 CURLM *multi;
310 CURLMcode mret;
311 fd_set rs;
312 fd_set ws;
313 fd_set es;
314 MHD_socket max;
315 int running;
316 struct CURLMsg *msg;
317 time_t start;
318 struct timeval tv;
319 unsigned int pos = 0;
320 int done_flag = 0;
321
322 multi = NULL;
323 cbc.buf = buf;
324 cbc.size = 2048;
325 cbc.pos = 0;
326 d = MHD_start_daemon (MHD_USE_DEBUG,
327 11082,
328 NULL, NULL, &ahc_echo, &done_flag, MHD_OPTION_END);
329 if (d == NULL)
330 return 256;
331 c = curl_easy_init ();
332 curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:11082/hello_world");
333 curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, ©Buffer);
334 curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
335 curl_easy_setopt (c, CURLOPT_READFUNCTION, &putBuffer);
336 curl_easy_setopt (c, CURLOPT_READDATA, &pos);
337 curl_easy_setopt (c, CURLOPT_UPLOAD, 1L);
338 /*
339 // by not giving the file size, we force chunking!
340 curl_easy_setopt (c, CURLOPT_INFILESIZE_LARGE, (curl_off_t) 8L);
341 */
342 curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
343 curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
344 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
345 curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
346 // NOTE: use of CONNECTTIMEOUT without also
347 // setting NOSIGNAL results in really weird
348 // crashes on my system!
349 curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
350
351
352 multi = curl_multi_init ();
353 if (multi == NULL)
354 {
355 curl_easy_cleanup (c);
356 MHD_stop_daemon (d);
357 return 512;
358 }
359 mret = curl_multi_add_handle (multi, c);
360 if (mret != CURLM_OK)
361 {
362 curl_multi_cleanup (multi);
363 curl_easy_cleanup (c);
364 MHD_stop_daemon (d);
365 return 1024;
366 }
367 start = time (NULL);
368 while ((time (NULL) - start < 5) && (multi != NULL))
369 {
370 max = 0;
371 FD_ZERO (&rs);
372 FD_ZERO (&ws);
373 FD_ZERO (&es);
374 curl_multi_perform (multi, &running);
375 mret = curl_multi_fdset (multi, &rs, &ws, &es, &max);
376 if (mret != CURLM_OK)
377 {
378 curl_multi_remove_handle (multi, c);
379 curl_multi_cleanup (multi);
380 curl_easy_cleanup (c);
381 MHD_stop_daemon (d);
382 return 2048;
383 }
384 if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &max))
385 {
386 curl_multi_remove_handle (multi, c);
387 curl_multi_cleanup (multi);
388 curl_easy_cleanup (c);
389 MHD_stop_daemon (d);
390 return 4096;
391 }
392 tv.tv_sec = 0;
393 tv.tv_usec = 1000;
394 select (max + 1, &rs, &ws, &es, &tv);
395 curl_multi_perform (multi, &running);
396 if (running == 0)
397 {
398 msg = curl_multi_info_read (multi, &running);
399 if (msg == NULL)
400 break;
401 if (msg->msg == CURLMSG_DONE)
402 {
403 if (msg->data.result != CURLE_OK)
404 printf ("%s failed at %s:%d: `%s'\n",
405 "curl_multi_perform",
406 __FILE__,
407 __LINE__, curl_easy_strerror (msg->data.result));
408 curl_multi_remove_handle (multi, c);
409 curl_multi_cleanup (multi);
410 curl_easy_cleanup (c);
411 c = NULL;
412 multi = NULL;
413 }
414 }
415 MHD_run (d);
416 }
417 if (multi != NULL)
418 {
419 curl_multi_remove_handle (multi, c);
420 curl_easy_cleanup (c);
421 curl_multi_cleanup (multi);
422 }
423 MHD_stop_daemon (d);
424 if (cbc.pos != strlen ("/hello_world"))
425 return 8192;
426 if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
427 return 16384;
428 return 0;
429 }
430
431
432
433 int
main(int argc,char * const * argv)434 main (int argc, char *const *argv)
435 {
436 unsigned int errorCount = 0;
437
438 if (0 != curl_global_init (CURL_GLOBAL_WIN32))
439 return 2;
440 errorCount += testInternalPut ();
441 errorCount += testMultithreadedPut ();
442 errorCount += testMultithreadedPoolPut ();
443 errorCount += testExternalPut ();
444 if (errorCount != 0)
445 fprintf (stderr, "Error (code: %u)\n", errorCount);
446 curl_global_cleanup ();
447 return errorCount != 0; /* 0 == pass */
448 }
449