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
54 static int oneone;
55
56 struct CBC
57 {
58 char *buf;
59 size_t pos;
60 size_t size;
61 };
62
63 static size_t
copyBuffer(void * ptr,size_t size,size_t nmemb,void * ctx)64 copyBuffer (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
75 static int
ahc_echo(void * cls,struct MHD_Connection * connection,const char * url,const char * method,const char * version,const char * upload_data,size_t * upload_data_size,void ** unused)76 ahc_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
107 static int
testMultithreadedGet()108 testMultithreadedGet ()
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
200 static int
testMultithreadedPoolGet()201 testMultithreadedPoolGet ()
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
298 int
main(int argc,char * const * argv)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 |= 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