aboutsummaryrefslogtreecommitdiff
path: root/src/microhttpd/test_upgrade.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/microhttpd/test_upgrade.c')
-rw-r--r--src/microhttpd/test_upgrade.c380
1 files changed, 36 insertions, 344 deletions
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);