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