1/* 2 This file is part of libmicrohttpd 3 Copyright (C) 2007 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_iplimit.c 23 * @brief Testcase for libmicrohttpd GET operations 24 * TODO: test parsing of query 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#ifdef _WIN32 41#ifndef WIN32_LEAN_AND_MEAN 42#define WIN32_LEAN_AND_MEAN 1 43#endif /* !WIN32_LEAN_AND_MEAN */ 44#include <windows.h> 45#endif 46 47#if defined(CPU_COUNT) && (CPU_COUNT+0) < 2 48#undef CPU_COUNT 49#endif 50#if !defined(CPU_COUNT) 51#define CPU_COUNT 2 52#endif 53 54static int oneone; 55 56struct CBC 57{ 58 char *buf; 59 size_t pos; 60 size_t size; 61}; 62 63static size_t 64copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx) 65{ 66 struct CBC *cbc = ctx; 67 68 if (cbc->pos + size * nmemb > cbc->size) 69 return 0; /* overflow */ 70 memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb); 71 cbc->pos += size * nmemb; 72 return size * nmemb; 73} 74 75static int 76ahc_echo (void *cls, 77 struct MHD_Connection *connection, 78 const char *url, 79 const char *method, 80 const char *version, 81 const char *upload_data, size_t *upload_data_size, 82 void **unused) 83{ 84 static int ptr; 85 const char *me = cls; 86 struct MHD_Response *response; 87 int ret; 88 89 if (0 != strcmp (me, method)) 90 return MHD_NO; /* unexpected method */ 91 if (&ptr != *unused) 92 { 93 *unused = &ptr; 94 return MHD_YES; 95 } 96 *unused = NULL; 97 response = MHD_create_response_from_buffer (strlen (url), 98 (void *) url, 99 MHD_RESPMEM_MUST_COPY); 100 ret = MHD_queue_response (connection, MHD_HTTP_OK, response); 101 MHD_destroy_response (response); 102 if (ret == MHD_NO) 103 abort (); 104 return ret; 105} 106 107static int 108testMultithreadedGet () 109{ 110 struct MHD_Daemon *d; 111 char buf[2048]; 112 int k; 113 unsigned int success; 114 unsigned int failure; 115 116 /* Test only valid for HTTP/1.1 (uses persistent connections) */ 117 if (!oneone) 118 return 0; 119 120 d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG, 121 1081, NULL, NULL, 122 &ahc_echo, "GET", 123 MHD_OPTION_PER_IP_CONNECTION_LIMIT, 2, 124 MHD_OPTION_END); 125 if (d == NULL) 126 return 16; 127 128 for (k = 0; k < 3; ++k) 129 { 130 struct CBC cbc[3]; 131 CURL *cenv[3]; 132 int i; 133 134 success = 0; 135 failure = 0; 136 for (i = 0; i < 3; ++i) 137 { 138 CURL *c; 139 CURLcode errornum; 140 141 cenv[i] = c = curl_easy_init (); 142 cbc[i].buf = buf; 143 cbc[i].size = 2048; 144 cbc[i].pos = 0; 145 146 curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:1081/hello_world"); 147 curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, ©Buffer); 148 curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc[i]); 149 curl_easy_setopt (c, CURLOPT_FAILONERROR, 1); 150 curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L); 151 curl_easy_setopt (c, CURLOPT_FORBID_REUSE, 0L); 152 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); 153 curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L); 154 // NOTE: use of CONNECTTIMEOUT without also 155 // setting NOSIGNAL results in really weird 156 // crashes on my system! 157 curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1); 158 159 errornum = curl_easy_perform (c); 160 if (CURLE_OK == errornum) 161 success++; 162 else 163 failure++; 164 } 165 166 /* Cleanup the environments */ 167 for (i = 0; i < 3; ++i) 168 curl_easy_cleanup (cenv[i]); 169 if ( (2 != success) || 170 (1 != failure) ) 171 { 172 fprintf (stderr, 173 "Unexpected number of success (%u) or failure (%u)\n", 174 success, 175 failure); 176 MHD_stop_daemon (d); 177 return 32; 178 } 179 180 sleep(2); 181 182 for (i = 0; i < 2; ++i) 183 { 184 if (cbc[i].pos != strlen ("/hello_world")) 185 { 186 MHD_stop_daemon (d); 187 return 64; 188 } 189 if (0 != strncmp ("/hello_world", cbc[i].buf, strlen ("/hello_world"))) 190 { 191 MHD_stop_daemon (d); 192 return 128; 193 } 194 } 195 } 196 MHD_stop_daemon (d); 197 return 0; 198} 199 200static int 201testMultithreadedPoolGet () 202{ 203 struct MHD_Daemon *d; 204 char buf[2048]; 205 int k; 206 207 /* Test only valid for HTTP/1.1 (uses persistent connections) */ 208 if (!oneone) 209 return 0; 210 211 d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG, 212 1081, NULL, NULL, &ahc_echo, "GET", 213 MHD_OPTION_PER_IP_CONNECTION_LIMIT, 2, 214 MHD_OPTION_THREAD_POOL_SIZE, CPU_COUNT, 215 MHD_OPTION_END); 216 if (d == NULL) 217 return 16; 218 219 for (k = 0; k < 3; ++k) 220 { 221 struct CBC cbc[3]; 222 CURL *cenv[3]; 223 int i; 224 225 for (i = 0; i < 3; ++i) 226 { 227 CURL *c; 228 CURLcode errornum; 229 230 cenv[i] = c = curl_easy_init (); 231 cbc[i].buf = buf; 232 cbc[i].size = 2048; 233 cbc[i].pos = 0; 234 235 curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:1081/hello_world"); 236 curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, ©Buffer); 237 curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc[i]); 238 curl_easy_setopt (c, CURLOPT_FAILONERROR, 1); 239 curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L); 240 curl_easy_setopt (c, CURLOPT_FORBID_REUSE, 0L); 241 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); 242 curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L); 243 // NOTE: use of CONNECTTIMEOUT without also 244 // setting NOSIGNAL results in really weird 245 // crashes on my system! 246 curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1); 247 248 errornum = curl_easy_perform (c); 249 if ( ( (CURLE_OK != errornum) && (i < 2) ) || 250 ( (CURLE_OK == errornum) && (i == 2) ) ) 251 { 252 int j; 253 254 /* First 2 should succeed */ 255 if (i < 2) 256 fprintf (stderr, 257 "curl_easy_perform failed: `%s'\n", 258 curl_easy_strerror (errornum)); 259 260 /* Last request should have failed */ 261 else 262 fprintf (stderr, 263 "No error on IP address over limit\n"); 264 265 for (j = 0; j < i; ++j) 266 curl_easy_cleanup (cenv[j]); 267 MHD_stop_daemon (d); 268 return 32; 269 } 270 } 271 272 /* Cleanup the environments */ 273 for (i = 0; i < 3; ++i) 274 curl_easy_cleanup (cenv[i]); 275 276 sleep(2); 277 278 for (i = 0; i < 2; ++i) 279 { 280 if (cbc[i].pos != strlen ("/hello_world")) 281 { 282 MHD_stop_daemon (d); 283 return 64; 284 } 285 if (0 != strncmp ("/hello_world", cbc[i].buf, strlen ("/hello_world"))) 286 { 287 MHD_stop_daemon (d); 288 return 128; 289 } 290 } 291 292 293 } 294 MHD_stop_daemon (d); 295 return 0; 296} 297 298int 299main (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 |= testMultithreadedGet (); 308 errorCount |= testMultithreadedPoolGet (); 309 if (errorCount != 0) 310 fprintf (stderr, "Error (code: %u)\n", errorCount); 311 curl_global_cleanup (); 312 return errorCount != 0; /* 0 == pass */ 313} 314