test_iplimit.c (9518B)
1 /* 2 This file is part of libmicrohttpd 3 Copyright (C) 2007 Christian Grothoff 4 Copyright (C) 2014-2022 Evgeny Grin (Karlson2k) 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., 51 Franklin Street, Fifth Floor, 19 Boston, MA 02110-1301, USA. 20 */ 21 22 /** 23 * @file test_iplimit.c 24 * @brief Testcase for libmicrohttpd limits per IP 25 * @author Christian Grothoff 26 * @author Karlson2k (Evgeny Grin) 27 */ 28 29 #include "MHD_config.h" 30 #include "platform.h" 31 #include <curl/curl.h> 32 #include <microhttpd.h> 33 #include <stdlib.h> 34 #include <string.h> 35 #include <time.h> 36 #include "mhd_has_in_name.h" 37 38 /* Turn any MHD_PANIC() or failing mhd_assert() reached from this 39 test into a marked, classifiable test error (TESTING.md, P5). */ 40 #include "mhd_panic_tripwire.h" 41 42 #ifndef WINDOWS 43 #include <unistd.h> 44 #endif 45 46 #ifdef _WIN32 47 #ifndef WIN32_LEAN_AND_MEAN 48 #define WIN32_LEAN_AND_MEAN 1 49 #endif /* !WIN32_LEAN_AND_MEAN */ 50 #include <windows.h> 51 #endif 52 53 #if defined(MHD_CPU_COUNT) && (MHD_CPU_COUNT + 0) < 2 54 #undef MHD_CPU_COUNT 55 #endif 56 #if ! defined(MHD_CPU_COUNT) 57 #define MHD_CPU_COUNT 2 58 #endif 59 60 static int oneone; 61 62 struct CBC 63 { 64 char *buf; 65 size_t pos; 66 size_t size; 67 }; 68 69 static size_t 70 copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx) 71 { 72 struct CBC *cbc = ctx; 73 74 if (cbc->pos + size * nmemb > cbc->size) 75 return 0; /* overflow */ 76 memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb); 77 cbc->pos += size * nmemb; 78 return size * nmemb; 79 } 80 81 82 static enum MHD_Result 83 ahc_echo (void *cls, 84 struct MHD_Connection *connection, 85 const char *url, 86 const char *method, 87 const char *version, 88 const char *upload_data, size_t *upload_data_size, 89 void **req_cls) 90 { 91 static int ptr; 92 struct MHD_Response *response; 93 enum MHD_Result ret; 94 (void) cls; 95 (void) version; (void) upload_data; (void) upload_data_size; /* Unused. Silent compiler warning. */ 96 97 if (0 != strcmp (MHD_HTTP_METHOD_GET, method)) 98 return MHD_NO; /* unexpected method */ 99 if (&ptr != *req_cls) 100 { 101 *req_cls = &ptr; 102 return MHD_YES; 103 } 104 *req_cls = NULL; 105 response = MHD_create_response_from_buffer_copy (strlen (url), 106 (const void *) url); 107 ret = MHD_queue_response (connection, MHD_HTTP_OK, response); 108 MHD_destroy_response (response); 109 if (ret == MHD_NO) 110 abort (); 111 return ret; 112 } 113 114 115 static unsigned int 116 testMultithreadedGet (void) 117 { 118 struct MHD_Daemon *d; 119 char buf[2048]; 120 int k; 121 unsigned int success; 122 unsigned int failure; 123 uint16_t port; 124 125 if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT)) 126 port = 0; 127 else 128 { 129 port = 1260; 130 if (oneone) 131 port += 5; 132 } 133 134 /* Test only valid for HTTP/1.1 (uses persistent connections) */ 135 if (! oneone) 136 return 0; 137 138 d = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG, 139 port, NULL, NULL, 140 &ahc_echo, NULL, 141 MHD_OPTION_PER_IP_CONNECTION_LIMIT, (unsigned int) 2, 142 MHD_OPTION_END); 143 if (d == NULL) 144 return 16; 145 if (0 == port) 146 { 147 const union MHD_DaemonInfo *dinfo; 148 dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT); 149 if ((NULL == dinfo) || (0 == dinfo->port) ) 150 { 151 MHD_stop_daemon (d); return 32; 152 } 153 port = dinfo->port; 154 } 155 156 for (k = 0; k < 3; ++k) 157 { 158 struct CBC cbc[3]; 159 CURL *cenv[3]; 160 int i; 161 162 success = 0; 163 failure = 0; 164 for (i = 0; i < 3; ++i) 165 { 166 CURL *c; 167 CURLcode errornum; 168 169 cenv[i] = c = curl_easy_init (); 170 cbc[i].buf = buf; 171 cbc[i].size = 2048; 172 cbc[i].pos = 0; 173 174 curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1/hello_world"); 175 curl_easy_setopt (c, CURLOPT_PORT, (long) port); 176 curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, ©Buffer); 177 curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc[i]); 178 curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L); 179 curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L); 180 curl_easy_setopt (c, CURLOPT_FORBID_REUSE, 0L); 181 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); 182 curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L); 183 /* NOTE: use of CONNECTTIMEOUT without also 184 * setting NOSIGNAL results in really weird 185 * crashes on my system! */ 186 curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L); 187 188 errornum = curl_easy_perform (c); 189 if (CURLE_OK == errornum) 190 success++; 191 else 192 failure++; 193 } 194 195 /* Cleanup the environments */ 196 for (i = 0; i < 3; ++i) 197 curl_easy_cleanup (cenv[i]); 198 if ( (2 != success) || 199 (1 != failure) ) 200 { 201 fprintf (stderr, 202 "Unexpected number of success (%u) or failure (%u)\n", 203 success, 204 failure); 205 MHD_stop_daemon (d); 206 return 32; 207 } 208 209 (void) sleep (2); 210 211 for (i = 0; i < 2; ++i) 212 { 213 if (cbc[i].pos != strlen ("/hello_world")) 214 { 215 MHD_stop_daemon (d); 216 return 64; 217 } 218 if (0 != strncmp ("/hello_world", cbc[i].buf, strlen ("/hello_world"))) 219 { 220 MHD_stop_daemon (d); 221 return 128; 222 } 223 } 224 } 225 MHD_stop_daemon (d); 226 return 0; 227 } 228 229 230 static unsigned int 231 testMultithreadedPoolGet (void) 232 { 233 struct MHD_Daemon *d; 234 char buf[2048]; 235 int k; 236 uint16_t port; 237 if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT)) 238 port = 0; 239 else 240 { 241 port = 1261; 242 if (oneone) 243 port += 5; 244 } 245 246 /* Test only valid for HTTP/1.1 (uses persistent connections) */ 247 if (! oneone) 248 return 0; 249 250 d = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG, 251 port, NULL, NULL, &ahc_echo, NULL, 252 MHD_OPTION_PER_IP_CONNECTION_LIMIT, (unsigned int) 2, 253 MHD_OPTION_THREAD_POOL_SIZE, MHD_CPU_COUNT, 254 MHD_OPTION_END); 255 if (d == NULL) 256 return 16; 257 if (0 == port) 258 { 259 const union MHD_DaemonInfo *dinfo; 260 dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT); 261 if ((NULL == dinfo) || (0 == dinfo->port) ) 262 { 263 MHD_stop_daemon (d); return 32; 264 } 265 port = dinfo->port; 266 } 267 268 for (k = 0; k < 3; ++k) 269 { 270 struct CBC cbc[3]; 271 CURL *cenv[3]; 272 int i; 273 274 for (i = 0; i < 3; ++i) 275 { 276 CURL *c; 277 CURLcode errornum; 278 279 cenv[i] = c = curl_easy_init (); 280 cbc[i].buf = buf; 281 cbc[i].size = 2048; 282 cbc[i].pos = 0; 283 284 curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1/hello_world"); 285 curl_easy_setopt (c, CURLOPT_PORT, (long) port); 286 curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, ©Buffer); 287 curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc[i]); 288 curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L); 289 curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L); 290 curl_easy_setopt (c, CURLOPT_FORBID_REUSE, 0L); 291 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); 292 curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L); 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, 1L); 297 298 errornum = curl_easy_perform (c); 299 if ( ( (CURLE_OK != errornum) && (i < 2) ) || 300 ( (CURLE_OK == errornum) && (i == 2) ) ) 301 { 302 int j; 303 304 /* First 2 should succeed */ 305 if (i < 2) 306 fprintf (stderr, 307 "curl_easy_perform failed: `%s'\n", 308 curl_easy_strerror (errornum)); 309 310 /* Last request should have failed */ 311 else 312 fprintf (stderr, 313 "No error on IP address over limit\n"); 314 315 for (j = 0; j < i; ++j) 316 curl_easy_cleanup (cenv[j]); 317 MHD_stop_daemon (d); 318 return 32; 319 } 320 } 321 322 /* Cleanup the environments */ 323 for (i = 0; i < 3; ++i) 324 curl_easy_cleanup (cenv[i]); 325 326 (void) sleep (2); 327 328 for (i = 0; i < 2; ++i) 329 { 330 if (cbc[i].pos != strlen ("/hello_world")) 331 { 332 MHD_stop_daemon (d); 333 return 64; 334 } 335 if (0 != strncmp ("/hello_world", cbc[i].buf, strlen ("/hello_world"))) 336 { 337 MHD_stop_daemon (d); 338 return 128; 339 } 340 } 341 342 343 } 344 MHD_stop_daemon (d); 345 return 0; 346 } 347 348 349 int 350 main (int argc, char *const *argv) 351 { 352 unsigned int errorCount = 0; 353 (void) argc; /* Unused. Silent compiler warning. */ 354 355 if ((NULL == argv) || (0 == argv[0])) 356 return 99; 357 oneone = has_in_name (argv[0], "11"); 358 if (0 != curl_global_init (CURL_GLOBAL_WIN32)) 359 return 2; 360 errorCount |= testMultithreadedGet (); 361 errorCount |= testMultithreadedPoolGet (); 362 if (errorCount != 0) 363 fprintf (stderr, "Error (code: %u)\n", errorCount); 364 curl_global_cleanup (); 365 return (0 == errorCount) ? 0 : 1; /* 0 == pass */ 366 }