diff options
Diffstat (limited to 'src/microhttpd/test_upgrade.c')
-rw-r--r-- | src/microhttpd/test_upgrade.c | 380 |
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 | */ | ||
47 | static pthread_t pt; | ||
48 | |||
49 | /** | ||
50 | * Will be set to the upgraded socket. | ||
51 | */ | ||
52 | static MHD_socket usock; | ||
53 | |||
54 | /** | ||
55 | * Thread we use to run the interaction with the upgraded socket. | ||
56 | */ | ||
57 | static 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 | */ | ||
66 | static void | ||
67 | make_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 | |||
79 | static void | ||
80 | send_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 | */ | ||
109 | static void | ||
110 | recv_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 | |||
157 | static void | ||
158 | recv_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 | */ | ||
192 | static void * | ||
193 | run_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 | */ | ||
215 | static void * | ||
216 | run_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 | */ | ||
286 | static void | ||
287 | upgrade_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 | */ |
344 | static int | 51 | static int |
345 | ahc_upgrade (void *cls, | 52 | test_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 | |||
370 | static int | ||
371 | test_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); |