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_get.c 23 * @brief Testcase for libmicrohttpd GET 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 struct CBC 44 { 45 char *buf; 46 size_t pos; 47 size_t size; 48 }; 49 50 static size_t 51 copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx) 52 { 53 struct CBC *cbc = ctx; 54 55 if (cbc->pos + size * nmemb > cbc->size) 56 return 0; /* overflow */ 57 memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb); 58 cbc->pos += size * nmemb; 59 return size * nmemb; 60 } 61 62 static int 63 ahc_echo (void *cls, 64 struct MHD_Connection *connection, 65 const char *url, 66 const char *method, 67 const char *version, 68 const char *upload_data, size_t *upload_data_size, 69 void **unused) 70 { 71 static int ptr; 72 const char *me = cls; 73 struct MHD_Response *response; 74 int ret; 75 76 if (0 != strcmp (me, method)) 77 return MHD_NO; /* unexpected method */ 78 if (&ptr != *unused) 79 { 80 *unused = &ptr; 81 return MHD_YES; 82 } 83 *unused = NULL; 84 response = MHD_create_response_from_buffer (strlen (url), 85 (void *) url, 86 MHD_RESPMEM_MUST_COPY); 87 ret = MHD_queue_response (connection, MHD_HTTP_OK, response); 88 MHD_destroy_response (response); 89 if (ret == MHD_NO) 90 abort (); 91 return ret; 92 } 93 94 95 static int 96 testInternalGet () 97 { 98 struct MHD_Daemon *d; 99 CURL *c; 100 char buf[2048]; 101 struct CBC cbc; 102 int i; 103 104 cbc.buf = buf; 105 cbc.size = 2048; 106 cbc.pos = 0; 107 d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY /* | MHD_USE_DEBUG */ , 108 11080, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END); 109 if (d == NULL) 110 return 1; 111 zzuf_socat_start (); 112 for (i = 0; i < LOOP_COUNT; i++) 113 { 114 fprintf (stderr, "."); 115 c = curl_easy_init (); 116 curl_easy_setopt (c, CURLOPT_URL, "http://localhost:11081/hello_world"); 117 curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, ©Buffer); 118 curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); 119 curl_easy_setopt (c, CURLOPT_FAILONERROR, 1); 120 curl_easy_setopt (c, CURLOPT_TIMEOUT_MS, CURL_TIMEOUT); 121 curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT_MS, CURL_TIMEOUT); 122 if (oneone) 123 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); 124 else 125 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); 126 // NOTE: use of CONNECTTIMEOUT without also 127 // setting NOSIGNAL results in really weird 128 // crashes on my system! 129 curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1); 130 curl_easy_perform (c); 131 curl_easy_cleanup (c); 132 } 133 fprintf (stderr, "\n"); 134 zzuf_socat_stop (); 135 MHD_stop_daemon (d); 136 return 0; 137 } 138 139 static int 140 testMultithreadedGet () 141 { 142 struct MHD_Daemon *d; 143 CURL *c; 144 char buf[2048]; 145 struct CBC cbc; 146 int i; 147 148 cbc.buf = buf; 149 cbc.size = 2048; 150 cbc.pos = 0; 151 d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION /* | MHD_USE_DEBUG */ , 152 11080, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END); 153 if (d == NULL) 154 return 16; 155 zzuf_socat_start (); 156 for (i = 0; i < LOOP_COUNT; i++) 157 { 158 fprintf (stderr, "."); 159 c = curl_easy_init (); 160 curl_easy_setopt (c, CURLOPT_URL, "http://localhost:11081/hello_world"); 161 curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, ©Buffer); 162 curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); 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 184 static int 185 testExternalGet () 186 { 187 struct MHD_Daemon *d; 188 CURL *c; 189 char buf[2048]; 190 struct CBC cbc; 191 CURLM *multi; 192 CURLMcode mret; 193 fd_set rs; 194 fd_set ws; 195 fd_set es; 196 int max; 197 int running; 198 time_t start; 199 struct timeval tv; 200 int i; 201 202 multi = NULL; 203 cbc.buf = buf; 204 cbc.size = 2048; 205 cbc.pos = 0; 206 d = MHD_start_daemon (MHD_NO_FLAG /* | MHD_USE_DEBUG */ , 207 11080, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END); 208 if (d == NULL) 209 return 256; 210 multi = curl_multi_init (); 211 if (multi == NULL) 212 { 213 MHD_stop_daemon (d); 214 return 512; 215 } 216 zzuf_socat_start (); 217 for (i = 0; i < LOOP_COUNT; i++) 218 { 219 fprintf (stderr, "."); 220 c = curl_easy_init (); 221 curl_easy_setopt (c, CURLOPT_URL, "http://localhost:11081/hello_world"); 222 curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, ©Buffer); 223 curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); 224 curl_easy_setopt (c, CURLOPT_FAILONERROR, 1); 225 if (oneone) 226 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); 227 else 228 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); 229 curl_easy_setopt (c, CURLOPT_TIMEOUT_MS, CURL_TIMEOUT); 230 curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT_MS, CURL_TIMEOUT); 231 // NOTE: use of CONNECTTIMEOUT without also 232 // setting NOSIGNAL results in really weird 233 // crashes on my system! 234 curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1); 235 mret = curl_multi_add_handle (multi, c); 236 if (mret != CURLM_OK) 237 { 238 curl_multi_cleanup (multi); 239 curl_easy_cleanup (c); 240 zzuf_socat_stop (); 241 MHD_stop_daemon (d); 242 return 1024; 243 } 244 start = time (NULL); 245 while ((time (NULL) - start < 5) && (c != NULL)) 246 { 247 max = 0; 248 FD_ZERO (&rs); 249 FD_ZERO (&ws); 250 FD_ZERO (&es); 251 curl_multi_perform (multi, &running); 252 mret = curl_multi_fdset (multi, &rs, &ws, &es, &max); 253 if (mret != CURLM_OK) 254 { 255 curl_multi_remove_handle (multi, c); 256 curl_multi_cleanup (multi); 257 curl_easy_cleanup (c); 258 zzuf_socat_stop (); 259 MHD_stop_daemon (d); 260 return 2048; 261 } 262 if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &max)) 263 { 264 curl_multi_remove_handle (multi, c); 265 curl_multi_cleanup (multi); 266 curl_easy_cleanup (c); 267 zzuf_socat_stop (); 268 MHD_stop_daemon (d); 269 return 4096; 270 } 271 tv.tv_sec = 0; 272 tv.tv_usec = 1000; 273 select (max + 1, &rs, &ws, &es, &tv); 274 curl_multi_perform (multi, &running); 275 if (running == 0) 276 { 277 curl_multi_info_read (multi, &running); 278 curl_multi_remove_handle (multi, c); 279 curl_easy_cleanup (c); 280 c = NULL; 281 } 282 MHD_run (d); 283 } 284 if (c != NULL) 285 { 286 curl_multi_remove_handle (multi, c); 287 curl_easy_cleanup (c); 288 } 289 } 290 fprintf (stderr, "\n"); 291 curl_multi_cleanup (multi); 292 zzuf_socat_stop (); 293 MHD_stop_daemon (d); 294 return 0; 295 } 296 297 298 int 299 main (int argc, char *const *argv) 300 { 301 unsigned int errorCount = 0; 302 303 oneone = (NULL != strrchr (argv[0], (int) '/')) ? 304 (NULL != strstr (strrchr (argv[0], (int) '/'), "11")) : 0; 305 if (0 != curl_global_init (CURL_GLOBAL_WIN32)) 306 return 2; 307 errorCount += testInternalGet (); 308 errorCount += testMultithreadedGet (); 309 errorCount += testExternalGet (); 310 if (errorCount != 0) 311 fprintf (stderr, "Error (code: %u)\n", errorCount); 312 curl_global_cleanup (); 313 return errorCount != 0; /* 0 == pass */ 314 } 315