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 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 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 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 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 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 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 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 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