aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2015-04-04 15:50:15 +0000
committerChristian Grothoff <christian@grothoff.org>2015-04-04 15:50:15 +0000
commit47c6b680069bd2e0c174151d3078e60cc3b043d7 (patch)
tree441536c8294d876efdd14de7c96518742ace8116
parent7d1a24e75480f07c644b1d5444c9755d79137067 (diff)
downloadlibmicrohttpd-47c6b680069bd2e0c174151d3078e60cc3b043d7.tar.gz
libmicrohttpd-47c6b680069bd2e0c174151d3078e60cc3b043d7.zip
fix thread-pool connection-limit shutdown issue, adding testcase
-rw-r--r--ChangeLog7
-rw-r--r--src/microhttpd/daemon.c12
-rw-r--r--src/testcurl/Makefile.am8
-rw-r--r--src/testcurl/test_concurrent_stop.c228
-rw-r--r--src/testcurl/test_iplimit.c58
5 files changed, 280 insertions, 33 deletions
diff --git a/ChangeLog b/ChangeLog
index 8f81473f..e4807d32 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,10 @@
1Sat Apr 4 17:48:13 CEST 2015
2 Fix issue in thread-pool mode where a MHD_stop_daemon()
3 might not reach threads that stopped listening because
4 we hit the maximum number of concurrent connections and
5 the option MHD_USE_PIPE_FOR_SHUTDOWN was also not used.
6 Testcase added as well. -CG
7
1Fri Apr 3 12:55:31 CEST 2015 8Fri Apr 3 12:55:31 CEST 2015
2 Update HTTPS testcases to avoid SSLv3, as SSLv3 is dead. 9 Update HTTPS testcases to avoid SSLv3, as SSLv3 is dead.
3 10
diff --git a/src/microhttpd/daemon.c b/src/microhttpd/daemon.c
index 7f4449e5..bdf08e00 100644
--- a/src/microhttpd/daemon.c
+++ b/src/microhttpd/daemon.c
@@ -2224,9 +2224,13 @@ MHD_select (struct MHD_Daemon *daemon,
2224 return MHD_NO; 2224 return MHD_NO;
2225 2225
2226 /* If we're at the connection limit, no need to 2226 /* If we're at the connection limit, no need to
2227 accept new connections. */ 2227 accept new connections; however, make sure
2228 if ( (daemon->connections == daemon->connection_limit) && 2228 we do not miss the shutdown, so only do this
2229 (MHD_INVALID_SOCKET != daemon->socket_fd) ) 2229 optimization if we have a shutdown signaling
2230 pipe. */
2231 if ( (MHD_INVALID_SOCKET != daemon->socket_fd) &&
2232 (daemon->connections == daemon->connection_limit) &&
2233 (0 != (daemon->options & MHD_USE_PIPE_FOR_SHUTDOWN)) )
2230 FD_CLR (daemon->socket_fd, &rs); 2234 FD_CLR (daemon->socket_fd, &rs);
2231 } 2235 }
2232 else 2236 else
@@ -2672,7 +2676,7 @@ MHD_epoll (struct MHD_Daemon *daemon,
2672 while ( (MHD_YES == MHD_accept_connection (daemon)) && 2676 while ( (MHD_YES == MHD_accept_connection (daemon)) &&
2673 (daemon->connections < daemon->connection_limit) && 2677 (daemon->connections < daemon->connection_limit) &&
2674 (series_length < 128) ) 2678 (series_length < 128) )
2675 series_length++; 2679 series_length++;
2676 } 2680 }
2677 } 2681 }
2678 } 2682 }
diff --git a/src/testcurl/Makefile.am b/src/testcurl/Makefile.am
index d418206d..2bedaaca 100644
--- a/src/testcurl/Makefile.am
+++ b/src/testcurl/Makefile.am
@@ -30,6 +30,7 @@ check_PROGRAMS = \
30 test_get_sendfile \ 30 test_get_sendfile \
31 test_urlparse \ 31 test_urlparse \
32 test_put \ 32 test_put \
33 test_concurrent_stop \
33 test_process_headers \ 34 test_process_headers \
34 test_process_arguments \ 35 test_process_arguments \
35 test_parse_cookies \ 36 test_parse_cookies \
@@ -85,6 +86,12 @@ test_start_stop_SOURCES = \
85test_start_stop_LDADD = \ 86test_start_stop_LDADD = \
86 $(top_builddir)/src/microhttpd/libmicrohttpd.la 87 $(top_builddir)/src/microhttpd/libmicrohttpd.la
87 88
89test_concurrent_stop_SOURCES = \
90 test_concurrent_stop.c
91test_concurrent_stop_LDADD = \
92 $(top_builddir)/src/microhttpd/libmicrohttpd.la \
93 @LIBCURL@
94
88test_options_SOURCES = \ 95test_options_SOURCES = \
89 test_options.c 96 test_options.c
90test_options_LDADD = \ 97test_options_LDADD = \
@@ -292,4 +299,3 @@ test_timeout_SOURCES = \
292test_timeout_LDADD = \ 299test_timeout_LDADD = \
293 $(top_builddir)/src/microhttpd/libmicrohttpd.la \ 300 $(top_builddir)/src/microhttpd/libmicrohttpd.la \
294 @LIBCURL@ 301 @LIBCURL@
295
diff --git a/src/testcurl/test_concurrent_stop.c b/src/testcurl/test_concurrent_stop.c
new file mode 100644
index 00000000..b9dbc03b
--- /dev/null
+++ b/src/testcurl/test_concurrent_stop.c
@@ -0,0 +1,228 @@
1/*
2 This file is part of libmicrohttpd
3 Copyright (C) 2007, 2009, 2011, 2015 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 3, 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_concurrent_stop.c
23 * @brief test stopping server while concurrent GETs are ongoing
24 * @author Christian Grothoff
25 */
26#include "MHD_config.h"
27#include "platform.h"
28#include <curl/curl.h>
29#include <microhttpd.h>
30#include <stdlib.h>
31#include <string.h>
32#include <time.h>
33#include "gauger.h"
34
35#ifdef CPU_COUNT
36#undef CPU_COUNT
37#endif
38#define CPU_COUNT 40
39
40
41/**
42 * How many rounds of operations do we do for each
43 * test (total number of requests will be ROUNDS * PAR).
44 */
45#define ROUNDS 50000
46
47/**
48 * How many requests do we do in parallel?
49 */
50#define PAR CPU_COUNT
51
52/**
53 * Do we use HTTP 1.1?
54 */
55static int oneone;
56
57/**
58 * Response to return (re-used).
59 */
60static struct MHD_Response *response;
61
62
63static size_t
64copyBuffer (void *ptr,
65 size_t size, size_t nmemb,
66 void *ctx)
67{
68 return size * nmemb;
69}
70
71static int
72ahc_echo (void *cls,
73 struct MHD_Connection *connection,
74 const char *url,
75 const char *method,
76 const char *version,
77 const char *upload_data, size_t *upload_data_size,
78 void **unused)
79{
80 static int ptr;
81 const char *me = cls;
82 int ret;
83
84 if (0 != strcmp (me, method))
85 return MHD_NO; /* unexpected method */
86 if (&ptr != *unused)
87 {
88 *unused = &ptr;
89 return MHD_YES;
90 }
91 *unused = NULL;
92 ret = MHD_queue_response (connection,
93 MHD_HTTP_OK,
94 response);
95 if (ret == MHD_NO)
96 abort ();
97 return ret;
98}
99
100
101static pid_t
102do_gets (int port)
103{
104 pid_t ret;
105 CURL *c;
106 CURLcode errornum;
107 unsigned int i;
108 unsigned int j;
109 pid_t par[PAR];
110 char url[64];
111
112 sprintf(url, "http://127.0.0.1:%d/hello_world", port);
113
114 ret = fork ();
115 if (ret == -1) abort ();
116 if (ret != 0)
117 return ret;
118 for (j=0;j<PAR;j++)
119 {
120 par[j] = fork ();
121 if (par[j] == 0)
122 {
123 for (i=0;i<ROUNDS;i++)
124 {
125 c = curl_easy_init ();
126 curl_easy_setopt (c, CURLOPT_URL, url);
127 curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
128 curl_easy_setopt (c, CURLOPT_WRITEDATA, NULL);
129 curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
130 curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
131 if (oneone)
132 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
133 else
134 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
135 curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
136 /* NOTE: use of CONNECTTIMEOUT without also
137 setting NOSIGNAL results in really weird
138 crashes on my system! */
139 curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
140 if (CURLE_OK != (errornum = curl_easy_perform (c)))
141 {
142 curl_easy_cleanup (c);
143 _exit (1);
144 }
145 curl_easy_cleanup (c);
146 }
147 _exit (0);
148 }
149 }
150 for (j=0;j<PAR;j++)
151 waitpid (par[j], NULL, 0);
152 _exit (0);
153}
154
155
156static void
157join_gets (pid_t pid)
158{
159 int status;
160
161 status = 1;
162 waitpid (pid, &status, 0);
163 if (0 != status)
164 abort ();
165}
166
167
168static int
169testMultithreadedGet (int port, int poll_flag)
170{
171 struct MHD_Daemon *d;
172 pid_t p;
173
174 d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION | MHD_USE_DEBUG | poll_flag,
175 port, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END);
176 if (d == NULL)
177 return 16;
178 p = do_gets (port);
179 sleep (1);
180 MHD_stop_daemon (d);
181 join_gets (p);
182 return 0;
183}
184
185
186static int
187testMultithreadedPoolGet (int port, int poll_flag)
188{
189 struct MHD_Daemon *d;
190 pid_t p;
191
192 d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG | poll_flag,
193 port,
194 NULL, NULL,
195 &ahc_echo, "GET",
196 MHD_OPTION_THREAD_POOL_SIZE, CPU_COUNT,
197 MHD_OPTION_END);
198 if (d == NULL)
199 return 16;
200 p = do_gets (port);
201 sleep (1);
202 MHD_stop_daemon (d);
203 join_gets (p);
204 return 0;
205}
206
207
208
209int
210main (int argc, char *const *argv)
211{
212 unsigned int errorCount = 0;
213 int port = 1081;
214
215 oneone = NULL != strstr (argv[0], "11");
216 if (0 != curl_global_init (CURL_GLOBAL_WIN32))
217 return 2;
218 response = MHD_create_response_from_buffer (strlen ("/hello_world"),
219 "/hello_world",
220 MHD_RESPMEM_MUST_COPY);
221 // errorCount += testMultithreadedGet (port++, 0);
222 errorCount += testMultithreadedPoolGet (port++, 0);
223 MHD_destroy_response (response);
224 if (errorCount != 0)
225 fprintf (stderr, "Error (code: %u)\n", errorCount);
226 curl_global_cleanup ();
227 return errorCount != 0; /* 0 == pass */
228}
diff --git a/src/testcurl/test_iplimit.c b/src/testcurl/test_iplimit.c
index 05d61c08..f8923719 100644
--- a/src/testcurl/test_iplimit.c
+++ b/src/testcurl/test_iplimit.c
@@ -110,13 +110,16 @@ testMultithreadedGet ()
110 struct MHD_Daemon *d; 110 struct MHD_Daemon *d;
111 char buf[2048]; 111 char buf[2048];
112 int k; 112 int k;
113 unsigned int success;
114 unsigned int failure;
113 115
114 /* Test only valid for HTTP/1.1 (uses persistent connections) */ 116 /* Test only valid for HTTP/1.1 (uses persistent connections) */
115 if (!oneone) 117 if (!oneone)
116 return 0; 118 return 0;
117 119
118 d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG, 120 d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG,
119 1081, NULL, NULL, &ahc_echo, "GET", 121 1081, NULL, NULL,
122 &ahc_echo, "GET",
120 MHD_OPTION_PER_IP_CONNECTION_LIMIT, 2, 123 MHD_OPTION_PER_IP_CONNECTION_LIMIT, 2,
121 MHD_OPTION_END); 124 MHD_OPTION_END);
122 if (d == NULL) 125 if (d == NULL)
@@ -128,11 +131,16 @@ testMultithreadedGet ()
128 CURL *cenv[3]; 131 CURL *cenv[3];
129 int i; 132 int i;
130 133
134 fprintf (stderr,
135 "Iteration %d\n",
136 k);
137 success = 0;
138 failure = 0;
131 for (i = 0; i < 3; ++i) 139 for (i = 0; i < 3; ++i)
132 { 140 {
133 CURL *c; 141 CURL *c;
134 CURLcode errornum; 142 CURLcode errornum;
135 143
136 cenv[i] = c = curl_easy_init (); 144 cenv[i] = c = curl_easy_init ();
137 cbc[i].buf = buf; 145 cbc[i].buf = buf;
138 cbc[i].size = 2048; 146 cbc[i].size = 2048;
@@ -152,32 +160,25 @@ testMultithreadedGet ()
152 curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1); 160 curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
153 161
154 errornum = curl_easy_perform (c); 162 errornum = curl_easy_perform (c);
155 if ( ( (CURLE_OK != errornum) && (i < 2) ) || 163 if (CURLE_OK == errornum)
156 ( (CURLE_OK == errornum) && (i == 2) ) ) 164 success++;
157 { 165 else
158 int j; 166 failure++;
159
160 /* First 2 should succeed */
161 if (i < 2)
162 fprintf (stderr,
163 "curl_easy_perform failed: `%s'\n",
164 curl_easy_strerror (errornum));
165
166 /* Last request should have failed */
167 else
168 fprintf (stderr,
169 "No error on IP address over limit\n");
170
171 for (j = 0; j < i; ++j)
172 curl_easy_cleanup (cenv[j]);
173 MHD_stop_daemon (d);
174 return 32;
175 }
176 } 167 }
177 168
178 /* Cleanup the environments */ 169 /* Cleanup the environments */
179 for (i = 0; i < 3; ++i) 170 for (i = 0; i < 3; ++i)
180 curl_easy_cleanup (cenv[i]); 171 curl_easy_cleanup (cenv[i]);
172 if ( (2 != success) ||
173 (1 != failure) )
174 {
175 fprintf (stderr,
176 "Unexpected number of success (%u) or failure (%u)\n",
177 success,
178 failure);
179 MHD_stop_daemon (d);
180 return 32;
181 }
181 182
182 sleep(2); 183 sleep(2);
183 184
@@ -194,8 +195,6 @@ testMultithreadedGet ()
194 return 128; 195 return 128;
195 } 196 }
196 } 197 }
197
198
199 } 198 }
200 MHD_stop_daemon (d); 199 MHD_stop_daemon (d);
201 return 0; 200 return 0;
@@ -226,11 +225,14 @@ testMultithreadedPoolGet ()
226 CURL *cenv[3]; 225 CURL *cenv[3];
227 int i; 226 int i;
228 227
228 fprintf (stderr,
229 "Iteration %d\n",
230 k);
229 for (i = 0; i < 3; ++i) 231 for (i = 0; i < 3; ++i)
230 { 232 {
231 CURL *c; 233 CURL *c;
232 CURLcode errornum; 234 CURLcode errornum;
233 235
234 cenv[i] = c = curl_easy_init (); 236 cenv[i] = c = curl_easy_init ();
235 cbc[i].buf = buf; 237 cbc[i].buf = buf;
236 cbc[i].size = 2048; 238 cbc[i].size = 2048;
@@ -307,8 +309,8 @@ main (int argc, char *const *argv)
307 oneone = NULL != strstr (argv[0], "11"); 309 oneone = NULL != strstr (argv[0], "11");
308 if (0 != curl_global_init (CURL_GLOBAL_WIN32)) 310 if (0 != curl_global_init (CURL_GLOBAL_WIN32))
309 return 2; 311 return 2;
310 errorCount += testMultithreadedGet (); 312 errorCount |= testMultithreadedGet ();
311 errorCount += testMultithreadedPoolGet (); 313 errorCount |= testMultithreadedPoolGet ();
312 if (errorCount != 0) 314 if (errorCount != 0)
313 fprintf (stderr, "Error (code: %u)\n", errorCount); 315 fprintf (stderr, "Error (code: %u)\n", errorCount);
314 curl_global_cleanup (); 316 curl_global_cleanup ();