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