diff options
author | Christian Grothoff <christian@grothoff.org> | 2015-04-04 15:50:15 +0000 |
---|---|---|
committer | Christian Grothoff <christian@grothoff.org> | 2015-04-04 15:50:15 +0000 |
commit | 47c6b680069bd2e0c174151d3078e60cc3b043d7 (patch) | |
tree | 441536c8294d876efdd14de7c96518742ace8116 | |
parent | 7d1a24e75480f07c644b1d5444c9755d79137067 (diff) | |
download | libmicrohttpd-47c6b680069bd2e0c174151d3078e60cc3b043d7.tar.gz libmicrohttpd-47c6b680069bd2e0c174151d3078e60cc3b043d7.zip |
fix thread-pool connection-limit shutdown issue, adding testcase
-rw-r--r-- | ChangeLog | 7 | ||||
-rw-r--r-- | src/microhttpd/daemon.c | 12 | ||||
-rw-r--r-- | src/testcurl/Makefile.am | 8 | ||||
-rw-r--r-- | src/testcurl/test_concurrent_stop.c | 228 | ||||
-rw-r--r-- | src/testcurl/test_iplimit.c | 58 |
5 files changed, 280 insertions, 33 deletions
@@ -1,3 +1,10 @@ | |||
1 | Sat 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 | |||
1 | Fri Apr 3 12:55:31 CEST 2015 | 8 | Fri 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 = \ | |||
85 | test_start_stop_LDADD = \ | 86 | test_start_stop_LDADD = \ |
86 | $(top_builddir)/src/microhttpd/libmicrohttpd.la | 87 | $(top_builddir)/src/microhttpd/libmicrohttpd.la |
87 | 88 | ||
89 | test_concurrent_stop_SOURCES = \ | ||
90 | test_concurrent_stop.c | ||
91 | test_concurrent_stop_LDADD = \ | ||
92 | $(top_builddir)/src/microhttpd/libmicrohttpd.la \ | ||
93 | @LIBCURL@ | ||
94 | |||
88 | test_options_SOURCES = \ | 95 | test_options_SOURCES = \ |
89 | test_options.c | 96 | test_options.c |
90 | test_options_LDADD = \ | 97 | test_options_LDADD = \ |
@@ -292,4 +299,3 @@ test_timeout_SOURCES = \ | |||
292 | test_timeout_LDADD = \ | 299 | test_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 | */ | ||
55 | static int oneone; | ||
56 | |||
57 | /** | ||
58 | * Response to return (re-used). | ||
59 | */ | ||
60 | static struct MHD_Response *response; | ||
61 | |||
62 | |||
63 | static size_t | ||
64 | copyBuffer (void *ptr, | ||
65 | size_t size, size_t nmemb, | ||
66 | void *ctx) | ||
67 | { | ||
68 | return size * nmemb; | ||
69 | } | ||
70 | |||
71 | static int | ||
72 | ahc_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 | |||
101 | static pid_t | ||
102 | do_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, ©Buffer); | ||
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 | |||
156 | static void | ||
157 | join_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 | |||
168 | static int | ||
169 | testMultithreadedGet (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 | |||
186 | static int | ||
187 | testMultithreadedPoolGet (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 | |||
209 | int | ||
210 | main (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 (); |