1 2 /* 3 This file is part of libmicrohttpd 4 Copyright (C) 2007 Christian Grothoff 5 6 libmicrohttpd is free software; you can redistribute it and/or modify 7 it under the terms of the GNU General Public License as published 8 by the Free Software Foundation; either version 2, or (at your 9 option) any later version. 10 11 libmicrohttpd is distributed in the hope that it will be useful, but 12 WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 General Public License for more details. 15 16 You should have received a copy of the GNU General Public License 17 along with libmicrohttpd; see the file COPYING. If not, write to the 18 Free Software Foundation, Inc., 59 Temple Place - Suite 330, 19 Boston, MA 02111-1307, USA. 20 */ 21 22 /** 23 * @file test_process_headers.c 24 * @brief Testcase for HTTP header access 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 static int oneone; 48 49 struct CBC 50 { 51 char *buf; 52 size_t pos; 53 size_t size; 54 }; 55 56 static size_t 57 copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx) 58 { 59 struct CBC *cbc = ctx; 60 61 if (cbc->pos + size * nmemb > cbc->size) 62 return 0; /* overflow */ 63 memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb); 64 cbc->pos += size * nmemb; 65 return size * nmemb; 66 } 67 68 static int 69 kv_cb (void *cls, enum MHD_ValueKind kind, const char *key, const char *value) 70 { 71 if ((0 == strcmp (key, MHD_HTTP_HEADER_HOST)) && 72 (0 == strcmp (value, "127.0.0.1:21080")) && (kind == MHD_HEADER_KIND)) 73 { 74 *((int *) cls) = 1; 75 return MHD_NO; 76 } 77 return MHD_YES; 78 } 79 80 static int 81 ahc_echo (void *cls, 82 struct MHD_Connection *connection, 83 const char *url, 84 const char *method, 85 const char *version, 86 const char *upload_data, size_t *upload_data_size, 87 void **unused) 88 { 89 static int ptr; 90 const char *me = cls; 91 struct MHD_Response *response; 92 int ret; 93 const char *hdr; 94 95 if (0 != strcmp (me, method)) 96 return MHD_NO; /* unexpected method */ 97 if (&ptr != *unused) 98 { 99 *unused = &ptr; 100 return MHD_YES; 101 } 102 *unused = NULL; 103 ret = 0; 104 MHD_get_connection_values (connection, MHD_HEADER_KIND, &kv_cb, &ret); 105 if (ret != 1) 106 abort (); 107 hdr = MHD_lookup_connection_value (connection, MHD_HEADER_KIND, "NotFound"); 108 if (hdr != NULL) 109 abort (); 110 hdr = MHD_lookup_connection_value (connection, 111 MHD_HEADER_KIND, MHD_HTTP_HEADER_ACCEPT); 112 if ((hdr == NULL) || (0 != strcmp (hdr, "*/*"))) 113 abort (); 114 hdr = MHD_lookup_connection_value (connection, 115 MHD_HEADER_KIND, MHD_HTTP_HEADER_HOST); 116 if ((hdr == NULL) || (0 != strcmp (hdr, "127.0.0.1:21080"))) 117 abort (); 118 MHD_set_connection_value (connection, 119 MHD_HEADER_KIND, "FakeHeader", "NowPresent"); 120 hdr = MHD_lookup_connection_value (connection, 121 MHD_HEADER_KIND, "FakeHeader"); 122 if ((hdr == NULL) || (0 != strcmp (hdr, "NowPresent"))) 123 abort (); 124 125 response = MHD_create_response_from_buffer (strlen (url), 126 (void *) url, 127 MHD_RESPMEM_MUST_COPY); 128 MHD_add_response_header (response, "MyHeader", "MyValue"); 129 hdr = MHD_get_response_header (response, "MyHeader"); 130 if (0 != strcmp ("MyValue", hdr)) 131 abort (); 132 MHD_add_response_header (response, "MyHeader", "MyValueToo"); 133 if (MHD_YES != MHD_del_response_header (response, "MyHeader", "MyValue")) 134 abort (); 135 hdr = MHD_get_response_header (response, "MyHeader"); 136 if (0 != strcmp ("MyValueToo", hdr)) 137 abort (); 138 if (1 != MHD_get_response_headers (response, NULL, NULL)) 139 abort (); 140 ret = MHD_queue_response (connection, MHD_HTTP_OK, response); 141 MHD_destroy_response (response); 142 if (ret == MHD_NO) 143 abort (); 144 return ret; 145 } 146 147 148 static int 149 testInternalGet () 150 { 151 struct MHD_Daemon *d; 152 CURL *c; 153 char buf[2048]; 154 struct CBC cbc; 155 CURLcode errornum; 156 157 cbc.buf = buf; 158 cbc.size = 2048; 159 cbc.pos = 0; 160 d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG, 161 21080, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END); 162 if (d == NULL) 163 return 1; 164 c = curl_easy_init (); 165 curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:21080/hello_world"); 166 curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, ©Buffer); 167 curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); 168 curl_easy_setopt (c, CURLOPT_FAILONERROR, 1); 169 curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L); 170 curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L); 171 if (oneone) 172 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); 173 else 174 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); 175 /* NOTE: use of CONNECTTIMEOUT without also 176 setting NOSIGNAL results in really weird 177 crashes on my system! */ 178 curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1); 179 if (CURLE_OK != (errornum = curl_easy_perform (c))) 180 { 181 fprintf (stderr, 182 "curl_easy_perform failed: `%s'\n", 183 curl_easy_strerror (errornum)); 184 curl_easy_cleanup (c); 185 MHD_stop_daemon (d); 186 return 2; 187 } 188 curl_easy_cleanup (c); 189 MHD_stop_daemon (d); 190 if (cbc.pos != strlen ("/hello_world")) 191 return 4; 192 if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world"))) 193 return 8; 194 return 0; 195 } 196 197 static int 198 testMultithreadedGet () 199 { 200 struct MHD_Daemon *d; 201 CURL *c; 202 char buf[2048]; 203 struct CBC cbc; 204 CURLcode errornum; 205 206 cbc.buf = buf; 207 cbc.size = 2048; 208 cbc.pos = 0; 209 d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION | MHD_USE_DEBUG, 210 21080, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END); 211 if (d == NULL) 212 return 16; 213 c = curl_easy_init (); 214 curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:21080/hello_world"); 215 curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, ©Buffer); 216 curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); 217 curl_easy_setopt (c, CURLOPT_FAILONERROR, 1); 218 curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L); 219 if (oneone) 220 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); 221 else 222 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); 223 curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L); 224 /* NOTE: use of CONNECTTIMEOUT without also 225 setting NOSIGNAL results in really weird 226 crashes on my system! */ 227 curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1); 228 if (CURLE_OK != (errornum = curl_easy_perform (c))) 229 { 230 fprintf (stderr, 231 "curl_easy_perform failed: `%s'\n", 232 curl_easy_strerror (errornum)); 233 curl_easy_cleanup (c); 234 MHD_stop_daemon (d); 235 return 32; 236 } 237 curl_easy_cleanup (c); 238 MHD_stop_daemon (d); 239 if (cbc.pos != strlen ("/hello_world")) 240 return 64; 241 if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world"))) 242 return 128; 243 return 0; 244 } 245 246 static int 247 testMultithreadedPoolGet () 248 { 249 struct MHD_Daemon *d; 250 CURL *c; 251 char buf[2048]; 252 struct CBC cbc; 253 CURLcode errornum; 254 255 cbc.buf = buf; 256 cbc.size = 2048; 257 cbc.pos = 0; 258 d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG, 259 21080, NULL, NULL, &ahc_echo, "GET", 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:21080/hello_world"); 265 curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, ©Buffer); 266 curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); 267 curl_easy_setopt (c, CURLOPT_FAILONERROR, 1); 268 curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L); 269 if (oneone) 270 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); 271 else 272 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); 273 curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L); 274 /* NOTE: use of CONNECTTIMEOUT without also 275 setting NOSIGNAL results in really weird 276 crashes on my system! */ 277 curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1); 278 if (CURLE_OK != (errornum = curl_easy_perform (c))) 279 { 280 fprintf (stderr, 281 "curl_easy_perform failed: `%s'\n", 282 curl_easy_strerror (errornum)); 283 curl_easy_cleanup (c); 284 MHD_stop_daemon (d); 285 return 32; 286 } 287 curl_easy_cleanup (c); 288 MHD_stop_daemon (d); 289 if (cbc.pos != strlen ("/hello_world")) 290 return 64; 291 if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world"))) 292 return 128; 293 return 0; 294 } 295 296 static int 297 testExternalGet () 298 { 299 struct MHD_Daemon *d; 300 CURL *c; 301 char buf[2048]; 302 struct CBC cbc; 303 CURLM *multi; 304 CURLMcode mret; 305 fd_set rs; 306 fd_set ws; 307 fd_set es; 308 MHD_socket max; 309 int running; 310 struct CURLMsg *msg; 311 time_t start; 312 struct timeval tv; 313 314 multi = NULL; 315 cbc.buf = buf; 316 cbc.size = 2048; 317 cbc.pos = 0; 318 d = MHD_start_daemon (MHD_USE_DEBUG, 319 21080, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END); 320 if (d == NULL) 321 return 256; 322 c = curl_easy_init (); 323 curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:21080/hello_world"); 324 curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, ©Buffer); 325 curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); 326 curl_easy_setopt (c, CURLOPT_FAILONERROR, 1); 327 if (oneone) 328 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); 329 else 330 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); 331 curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L); 332 curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L); 333 /* NOTE: use of CONNECTTIMEOUT without also 334 setting NOSIGNAL results in really weird 335 crashes on my system! */ 336 curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1); 337 338 339 multi = curl_multi_init (); 340 if (multi == NULL) 341 { 342 curl_easy_cleanup (c); 343 MHD_stop_daemon (d); 344 return 512; 345 } 346 mret = curl_multi_add_handle (multi, c); 347 if (mret != CURLM_OK) 348 { 349 curl_multi_cleanup (multi); 350 curl_easy_cleanup (c); 351 MHD_stop_daemon (d); 352 return 1024; 353 } 354 start = time (NULL); 355 while ((time (NULL) - start < 5) && (multi != NULL)) 356 { 357 max = 0; 358 FD_ZERO (&rs); 359 FD_ZERO (&ws); 360 FD_ZERO (&es); 361 curl_multi_perform (multi, &running); 362 mret = curl_multi_fdset (multi, &rs, &ws, &es, &max); 363 if (mret != CURLM_OK) 364 { 365 curl_multi_remove_handle (multi, c); 366 curl_multi_cleanup (multi); 367 curl_easy_cleanup (c); 368 MHD_stop_daemon (d); 369 return 2048; 370 } 371 if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &max)) 372 { 373 curl_multi_remove_handle (multi, c); 374 curl_multi_cleanup (multi); 375 curl_easy_cleanup (c); 376 MHD_stop_daemon (d); 377 return 4096; 378 } 379 tv.tv_sec = 0; 380 tv.tv_usec = 1000; 381 select (max + 1, &rs, &ws, &es, &tv); 382 curl_multi_perform (multi, &running); 383 if (running == 0) 384 { 385 msg = curl_multi_info_read (multi, &running); 386 if (msg == NULL) 387 break; 388 if (msg->msg == CURLMSG_DONE) 389 { 390 if (msg->data.result != CURLE_OK) 391 printf ("%s failed at %s:%d: `%s'\n", 392 "curl_multi_perform", 393 __FILE__, 394 __LINE__, curl_easy_strerror (msg->data.result)); 395 curl_multi_remove_handle (multi, c); 396 curl_multi_cleanup (multi); 397 curl_easy_cleanup (c); 398 c = NULL; 399 multi = NULL; 400 } 401 } 402 MHD_run (d); 403 } 404 if (multi != NULL) 405 { 406 curl_multi_remove_handle (multi, c); 407 curl_easy_cleanup (c); 408 curl_multi_cleanup (multi); 409 } 410 MHD_stop_daemon (d); 411 if (cbc.pos != strlen ("/hello_world")) 412 return 8192; 413 if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world"))) 414 return 16384; 415 return 0; 416 } 417 418 419 420 int 421 main (int argc, char *const *argv) 422 { 423 unsigned int errorCount = 0; 424 425 oneone = (NULL != strrchr (argv[0], (int) '/')) ? 426 (NULL != strstr (strrchr (argv[0], (int) '/'), "11")) : 0; 427 errorCount += testMultithreadedGet (); 428 errorCount += testMultithreadedPoolGet (); 429 errorCount += testExternalGet (); 430 if (errorCount != 0) 431 fprintf (stderr, "Error (code: %u)\n", errorCount); 432 curl_global_cleanup (); 433 return errorCount != 0; /* 0 == pass */ 434 } 435