aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2016-09-04 15:05:41 +0000
committerChristian Grothoff <christian@grothoff.org>2016-09-04 15:05:41 +0000
commit39bc8f5c76f844e56a04d5993ccfee9f2b41d17e (patch)
treec52459a1ca25accef1565de668d95fd9f0cffed5
parent1f2539eed6da8dcf7f4f1ede8ab14c278f9b0c5e (diff)
downloadlibmicrohttpd-39bc8f5c76f844e56a04d5993ccfee9f2b41d17e.tar.gz
libmicrohttpd-39bc8f5c76f844e56a04d5993ccfee9f2b41d17e.zip
factor out shared test logic, test with external select as well
-rw-r--r--src/microhttpd/Makefile.am2
-rw-r--r--src/microhttpd/test_upgrade.c380
-rw-r--r--src/microhttpd/test_upgrade_common.c451
-rw-r--r--src/microhttpd/test_upgrade_ssl.c383
4 files changed, 524 insertions, 692 deletions
diff --git a/src/microhttpd/Makefile.am b/src/microhttpd/Makefile.am
index b60ff81e..13c2fa65 100644
--- a/src/microhttpd/Makefile.am
+++ b/src/microhttpd/Makefile.am
@@ -8,6 +8,8 @@ AM_CFLAGS = $(HIDDEN_VISIBILITY_CFLAGS)
8lib_LTLIBRARIES = \ 8lib_LTLIBRARIES = \
9 libmicrohttpd.la 9 libmicrohttpd.la
10 10
11EXTRA_DIST = test_upgrade_common.c
12
11noinst_DATA = 13noinst_DATA =
12MOSTLYCLEANFILES = 14MOSTLYCLEANFILES =
13 15
diff --git a/src/microhttpd/test_upgrade.c b/src/microhttpd/test_upgrade.c
index c6005407..91b85b1e 100644
--- a/src/microhttpd/test_upgrade.c
+++ b/src/microhttpd/test_upgrade.c
@@ -4,7 +4,7 @@
4 4
5 libmicrohttpd is free software; you can redistribute it and/or modify 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 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 7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version. 8 option) any later version.
9 9
10 libmicrohttpd is distributed in the hope that it will be useful, but 10 libmicrohttpd is distributed in the hope that it will be useful, but
@@ -39,337 +39,18 @@
39#include <netinet/in.h> 39#include <netinet/in.h>
40#include <netinet/ip.h> 40#include <netinet/ip.h>
41#include "mhd_sockets.h" 41#include "mhd_sockets.h"
42#include "test_upgrade_common.c"
42 43
43 44
44/** 45/**
45 * Thread we use to run the interaction with the upgraded socket. 46 * Test upgrading a connection.
46 */
47static pthread_t pt;
48
49/**
50 * Will be set to the upgraded socket.
51 */
52static MHD_socket usock;
53
54/**
55 * Thread we use to run the interaction with the upgraded socket.
56 */
57static pthread_t pt_client;
58
59
60/**
61 * Change itc FD options to be non-blocking.
62 *
63 * @param fd the FD to manipulate
64 * @return non-zero if succeeded, zero otherwise
65 */
66static void
67make_blocking (MHD_socket fd)
68{
69 int flags;
70
71 flags = fcntl (fd, F_GETFL);
72 if (-1 == flags)
73 return;
74 if ((flags & ~O_NONBLOCK) != flags)
75 fcntl (fd, F_SETFL, flags & ~O_NONBLOCK);
76}
77
78
79static void
80send_all (MHD_socket sock,
81 const char *text)
82{
83 size_t len = strlen (text);
84 ssize_t ret;
85
86 make_blocking (sock);
87 for (size_t off = 0; off < len; off += ret)
88 {
89 ret = write (sock,
90 &text[off],
91 len - off);
92 if (-1 == ret)
93 {
94 if (EAGAIN == errno)
95 {
96 ret = 0;
97 continue;
98 }
99 abort ();
100 }
101 }
102}
103
104
105/**
106 * Read character-by-character until we
107 * get '\r\n\r\n'.
108 */
109static void
110recv_hdr (MHD_socket sock)
111{
112 unsigned int i;
113 char next;
114 char c;
115 ssize_t ret;
116
117 make_blocking (sock);
118 next = '\r';
119 i = 0;
120 while (i < 4)
121 {
122 ret = read (sock,
123 &c,
124 1);
125 if (-1 == ret)
126 {
127 if (EAGAIN == errno)
128 {
129 ret = 0;
130 continue;
131 }
132 abort ();
133 }
134 if (0 == ret)
135 continue;
136 if (c == next)
137 {
138 i++;
139 if (next == '\r')
140 next = '\n';
141 else
142 next = '\r';
143 continue;
144 }
145 if (c == '\r')
146 {
147 i = 1;
148 next = '\n';
149 continue;
150 }
151 i = 0;
152 next = '\r';
153 }
154}
155
156
157static void
158recv_all (MHD_socket sock,
159 const char *text)
160{
161 size_t len = strlen (text);
162 char buf[len];
163 ssize_t ret;
164
165 make_blocking (sock);
166 for (size_t off = 0; off < len; off += ret)
167 {
168 ret = read (sock,
169 &buf[off],
170 len - off);
171 if (-1 == ret)
172 {
173 if (EAGAIN == errno)
174 {
175 ret = 0;
176 continue;
177 }
178 abort ();
179 }
180 }
181 if (0 != strncmp (text, buf, len))
182 abort();
183}
184
185
186/**
187 * Main function for the thread that runs the interaction with
188 * the upgraded socket.
189 *
190 * @param cls the handle for the upgrade
191 */
192static void *
193run_usock (void *cls)
194{
195 struct MHD_UpgradeResponseHandle *urh = cls;
196
197 send_all (usock,
198 "Hello");
199 recv_all (usock,
200 "World");
201 send_all (usock,
202 "Finished");
203 MHD_upgrade_action (urh,
204 MHD_UPGRADE_ACTION_CLOSE);
205 return NULL;
206}
207
208
209/**
210 * Main function for the thread that runs the client-side of the
211 * interaction with the upgraded socket.
212 *
213 * @param cls the client socket
214 */
215static void *
216run_usock_client (void *cls)
217{
218 MHD_socket *sock = cls;
219
220 send_all (*sock,
221 "GET / HTTP/1.1\r\nConnection: Upgrade\r\n\r\n");
222 recv_hdr (*sock);
223 recv_all (*sock,
224 "Hello");
225 send_all (*sock,
226 "World");
227 recv_all (*sock,
228 "Finished");
229 MHD_socket_close_ (*sock);
230 return NULL;
231}
232
233
234/**
235 * Function called after a protocol "upgrade" response was sent
236 * successfully and the socket should now be controlled by some
237 * protocol other than HTTP.
238 *
239 * Any data received on the socket will be made available in
240 * 'data_in'. The function should update 'data_in_size' to
241 * reflect the number of bytes consumed from 'data_in' (the remaining
242 * bytes will be made available in the next call to the handler).
243 *
244 * Any data that should be transmitted on the socket should be
245 * stored in 'data_out'. '*data_out_size' is initially set to
246 * the available buffer space in 'data_out'. It should be set to
247 * the number of bytes stored in 'data_out' (which can be zero).
248 *
249 * The return value is a BITMASK that indicates how the function
250 * intends to interact with the event loop. It can request to be
251 * notified for reading, writing, request to UNCORK the send buffer
252 * (which MHD is allowed to ignore, if it is not possible to uncork on
253 * the local platform), to wait for the 'external' select loop to
254 * trigger another round. It is also possible to specify "no events"
255 * to terminate the connection; in this case, the
256 * #MHD_RequestCompletedCallback will be called and all resources of
257 * the connection will be released.
258 * 47 *
259 * Except when in 'thread-per-connection' mode, implementations 48 * @param flags which event loop style should be tested
260 * of this function should never block (as it will still be called 49 * @param pool size of the thread pool, 0 to disable
261 * from within the main event loop).
262 *
263 * @param cls closure, whatever was given to #MHD_create_response_for_upgrade().
264 * @param connection original HTTP connection handle,
265 * giving the function a last chance
266 * to inspect the original HTTP request
267 * @param con_cls last value left in `*con_cls` in the `MHD_AccessHandlerCallback`
268 * @param extra_in if we happened to have read bytes after the
269 * HTTP header already (because the client sent
270 * more than the HTTP header of the request before
271 * we sent the upgrade response),
272 * these are the extra bytes already read from @a sock
273 * by MHD. The application should treat these as if
274 * it had read them from @a sock.
275 * @param extra_in_size number of bytes in @a extra_in
276 * @param sock socket to use for bi-directional communication
277 * with the client. For HTTPS, this may not be a socket
278 * that is directly connected to the client and thus certain
279 * operations (TCP-specific setsockopt(), getsockopt(), etc.)
280 * may not work as expected (as the socket could be from a
281 * socketpair() or a TCP-loopback)
282 * @param urh argument for #MHD_upgrade_action()s on this @a connection.
283 * Applications must eventually use this function to perform the
284 * close() action on the @a sock.
285 */
286static void
287upgrade_cb (void *cls,
288 struct MHD_Connection *connection,
289 void *con_cls,
290 const char *extra_in,
291 size_t extra_in_size,
292 MHD_socket sock,
293 struct MHD_UpgradeResponseHandle *urh)
294{
295 usock = sock;
296 if (0 != extra_in_size)
297 abort ();
298 pthread_create (&pt,
299 NULL,
300 &run_usock,
301 urh);
302}
303
304
305/**
306 * A client has requested the given url using the given method
307 * (#MHD_HTTP_METHOD_GET, #MHD_HTTP_METHOD_PUT,
308 * #MHD_HTTP_METHOD_DELETE, #MHD_HTTP_METHOD_POST, etc). The callback
309 * must call MHD callbacks to provide content to give back to the
310 * client and return an HTTP status code (i.e. #MHD_HTTP_OK,
311 * #MHD_HTTP_NOT_FOUND, etc.).
312 *
313 * @param cls argument given together with the function
314 * pointer when the handler was registered with MHD
315 * @param url the requested url
316 * @param method the HTTP method used (#MHD_HTTP_METHOD_GET,
317 * #MHD_HTTP_METHOD_PUT, etc.)
318 * @param version the HTTP version string (i.e.
319 * #MHD_HTTP_VERSION_1_1)
320 * @param upload_data the data being uploaded (excluding HEADERS,
321 * for a POST that fits into memory and that is encoded
322 * with a supported encoding, the POST data will NOT be
323 * given in upload_data and is instead available as
324 * part of #MHD_get_connection_values; very large POST
325 * data *will* be made available incrementally in
326 * @a upload_data)
327 * @param upload_data_size set initially to the size of the
328 * @a upload_data provided; the method must update this
329 * value to the number of bytes NOT processed;
330 * @param con_cls pointer that the callback can set to some
331 * address and that will be preserved by MHD for future
332 * calls for this request; since the access handler may
333 * be called many times (i.e., for a PUT/POST operation
334 * with plenty of upload data) this allows the application
335 * to easily associate some request-specific state.
336 * If necessary, this state can be cleaned up in the
337 * global #MHD_RequestCompletedCallback (which
338 * can be set with the #MHD_OPTION_NOTIFY_COMPLETED).
339 * Initially, `*con_cls` will be NULL.
340 * @return #MHD_YES if the connection was handled successfully,
341 * #MHD_NO if the socket must be closed due to a serios
342 * error while handling the request
343 */ 50 */
344static int 51static int
345ahc_upgrade (void *cls, 52test_upgrade (int flags,
346 struct MHD_Connection *connection, 53 unsigned int pool)
347 const char *url,
348 const char *method,
349 const char *version,
350 const char *upload_data,
351 size_t *upload_data_size,
352 void **con_cls)
353{
354 struct MHD_Response *resp;
355 int ret;
356
357 resp = MHD_create_response_for_upgrade (&upgrade_cb,
358 NULL);
359 MHD_add_response_header (resp,
360 MHD_HTTP_HEADER_UPGRADE,
361 "Hello World Protocol");
362 ret = MHD_queue_response (connection,
363 MHD_HTTP_SWITCHING_PROTOCOLS,
364 resp);
365 MHD_destroy_response (resp);
366 return ret;
367}
368
369
370static int
371test_upgrade_internal (int flags,
372 unsigned int pool)
373{ 54{
374 struct MHD_Daemon *d; 55 struct MHD_Daemon *d;
375 MHD_socket sock; 56 MHD_socket sock;
@@ -399,7 +80,9 @@ test_upgrade_internal (int flags,
399 NULL, 80 NULL,
400 &run_usock_client, 81 &run_usock_client,
401 &sock); 82 &sock);
402 83 if (0 == (flags & (MHD_USE_SELECT_INTERNALLY |
84 MHD_USE_THREAD_PER_CONNECTION)) )
85 run_mhd_loop (d, flags);
403 pthread_join (pt_client, 86 pthread_join (pt_client,
404 NULL); 87 NULL);
405 pthread_join (pt, 88 pthread_join (pt,
@@ -415,27 +98,36 @@ main (int argc,
415{ 98{
416 int error_count = 0; 99 int error_count = 0;
417 100
418 error_count += test_upgrade_internal (MHD_USE_THREAD_PER_CONNECTION, 101 /* try external select */
419 0); 102 error_count += test_upgrade (0,
420 error_count += test_upgrade_internal (MHD_USE_THREAD_PER_CONNECTION | MHD_USE_POLL, 103 0);
421 0); 104
422 error_count += test_upgrade_internal (MHD_USE_SELECT_INTERNALLY, 105 /* Test thread-per-connection */
423 0); 106 error_count += test_upgrade (MHD_USE_THREAD_PER_CONNECTION,
424 error_count += test_upgrade_internal (MHD_USE_SELECT_INTERNALLY, 107 0);
425 2); 108 error_count += test_upgrade (MHD_USE_THREAD_PER_CONNECTION | MHD_USE_POLL,
109 0);
110
111 /* Test different event loops, with and without thread pool */
112 error_count += test_upgrade (MHD_USE_SELECT_INTERNALLY,
113 0);
114 error_count += test_upgrade (MHD_USE_SELECT_INTERNALLY,
115 2);
426#ifdef HAVE_POLL 116#ifdef HAVE_POLL
427 error_count += test_upgrade_internal (MHD_USE_POLL_INTERNALLY, 117 error_count += test_upgrade (MHD_USE_POLL_INTERNALLY,
428 0); 118 0);
429 error_count += test_upgrade_internal (MHD_USE_POLL_INTERNALLY, 119 error_count += test_upgrade (MHD_USE_POLL_INTERNALLY,
430 2); 120 2);
431#endif 121#endif
432#ifdef EPOLL_SUPPORT 122#ifdef EPOLL_SUPPORT
433 error_count += test_upgrade_internal (MHD_USE_EPOLL_INTERNALLY, 123 error_count += test_upgrade (MHD_USE_EPOLL_INTERNALLY,
434 0); 124 0);
435 error_count += test_upgrade_internal (MHD_USE_EPOLL_INTERNALLY, 125 error_count += test_upgrade (MHD_USE_EPOLL_INTERNALLY,
436 2); 126 2);
437#endif 127#endif
438 if (error_count != 0) 128
129 /* report result */
130 if (0 != error_count)
439 fprintf (stderr, 131 fprintf (stderr,
440 "Error (code: %u)\n", 132 "Error (code: %u)\n",
441 error_count); 133 error_count);
diff --git a/src/microhttpd/test_upgrade_common.c b/src/microhttpd/test_upgrade_common.c
new file mode 100644
index 00000000..9985ef5e
--- /dev/null
+++ b/src/microhttpd/test_upgrade_common.c
@@ -0,0 +1,451 @@
1/*
2 This file is part of libmicrohttpd
3 Copyright (C) 2016 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., 51 Franklin Street, Fifth Floor,
18 Boston, MA 02110-1301, USA.
19*/
20
21/**
22 * @file test_upgrade_common.c
23 * @brief Shared logic for testcases for libmicrohttpd upgrading a connection
24 * @author Christian Grothoff
25 */
26
27
28/**
29 * Thread we use to run the interaction with the upgraded socket.
30 */
31static pthread_t pt;
32
33/**
34 * Will be set to the upgraded socket.
35 */
36static MHD_socket usock;
37
38/**
39 * Thread we use to run the interaction with the upgraded socket.
40 */
41static pthread_t pt_client;
42
43/**
44 * Flag set to 1 once the test is finished.
45 */
46static int done;
47
48/**
49 * Change itc FD options to be non-blocking.
50 *
51 * @param fd the FD to manipulate
52 * @return non-zero if succeeded, zero otherwise
53 */
54static void
55make_blocking (MHD_socket fd)
56{
57 int flags;
58
59 flags = fcntl (fd, F_GETFL);
60 if (-1 == flags)
61 return;
62 if ((flags & ~O_NONBLOCK) != flags)
63 fcntl (fd, F_SETFL, flags & ~O_NONBLOCK);
64}
65
66
67static void
68send_all (MHD_socket sock,
69 const char *text)
70{
71 size_t len = strlen (text);
72 ssize_t ret;
73
74 make_blocking (sock);
75 for (size_t off = 0; off < len; off += ret)
76 {
77 ret = write (sock,
78 &text[off],
79 len - off);
80 if (-1 == ret)
81 {
82 if (EAGAIN == errno)
83 {
84 ret = 0;
85 continue;
86 }
87 abort ();
88 }
89 }
90}
91
92
93/**
94 * Read character-by-character until we
95 * get '\r\n\r\n'.
96 */
97static void
98recv_hdr (MHD_socket sock)
99{
100 unsigned int i;
101 char next;
102 char c;
103 ssize_t ret;
104
105 make_blocking (sock);
106 next = '\r';
107 i = 0;
108 while (i < 4)
109 {
110 ret = read (sock,
111 &c,
112 1);
113 if (-1 == ret)
114 {
115 if (EAGAIN == errno)
116 {
117 ret = 0;
118 continue;
119 }
120 abort ();
121 }
122 if (0 == ret)
123 continue;
124 if (c == next)
125 {
126 i++;
127 if (next == '\r')
128 next = '\n';
129 else
130 next = '\r';
131 continue;
132 }
133 if (c == '\r')
134 {
135 i = 1;
136 next = '\n';
137 continue;
138 }
139 i = 0;
140 next = '\r';
141 }
142}
143
144
145static void
146recv_all (MHD_socket sock,
147 const char *text)
148{
149 size_t len = strlen (text);
150 char buf[len];
151 ssize_t ret;
152
153 make_blocking (sock);
154 for (size_t off = 0; off < len; off += ret)
155 {
156 ret = read (sock,
157 &buf[off],
158 len - off);
159 if (-1 == ret)
160 {
161 if (EAGAIN == errno)
162 {
163 ret = 0;
164 continue;
165 }
166 abort ();
167 }
168 }
169 if (0 != strncmp (text, buf, len))
170 abort();
171}
172
173
174/**
175 * Main function for the thread that runs the interaction with
176 * the upgraded socket.
177 *
178 * @param cls the handle for the upgrade
179 */
180static void *
181run_usock (void *cls)
182{
183 struct MHD_UpgradeResponseHandle *urh = cls;
184
185 send_all (usock,
186 "Hello");
187 recv_all (usock,
188 "World");
189 send_all (usock,
190 "Finished");
191 MHD_upgrade_action (urh,
192 MHD_UPGRADE_ACTION_CLOSE);
193 return NULL;
194}
195
196
197/**
198 * Main function for the thread that runs the client-side of the
199 * interaction with the upgraded socket.
200 *
201 * @param cls the client socket
202 */
203static void *
204run_usock_client (void *cls)
205{
206 MHD_socket *sock = cls;
207
208 send_all (*sock,
209 "GET / HTTP/1.1\r\nConnection: Upgrade\r\n\r\n");
210 recv_hdr (*sock);
211 recv_all (*sock,
212 "Hello");
213 send_all (*sock,
214 "World");
215 recv_all (*sock,
216 "Finished");
217 MHD_socket_close_ (*sock);
218 done = 1;
219 return NULL;
220}
221
222
223/**
224 * Function called after a protocol "upgrade" response was sent
225 * successfully and the socket should now be controlled by some
226 * protocol other than HTTP.
227 *
228 * Any data received on the socket will be made available in
229 * 'data_in'. The function should update 'data_in_size' to
230 * reflect the number of bytes consumed from 'data_in' (the remaining
231 * bytes will be made available in the next call to the handler).
232 *
233 * Any data that should be transmitted on the socket should be
234 * stored in 'data_out'. '*data_out_size' is initially set to
235 * the available buffer space in 'data_out'. It should be set to
236 * the number of bytes stored in 'data_out' (which can be zero).
237 *
238 * The return value is a BITMASK that indicates how the function
239 * intends to interact with the event loop. It can request to be
240 * notified for reading, writing, request to UNCORK the send buffer
241 * (which MHD is allowed to ignore, if it is not possible to uncork on
242 * the local platform), to wait for the 'external' select loop to
243 * trigger another round. It is also possible to specify "no events"
244 * to terminate the connection; in this case, the
245 * #MHD_RequestCompletedCallback will be called and all resources of
246 * the connection will be released.
247 *
248 * Except when in 'thread-per-connection' mode, implementations
249 * of this function should never block (as it will still be called
250 * from within the main event loop).
251 *
252 * @param cls closure, whatever was given to #MHD_create_response_for_upgrade().
253 * @param connection original HTTP connection handle,
254 * giving the function a last chance
255 * to inspect the original HTTP request
256 * @param con_cls last value left in `*con_cls` in the `MHD_AccessHandlerCallback`
257 * @param extra_in if we happened to have read bytes after the
258 * HTTP header already (because the client sent
259 * more than the HTTP header of the request before
260 * we sent the upgrade response),
261 * these are the extra bytes already read from @a sock
262 * by MHD. The application should treat these as if
263 * it had read them from @a sock.
264 * @param extra_in_size number of bytes in @a extra_in
265 * @param sock socket to use for bi-directional communication
266 * with the client. For HTTPS, this may not be a socket
267 * that is directly connected to the client and thus certain
268 * operations (TCP-specific setsockopt(), getsockopt(), etc.)
269 * may not work as expected (as the socket could be from a
270 * socketpair() or a TCP-loopback)
271 * @param urh argument for #MHD_upgrade_action()s on this @a connection.
272 * Applications must eventually use this function to perform the
273 * close() action on the @a sock.
274 */
275static void
276upgrade_cb (void *cls,
277 struct MHD_Connection *connection,
278 void *con_cls,
279 const char *extra_in,
280 size_t extra_in_size,
281 MHD_socket sock,
282 struct MHD_UpgradeResponseHandle *urh)
283{
284 usock = sock;
285 if (0 != extra_in_size)
286 abort ();
287 pthread_create (&pt,
288 NULL,
289 &run_usock,
290 urh);
291}
292
293
294/**
295 * A client has requested the given url using the given method
296 * (#MHD_HTTP_METHOD_GET, #MHD_HTTP_METHOD_PUT,
297 * #MHD_HTTP_METHOD_DELETE, #MHD_HTTP_METHOD_POST, etc). The callback
298 * must call MHD callbacks to provide content to give back to the
299 * client and return an HTTP status code (i.e. #MHD_HTTP_OK,
300 * #MHD_HTTP_NOT_FOUND, etc.).
301 *
302 * @param cls argument given together with the function
303 * pointer when the handler was registered with MHD
304 * @param url the requested url
305 * @param method the HTTP method used (#MHD_HTTP_METHOD_GET,
306 * #MHD_HTTP_METHOD_PUT, etc.)
307 * @param version the HTTP version string (i.e.
308 * #MHD_HTTP_VERSION_1_1)
309 * @param upload_data the data being uploaded (excluding HEADERS,
310 * for a POST that fits into memory and that is encoded
311 * with a supported encoding, the POST data will NOT be
312 * given in upload_data and is instead available as
313 * part of #MHD_get_connection_values; very large POST
314 * data *will* be made available incrementally in
315 * @a upload_data)
316 * @param upload_data_size set initially to the size of the
317 * @a upload_data provided; the method must update this
318 * value to the number of bytes NOT processed;
319 * @param con_cls pointer that the callback can set to some
320 * address and that will be preserved by MHD for future
321 * calls for this request; since the access handler may
322 * be called many times (i.e., for a PUT/POST operation
323 * with plenty of upload data) this allows the application
324 * to easily associate some request-specific state.
325 * If necessary, this state can be cleaned up in the
326 * global #MHD_RequestCompletedCallback (which
327 * can be set with the #MHD_OPTION_NOTIFY_COMPLETED).
328 * Initially, `*con_cls` will be NULL.
329 * @return #MHD_YES if the connection was handled successfully,
330 * #MHD_NO if the socket must be closed due to a serios
331 * error while handling the request
332 */
333static int
334ahc_upgrade (void *cls,
335 struct MHD_Connection *connection,
336 const char *url,
337 const char *method,
338 const char *version,
339 const char *upload_data,
340 size_t *upload_data_size,
341 void **con_cls)
342{
343 struct MHD_Response *resp;
344 int ret;
345
346 resp = MHD_create_response_for_upgrade (&upgrade_cb,
347 NULL);
348 MHD_add_response_header (resp,
349 MHD_HTTP_HEADER_UPGRADE,
350 "Hello World Protocol");
351 ret = MHD_queue_response (connection,
352 MHD_HTTP_SWITCHING_PROTOCOLS,
353 resp);
354 MHD_destroy_response (resp);
355 return ret;
356}
357
358
359/**
360 * Run the MHD external event loop using select.
361 *
362 * @param daemon daemon to run it for
363 * @param flags the flags the daemon was started with
364 */
365static void
366run_mhd_select_loop (struct MHD_Daemon *daemon)
367{
368 fd_set rs;
369 fd_set ws;
370 fd_set es;
371 MHD_socket max_fd;
372 MHD_UNSIGNED_LONG_LONG to;
373 struct timeval tv;
374
375 while (! done)
376 {
377 FD_ZERO (&rs);
378 FD_ZERO (&ws);
379 FD_ZERO (&es);
380 max_fd = -1;
381 to = 1000;
382
383 if (MHD_YES !=
384 MHD_get_fdset (daemon,
385 &rs,
386 &ws,
387 &es,
388 &max_fd))
389 abort ();
390 MHD_get_timeout (daemon,
391 &to);
392 tv.tv_sec = to / 1000;
393 tv.tv_usec = 1000 * (to % 1000);
394 select (max_fd + 1,
395 &rs,
396 &ws,
397 &es,
398 &tv);
399 MHD_run_from_select (daemon,
400 &rs,
401 &ws,
402 &es);
403 }
404}
405
406
407/**
408 * Run the MHD external event loop using select.
409 *
410 * @param daemon daemon to run it for
411 * @param flags the flags the daemon was started with
412 */
413static void
414run_mhd_poll_loop (struct MHD_Daemon *daemon)
415{
416 abort (); // not implemented
417}
418
419
420/**
421 * Run the MHD external event loop using select.
422 *
423 * @param daemon daemon to run it for
424 * @param flags the flags the daemon was started with
425 */
426static void
427run_mhd_epoll_loop (struct MHD_Daemon *daemon)
428{
429 abort (); // not implemented
430}
431
432
433/**
434 * Run the MHD external event loop using select.
435 *
436 * @param daemon daemon to run it for
437 * @param flags the flags the daemon was started with
438 */
439static void
440run_mhd_loop (struct MHD_Daemon *daemon,
441 int flags)
442{
443 if (0 != (flags & MHD_USE_POLL))
444 run_mhd_poll_loop (daemon);
445#if EPOLL_SUPPORT
446 else if (0 != (flags & MHD_USE_EPOLL))
447 run_mhd_epoll_loop (daemon);
448#endif
449 else
450 run_mhd_select_loop (daemon);
451}
diff --git a/src/microhttpd/test_upgrade_ssl.c b/src/microhttpd/test_upgrade_ssl.c
index 26fd53fa..91d22daf 100644
--- a/src/microhttpd/test_upgrade_ssl.c
+++ b/src/microhttpd/test_upgrade_ssl.c
@@ -4,7 +4,7 @@
4 4
5 libmicrohttpd is free software; you can redistribute it and/or modify 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 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 7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version. 8 option) any later version.
9 9
10 libmicrohttpd is distributed in the hope that it will be useful, but 10 libmicrohttpd is distributed in the hope that it will be useful, but
@@ -40,26 +40,12 @@
40#include <netinet/ip.h> 40#include <netinet/ip.h>
41#include <pthread.h> 41#include <pthread.h>
42#include "mhd_sockets.h" 42#include "mhd_sockets.h"
43#include "test_upgrade_common.c"
43 44
44#include "../testcurl/https/tls_test_keys.h" 45#include "../testcurl/https/tls_test_keys.h"
45 46
46 47
47/** 48/**
48 * Thread we use to run the interaction with the upgraded socket.
49 */
50static pthread_t pt;
51
52/**
53 * Will be set to the upgraded socket.
54 */
55static MHD_socket usock;
56
57/**
58 * Thread we use to run the interaction with the upgraded socket.
59 */
60static pthread_t pt_client;
61
62/**
63 * Fork child that connects via OpenSSL to our @a port. Allows us to 49 * Fork child that connects via OpenSSL to our @a port. Allows us to
64 * talk to our port over a socket in @a sp without having to worry 50 * talk to our port over a socket in @a sp without having to worry
65 * about TLS. 51 * about TLS.
@@ -109,322 +95,14 @@ openssl_connect (int *sock,
109 95
110 96
111/** 97/**
112 * Change itc FD options to be non-blocking. 98 * Test upgrading a connection.
113 *
114 * @param fd the FD to manipulate
115 * @return non-zero if succeeded, zero otherwise
116 */
117static void
118make_blocking (MHD_socket fd)
119{
120 int flags;
121
122 flags = fcntl (fd, F_GETFL);
123 if (-1 == flags)
124 return;
125 if ((flags & ~O_NONBLOCK) != flags)
126 fcntl (fd, F_SETFL, flags & ~O_NONBLOCK);
127}
128
129
130static void
131send_all (MHD_socket sock,
132 const char *text)
133{
134 size_t len = strlen (text);
135 ssize_t ret;
136
137 make_blocking (sock);
138 for (size_t off = 0; off < len; off += ret)
139 {
140 ret = write (sock,
141 &text[off],
142 len - off);
143 if (-1 == ret)
144 {
145 if (EAGAIN == errno)
146 {
147 ret = 0;
148 continue;
149 }
150 abort ();
151 }
152 }
153}
154
155
156/**
157 * Read character-by-character until we
158 * get '\r\n\r\n'.
159 */
160static void
161recv_hdr (MHD_socket sock)
162{
163 unsigned int i;
164 char next;
165 char c;
166 ssize_t ret;
167
168 make_blocking (sock);
169 next = '\r';
170 i = 0;
171 while (i < 4)
172 {
173 ret = read (sock,
174 &c,
175 1);
176 if (0 == ret)
177 abort (); /* this is fatal */
178 if (-1 == ret)
179 {
180 if (EAGAIN == errno)
181 {
182 ret = 0;
183 continue;
184 }
185 abort ();
186 }
187 if (0 == ret)
188 continue;
189 if (c == next)
190 {
191 i++;
192 if (next == '\r')
193 next = '\n';
194 else
195 next = '\r';
196 continue;
197 }
198 if (c == '\r')
199 {
200 i = 1;
201 next = '\n';
202 continue;
203 }
204 i = 0;
205 next = '\r';
206 }
207}
208
209
210static void
211recv_all (MHD_socket sock,
212 const char *text)
213{
214 size_t len = strlen (text);
215 char buf[len];
216 ssize_t ret;
217
218 make_blocking (sock);
219 for (size_t off = 0; off < len; off += ret)
220 {
221 ret = read (sock,
222 &buf[off],
223 len - off);
224 if (0 == ret)
225 abort (); /* this is fatal */
226 if (-1 == ret)
227 {
228 if (EAGAIN == errno)
229 {
230 ret = 0;
231 continue;
232 }
233 abort ();
234 }
235 }
236 if (0 != strncmp (text, buf, len))
237 abort();
238}
239
240
241/**
242 * Main function for the thread that runs the interaction with
243 * the upgraded socket.
244 *
245 * @param cls the handle for the upgrade
246 */
247static void *
248run_usock (void *cls)
249{
250 struct MHD_UpgradeResponseHandle *urh = cls;
251
252 send_all (usock,
253 "Hello");
254 recv_all (usock,
255 "World");
256 send_all (usock,
257 "Finished");
258 MHD_upgrade_action (urh,
259 MHD_UPGRADE_ACTION_CLOSE);
260 return NULL;
261}
262
263
264/**
265 * Main function for the thread that runs the client-side of the
266 * interaction with the upgraded socket.
267 *
268 * @param cls the client socket
269 */
270static void *
271run_usock_client (void *cls)
272{
273 MHD_socket *sock = cls;
274
275 send_all (*sock,
276 "GET / HTTP/1.1\r\nConnection: Upgrade\r\n\r\n");
277 recv_hdr (*sock);
278 recv_all (*sock,
279 "Hello");
280 send_all (*sock,
281 "World");
282 recv_all (*sock,
283 "Finished");
284 MHD_socket_close_ (*sock);
285 return NULL;
286}
287
288
289/**
290 * Function called after a protocol "upgrade" response was sent
291 * successfully and the socket should now be controlled by some
292 * protocol other than HTTP.
293 *
294 * Any data received on the socket will be made available in
295 * 'data_in'. The function should update 'data_in_size' to
296 * reflect the number of bytes consumed from 'data_in' (the remaining
297 * bytes will be made available in the next call to the handler).
298 *
299 * Any data that should be transmitted on the socket should be
300 * stored in 'data_out'. '*data_out_size' is initially set to
301 * the available buffer space in 'data_out'. It should be set to
302 * the number of bytes stored in 'data_out' (which can be zero).
303 *
304 * The return value is a BITMASK that indicates how the function
305 * intends to interact with the event loop. It can request to be
306 * notified for reading, writing, request to UNCORK the send buffer
307 * (which MHD is allowed to ignore, if it is not possible to uncork on
308 * the local platform), to wait for the 'external' select loop to
309 * trigger another round. It is also possible to specify "no events"
310 * to terminate the connection; in this case, the
311 * #MHD_RequestCompletedCallback will be called and all resources of
312 * the connection will be released.
313 *
314 * Except when in 'thread-per-connection' mode, implementations
315 * of this function should never block (as it will still be called
316 * from within the main event loop).
317 *
318 * @param cls closure, whatever was given to #MHD_create_response_for_upgrade().
319 * @param connection original HTTP connection handle,
320 * giving the function a last chance
321 * to inspect the original HTTP request
322 * @param con_cls last value left in `*con_cls` in the `MHD_AccessHandlerCallback`
323 * @param extra_in if we happened to have read bytes after the
324 * HTTP header already (because the client sent
325 * more than the HTTP header of the request before
326 * we sent the upgrade response),
327 * these are the extra bytes already read from @a sock
328 * by MHD. The application should treat these as if
329 * it had read them from @a sock.
330 * @param extra_in_size number of bytes in @a extra_in
331 * @param sock socket to use for bi-directional communication
332 * with the client. For HTTPS, this may not be a socket
333 * that is directly connected to the client and thus certain
334 * operations (TCP-specific setsockopt(), getsockopt(), etc.)
335 * may not work as expected (as the socket could be from a
336 * socketpair() or a TCP-loopback)
337 * @param urh argument for #MHD_upgrade_action()s on this @a connection.
338 * Applications must eventually use this function to perform the
339 * close() action on the @a sock.
340 */
341static void
342upgrade_cb (void *cls,
343 struct MHD_Connection *connection,
344 void *con_cls,
345 const char *extra_in,
346 size_t extra_in_size,
347 MHD_socket sock,
348 struct MHD_UpgradeResponseHandle *urh)
349{
350 usock = sock;
351 if (0 != extra_in_size)
352 abort ();
353 pthread_create (&pt,
354 NULL,
355 &run_usock,
356 urh);
357}
358
359
360/**
361 * A client has requested the given url using the given method
362 * (#MHD_HTTP_METHOD_GET, #MHD_HTTP_METHOD_PUT,
363 * #MHD_HTTP_METHOD_DELETE, #MHD_HTTP_METHOD_POST, etc). The callback
364 * must call MHD callbacks to provide content to give back to the
365 * client and return an HTTP status code (i.e. #MHD_HTTP_OK,
366 * #MHD_HTTP_NOT_FOUND, etc.).
367 * 99 *
368 * @param cls argument given together with the function 100 * @param flags which event loop style should be tested
369 * pointer when the handler was registered with MHD 101 * @param pool size of the thread pool, 0 to disable
370 * @param url the requested url
371 * @param method the HTTP method used (#MHD_HTTP_METHOD_GET,
372 * #MHD_HTTP_METHOD_PUT, etc.)
373 * @param version the HTTP version string (i.e.
374 * #MHD_HTTP_VERSION_1_1)
375 * @param upload_data the data being uploaded (excluding HEADERS,
376 * for a POST that fits into memory and that is encoded
377 * with a supported encoding, the POST data will NOT be
378 * given in upload_data and is instead available as
379 * part of #MHD_get_connection_values; very large POST
380 * data *will* be made available incrementally in
381 * @a upload_data)
382 * @param upload_data_size set initially to the size of the
383 * @a upload_data provided; the method must update this
384 * value to the number of bytes NOT processed;
385 * @param con_cls pointer that the callback can set to some
386 * address and that will be preserved by MHD for future
387 * calls for this request; since the access handler may
388 * be called many times (i.e., for a PUT/POST operation
389 * with plenty of upload data) this allows the application
390 * to easily associate some request-specific state.
391 * If necessary, this state can be cleaned up in the
392 * global #MHD_RequestCompletedCallback (which
393 * can be set with the #MHD_OPTION_NOTIFY_COMPLETED).
394 * Initially, `*con_cls` will be NULL.
395 * @return #MHD_YES if the connection was handled successfully,
396 * #MHD_NO if the socket must be closed due to a serios
397 * error while handling the request
398 */ 102 */
399static int 103static int
400ahc_upgrade (void *cls, 104test_upgrade (int flags,
401 struct MHD_Connection *connection, 105 unsigned int pool)
402 const char *url,
403 const char *method,
404 const char *version,
405 const char *upload_data,
406 size_t *upload_data_size,
407 void **con_cls)
408{
409 struct MHD_Response *resp;
410 int ret;
411
412 resp = MHD_create_response_for_upgrade (&upgrade_cb,
413 NULL);
414 MHD_add_response_header (resp,
415 MHD_HTTP_HEADER_UPGRADE,
416 "Hello World Protocol");
417 ret = MHD_queue_response (connection,
418 MHD_HTTP_SWITCHING_PROTOCOLS,
419 resp);
420 MHD_destroy_response (resp);
421 return ret;
422}
423
424
425static int
426test_upgrade_internal (int flags,
427 unsigned int pool)
428{ 106{
429 struct MHD_Daemon *d; 107 struct MHD_Daemon *d;
430 MHD_socket sock; 108 MHD_socket sock;
@@ -452,9 +130,14 @@ test_upgrade_internal (int flags,
452 NULL, 130 NULL,
453 &run_usock_client, 131 &run_usock_client,
454 &sock); 132 &sock);
455 133 if (0 == (flags & (MHD_USE_SELECT_INTERNALLY |
134 MHD_USE_THREAD_PER_CONNECTION)) )
135 run_mhd_loop (d, flags);
456 pthread_join (pt_client, 136 pthread_join (pt_client,
457 NULL); 137 NULL);
138 if (0 == (flags & (MHD_USE_SELECT_INTERNALLY |
139 MHD_USE_THREAD_PER_CONNECTION)) )
140 run_mhd_loop (d, flags);
458 pthread_join (pt, 141 pthread_join (pt,
459 NULL); 142 NULL);
460 waitpid (pid, 143 waitpid (pid,
@@ -474,30 +157,34 @@ main (int argc,
474 if (0 != system ("openssl version 1> /dev/null")) 157 if (0 != system ("openssl version 1> /dev/null"))
475 return 77; /* openssl not available, can't run the test */ 158 return 77; /* openssl not available, can't run the test */
476 159
160 /* try external select */
161 error_count += test_upgrade (0,
162 0);
163
477 /* Test thread-per-connection */ 164 /* Test thread-per-connection */
478 error_count += test_upgrade_internal (MHD_USE_THREAD_PER_CONNECTION, 165 error_count += test_upgrade (MHD_USE_THREAD_PER_CONNECTION,
479 0); 166 0);
480 error_count += test_upgrade_internal (MHD_USE_THREAD_PER_CONNECTION | MHD_USE_POLL, 167 error_count += test_upgrade (MHD_USE_THREAD_PER_CONNECTION | MHD_USE_POLL,
481 0); 168 0);
482 169
483 /* Test different event loops, with and without thread pool */ 170 /* Test different event loops, with and without thread pool */
484 error_count += test_upgrade_internal (MHD_USE_SELECT_INTERNALLY, 171 error_count += test_upgrade (MHD_USE_SELECT_INTERNALLY,
485 0); 172 0);
486 error_count += test_upgrade_internal (MHD_USE_SELECT_INTERNALLY, 173 error_count += test_upgrade (MHD_USE_SELECT_INTERNALLY,
487 2); 174 2);
488#ifdef HAVE_POLL 175#ifdef HAVE_POLL
489 error_count += test_upgrade_internal (MHD_USE_POLL_INTERNALLY, 176 error_count += test_upgrade (MHD_USE_POLL_INTERNALLY,
490 0); 177 0);
491 error_count += test_upgrade_internal (MHD_USE_POLL_INTERNALLY, 178 error_count += test_upgrade (MHD_USE_POLL_INTERNALLY,
492 2); 179 2);
493#endif 180#endif
494#ifdef EPOLL_SUPPORT 181#ifdef EPOLL_SUPPORT
495 error_count += test_upgrade_internal (MHD_USE_EPOLL_INTERNALLY | 182 error_count += test_upgrade (MHD_USE_EPOLL_INTERNALLY |
496 MHD_USE_TLS_EPOLL_UPGRADE, 183 MHD_USE_TLS_EPOLL_UPGRADE,
497 0); 184 0);
498 error_count += test_upgrade_internal (MHD_USE_EPOLL_INTERNALLY | 185 error_count += test_upgrade (MHD_USE_EPOLL_INTERNALLY |
499 MHD_USE_TLS_EPOLL_UPGRADE, 186 MHD_USE_TLS_EPOLL_UPGRADE,
500 2); 187 2);
501#endif 188#endif
502 189
503 /* report result */ 190 /* report result */