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