diff options
author | Evgeny Grin (Karlson2k) <k2k@narod.ru> | 2016-11-05 13:43:44 +0300 |
---|---|---|
committer | Evgeny Grin (Karlson2k) <k2k@narod.ru> | 2016-11-07 14:39:09 +0300 |
commit | 9ea89671719e23e5bcff40b17a3c0fe66c047cc3 (patch) | |
tree | 7d4b77138644aeb68b39fd1afb5c511cc1a833ef | |
parent | 8b10b7801c17f9d589cdb4b5131e5621ee088827 (diff) | |
download | libmicrohttpd-9ea89671719e23e5bcff40b17a3c0fe66c047cc3.tar.gz libmicrohttpd-9ea89671719e23e5bcff40b17a3c0fe66c047cc3.zip |
Combine upgrade tests into single source file, port to platforms without fork()
-rw-r--r-- | configure.ac | 42 | ||||
-rw-r--r-- | src/microhttpd/Makefile.am | 30 | ||||
-rw-r--r-- | src/microhttpd/test_upgrade.c | 1062 | ||||
-rw-r--r-- | src/microhttpd/test_upgrade_common.c | 562 | ||||
-rw-r--r-- | src/microhttpd/test_upgrade_ssl.c | 236 |
5 files changed, 1109 insertions, 823 deletions
diff --git a/configure.ac b/configure.ac index c8a7c3f8..7a74e240 100644 --- a/configure.ac +++ b/configure.ac | |||
@@ -1173,6 +1173,48 @@ AS_VAR_IF([[enable_httpupgrade]],[["yes"]], | |||
1173 | AM_CONDITIONAL([ENABLE_UPGRADE], [[test "x$enable_httpupgrade" = "xyes"]]) | 1173 | AM_CONDITIONAL([ENABLE_UPGRADE], [[test "x$enable_httpupgrade" = "xyes"]]) |
1174 | AC_MSG_RESULT([[$enable_httpupgrade]]) | 1174 | AC_MSG_RESULT([[$enable_httpupgrade]]) |
1175 | 1175 | ||
1176 | # Check for fork() and waitpid(). They are used for tests. | ||
1177 | AC_MSG_CHECKING([[for fork()]]) | ||
1178 | AC_LINK_IFELSE( | ||
1179 | [ | ||
1180 | AC_LANG_PROGRAM( | ||
1181 | [[ | ||
1182 | #ifdef HAVE_SYS_TYPES_H | ||
1183 | #include <sys/types.h> | ||
1184 | #endif | ||
1185 | #ifdef HAVE_UNISTD_H | ||
1186 | #include <unistd.h> | ||
1187 | #endif | ||
1188 | ]], [[ | ||
1189 | pid_t p = fork (); | ||
1190 | if (0 == p) | ||
1191 | return 1; | ||
1192 | ]]) | ||
1193 | ], [ | ||
1194 | AC_DEFINE([[HAVE_FORK]], [[1]], [Define to 1 if you have the usable `fork' function.]) | ||
1195 | AC_MSG_RESULT([[yes]]) | ||
1196 | |||
1197 | AC_MSG_CHECKING([[for waitpid()]]) | ||
1198 | AC_LINK_IFELSE( | ||
1199 | [ | ||
1200 | AC_LANG_PROGRAM( | ||
1201 | [[ | ||
1202 | #include <sys/wait.h> | ||
1203 | ]], [[ | ||
1204 | pid_t p = fork (); | ||
1205 | if (0 == p) | ||
1206 | return 1; | ||
1207 | waitpid (p, (void*)0, 0) | ||
1208 | ]]) | ||
1209 | ], [ | ||
1210 | AC_DEFINE([[HAVE_WAITPID]], [[1]], [Define to 1 if you have the usable `waitpid' function.]) | ||
1211 | AC_MSG_RESULT([[yes]]) | ||
1212 | ],[ | ||
1213 | AC_MSG_RESULT([[no]]) | ||
1214 | ]) | ||
1215 | ],[ | ||
1216 | AC_MSG_RESULT([[no]]) | ||
1217 | ]) | ||
1176 | 1218 | ||
1177 | 1219 | ||
1178 | MHD_LIB_LDFLAGS="$MHD_LIB_LDFLAGS -export-dynamic -no-undefined" | 1220 | MHD_LIB_LDFLAGS="$MHD_LIB_LDFLAGS -export-dynamic -no-undefined" |
diff --git a/src/microhttpd/Makefile.am b/src/microhttpd/Makefile.am index 81dc95c2..cc48d11d 100644 --- a/src/microhttpd/Makefile.am +++ b/src/microhttpd/Makefile.am | |||
@@ -152,10 +152,12 @@ check_PROGRAMS = \ | |||
152 | test_shutdown_poll \ | 152 | test_shutdown_poll \ |
153 | test_daemon | 153 | test_daemon |
154 | 154 | ||
155 | if HAVE_POSIX_THREADS | ||
155 | if ENABLE_UPGRADE | 156 | if ENABLE_UPGRADE |
156 | check_PROGRAMS += test_upgrade | 157 | check_PROGRAMS += test_upgrade |
157 | if ENABLE_HTTPS | 158 | if ENABLE_HTTPS |
158 | check_PROGRAMS += test_upgrade_ssl | 159 | check_PROGRAMS += test_upgrade_tls |
160 | endif | ||
159 | endif | 161 | endif |
160 | endif | 162 | endif |
161 | 163 | ||
@@ -180,14 +182,28 @@ test_daemon_LDADD = \ | |||
180 | $(top_builddir)/src/microhttpd/libmicrohttpd.la | 182 | $(top_builddir)/src/microhttpd/libmicrohttpd.la |
181 | 183 | ||
182 | test_upgrade_SOURCES = \ | 184 | test_upgrade_SOURCES = \ |
183 | test_upgrade.c | 185 | test_upgrade.c test_helpers.h mhd_sockets.h |
186 | test_upgrade_CPPFLAGS = \ | ||
187 | $(AM_CPPFLAGS) $(GNUTLS_CPPFLAGS) | ||
188 | test_upgrade_CFLAGS = \ | ||
189 | $(AM_CFLAGS) $(PTHREAD_CFLAGS) | ||
190 | test_upgrade_LDFLAGS = \ | ||
191 | $(GNUTLS_LDFLAGS) | ||
184 | test_upgrade_LDADD = \ | 192 | test_upgrade_LDADD = \ |
185 | $(top_builddir)/src/microhttpd/libmicrohttpd.la | 193 | $(top_builddir)/src/microhttpd/libmicrohttpd.la \ |
194 | $(PTHREAD_LIBS) | ||
186 | 195 | ||
187 | test_upgrade_ssl_SOURCES = \ | 196 | test_upgrade_tls_SOURCES = \ |
188 | test_upgrade_ssl.c | 197 | test_upgrade.c test_helpers.h mhd_sockets.h |
189 | test_upgrade_ssl_LDADD = \ | 198 | test_upgrade_tls_CPPFLAGS = \ |
190 | $(top_builddir)/src/microhttpd/libmicrohttpd.la | 199 | $(AM_CPPFLAGS) $(GNUTLS_CPPFLAGS) |
200 | test_upgrade_tls_CFLAGS = \ | ||
201 | $(AM_CFLAGS) $(PTHREAD_CFLAGS) | ||
202 | test_upgrade_tls_LDFLAGS = \ | ||
203 | $(GNUTLS_LDFLAGS) | ||
204 | test_upgrade_tls_LDADD = \ | ||
205 | $(top_builddir)/src/microhttpd/libmicrohttpd.la \ | ||
206 | $(PTHREAD_LIBS) | ||
191 | 207 | ||
192 | test_postprocessor_SOURCES = \ | 208 | test_postprocessor_SOURCES = \ |
193 | test_postprocessor.c | 209 | test_postprocessor.c |
diff --git a/src/microhttpd/test_upgrade.c b/src/microhttpd/test_upgrade.c index 72583451..e9836461 100644 --- a/src/microhttpd/test_upgrade.c +++ b/src/microhttpd/test_upgrade.c | |||
@@ -22,30 +22,961 @@ | |||
22 | * @file test_upgrade.c | 22 | * @file test_upgrade.c |
23 | * @brief Testcase for libmicrohttpd upgrading a connection | 23 | * @brief Testcase for libmicrohttpd upgrading a connection |
24 | * @author Christian Grothoff | 24 | * @author Christian Grothoff |
25 | * @author Karlson2k (Evgeny Grin) | ||
25 | */ | 26 | */ |
26 | 27 | ||
27 | #include "platform.h" | ||
28 | #include "microhttpd.h" | ||
29 | #include <stdlib.h> | 28 | #include <stdlib.h> |
30 | #include <string.h> | 29 | #include <string.h> |
31 | #include <stdio.h> | 30 | #include <stdio.h> |
32 | #include <pthread.h> | 31 | #include <pthread.h> |
33 | 32 | #include <stdlib.h> | |
33 | #include <stddef.h> | ||
34 | #ifndef WINDOWS | 34 | #ifndef WINDOWS |
35 | #include <unistd.h> | 35 | #include <unistd.h> |
36 | #endif | 36 | #endif |
37 | #include "mhd_options.h" | ||
38 | #ifdef HAVE_STDBOOL_H | ||
39 | #include <stdbool.h> | ||
40 | #endif /* HAVE_STDBOOL_H */ | ||
37 | 41 | ||
38 | #include "mhd_sockets.h" | 42 | #include "mhd_sockets.h" |
39 | #ifdef HAVE_NETINET_IP_H | 43 | #ifdef HAVE_NETINET_IP_H |
40 | #include <netinet/ip.h> | 44 | #include <netinet/ip.h> |
41 | #endif /* HAVE_NETINET_IP_H */ | 45 | #endif /* HAVE_NETINET_IP_H */ |
42 | 46 | ||
43 | static int verbose = 0; | 47 | #include "platform.h" |
48 | #include "microhttpd.h" | ||
49 | |||
44 | #include "test_helpers.h" | 50 | #include "test_helpers.h" |
45 | #include "test_upgrade_common.c" | 51 | |
52 | #ifdef HTTPS_SUPPORT | ||
53 | #include <gnutls/gnutls.h> | ||
54 | #include "../testcurl/https/tls_test_keys.h" | ||
55 | |||
56 | #if defined(HAVE_FORK) && defined(HAVE_WAITPID) | ||
57 | #include <sys/types.h> | ||
58 | #include <sys/wait.h> | ||
59 | #endif /* HAVE_FORK && HAVE_WAITPID */ | ||
60 | #endif /* HTTPS_SUPPORT */ | ||
61 | |||
62 | static int verbose = 0; | ||
63 | |||
64 | enum tls_tool | ||
65 | { | ||
66 | TLS_CLI_NO_TOOL = 0, | ||
67 | TLS_CLI_GNUTLS, | ||
68 | TLS_CLI_OPENSSL, | ||
69 | TLS_LIB_GNUTLS | ||
70 | }; | ||
71 | |||
72 | enum tls_tool use_tls_tool; | ||
73 | |||
74 | #if defined(HTTPS_SUPPORT) && defined(HAVE_FORK) && defined(HAVE_WAITPID) | ||
75 | /** | ||
76 | * Fork child that connects via GnuTLS-CLI to our @a port. Allows us to | ||
77 | * talk to our port over a socket in @a sp without having to worry | ||
78 | * about TLS. | ||
79 | * | ||
80 | * @param location where the socket is returned | ||
81 | * @return -1 on error, otherwise PID of TLS child process | ||
82 | */ | ||
83 | static pid_t | ||
84 | gnutlscli_connect (int *sock, | ||
85 | uint16_t port) | ||
86 | { | ||
87 | pid_t chld; | ||
88 | int sp[2]; | ||
89 | char destination[30]; | ||
90 | |||
91 | if (0 != socketpair (AF_UNIX, | ||
92 | SOCK_STREAM, | ||
93 | 0, | ||
94 | sp)) | ||
95 | return -1; | ||
96 | chld = fork (); | ||
97 | if (0 != chld) | ||
98 | { | ||
99 | *sock = sp[1]; | ||
100 | MHD_socket_close_chk_ (sp[0]); | ||
101 | return chld; | ||
102 | } | ||
103 | MHD_socket_close_chk_ (sp[1]); | ||
104 | (void) close (0); | ||
105 | (void) close (1); | ||
106 | dup2 (sp[0], 0); | ||
107 | dup2 (sp[0], 1); | ||
108 | MHD_socket_close_chk_ (sp[0]); | ||
109 | if (TLS_CLI_GNUTLS == use_tls_tool) | ||
110 | { | ||
111 | snprintf (destination, | ||
112 | sizeof(destination), | ||
113 | "%u", | ||
114 | (unsigned int) port); | ||
115 | execlp ("gnutls-cli", | ||
116 | "gnutls-cli", | ||
117 | "--insecure", | ||
118 | "-p", | ||
119 | destination, | ||
120 | "localhost", | ||
121 | (char *) NULL); | ||
122 | } | ||
123 | else if (TLS_CLI_OPENSSL == use_tls_tool) | ||
124 | { | ||
125 | snprintf (destination, | ||
126 | sizeof(destination), | ||
127 | "localhost:%u", | ||
128 | (unsigned int) port); | ||
129 | execlp ("openssl", | ||
130 | "openssl", | ||
131 | "s_client", | ||
132 | "-connect", | ||
133 | destination, | ||
134 | "-verify", | ||
135 | "0", | ||
136 | (char *) NULL); | ||
137 | } | ||
138 | _exit (1); | ||
139 | } | ||
140 | #endif /* HTTPS_SUPPORT && HAVE_FORK && HAVE_WAITPID */ | ||
141 | |||
142 | |||
143 | /** | ||
144 | * Wrapper structure for plain&TLS sockets | ||
145 | */ | ||
146 | struct wr_socket_strc | ||
147 | { | ||
148 | /** | ||
149 | * Real network socket | ||
150 | */ | ||
151 | MHD_socket fd; | ||
152 | |||
153 | /** | ||
154 | * Type of this socket | ||
155 | */ | ||
156 | enum wr_type | ||
157 | { | ||
158 | wr_invalid = 0, | ||
159 | wr_plain = 1, | ||
160 | wr_tls = 2 | ||
161 | } t; | ||
162 | #ifdef HTTPS_SUPPORT | ||
163 | /** | ||
164 | * TLS credentials | ||
165 | */ | ||
166 | gnutls_certificate_credentials_t tls_crd; | ||
167 | |||
168 | /** | ||
169 | * TLS session. | ||
170 | */ | ||
171 | gnutls_session_t tls_s; | ||
172 | |||
173 | /** | ||
174 | * TLS handshake already succeed? | ||
175 | */ | ||
176 | bool tls_connected; | ||
177 | #endif | ||
178 | }; | ||
179 | |||
180 | |||
181 | /** | ||
182 | * Pseudo type for plain&TLS sockets | ||
183 | */ | ||
184 | typedef struct wr_socket_strc* wr_socket; | ||
185 | |||
186 | |||
187 | /** | ||
188 | * Invalid value of wr_socket | ||
189 | */ | ||
190 | #define WR_BAD (NULL) | ||
191 | |||
192 | |||
193 | /** | ||
194 | * Get underlying real socket. | ||
195 | * @return FD of real socket | ||
196 | */ | ||
197 | #define wr_fd(s) ((s)->fd) | ||
198 | |||
199 | |||
200 | /** | ||
201 | * Create wr_socket with plain TCP underlying socket | ||
202 | * @return created socket on success, WR_BAD otherwise | ||
203 | */ | ||
204 | static wr_socket wr_create_plain_sckt(void) | ||
205 | { | ||
206 | wr_socket s = (wr_socket)malloc(sizeof(struct wr_socket_strc)); | ||
207 | if (WR_BAD == s) | ||
208 | return WR_BAD; | ||
209 | s->t = wr_plain; | ||
210 | s->fd = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP); | ||
211 | if (MHD_INVALID_SOCKET != s->fd) | ||
212 | return s; | ||
213 | free(s); | ||
214 | return WR_BAD; | ||
215 | } | ||
46 | 216 | ||
47 | 217 | ||
48 | /** | 218 | /** |
219 | * Create wr_socket with TLS TCP underlying socket | ||
220 | * @return created socket on success, WR_BAD otherwise | ||
221 | */ | ||
222 | static wr_socket wr_create_tls_sckt(void) | ||
223 | { | ||
224 | #ifdef HTTPS_SUPPORT | ||
225 | wr_socket s = (wr_socket)malloc(sizeof(struct wr_socket_strc)); | ||
226 | if (WR_BAD == s) | ||
227 | return WR_BAD; | ||
228 | s->t = wr_tls; | ||
229 | s->tls_connected = 0; | ||
230 | s->fd = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP); | ||
231 | if (MHD_INVALID_SOCKET != s->fd) | ||
232 | { | ||
233 | if (GNUTLS_E_SUCCESS == gnutls_init (&(s->tls_s), GNUTLS_CLIENT)) | ||
234 | { | ||
235 | if (GNUTLS_E_SUCCESS == gnutls_set_default_priority (s->tls_s)) | ||
236 | { | ||
237 | if (GNUTLS_E_SUCCESS == gnutls_certificate_allocate_credentials (&(s->tls_crd))) | ||
238 | { | ||
239 | if (GNUTLS_E_SUCCESS == gnutls_credentials_set (s->tls_s, GNUTLS_CRD_CERTIFICATE, s->tls_crd)) | ||
240 | { | ||
241 | gnutls_transport_set_int (s->tls_s, (int )(s->fd)); | ||
242 | return s; | ||
243 | } | ||
244 | gnutls_certificate_free_credentials (s->tls_crd); | ||
245 | } | ||
246 | } | ||
247 | gnutls_deinit (s->tls_s); | ||
248 | } | ||
249 | (void)MHD_socket_close_ (s->fd); | ||
250 | } | ||
251 | free(s); | ||
252 | #endif /* HTTPS_SUPPORT */ | ||
253 | return WR_BAD; | ||
254 | } | ||
255 | |||
256 | |||
257 | /** | ||
258 | * Create wr_socket with plain TCP underlying socket | ||
259 | * from already created TCP socket. | ||
260 | * @param plain_sk real TCP socket | ||
261 | * @return created socket on success, WR_BAD otherwise | ||
262 | */ | ||
263 | static wr_socket wr_create_from_plain_sckt(MHD_socket plain_sk) | ||
264 | { | ||
265 | wr_socket s = (wr_socket)malloc(sizeof(struct wr_socket_strc)); | ||
266 | if (WR_BAD == s) | ||
267 | return WR_BAD; | ||
268 | s->t = wr_plain; | ||
269 | s->fd = plain_sk; | ||
270 | return s; | ||
271 | } | ||
272 | |||
273 | |||
274 | /** | ||
275 | * Connect socket to specified address. | ||
276 | * @param s socket to use | ||
277 | * @param addr address to connect | ||
278 | * @param length of sturcture pointed by @a addr | ||
279 | * @return zero on success, -1 otherwise. | ||
280 | */ | ||
281 | static int wr_connect(wr_socket s, struct sockaddr * addr, int length) | ||
282 | { | ||
283 | if (0 != connect(s->fd, addr, length)) | ||
284 | return -1; | ||
285 | if (wr_plain == s->t) | ||
286 | return 0; | ||
287 | #ifdef HTTPS_SUPPORT | ||
288 | if (wr_tls == s->t) | ||
289 | { | ||
290 | s->tls_connected = 0; | ||
291 | return 0; | ||
292 | /* Do not try handshake here as | ||
293 | * it require processing on MHD side and | ||
294 | * when testing with "external" polling, | ||
295 | * test will call MHD processing only | ||
296 | * after return from wr_connect(). */ | ||
297 | /* | ||
298 | int res = gnutls_handshake (s->tls_s); | ||
299 | if (GNUTLS_E_SUCCESS == res) | ||
300 | { | ||
301 | s->tls_connected = !0; | ||
302 | return 0; | ||
303 | } | ||
304 | if (GNUTLS_E_AGAIN == res) | ||
305 | return 0; | ||
306 | */ | ||
307 | } | ||
308 | #endif /* HTTPS_SUPPORT */ | ||
309 | return -1; | ||
310 | } | ||
311 | |||
312 | #ifdef HTTPS_SUPPORT | ||
313 | /* Only to be called from wr_send() and wr_recv() ! */ | ||
314 | static bool wr_handshake(wr_socket s) | ||
315 | { | ||
316 | int res = gnutls_handshake (s->tls_s); | ||
317 | if (GNUTLS_E_SUCCESS == res) | ||
318 | s->tls_connected = !0; | ||
319 | else if (GNUTLS_E_AGAIN == res) | ||
320 | MHD_socket_set_error_ (MHD_SCKT_EAGAIN_); | ||
321 | else | ||
322 | MHD_socket_set_error_ (MHD_SCKT_ECONNABORTED_); /* hard error */ | ||
323 | return s->tls_connected; | ||
324 | } | ||
325 | #endif /* HTTPS_SUPPORT */ | ||
326 | |||
327 | |||
328 | /** | ||
329 | * Send data to remote by socket. | ||
330 | * @param s the socket to use | ||
331 | * @param buf the buffer with data to send | ||
332 | * @param len the length of data in @a buf | ||
333 | * @return number of bytes were sent if succeed, | ||
334 | * -1 if failed. Use #MHD_socket_get_error_() | ||
335 | * to get socket error. | ||
336 | */ | ||
337 | static ssize_t wr_send(wr_socket s, const void *buf, size_t len) | ||
338 | { | ||
339 | if (wr_plain == s->t) | ||
340 | return MHD_send_(s->fd, buf, len); | ||
341 | #ifdef HTTPS_SUPPORT | ||
342 | if (wr_tls == s->t) | ||
343 | { | ||
344 | ssize_t ret; | ||
345 | if (!s->tls_connected && !wr_handshake (s)) | ||
346 | return -1; | ||
347 | |||
348 | ret = gnutls_record_send (s->tls_s, buf, len); | ||
349 | if (ret > 0) | ||
350 | return ret; | ||
351 | if (GNUTLS_E_AGAIN == ret) | ||
352 | MHD_socket_set_error_ (MHD_SCKT_EAGAIN_); | ||
353 | else | ||
354 | MHD_socket_set_error_ (MHD_SCKT_ECONNABORTED_); /* hard error */ | ||
355 | } | ||
356 | #endif /* HTTPS_SUPPORT */ | ||
357 | return -1; | ||
358 | } | ||
359 | |||
360 | |||
361 | /** | ||
362 | * Receive data from remote by socket. | ||
363 | * @param s the socket to use | ||
364 | * @param buf the buffer to store received data | ||
365 | * @param len the length of @a buf | ||
366 | * @return number of bytes were received if succeed, | ||
367 | * -1 if failed. Use #MHD_socket_get_error_() | ||
368 | * to get socket error. | ||
369 | */ | ||
370 | static ssize_t wr_recv(wr_socket s, void *buf, size_t len) | ||
371 | { | ||
372 | if (wr_plain == s->t) | ||
373 | return MHD_recv_ (s->fd, buf, len); | ||
374 | #ifdef HTTPS_SUPPORT | ||
375 | if (wr_tls == s->t) | ||
376 | { | ||
377 | ssize_t ret; | ||
378 | if (!s->tls_connected && !wr_handshake (s)) | ||
379 | return -1; | ||
380 | |||
381 | ret = gnutls_record_recv (s->tls_s, buf, len); | ||
382 | if (ret > 0) | ||
383 | return ret; | ||
384 | if (GNUTLS_E_AGAIN == ret) | ||
385 | MHD_socket_set_error_ (MHD_SCKT_EAGAIN_); | ||
386 | else | ||
387 | MHD_socket_set_error_ (MHD_SCKT_ECONNABORTED_); /* hard error */ | ||
388 | } | ||
389 | #endif /* HTTPS_SUPPORT */ | ||
390 | return -1; | ||
391 | } | ||
392 | |||
393 | |||
394 | /** | ||
395 | * Perform shutdown of TCP socket for plain sockets or | ||
396 | * shutdown of TLS layer for TLS sockets. | ||
397 | * @param s the socket to shutdown | ||
398 | * @param how SHUT_WR or SHUT_RDWR | ||
399 | * @return zero on succeed, -1 otherwise | ||
400 | */ | ||
401 | static int wr_shutdown(wr_socket s, int how) | ||
402 | { | ||
403 | if (wr_plain == s->t) | ||
404 | return shutdown (s->fd, how); | ||
405 | #ifdef HTTPS_SUPPORT | ||
406 | if (wr_tls == s->t) | ||
407 | { | ||
408 | ssize_t ret; | ||
409 | if (SHUT_WR == how) | ||
410 | ret = gnutls_bye (s->tls_s, GNUTLS_SHUT_WR); | ||
411 | else | ||
412 | ret = gnutls_bye (s->tls_s, GNUTLS_SHUT_RDWR); | ||
413 | |||
414 | if (GNUTLS_E_SUCCESS == ret) | ||
415 | return 0; | ||
416 | } | ||
417 | #endif /* HTTPS_SUPPORT */ | ||
418 | return -1; | ||
419 | } | ||
420 | |||
421 | |||
422 | /** | ||
423 | * Close socket and release allocated resourced | ||
424 | * @param s the socket to close | ||
425 | * @return zero on succeed, -1 otherwise | ||
426 | */ | ||
427 | static int wr_close(wr_socket s) | ||
428 | { | ||
429 | int ret = (MHD_socket_close_(s->fd)) ? 0 : -1; | ||
430 | #ifdef HTTPS_SUPPORT | ||
431 | if (wr_tls == s->t) | ||
432 | { | ||
433 | gnutls_deinit (s->tls_s); | ||
434 | gnutls_certificate_free_credentials (s->tls_crd); | ||
435 | } | ||
436 | #endif /* HTTPS_SUPPORT */ | ||
437 | free(s); | ||
438 | return ret; | ||
439 | } | ||
440 | |||
441 | |||
442 | /** | ||
443 | * Thread we use to run the interaction with the upgraded socket. | ||
444 | */ | ||
445 | static pthread_t pt; | ||
446 | |||
447 | /** | ||
448 | * Will be set to the upgraded socket. | ||
449 | */ | ||
450 | static wr_socket usock; | ||
451 | |||
452 | /** | ||
453 | * Thread we use to run the interaction with the upgraded socket. | ||
454 | */ | ||
455 | static pthread_t pt_client; | ||
456 | |||
457 | /** | ||
458 | * Flag set to 1 once the test is finished. | ||
459 | */ | ||
460 | static int done; | ||
461 | |||
462 | |||
463 | static void | ||
464 | notify_completed_cb (void *cls, | ||
465 | struct MHD_Connection *connection, | ||
466 | void **con_cls, | ||
467 | enum MHD_RequestTerminationCode toe) | ||
468 | { | ||
469 | if ( (toe != MHD_REQUEST_TERMINATED_COMPLETED_OK) && | ||
470 | (toe != MHD_REQUEST_TERMINATED_CLIENT_ABORT) && | ||
471 | (toe != MHD_REQUEST_TERMINATED_DAEMON_SHUTDOWN) ) | ||
472 | abort (); | ||
473 | if (((long) *con_cls) != (long) pthread_self ()) | ||
474 | abort (); | ||
475 | *con_cls = NULL; | ||
476 | } | ||
477 | |||
478 | |||
479 | /** | ||
480 | * Logging callback. | ||
481 | * | ||
482 | * @param cls logging closure (NULL) | ||
483 | * @param uri access URI | ||
484 | * @param connection connection handle | ||
485 | * @return #TEST_PTR | ||
486 | */ | ||
487 | static void * | ||
488 | log_cb (void *cls, | ||
489 | const char *uri, | ||
490 | struct MHD_Connection *connection) | ||
491 | { | ||
492 | if (0 != strcmp (uri, | ||
493 | "/")) | ||
494 | abort (); | ||
495 | return (void *) (long) pthread_self (); | ||
496 | } | ||
497 | |||
498 | |||
499 | /** | ||
500 | * Function to check that MHD properly notifies about starting | ||
501 | * and stopping. | ||
502 | * | ||
503 | * @param cls client-defined closure | ||
504 | * @param connection connection handle | ||
505 | * @param socket_context socket-specific pointer where the | ||
506 | * client can associate some state specific | ||
507 | * to the TCP connection; note that this is | ||
508 | * different from the "con_cls" which is per | ||
509 | * HTTP request. The client can initialize | ||
510 | * during #MHD_CONNECTION_NOTIFY_STARTED and | ||
511 | * cleanup during #MHD_CONNECTION_NOTIFY_CLOSED | ||
512 | * and access in the meantime using | ||
513 | * #MHD_CONNECTION_INFO_SOCKET_CONTEXT. | ||
514 | * @param toe reason for connection notification | ||
515 | * @see #MHD_OPTION_NOTIFY_CONNECTION | ||
516 | * @ingroup request | ||
517 | */ | ||
518 | static void | ||
519 | notify_connection_cb (void *cls, | ||
520 | struct MHD_Connection *connection, | ||
521 | void **socket_context, | ||
522 | enum MHD_ConnectionNotificationCode toe) | ||
523 | { | ||
524 | static int started; | ||
525 | |||
526 | switch (toe) | ||
527 | { | ||
528 | case MHD_CONNECTION_NOTIFY_STARTED: | ||
529 | if (MHD_NO != started) | ||
530 | abort (); | ||
531 | started = MHD_YES; | ||
532 | *socket_context = &started; | ||
533 | break; | ||
534 | case MHD_CONNECTION_NOTIFY_CLOSED: | ||
535 | if (MHD_YES != started) | ||
536 | abort (); | ||
537 | if (&started != *socket_context) | ||
538 | abort (); | ||
539 | *socket_context = NULL; | ||
540 | started = MHD_NO; | ||
541 | break; | ||
542 | } | ||
543 | } | ||
544 | |||
545 | |||
546 | /** | ||
547 | * Change socket to blocking. | ||
548 | * | ||
549 | * @param fd the socket to manipulate | ||
550 | * @return non-zero if succeeded, zero otherwise | ||
551 | */ | ||
552 | static void | ||
553 | make_blocking (MHD_socket fd) | ||
554 | { | ||
555 | #if defined(MHD_POSIX_SOCKETS) | ||
556 | int flags; | ||
557 | |||
558 | flags = fcntl (fd, F_GETFL); | ||
559 | if (-1 == flags) | ||
560 | return; | ||
561 | if ((flags & ~O_NONBLOCK) != flags) | ||
562 | if (-1 == fcntl (fd, F_SETFL, flags & ~O_NONBLOCK)) | ||
563 | abort (); | ||
564 | #elif defined(MHD_WINSOCK_SOCKETS) | ||
565 | unsigned long flags = 1; | ||
566 | |||
567 | ioctlsocket (fd, FIONBIO, &flags); | ||
568 | #endif /* MHD_WINSOCK_SOCKETS */ | ||
569 | |||
570 | } | ||
571 | |||
572 | |||
573 | static void | ||
574 | send_all (wr_socket sock, | ||
575 | const char *text) | ||
576 | { | ||
577 | size_t len = strlen (text); | ||
578 | ssize_t ret; | ||
579 | |||
580 | make_blocking (wr_fd (sock)); | ||
581 | for (size_t off = 0; off < len; off += ret) | ||
582 | { | ||
583 | ret = wr_send (sock, | ||
584 | &text[off], | ||
585 | len - off); | ||
586 | if (0 > ret) | ||
587 | { | ||
588 | if (MHD_SCKT_ERR_IS_EAGAIN_ (MHD_socket_get_error_ ())) | ||
589 | { | ||
590 | ret = 0; | ||
591 | continue; | ||
592 | } | ||
593 | abort (); | ||
594 | } | ||
595 | } | ||
596 | } | ||
597 | |||
598 | |||
599 | /** | ||
600 | * Read character-by-character until we | ||
601 | * get '\r\n\r\n'. | ||
602 | */ | ||
603 | static void | ||
604 | recv_hdr (wr_socket sock) | ||
605 | { | ||
606 | unsigned int i; | ||
607 | char next; | ||
608 | char c; | ||
609 | ssize_t ret; | ||
610 | |||
611 | make_blocking (wr_fd (sock)); | ||
612 | next = '\r'; | ||
613 | i = 0; | ||
614 | while (i < 4) | ||
615 | { | ||
616 | ret = wr_recv (sock, | ||
617 | &c, | ||
618 | 1); | ||
619 | if (0 > ret) | ||
620 | { | ||
621 | if (MHD_SCKT_ERR_IS_EAGAIN_ (MHD_socket_get_error_ ())) | ||
622 | continue; | ||
623 | abort (); | ||
624 | } | ||
625 | if (0 == ret) | ||
626 | continue; | ||
627 | if (c == next) | ||
628 | { | ||
629 | i++; | ||
630 | if (next == '\r') | ||
631 | next = '\n'; | ||
632 | else | ||
633 | next = '\r'; | ||
634 | continue; | ||
635 | } | ||
636 | if (c == '\r') | ||
637 | { | ||
638 | i = 1; | ||
639 | next = '\n'; | ||
640 | continue; | ||
641 | } | ||
642 | i = 0; | ||
643 | next = '\r'; | ||
644 | } | ||
645 | } | ||
646 | |||
647 | |||
648 | static void | ||
649 | recv_all (wr_socket sock, | ||
650 | const char *text) | ||
651 | { | ||
652 | size_t len = strlen (text); | ||
653 | char buf[len]; | ||
654 | ssize_t ret; | ||
655 | |||
656 | make_blocking (wr_fd (sock)); | ||
657 | for (size_t off = 0; off < len; off += ret) | ||
658 | { | ||
659 | ret = wr_recv (sock, | ||
660 | &buf[off], | ||
661 | len - off); | ||
662 | if (0 > ret) | ||
663 | { | ||
664 | if (MHD_SCKT_ERR_IS_EAGAIN_ (MHD_socket_get_error_ ())) | ||
665 | { | ||
666 | ret = 0; | ||
667 | continue; | ||
668 | } | ||
669 | abort (); | ||
670 | } | ||
671 | } | ||
672 | if (0 != strncmp (text, buf, len)) | ||
673 | abort(); | ||
674 | } | ||
675 | |||
676 | |||
677 | /** | ||
678 | * Main function for the thread that runs the interaction with | ||
679 | * the upgraded socket. | ||
680 | * | ||
681 | * @param cls the handle for the upgrade | ||
682 | */ | ||
683 | static void * | ||
684 | run_usock (void *cls) | ||
685 | { | ||
686 | struct MHD_UpgradeResponseHandle *urh = cls; | ||
687 | |||
688 | send_all (usock, | ||
689 | "Hello"); | ||
690 | recv_all (usock, | ||
691 | "World"); | ||
692 | send_all (usock, | ||
693 | "Finished"); | ||
694 | MHD_upgrade_action (urh, | ||
695 | MHD_UPGRADE_ACTION_CLOSE); | ||
696 | return NULL; | ||
697 | } | ||
698 | |||
699 | |||
700 | /** | ||
701 | * Main function for the thread that runs the client-side of the | ||
702 | * interaction with the upgraded socket. | ||
703 | * | ||
704 | * @param cls the client socket | ||
705 | */ | ||
706 | static void * | ||
707 | run_usock_client (void *cls) | ||
708 | { | ||
709 | wr_socket *sock = cls; | ||
710 | |||
711 | send_all (*sock, | ||
712 | "GET / HTTP/1.1\r\nConnection: Upgrade\r\n\r\n"); | ||
713 | recv_hdr (*sock); | ||
714 | recv_all (*sock, | ||
715 | "Hello"); | ||
716 | send_all (*sock, | ||
717 | "World"); | ||
718 | recv_all (*sock, | ||
719 | "Finished"); | ||
720 | wr_close (*sock); | ||
721 | done = 1; | ||
722 | return NULL; | ||
723 | } | ||
724 | |||
725 | |||
726 | /** | ||
727 | * Function called after a protocol "upgrade" response was sent | ||
728 | * successfully and the socket should now be controlled by some | ||
729 | * protocol other than HTTP. | ||
730 | * | ||
731 | * Any data already received on the socket will be made available in | ||
732 | * @e extra_in. This can happen if the application sent extra data | ||
733 | * before MHD send the upgrade response. The application should | ||
734 | * treat data from @a extra_in as if it had read it from the socket. | ||
735 | * | ||
736 | * Note that the application must not close() @a sock directly, | ||
737 | * but instead use #MHD_upgrade_action() for special operations | ||
738 | * on @a sock. | ||
739 | * | ||
740 | * Except when in 'thread-per-connection' mode, implementations | ||
741 | * of this function should never block (as it will still be called | ||
742 | * from within the main event loop). | ||
743 | * | ||
744 | * @param cls closure, whatever was given to #MHD_create_response_for_upgrade(). | ||
745 | * @param connection original HTTP connection handle, | ||
746 | * giving the function a last chance | ||
747 | * to inspect the original HTTP request | ||
748 | * @param con_cls last value left in `con_cls` of the `MHD_AccessHandlerCallback` | ||
749 | * @param extra_in if we happened to have read bytes after the | ||
750 | * HTTP header already (because the client sent | ||
751 | * more than the HTTP header of the request before | ||
752 | * we sent the upgrade response), | ||
753 | * these are the extra bytes already read from @a sock | ||
754 | * by MHD. The application should treat these as if | ||
755 | * it had read them from @a sock. | ||
756 | * @param extra_in_size number of bytes in @a extra_in | ||
757 | * @param sock socket to use for bi-directional communication | ||
758 | * with the client. For HTTPS, this may not be a socket | ||
759 | * that is directly connected to the client and thus certain | ||
760 | * operations (TCP-specific setsockopt(), getsockopt(), etc.) | ||
761 | * may not work as expected (as the socket could be from a | ||
762 | * socketpair() or a TCP-loopback). The application is expected | ||
763 | * to perform read()/recv() and write()/send() calls on the socket. | ||
764 | * The application may also call shutdown(), but must not call | ||
765 | * close() directly. | ||
766 | * @param urh argument for #MHD_upgrade_action()s on this @a connection. | ||
767 | * Applications must eventually use this callback to (indirectly) | ||
768 | * perform the close() action on the @a sock. | ||
769 | */ | ||
770 | static void | ||
771 | upgrade_cb (void *cls, | ||
772 | struct MHD_Connection *connection, | ||
773 | void *con_cls, | ||
774 | const char *extra_in, | ||
775 | size_t extra_in_size, | ||
776 | MHD_socket sock, | ||
777 | struct MHD_UpgradeResponseHandle *urh) | ||
778 | { | ||
779 | usock = wr_create_from_plain_sckt (sock); | ||
780 | if (0 != extra_in_size) | ||
781 | abort (); | ||
782 | if (0 != pthread_create (&pt, | ||
783 | NULL, | ||
784 | &run_usock, | ||
785 | urh)) | ||
786 | abort (); | ||
787 | } | ||
788 | |||
789 | |||
790 | /** | ||
791 | * A client has requested the given url using the given method | ||
792 | * (#MHD_HTTP_METHOD_GET, #MHD_HTTP_METHOD_PUT, | ||
793 | * #MHD_HTTP_METHOD_DELETE, #MHD_HTTP_METHOD_POST, etc). The callback | ||
794 | * must call MHD callbacks to provide content to give back to the | ||
795 | * client and return an HTTP status code (i.e. #MHD_HTTP_OK, | ||
796 | * #MHD_HTTP_NOT_FOUND, etc.). | ||
797 | * | ||
798 | * @param cls argument given together with the function | ||
799 | * pointer when the handler was registered with MHD | ||
800 | * @param url the requested url | ||
801 | * @param method the HTTP method used (#MHD_HTTP_METHOD_GET, | ||
802 | * #MHD_HTTP_METHOD_PUT, etc.) | ||
803 | * @param version the HTTP version string (i.e. | ||
804 | * #MHD_HTTP_VERSION_1_1) | ||
805 | * @param upload_data the data being uploaded (excluding HEADERS, | ||
806 | * for a POST that fits into memory and that is encoded | ||
807 | * with a supported encoding, the POST data will NOT be | ||
808 | * given in upload_data and is instead available as | ||
809 | * part of #MHD_get_connection_values; very large POST | ||
810 | * data *will* be made available incrementally in | ||
811 | * @a upload_data) | ||
812 | * @param upload_data_size set initially to the size of the | ||
813 | * @a upload_data provided; the method must update this | ||
814 | * value to the number of bytes NOT processed; | ||
815 | * @param con_cls pointer that the callback can set to some | ||
816 | * address and that will be preserved by MHD for future | ||
817 | * calls for this request; since the access handler may | ||
818 | * be called many times (i.e., for a PUT/POST operation | ||
819 | * with plenty of upload data) this allows the application | ||
820 | * to easily associate some request-specific state. | ||
821 | * If necessary, this state can be cleaned up in the | ||
822 | * global #MHD_RequestCompletedCallback (which | ||
823 | * can be set with the #MHD_OPTION_NOTIFY_COMPLETED). | ||
824 | * Initially, `*con_cls` will be NULL. | ||
825 | * @return #MHD_YES if the connection was handled successfully, | ||
826 | * #MHD_NO if the socket must be closed due to a serios | ||
827 | * error while handling the request | ||
828 | */ | ||
829 | static int | ||
830 | ahc_upgrade (void *cls, | ||
831 | struct MHD_Connection *connection, | ||
832 | const char *url, | ||
833 | const char *method, | ||
834 | const char *version, | ||
835 | const char *upload_data, | ||
836 | size_t *upload_data_size, | ||
837 | void **con_cls) | ||
838 | { | ||
839 | struct MHD_Response *resp; | ||
840 | int ret; | ||
841 | |||
842 | if (((long) *con_cls) != (long) pthread_self ()) | ||
843 | abort (); | ||
844 | resp = MHD_create_response_for_upgrade (&upgrade_cb, | ||
845 | NULL); | ||
846 | MHD_add_response_header (resp, | ||
847 | MHD_HTTP_HEADER_UPGRADE, | ||
848 | "Hello World Protocol"); | ||
849 | ret = MHD_queue_response (connection, | ||
850 | MHD_HTTP_SWITCHING_PROTOCOLS, | ||
851 | resp); | ||
852 | MHD_destroy_response (resp); | ||
853 | return ret; | ||
854 | } | ||
855 | |||
856 | |||
857 | /** | ||
858 | * Run the MHD external event loop using select. | ||
859 | * | ||
860 | * @param daemon daemon to run it for | ||
861 | */ | ||
862 | static void | ||
863 | run_mhd_select_loop (struct MHD_Daemon *daemon) | ||
864 | { | ||
865 | fd_set rs; | ||
866 | fd_set ws; | ||
867 | fd_set es; | ||
868 | MHD_socket max_fd; | ||
869 | MHD_UNSIGNED_LONG_LONG to; | ||
870 | struct timeval tv; | ||
871 | |||
872 | while (! done) | ||
873 | { | ||
874 | FD_ZERO (&rs); | ||
875 | FD_ZERO (&ws); | ||
876 | FD_ZERO (&es); | ||
877 | max_fd = -1; | ||
878 | to = 1000; | ||
879 | |||
880 | if (MHD_YES != | ||
881 | MHD_get_fdset (daemon, | ||
882 | &rs, | ||
883 | &ws, | ||
884 | &es, | ||
885 | &max_fd)) | ||
886 | abort (); | ||
887 | (void) MHD_get_timeout (daemon, | ||
888 | &to); | ||
889 | if (1000 < to) | ||
890 | to = 1000; | ||
891 | tv.tv_sec = to / 1000; | ||
892 | tv.tv_usec = 1000 * (to % 1000); | ||
893 | if (0 > MHD_SYS_select_ (max_fd + 1, | ||
894 | &rs, | ||
895 | &ws, | ||
896 | &es, | ||
897 | &tv)) | ||
898 | abort (); | ||
899 | MHD_run_from_select (daemon, | ||
900 | &rs, | ||
901 | &ws, | ||
902 | &es); | ||
903 | } | ||
904 | } | ||
905 | |||
906 | |||
907 | /** | ||
908 | * Run the MHD external event loop using select. | ||
909 | * | ||
910 | * @param daemon daemon to run it for | ||
911 | */ | ||
912 | static void | ||
913 | run_mhd_poll_loop (struct MHD_Daemon *daemon) | ||
914 | { | ||
915 | abort (); /* currently not implementable with existing MHD API */ | ||
916 | } | ||
917 | |||
918 | |||
919 | /** | ||
920 | * Run the MHD external event loop using select. | ||
921 | * | ||
922 | * @param daemon daemon to run it for | ||
923 | */ | ||
924 | static void | ||
925 | run_mhd_epoll_loop (struct MHD_Daemon *daemon) | ||
926 | { | ||
927 | const union MHD_DaemonInfo *di; | ||
928 | MHD_socket ep; | ||
929 | fd_set rs; | ||
930 | MHD_UNSIGNED_LONG_LONG to; | ||
931 | struct timeval tv; | ||
932 | |||
933 | di = MHD_get_daemon_info (daemon, | ||
934 | MHD_DAEMON_INFO_EPOLL_FD); | ||
935 | ep = di->listen_fd; | ||
936 | while (! done) | ||
937 | { | ||
938 | FD_ZERO (&rs); | ||
939 | to = 1000; | ||
940 | |||
941 | FD_SET (ep, &rs); | ||
942 | (void) MHD_get_timeout (daemon, | ||
943 | &to); | ||
944 | if (1000 < to) | ||
945 | to = 1000; | ||
946 | tv.tv_sec = to / 1000; | ||
947 | tv.tv_usec = 1000 * (to % 1000); | ||
948 | select (ep + 1, | ||
949 | &rs, | ||
950 | NULL, | ||
951 | NULL, | ||
952 | &tv); | ||
953 | MHD_run (daemon); | ||
954 | } | ||
955 | } | ||
956 | |||
957 | |||
958 | /** | ||
959 | * Run the MHD external event loop using select. | ||
960 | * | ||
961 | * @param daemon daemon to run it for | ||
962 | */ | ||
963 | static void | ||
964 | run_mhd_loop (struct MHD_Daemon *daemon, | ||
965 | int flags) | ||
966 | { | ||
967 | if (0 != (flags & MHD_USE_POLL)) | ||
968 | run_mhd_poll_loop (daemon); | ||
969 | #if EPOLL_SUPPORT | ||
970 | else if (0 != (flags & MHD_USE_EPOLL)) | ||
971 | run_mhd_epoll_loop (daemon); | ||
972 | #endif | ||
973 | else | ||
974 | run_mhd_select_loop (daemon); | ||
975 | } | ||
976 | |||
977 | static bool test_tls; | ||
978 | |||
979 | /** | ||
49 | * Test upgrading a connection. | 980 | * Test upgrading a connection. |
50 | * | 981 | * |
51 | * @param flags which event loop style should be tested | 982 | * @param flags which event loop style should be tested |
@@ -55,13 +986,17 @@ static int | |||
55 | test_upgrade (int flags, | 986 | test_upgrade (int flags, |
56 | unsigned int pool) | 987 | unsigned int pool) |
57 | { | 988 | { |
58 | struct MHD_Daemon *d; | 989 | struct MHD_Daemon *d = NULL; |
59 | MHD_socket sock; | 990 | wr_socket sock; |
60 | struct sockaddr_in sa; | 991 | struct sockaddr_in sa; |
992 | #if defined(HTTPS_SUPPORT) && defined(HAVE_FORK) && defined(HAVE_WAITPID) | ||
993 | pid_t pid = -1; | ||
994 | #endif /* HTTPS_SUPPORT && HAVE_FORK && HAVE_WAITPID */ | ||
61 | 995 | ||
62 | done = 0; | 996 | done = 0; |
63 | 997 | ||
64 | d = MHD_start_daemon (flags | MHD_USE_DEBUG | MHD_USE_UPGRADE, | 998 | if (!test_tls) |
999 | d = MHD_start_daemon (flags | MHD_USE_DEBUG | MHD_USE_UPGRADE, | ||
65 | 1080, | 1000 | 1080, |
66 | NULL, NULL, | 1001 | NULL, NULL, |
67 | &ahc_upgrade, NULL, | 1002 | &ahc_upgrade, NULL, |
@@ -70,18 +1005,52 @@ test_upgrade (int flags, | |||
70 | MHD_OPTION_NOTIFY_CONNECTION, ¬ify_connection_cb, NULL, | 1005 | MHD_OPTION_NOTIFY_CONNECTION, ¬ify_connection_cb, NULL, |
71 | MHD_OPTION_THREAD_POOL_SIZE, pool, | 1006 | MHD_OPTION_THREAD_POOL_SIZE, pool, |
72 | MHD_OPTION_END); | 1007 | MHD_OPTION_END); |
1008 | #ifdef HTTPS_SUPPORT | ||
1009 | else | ||
1010 | d = MHD_start_daemon (flags | MHD_USE_DEBUG | MHD_USE_UPGRADE | MHD_USE_TLS, | ||
1011 | 1080, | ||
1012 | NULL, NULL, | ||
1013 | &ahc_upgrade, NULL, | ||
1014 | MHD_OPTION_URI_LOG_CALLBACK, &log_cb, NULL, | ||
1015 | MHD_OPTION_NOTIFY_COMPLETED, ¬ify_completed_cb, NULL, | ||
1016 | MHD_OPTION_NOTIFY_CONNECTION, ¬ify_connection_cb, NULL, | ||
1017 | MHD_OPTION_HTTPS_MEM_KEY, srv_signed_key_pem, | ||
1018 | MHD_OPTION_HTTPS_MEM_CERT, srv_signed_cert_pem, | ||
1019 | MHD_OPTION_THREAD_POOL_SIZE, pool, | ||
1020 | MHD_OPTION_END); | ||
1021 | #endif /* HTTPS_SUPPORT */ | ||
73 | if (NULL == d) | 1022 | if (NULL == d) |
74 | return 2; | 1023 | return 2; |
75 | sock = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP); | 1024 | if (!test_tls || TLS_LIB_GNUTLS == use_tls_tool) |
76 | if (MHD_INVALID_SOCKET == sock) | 1025 | { |
77 | abort (); | 1026 | sock = test_tls ? wr_create_tls_sckt () : wr_create_plain_sckt (); |
78 | sa.sin_family = AF_INET; | 1027 | if (WR_BAD == sock) |
79 | sa.sin_port = htons (1080); | 1028 | abort (); |
80 | sa.sin_addr.s_addr = htonl (INADDR_LOOPBACK); | 1029 | sa.sin_family = AF_INET; |
81 | if (0 != connect (sock, | 1030 | sa.sin_port = htons (1080); |
82 | (struct sockaddr *) &sa, | 1031 | sa.sin_addr.s_addr = htonl (INADDR_LOOPBACK); |
83 | sizeof (sa))) | 1032 | if (0 != wr_connect (sock, |
84 | abort (); | 1033 | (struct sockaddr *) &sa, |
1034 | sizeof (sa))) | ||
1035 | abort (); | ||
1036 | } | ||
1037 | else | ||
1038 | { | ||
1039 | #if defined(HTTPS_SUPPORT) && defined(HAVE_FORK) && defined(HAVE_WAITPID) | ||
1040 | MHD_socket tls_fork_sock; | ||
1041 | if (-1 == (pid = gnutlscli_connect (&tls_fork_sock, 1080))) | ||
1042 | { | ||
1043 | MHD_stop_daemon (d); | ||
1044 | return 4; | ||
1045 | } | ||
1046 | sock = wr_create_from_plain_sckt (tls_fork_sock); | ||
1047 | if (WR_BAD == sock) | ||
1048 | abort (); | ||
1049 | #else /* !HTTPS_SUPPORT || !HAVE_FORK || !HAVE_WAITPID */ | ||
1050 | abort (); | ||
1051 | #endif /* !HTTPS_SUPPORT || !HAVE_FORK || !HAVE_WAITPID */ | ||
1052 | } | ||
1053 | |||
85 | if (0 != pthread_create (&pt_client, | 1054 | if (0 != pthread_create (&pt_client, |
86 | NULL, | 1055 | NULL, |
87 | &run_usock_client, | 1056 | &run_usock_client, |
@@ -94,6 +1063,10 @@ test_upgrade (int flags, | |||
94 | NULL); | 1063 | NULL); |
95 | pthread_join (pt, | 1064 | pthread_join (pt, |
96 | NULL); | 1065 | NULL); |
1066 | #if defined(HTTPS_SUPPORT) && defined(HAVE_FORK) && defined(HAVE_WAITPID) | ||
1067 | if (test_tls && TLS_LIB_GNUTLS != use_tls_tool) | ||
1068 | waitpid (pid, NULL, 0); | ||
1069 | #endif /* HTTPS_SUPPORT && HAVE_FORK && HAVE_WAITPID */ | ||
97 | MHD_stop_daemon (d); | 1070 | MHD_stop_daemon (d); |
98 | return 0; | 1071 | return 0; |
99 | } | 1072 | } |
@@ -106,9 +1079,58 @@ main (int argc, | |||
106 | int error_count = 0; | 1079 | int error_count = 0; |
107 | int res; | 1080 | int res; |
108 | 1081 | ||
1082 | use_tls_tool = TLS_CLI_NO_TOOL; | ||
1083 | test_tls = has_in_name(argv[0], "_tls"); | ||
1084 | |||
109 | if (has_param(argc, argv, "-v") || has_param(argc, argv, "--verbose")) | 1085 | if (has_param(argc, argv, "-v") || has_param(argc, argv, "--verbose")) |
110 | verbose = 1; | 1086 | verbose = 1; |
111 | 1087 | ||
1088 | if (test_tls) | ||
1089 | { | ||
1090 | #ifdef HTTPS_SUPPORT | ||
1091 | if (has_param(argc, argv, "--use-gnutls-cli")) | ||
1092 | use_tls_tool = TLS_CLI_GNUTLS; | ||
1093 | else if (has_param(argc, argv, "--use-openssl")) | ||
1094 | use_tls_tool = TLS_CLI_OPENSSL; | ||
1095 | else if (has_param(argc, argv, "--use-gnutls-lib")) | ||
1096 | use_tls_tool = TLS_LIB_GNUTLS; | ||
1097 | #if defined(HAVE_FORK) && defined(HAVE_WAITPID) | ||
1098 | else if (0 == system ("gnutls-cli --version 1> /dev/null")) | ||
1099 | use_tls_tool = TLS_CLI_GNUTLS; | ||
1100 | else if (0 == system ("openssl version 1> /dev/null")) | ||
1101 | use_tls_tool = TLS_CLI_OPENSSL; | ||
1102 | #endif /* HAVE_FORK && HAVE_WAITPID */ | ||
1103 | else | ||
1104 | use_tls_tool = TLS_LIB_GNUTLS; /* Should be available as MHD use it. */ | ||
1105 | if (verbose) | ||
1106 | { | ||
1107 | switch (use_tls_tool) | ||
1108 | { | ||
1109 | case TLS_CLI_GNUTLS: | ||
1110 | printf ("GnuTLS-CLI will be used for testing.\n"); | ||
1111 | break; | ||
1112 | case TLS_CLI_OPENSSL: | ||
1113 | printf ("Command line version of OpenSSL will be used for testing.\n"); | ||
1114 | break; | ||
1115 | case TLS_LIB_GNUTLS: | ||
1116 | printf ("GnuTLS library will be used for testing.\n"); | ||
1117 | break; | ||
1118 | default: | ||
1119 | abort (); | ||
1120 | } | ||
1121 | } | ||
1122 | if (TLS_LIB_GNUTLS == use_tls_tool && GNUTLS_E_SUCCESS != gnutls_global_init()) | ||
1123 | abort (); | ||
1124 | |||
1125 | #else /* ! HTTPS_SUPPORT */ | ||
1126 | fprintf (stderr, "HTTPS support was disabled by configure.\n"); | ||
1127 | return 99; | ||
1128 | #endif /* ! HTTPS_SUPPORT */ | ||
1129 | } | ||
1130 | |||
1131 | /* run tests */ | ||
1132 | if (verbose) | ||
1133 | printf ("Starting HTTP \"Upgrade\" tests with %s connections.\n", test_tls ? "TLS" : "plain"); | ||
112 | /* try external select */ | 1134 | /* try external select */ |
113 | res = test_upgrade (0, | 1135 | res = test_upgrade (0, |
114 | 0); | 1136 | 0); |
@@ -194,5 +1216,9 @@ main (int argc, | |||
194 | fprintf (stderr, | 1216 | fprintf (stderr, |
195 | "Error (code: %u)\n", | 1217 | "Error (code: %u)\n", |
196 | error_count); | 1218 | error_count); |
1219 | #ifdef HTTPS_SUPPORT | ||
1220 | if (test_tls && TLS_LIB_GNUTLS == use_tls_tool) | ||
1221 | gnutls_global_deinit(); | ||
1222 | #endif /* HTTPS_SUPPORT */ | ||
197 | return error_count != 0; /* 0 == pass */ | 1223 | return error_count != 0; /* 0 == pass */ |
198 | } | 1224 | } |
diff --git a/src/microhttpd/test_upgrade_common.c b/src/microhttpd/test_upgrade_common.c deleted file mode 100644 index a96ffcdb..00000000 --- a/src/microhttpd/test_upgrade_common.c +++ /dev/null | |||
@@ -1,562 +0,0 @@ | |||
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 | #include "mhd_sockets.h" | ||
28 | |||
29 | /** | ||
30 | * Thread we use to run the interaction with the upgraded socket. | ||
31 | */ | ||
32 | static pthread_t pt; | ||
33 | |||
34 | /** | ||
35 | * Will be set to the upgraded socket. | ||
36 | */ | ||
37 | static MHD_socket usock; | ||
38 | |||
39 | /** | ||
40 | * Thread we use to run the interaction with the upgraded socket. | ||
41 | */ | ||
42 | static pthread_t pt_client; | ||
43 | |||
44 | /** | ||
45 | * Flag set to 1 once the test is finished. | ||
46 | */ | ||
47 | static int done; | ||
48 | |||
49 | |||
50 | static void | ||
51 | notify_completed_cb (void *cls, | ||
52 | struct MHD_Connection *connection, | ||
53 | void **con_cls, | ||
54 | enum MHD_RequestTerminationCode toe) | ||
55 | { | ||
56 | if ( (toe != MHD_REQUEST_TERMINATED_COMPLETED_OK) && | ||
57 | (toe != MHD_REQUEST_TERMINATED_CLIENT_ABORT) && | ||
58 | (toe != MHD_REQUEST_TERMINATED_DAEMON_SHUTDOWN) ) | ||
59 | abort (); | ||
60 | if (((long) *con_cls) != (long) pthread_self ()) | ||
61 | abort (); | ||
62 | *con_cls = NULL; | ||
63 | } | ||
64 | |||
65 | |||
66 | /** | ||
67 | * Logging callback. | ||
68 | * | ||
69 | * @param cls logging closure (NULL) | ||
70 | * @param uri access URI | ||
71 | * @param connection connection handle | ||
72 | * @return #TEST_PTR | ||
73 | */ | ||
74 | static void * | ||
75 | log_cb (void *cls, | ||
76 | const char *uri, | ||
77 | struct MHD_Connection *connection) | ||
78 | { | ||
79 | if (0 != strcmp (uri, | ||
80 | "/")) | ||
81 | abort (); | ||
82 | return (void *) (long) pthread_self (); | ||
83 | } | ||
84 | |||
85 | |||
86 | /** | ||
87 | * Function to check that MHD properly notifies about starting | ||
88 | * and stopping. | ||
89 | * | ||
90 | * @param cls client-defined closure | ||
91 | * @param connection connection handle | ||
92 | * @param socket_context socket-specific pointer where the | ||
93 | * client can associate some state specific | ||
94 | * to the TCP connection; note that this is | ||
95 | * different from the "con_cls" which is per | ||
96 | * HTTP request. The client can initialize | ||
97 | * during #MHD_CONNECTION_NOTIFY_STARTED and | ||
98 | * cleanup during #MHD_CONNECTION_NOTIFY_CLOSED | ||
99 | * and access in the meantime using | ||
100 | * #MHD_CONNECTION_INFO_SOCKET_CONTEXT. | ||
101 | * @param toe reason for connection notification | ||
102 | * @see #MHD_OPTION_NOTIFY_CONNECTION | ||
103 | * @ingroup request | ||
104 | */ | ||
105 | static void | ||
106 | notify_connection_cb (void *cls, | ||
107 | struct MHD_Connection *connection, | ||
108 | void **socket_context, | ||
109 | enum MHD_ConnectionNotificationCode toe) | ||
110 | { | ||
111 | static int started; | ||
112 | |||
113 | switch (toe) | ||
114 | { | ||
115 | case MHD_CONNECTION_NOTIFY_STARTED: | ||
116 | if (MHD_NO != started) | ||
117 | abort (); | ||
118 | started = MHD_YES; | ||
119 | *socket_context = &started; | ||
120 | break; | ||
121 | case MHD_CONNECTION_NOTIFY_CLOSED: | ||
122 | if (MHD_YES != started) | ||
123 | abort (); | ||
124 | if (&started != *socket_context) | ||
125 | abort (); | ||
126 | *socket_context = NULL; | ||
127 | started = MHD_NO; | ||
128 | break; | ||
129 | } | ||
130 | } | ||
131 | |||
132 | |||
133 | /** | ||
134 | * Change socket to blocking. | ||
135 | * | ||
136 | * @param fd the socket to manipulate | ||
137 | * @return non-zero if succeeded, zero otherwise | ||
138 | */ | ||
139 | static void | ||
140 | make_blocking (MHD_socket fd) | ||
141 | { | ||
142 | #if defined(MHD_POSIX_SOCKETS) | ||
143 | int flags; | ||
144 | |||
145 | flags = fcntl (fd, F_GETFL); | ||
146 | if (-1 == flags) | ||
147 | return; | ||
148 | if ((flags & ~O_NONBLOCK) != flags) | ||
149 | if (-1 == fcntl (fd, F_SETFL, flags & ~O_NONBLOCK)) | ||
150 | abort (); | ||
151 | #elif defined(MHD_WINSOCK_SOCKETS) | ||
152 | unsigned long flags = 1; | ||
153 | |||
154 | ioctlsocket (fd, FIONBIO, &flags); | ||
155 | #endif /* MHD_WINSOCK_SOCKETS */ | ||
156 | |||
157 | } | ||
158 | |||
159 | |||
160 | static void | ||
161 | send_all (MHD_socket sock, | ||
162 | const char *text) | ||
163 | { | ||
164 | size_t len = strlen (text); | ||
165 | ssize_t ret; | ||
166 | |||
167 | make_blocking (sock); | ||
168 | for (size_t off = 0; off < len; off += ret) | ||
169 | { | ||
170 | ret = MHD_send_ (sock, | ||
171 | &text[off], | ||
172 | len - off); | ||
173 | if (0 > ret) | ||
174 | { | ||
175 | if (MHD_SCKT_ERR_IS_EAGAIN_ (MHD_socket_get_error_ ())) | ||
176 | { | ||
177 | ret = 0; | ||
178 | continue; | ||
179 | } | ||
180 | abort (); | ||
181 | } | ||
182 | } | ||
183 | } | ||
184 | |||
185 | |||
186 | /** | ||
187 | * Read character-by-character until we | ||
188 | * get '\r\n\r\n'. | ||
189 | */ | ||
190 | static void | ||
191 | recv_hdr (MHD_socket sock) | ||
192 | { | ||
193 | unsigned int i; | ||
194 | char next; | ||
195 | char c; | ||
196 | ssize_t ret; | ||
197 | |||
198 | make_blocking (sock); | ||
199 | next = '\r'; | ||
200 | i = 0; | ||
201 | while (i < 4) | ||
202 | { | ||
203 | ret = MHD_recv_ (sock, | ||
204 | &c, | ||
205 | 1); | ||
206 | if (0 > ret) | ||
207 | { | ||
208 | if (MHD_SCKT_ERR_IS_EAGAIN_ (MHD_socket_get_error_ ())) | ||
209 | continue; | ||
210 | abort (); | ||
211 | } | ||
212 | if (0 == ret) | ||
213 | continue; | ||
214 | if (c == next) | ||
215 | { | ||
216 | i++; | ||
217 | if (next == '\r') | ||
218 | next = '\n'; | ||
219 | else | ||
220 | next = '\r'; | ||
221 | continue; | ||
222 | } | ||
223 | if (c == '\r') | ||
224 | { | ||
225 | i = 1; | ||
226 | next = '\n'; | ||
227 | continue; | ||
228 | } | ||
229 | i = 0; | ||
230 | next = '\r'; | ||
231 | } | ||
232 | } | ||
233 | |||
234 | |||
235 | static void | ||
236 | recv_all (MHD_socket sock, | ||
237 | const char *text) | ||
238 | { | ||
239 | size_t len = strlen (text); | ||
240 | char buf[len]; | ||
241 | ssize_t ret; | ||
242 | |||
243 | make_blocking (sock); | ||
244 | for (size_t off = 0; off < len; off += ret) | ||
245 | { | ||
246 | ret = MHD_recv_ (sock, | ||
247 | &buf[off], | ||
248 | len - off); | ||
249 | if (0 > ret) | ||
250 | { | ||
251 | if (MHD_SCKT_ERR_IS_EAGAIN_ (MHD_socket_get_error_ ())) | ||
252 | { | ||
253 | ret = 0; | ||
254 | continue; | ||
255 | } | ||
256 | abort (); | ||
257 | } | ||
258 | } | ||
259 | if (0 != strncmp (text, buf, len)) | ||
260 | abort(); | ||
261 | } | ||
262 | |||
263 | |||
264 | /** | ||
265 | * Main function for the thread that runs the interaction with | ||
266 | * the upgraded socket. | ||
267 | * | ||
268 | * @param cls the handle for the upgrade | ||
269 | */ | ||
270 | static void * | ||
271 | run_usock (void *cls) | ||
272 | { | ||
273 | struct MHD_UpgradeResponseHandle *urh = cls; | ||
274 | |||
275 | send_all (usock, | ||
276 | "Hello"); | ||
277 | recv_all (usock, | ||
278 | "World"); | ||
279 | send_all (usock, | ||
280 | "Finished"); | ||
281 | MHD_upgrade_action (urh, | ||
282 | MHD_UPGRADE_ACTION_CLOSE); | ||
283 | return NULL; | ||
284 | } | ||
285 | |||
286 | |||
287 | /** | ||
288 | * Main function for the thread that runs the client-side of the | ||
289 | * interaction with the upgraded socket. | ||
290 | * | ||
291 | * @param cls the client socket | ||
292 | */ | ||
293 | static void * | ||
294 | run_usock_client (void *cls) | ||
295 | { | ||
296 | MHD_socket *sock = cls; | ||
297 | |||
298 | send_all (*sock, | ||
299 | "GET / HTTP/1.1\r\nConnection: Upgrade\r\n\r\n"); | ||
300 | recv_hdr (*sock); | ||
301 | recv_all (*sock, | ||
302 | "Hello"); | ||
303 | send_all (*sock, | ||
304 | "World"); | ||
305 | recv_all (*sock, | ||
306 | "Finished"); | ||
307 | MHD_socket_close_chk_ (*sock); | ||
308 | done = 1; | ||
309 | return NULL; | ||
310 | } | ||
311 | |||
312 | |||
313 | /** | ||
314 | * Function called after a protocol "upgrade" response was sent | ||
315 | * successfully and the socket should now be controlled by some | ||
316 | * protocol other than HTTP. | ||
317 | * | ||
318 | * Any data already received on the socket will be made available in | ||
319 | * @e extra_in. This can happen if the application sent extra data | ||
320 | * before MHD send the upgrade response. The application should | ||
321 | * treat data from @a extra_in as if it had read it from the socket. | ||
322 | * | ||
323 | * Note that the application must not close() @a sock directly, | ||
324 | * but instead use #MHD_upgrade_action() for special operations | ||
325 | * on @a sock. | ||
326 | * | ||
327 | * Except when in 'thread-per-connection' mode, implementations | ||
328 | * of this function should never block (as it will still be called | ||
329 | * from within the main event loop). | ||
330 | * | ||
331 | * @param cls closure, whatever was given to #MHD_create_response_for_upgrade(). | ||
332 | * @param connection original HTTP connection handle, | ||
333 | * giving the function a last chance | ||
334 | * to inspect the original HTTP request | ||
335 | * @param con_cls last value left in `con_cls` of the `MHD_AccessHandlerCallback` | ||
336 | * @param extra_in if we happened to have read bytes after the | ||
337 | * HTTP header already (because the client sent | ||
338 | * more than the HTTP header of the request before | ||
339 | * we sent the upgrade response), | ||
340 | * these are the extra bytes already read from @a sock | ||
341 | * by MHD. The application should treat these as if | ||
342 | * it had read them from @a sock. | ||
343 | * @param extra_in_size number of bytes in @a extra_in | ||
344 | * @param sock socket to use for bi-directional communication | ||
345 | * with the client. For HTTPS, this may not be a socket | ||
346 | * that is directly connected to the client and thus certain | ||
347 | * operations (TCP-specific setsockopt(), getsockopt(), etc.) | ||
348 | * may not work as expected (as the socket could be from a | ||
349 | * socketpair() or a TCP-loopback). The application is expected | ||
350 | * to perform read()/recv() and write()/send() calls on the socket. | ||
351 | * The application may also call shutdown(), but must not call | ||
352 | * close() directly. | ||
353 | * @param urh argument for #MHD_upgrade_action()s on this @a connection. | ||
354 | * Applications must eventually use this callback to (indirectly) | ||
355 | * perform the close() action on the @a sock. | ||
356 | */ | ||
357 | static void | ||
358 | upgrade_cb (void *cls, | ||
359 | struct MHD_Connection *connection, | ||
360 | void *con_cls, | ||
361 | const char *extra_in, | ||
362 | size_t extra_in_size, | ||
363 | MHD_socket sock, | ||
364 | struct MHD_UpgradeResponseHandle *urh) | ||
365 | { | ||
366 | usock = sock; | ||
367 | if (0 != extra_in_size) | ||
368 | abort (); | ||
369 | if (0 != pthread_create (&pt, | ||
370 | NULL, | ||
371 | &run_usock, | ||
372 | urh)) | ||
373 | abort (); | ||
374 | } | ||
375 | |||
376 | |||
377 | /** | ||
378 | * A client has requested the given url using the given method | ||
379 | * (#MHD_HTTP_METHOD_GET, #MHD_HTTP_METHOD_PUT, | ||
380 | * #MHD_HTTP_METHOD_DELETE, #MHD_HTTP_METHOD_POST, etc). The callback | ||
381 | * must call MHD callbacks to provide content to give back to the | ||
382 | * client and return an HTTP status code (i.e. #MHD_HTTP_OK, | ||
383 | * #MHD_HTTP_NOT_FOUND, etc.). | ||
384 | * | ||
385 | * @param cls argument given together with the function | ||
386 | * pointer when the handler was registered with MHD | ||
387 | * @param url the requested url | ||
388 | * @param method the HTTP method used (#MHD_HTTP_METHOD_GET, | ||
389 | * #MHD_HTTP_METHOD_PUT, etc.) | ||
390 | * @param version the HTTP version string (i.e. | ||
391 | * #MHD_HTTP_VERSION_1_1) | ||
392 | * @param upload_data the data being uploaded (excluding HEADERS, | ||
393 | * for a POST that fits into memory and that is encoded | ||
394 | * with a supported encoding, the POST data will NOT be | ||
395 | * given in upload_data and is instead available as | ||
396 | * part of #MHD_get_connection_values; very large POST | ||
397 | * data *will* be made available incrementally in | ||
398 | * @a upload_data) | ||
399 | * @param upload_data_size set initially to the size of the | ||
400 | * @a upload_data provided; the method must update this | ||
401 | * value to the number of bytes NOT processed; | ||
402 | * @param con_cls pointer that the callback can set to some | ||
403 | * address and that will be preserved by MHD for future | ||
404 | * calls for this request; since the access handler may | ||
405 | * be called many times (i.e., for a PUT/POST operation | ||
406 | * with plenty of upload data) this allows the application | ||
407 | * to easily associate some request-specific state. | ||
408 | * If necessary, this state can be cleaned up in the | ||
409 | * global #MHD_RequestCompletedCallback (which | ||
410 | * can be set with the #MHD_OPTION_NOTIFY_COMPLETED). | ||
411 | * Initially, `*con_cls` will be NULL. | ||
412 | * @return #MHD_YES if the connection was handled successfully, | ||
413 | * #MHD_NO if the socket must be closed due to a serios | ||
414 | * error while handling the request | ||
415 | */ | ||
416 | static int | ||
417 | ahc_upgrade (void *cls, | ||
418 | struct MHD_Connection *connection, | ||
419 | const char *url, | ||
420 | const char *method, | ||
421 | const char *version, | ||
422 | const char *upload_data, | ||
423 | size_t *upload_data_size, | ||
424 | void **con_cls) | ||
425 | { | ||
426 | struct MHD_Response *resp; | ||
427 | int ret; | ||
428 | |||
429 | if (((long) *con_cls) != (long) pthread_self ()) | ||
430 | abort (); | ||
431 | resp = MHD_create_response_for_upgrade (&upgrade_cb, | ||
432 | NULL); | ||
433 | MHD_add_response_header (resp, | ||
434 | MHD_HTTP_HEADER_UPGRADE, | ||
435 | "Hello World Protocol"); | ||
436 | ret = MHD_queue_response (connection, | ||
437 | MHD_HTTP_SWITCHING_PROTOCOLS, | ||
438 | resp); | ||
439 | MHD_destroy_response (resp); | ||
440 | return ret; | ||
441 | } | ||
442 | |||
443 | |||
444 | /** | ||
445 | * Run the MHD external event loop using select. | ||
446 | * | ||
447 | * @param daemon daemon to run it for | ||
448 | */ | ||
449 | static void | ||
450 | run_mhd_select_loop (struct MHD_Daemon *daemon) | ||
451 | { | ||
452 | fd_set rs; | ||
453 | fd_set ws; | ||
454 | fd_set es; | ||
455 | MHD_socket max_fd; | ||
456 | MHD_UNSIGNED_LONG_LONG to; | ||
457 | struct timeval tv; | ||
458 | |||
459 | while (! done) | ||
460 | { | ||
461 | FD_ZERO (&rs); | ||
462 | FD_ZERO (&ws); | ||
463 | FD_ZERO (&es); | ||
464 | max_fd = -1; | ||
465 | to = 1000; | ||
466 | |||
467 | if (MHD_YES != | ||
468 | MHD_get_fdset (daemon, | ||
469 | &rs, | ||
470 | &ws, | ||
471 | &es, | ||
472 | &max_fd)) | ||
473 | abort (); | ||
474 | (void) MHD_get_timeout (daemon, | ||
475 | &to); | ||
476 | if (1000 < to) | ||
477 | to = 1000; | ||
478 | tv.tv_sec = to / 1000; | ||
479 | tv.tv_usec = 1000 * (to % 1000); | ||
480 | if (0 > MHD_SYS_select_ (max_fd + 1, | ||
481 | &rs, | ||
482 | &ws, | ||
483 | &es, | ||
484 | &tv)) | ||
485 | abort (); | ||
486 | MHD_run_from_select (daemon, | ||
487 | &rs, | ||
488 | &ws, | ||
489 | &es); | ||
490 | } | ||
491 | } | ||
492 | |||
493 | |||
494 | /** | ||
495 | * Run the MHD external event loop using select. | ||
496 | * | ||
497 | * @param daemon daemon to run it for | ||
498 | */ | ||
499 | static void | ||
500 | run_mhd_poll_loop (struct MHD_Daemon *daemon) | ||
501 | { | ||
502 | abort (); /* currently not implementable with existing MHD API */ | ||
503 | } | ||
504 | |||
505 | |||
506 | /** | ||
507 | * Run the MHD external event loop using select. | ||
508 | * | ||
509 | * @param daemon daemon to run it for | ||
510 | */ | ||
511 | static void | ||
512 | run_mhd_epoll_loop (struct MHD_Daemon *daemon) | ||
513 | { | ||
514 | const union MHD_DaemonInfo *di; | ||
515 | MHD_socket ep; | ||
516 | fd_set rs; | ||
517 | MHD_UNSIGNED_LONG_LONG to; | ||
518 | struct timeval tv; | ||
519 | |||
520 | di = MHD_get_daemon_info (daemon, | ||
521 | MHD_DAEMON_INFO_EPOLL_FD); | ||
522 | ep = di->listen_fd; | ||
523 | while (! done) | ||
524 | { | ||
525 | FD_ZERO (&rs); | ||
526 | to = 1000; | ||
527 | |||
528 | FD_SET (ep, &rs); | ||
529 | (void) MHD_get_timeout (daemon, | ||
530 | &to); | ||
531 | if (1000 < to) | ||
532 | to = 1000; | ||
533 | tv.tv_sec = to / 1000; | ||
534 | tv.tv_usec = 1000 * (to % 1000); | ||
535 | select (ep + 1, | ||
536 | &rs, | ||
537 | NULL, | ||
538 | NULL, | ||
539 | &tv); | ||
540 | MHD_run (daemon); | ||
541 | } | ||
542 | } | ||
543 | |||
544 | |||
545 | /** | ||
546 | * Run the MHD external event loop using select. | ||
547 | * | ||
548 | * @param daemon daemon to run it for | ||
549 | */ | ||
550 | static void | ||
551 | run_mhd_loop (struct MHD_Daemon *daemon, | ||
552 | int flags) | ||
553 | { | ||
554 | if (0 != (flags & MHD_USE_POLL)) | ||
555 | run_mhd_poll_loop (daemon); | ||
556 | #if EPOLL_SUPPORT | ||
557 | else if (0 != (flags & MHD_USE_EPOLL)) | ||
558 | run_mhd_epoll_loop (daemon); | ||
559 | #endif | ||
560 | else | ||
561 | run_mhd_select_loop (daemon); | ||
562 | } | ||
diff --git a/src/microhttpd/test_upgrade_ssl.c b/src/microhttpd/test_upgrade_ssl.c deleted file mode 100644 index bb3d2c3b..00000000 --- a/src/microhttpd/test_upgrade_ssl.c +++ /dev/null | |||
@@ -1,236 +0,0 @@ | |||
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 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., 51 Franklin Street, Fifth Floor, | ||
18 | Boston, MA 02110-1301, USA. | ||
19 | */ | ||
20 | |||
21 | /** | ||
22 | * @file test_upgrade_ssl.c | ||
23 | * @brief Testcase for libmicrohttpd upgrading a connection | ||
24 | * @author Christian Grothoff | ||
25 | */ | ||
26 | |||
27 | #include "platform.h" | ||
28 | #include "microhttpd.h" | ||
29 | #include <stdlib.h> | ||
30 | #include <string.h> | ||
31 | #include <stdio.h> | ||
32 | #include <sys/types.h> | ||
33 | #include <sys/wait.h> | ||
34 | |||
35 | #ifndef WINDOWS | ||
36 | #include <unistd.h> | ||
37 | #endif | ||
38 | |||
39 | #include <pthread.h> | ||
40 | #include "mhd_sockets.h" | ||
41 | #ifdef HAVE_NETINET_IP_H | ||
42 | #include <netinet/ip.h> | ||
43 | #endif /* HAVE_NETINET_IP_H */ | ||
44 | #include "mhd_sockets.h" | ||
45 | #include "test_upgrade_common.c" | ||
46 | |||
47 | #include "../testcurl/https/tls_test_keys.h" | ||
48 | |||
49 | |||
50 | enum tls_cli_tool | ||
51 | { | ||
52 | TLS_CLI_NO_TOOL = 0, | ||
53 | TLS_CLI_GNUTLS, | ||
54 | TLS_CLI_OPENSSL | ||
55 | }; | ||
56 | |||
57 | enum tls_cli_tool use_tool; | ||
58 | |||
59 | /** | ||
60 | * Fork child that connects via OpenSSL to our @a port. Allows us to | ||
61 | * talk to our port over a socket in @a sp without having to worry | ||
62 | * about TLS. | ||
63 | * | ||
64 | * @param location where the socket is returned | ||
65 | * @return -1 on error, otherwise PID of SSL child process | ||
66 | */ | ||
67 | static pid_t | ||
68 | openssl_connect (int *sock, | ||
69 | uint16_t port) | ||
70 | { | ||
71 | pid_t chld; | ||
72 | int sp[2]; | ||
73 | char destination[30]; | ||
74 | |||
75 | if (0 != socketpair (AF_UNIX, | ||
76 | SOCK_STREAM, | ||
77 | 0, | ||
78 | sp)) | ||
79 | return -1; | ||
80 | chld = fork (); | ||
81 | if (0 != chld) | ||
82 | { | ||
83 | *sock = sp[1]; | ||
84 | MHD_socket_close_chk_ (sp[0]); | ||
85 | return chld; | ||
86 | } | ||
87 | MHD_socket_close_chk_ (sp[1]); | ||
88 | (void) close (0); | ||
89 | (void) close (1); | ||
90 | dup2 (sp[0], 0); | ||
91 | dup2 (sp[0], 1); | ||
92 | MHD_socket_close_chk_ (sp[0]); | ||
93 | if (TLS_CLI_GNUTLS == use_tool) | ||
94 | { | ||
95 | snprintf (destination, | ||
96 | sizeof(destination), | ||
97 | "%u", | ||
98 | (unsigned int) port); | ||
99 | execlp ("gnutls-cli", | ||
100 | "gnutls-cli", | ||
101 | "--insecure", | ||
102 | "-p", | ||
103 | destination, | ||
104 | "localhost", | ||
105 | (char *) NULL); | ||
106 | } | ||
107 | else if (TLS_CLI_OPENSSL == use_tool) | ||
108 | { | ||
109 | snprintf (destination, | ||
110 | sizeof(destination), | ||
111 | "localhost:%u", | ||
112 | (unsigned int) port); | ||
113 | execlp ("openssl", | ||
114 | "openssl", | ||
115 | "s_client", | ||
116 | "-connect", | ||
117 | destination, | ||
118 | "-verify", | ||
119 | "0", | ||
120 | (char *) NULL); | ||
121 | } | ||
122 | _exit (1); | ||
123 | } | ||
124 | |||
125 | |||
126 | /** | ||
127 | * Test upgrading a connection. | ||
128 | * | ||
129 | * @param flags which event loop style should be tested | ||
130 | * @param pool size of the thread pool, 0 to disable | ||
131 | */ | ||
132 | static int | ||
133 | test_upgrade (int flags, | ||
134 | unsigned int pool) | ||
135 | { | ||
136 | struct MHD_Daemon *d; | ||
137 | MHD_socket sock; | ||
138 | pid_t pid; | ||
139 | |||
140 | done = 0; | ||
141 | |||
142 | d = MHD_start_daemon (flags | MHD_USE_DEBUG | MHD_USE_UPGRADE | MHD_USE_TLS, | ||
143 | 1080, | ||
144 | NULL, NULL, | ||
145 | &ahc_upgrade, NULL, | ||
146 | MHD_OPTION_URI_LOG_CALLBACK, &log_cb, NULL, | ||
147 | MHD_OPTION_NOTIFY_COMPLETED, ¬ify_completed_cb, NULL, | ||
148 | MHD_OPTION_NOTIFY_CONNECTION, ¬ify_connection_cb, NULL, | ||
149 | MHD_OPTION_HTTPS_MEM_KEY, srv_signed_key_pem, | ||
150 | MHD_OPTION_HTTPS_MEM_CERT, srv_signed_cert_pem, | ||
151 | MHD_OPTION_THREAD_POOL_SIZE, pool, | ||
152 | MHD_OPTION_END); | ||
153 | if (NULL == d) | ||
154 | return 2; | ||
155 | if (-1 == (pid = openssl_connect (&sock, 1080))) | ||
156 | { | ||
157 | MHD_stop_daemon (d); | ||
158 | return 4; | ||
159 | } | ||
160 | |||
161 | pthread_create (&pt_client, | ||
162 | NULL, | ||
163 | &run_usock_client, | ||
164 | &sock); | ||
165 | if (0 == (flags & (MHD_USE_SELECT_INTERNALLY | | ||
166 | MHD_USE_THREAD_PER_CONNECTION)) ) | ||
167 | run_mhd_loop (d, flags); | ||
168 | pthread_join (pt_client, | ||
169 | NULL); | ||
170 | if (0 == (flags & (MHD_USE_SELECT_INTERNALLY | | ||
171 | MHD_USE_THREAD_PER_CONNECTION)) ) | ||
172 | run_mhd_loop (d, flags); | ||
173 | pthread_join (pt, | ||
174 | NULL); | ||
175 | waitpid (pid, | ||
176 | NULL, | ||
177 | 0); | ||
178 | MHD_stop_daemon (d); | ||
179 | return 0; | ||
180 | } | ||
181 | |||
182 | |||
183 | int | ||
184 | main (int argc, | ||
185 | char *const *argv) | ||
186 | { | ||
187 | int error_count = 0; | ||
188 | |||
189 | use_tool = TLS_CLI_NO_TOOL; | ||
190 | if (0 == system ("gnutls-cli --version 1> /dev/null")) | ||
191 | use_tool = TLS_CLI_GNUTLS; | ||
192 | else if (0 == system ("openssl version 1> /dev/null")) | ||
193 | use_tool = TLS_CLI_OPENSSL; | ||
194 | else | ||
195 | return 77; /* not possible to test */ | ||
196 | |||
197 | /* try external select */ | ||
198 | error_count += test_upgrade (0, | ||
199 | 0); | ||
200 | #ifdef EPOLL_SUPPORT | ||
201 | error_count += test_upgrade (MHD_USE_EPOLL | MHD_USE_TLS, | ||
202 | 0); | ||
203 | #endif | ||
204 | |||
205 | /* Test thread-per-connection */ | ||
206 | error_count += test_upgrade (MHD_USE_THREAD_PER_CONNECTION, | ||
207 | 0); | ||
208 | error_count += test_upgrade (MHD_USE_THREAD_PER_CONNECTION | MHD_USE_POLL, | ||
209 | 0); | ||
210 | |||
211 | /* Test different event loops, with and without thread pool */ | ||
212 | error_count += test_upgrade (MHD_USE_SELECT_INTERNALLY, | ||
213 | 0); | ||
214 | error_count += test_upgrade (MHD_USE_SELECT_INTERNALLY, | ||
215 | 2); | ||
216 | #ifdef HAVE_POLL | ||
217 | error_count += test_upgrade (MHD_USE_POLL_INTERNALLY, | ||
218 | 0); | ||
219 | error_count += test_upgrade (MHD_USE_POLL_INTERNALLY, | ||
220 | 2); | ||
221 | #endif | ||
222 | #ifdef EPOLL_SUPPORT | ||
223 | error_count += test_upgrade (MHD_USE_EPOLL_INTERNALLY | | ||
224 | MHD_USE_TLS, | ||
225 | 0); | ||
226 | error_count += test_upgrade (MHD_USE_EPOLL_INTERNALLY | | ||
227 | MHD_USE_TLS, | ||
228 | 2); | ||
229 | #endif | ||
230 | /* report result */ | ||
231 | if (0 != error_count) | ||
232 | fprintf (stderr, | ||
233 | "Error (code: %u)\n", | ||
234 | error_count); | ||
235 | return error_count != 0; /* 0 == pass */ | ||
236 | } | ||