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