diff options
Diffstat (limited to 'src/microhttpd')
-rw-r--r-- | src/microhttpd/.gitignore | 2 | ||||
-rw-r--r-- | src/microhttpd/Makefile.am | 34 | ||||
-rw-r--r-- | src/microhttpd/daemon.c | 167 | ||||
-rw-r--r-- | src/microhttpd/response.c | 88 | ||||
-rw-r--r-- | src/microhttpd/test_upgrade_large.c | 1353 |
5 files changed, 1513 insertions, 131 deletions
diff --git a/src/microhttpd/.gitignore b/src/microhttpd/.gitignore index ec2e027d..611f88ec 100644 --- a/src/microhttpd/.gitignore +++ b/src/microhttpd/.gitignore | |||
@@ -57,3 +57,5 @@ test_shutdown_poll | |||
57 | test_shutdown_select | 57 | test_shutdown_select |
58 | test_md5 | 58 | test_md5 |
59 | test_sha256 | 59 | test_sha256 |
60 | test_upgrade_large | ||
61 | test_upgrade_large_tls | ||
diff --git a/src/microhttpd/Makefile.am b/src/microhttpd/Makefile.am index 3fb65fe1..8bc60879 100644 --- a/src/microhttpd/Makefile.am +++ b/src/microhttpd/Makefile.am | |||
@@ -165,17 +165,17 @@ check_PROGRAMS = \ | |||
165 | if HAVE_POSIX_THREADS | 165 | if HAVE_POSIX_THREADS |
166 | if ENABLE_UPGRADE | 166 | if ENABLE_UPGRADE |
167 | if USE_POSIX_THREADS | 167 | if USE_POSIX_THREADS |
168 | check_PROGRAMS += test_upgrade | 168 | check_PROGRAMS += test_upgrade test_upgrade_large |
169 | endif | 169 | endif |
170 | if USE_W32_THREADS | 170 | if USE_W32_THREADS |
171 | check_PROGRAMS += test_upgrade | 171 | check_PROGRAMS += test_upgrade test_upgrade_large |
172 | endif | 172 | endif |
173 | if ENABLE_HTTPS | 173 | if ENABLE_HTTPS |
174 | if USE_POSIX_THREADS | 174 | if USE_POSIX_THREADS |
175 | check_PROGRAMS += test_upgrade_tls | 175 | check_PROGRAMS += test_upgrade_tls test_upgrade_large_tls |
176 | endif | 176 | endif |
177 | if USE_W32_THREADS | 177 | if USE_W32_THREADS |
178 | check_PROGRAMS += test_upgrade_tls | 178 | check_PROGRAMS += test_upgrade_tls test_upgrade_large_tls |
179 | endif | 179 | endif |
180 | endif | 180 | endif |
181 | endif | 181 | endif |
@@ -230,6 +230,19 @@ test_upgrade_LDADD = \ | |||
230 | $(MHD_TLS_LIB_LDFLAGS) $(MHD_TLS_LIBDEPS) \ | 230 | $(MHD_TLS_LIB_LDFLAGS) $(MHD_TLS_LIBDEPS) \ |
231 | $(PTHREAD_LIBS) | 231 | $(PTHREAD_LIBS) |
232 | 232 | ||
233 | test_upgrade_large_SOURCES = \ | ||
234 | test_upgrade_large.c test_helpers.h mhd_sockets.h | ||
235 | test_upgrade_large_CPPFLAGS = \ | ||
236 | $(AM_CPPFLAGS) $(MHD_TLS_LIB_CPPFLAGS) | ||
237 | test_upgrade_large_CFLAGS = \ | ||
238 | $(AM_CFLAGS) $(PTHREAD_CFLAGS) $(MHD_TLS_LIB_CFLAGS) | ||
239 | test_upgrade_large_LDFLAGS = \ | ||
240 | $(MHD_TLS_LIB_LDFLAGS) | ||
241 | test_upgrade_large_LDADD = \ | ||
242 | $(top_builddir)/src/microhttpd/libmicrohttpd.la \ | ||
243 | $(MHD_TLS_LIB_LDFLAGS) $(MHD_TLS_LIBDEPS) \ | ||
244 | $(PTHREAD_LIBS) | ||
245 | |||
233 | test_upgrade_tls_SOURCES = \ | 246 | test_upgrade_tls_SOURCES = \ |
234 | test_upgrade.c test_helpers.h mhd_sockets.h | 247 | test_upgrade.c test_helpers.h mhd_sockets.h |
235 | test_upgrade_tls_CPPFLAGS = \ | 248 | test_upgrade_tls_CPPFLAGS = \ |
@@ -243,6 +256,19 @@ test_upgrade_tls_LDADD = \ | |||
243 | $(MHD_TLS_LIB_LDFLAGS) $(MHD_TLS_LIBDEPS) \ | 256 | $(MHD_TLS_LIB_LDFLAGS) $(MHD_TLS_LIBDEPS) \ |
244 | $(PTHREAD_LIBS) | 257 | $(PTHREAD_LIBS) |
245 | 258 | ||
259 | test_upgrade_large_tls_SOURCES = \ | ||
260 | test_upgrade_large.c test_helpers.h mhd_sockets.h | ||
261 | test_upgrade_large_tls_CPPFLAGS = \ | ||
262 | $(AM_CPPFLAGS) $(MHD_TLS_LIB_CPPFLAGS) | ||
263 | test_upgrade_large_tls_CFLAGS = \ | ||
264 | $(AM_CFLAGS) $(PTHREAD_CFLAGS) $(MHD_TLS_LIB_CFLAGS) | ||
265 | test_upgrade_large_tls_LDFLAGS = \ | ||
266 | $(MHD_TLS_LIB_LDFLAGS) | ||
267 | test_upgrade_large_tls_LDADD = \ | ||
268 | $(top_builddir)/src/microhttpd/libmicrohttpd.la \ | ||
269 | $(MHD_TLS_LIB_LDFLAGS) $(MHD_TLS_LIBDEPS) \ | ||
270 | $(PTHREAD_LIBS) | ||
271 | |||
246 | test_postprocessor_SOURCES = \ | 272 | test_postprocessor_SOURCES = \ |
247 | test_postprocessor.c | 273 | test_postprocessor.c |
248 | test_postprocessor_CPPFLAGS = \ | 274 | test_postprocessor_CPPFLAGS = \ |
diff --git a/src/microhttpd/daemon.c b/src/microhttpd/daemon.c index b312c305..3fc992de 100644 --- a/src/microhttpd/daemon.c +++ b/src/microhttpd/daemon.c | |||
@@ -4178,28 +4178,29 @@ run_epoll_for_upgrade (struct MHD_Daemon *daemon) | |||
4178 | struct MHD_UpgradeResponseHandle * prev; | 4178 | struct MHD_UpgradeResponseHandle * prev; |
4179 | 4179 | ||
4180 | num_events = MAX_EVENTS; | 4180 | num_events = MAX_EVENTS; |
4181 | while (MAX_EVENTS == num_events) | 4181 | while (0 != num_events) |
4182 | { | 4182 | { |
4183 | unsigned int i; | 4183 | unsigned int i; |
4184 | /* update event masks */ | 4184 | /* update event masks */ |
4185 | num_events = epoll_wait (daemon->epoll_upgrade_fd, | 4185 | num_events = epoll_wait (daemon->epoll_upgrade_fd, |
4186 | events, | 4186 | events, |
4187 | MAX_EVENTS, | 4187 | MAX_EVENTS, |
4188 | 0); | 4188 | 0); |
4189 | if (-1 == num_events) | 4189 | if (-1 == num_events) |
4190 | { | 4190 | { |
4191 | const int err = MHD_socket_get_error_ (); | 4191 | const int err = MHD_socket_get_error_ (); |
4192 | |||
4192 | if (MHD_SCKT_ERR_IS_EINTR_ (err)) | 4193 | if (MHD_SCKT_ERR_IS_EINTR_ (err)) |
4193 | return MHD_YES; | 4194 | return MHD_YES; |
4194 | #ifdef HAVE_MESSAGES | 4195 | #ifdef HAVE_MESSAGES |
4195 | MHD_DLOG (daemon, | 4196 | MHD_DLOG (daemon, |
4196 | _("Call to epoll_wait failed: %s\n"), | 4197 | _("Call to epoll_wait failed: %s\n"), |
4197 | MHD_socket_strerr_ (err)); | 4198 | MHD_socket_strerr_ (err)); |
4198 | #endif | 4199 | #endif |
4199 | return MHD_NO; | 4200 | return MHD_NO; |
4200 | } | 4201 | } |
4201 | for (i = 0; i < (unsigned int) num_events; i++) | 4202 | for (i = 0; i < (unsigned int) num_events; i++) |
4202 | { | 4203 | { |
4203 | struct UpgradeEpollHandle * const ueh = events[i].data.ptr; | 4204 | struct UpgradeEpollHandle * const ueh = events[i].data.ptr; |
4204 | struct MHD_UpgradeResponseHandle * const urh = ueh->urh; | 4205 | struct MHD_UpgradeResponseHandle * const urh = ueh->urh; |
4205 | bool new_err_state = false; | 4206 | bool new_err_state = false; |
@@ -4217,24 +4218,24 @@ run_epoll_for_upgrade (struct MHD_Daemon *daemon) | |||
4217 | 4218 | ||
4218 | if ( (0 == (ueh->celi & MHD_EPOLL_STATE_ERROR)) && | 4219 | if ( (0 == (ueh->celi & MHD_EPOLL_STATE_ERROR)) && |
4219 | (0 != (events[i].events & (EPOLLERR | EPOLLPRI))) ) | 4220 | (0 != (events[i].events & (EPOLLERR | EPOLLPRI))) ) |
4220 | { | 4221 | { |
4221 | /* Process new error state only one time | 4222 | /* Process new error state only one time |
4222 | * and avoid continuously marking this connection | 4223 | * and avoid continuously marking this connection |
4223 | * as 'ready'. */ | 4224 | * as 'ready'. */ |
4224 | ueh->celi |= MHD_EPOLL_STATE_ERROR; | 4225 | ueh->celi |= MHD_EPOLL_STATE_ERROR; |
4225 | new_err_state = true; | 4226 | new_err_state = true; |
4226 | } | 4227 | } |
4227 | 4228 | ||
4228 | if (! urh->in_eready_list) | 4229 | if (! urh->in_eready_list) |
4229 | { | 4230 | { |
4230 | if (new_err_state || | 4231 | if (new_err_state || |
4231 | is_urh_ready(urh)) | 4232 | is_urh_ready(urh)) |
4232 | { | 4233 | { |
4233 | EDLL_insert (daemon->eready_urh_head, | 4234 | EDLL_insert (daemon->eready_urh_head, |
4234 | daemon->eready_urh_tail, | 4235 | daemon->eready_urh_tail, |
4235 | urh); | 4236 | urh); |
4236 | urh->in_eready_list = true; | 4237 | urh->in_eready_list = true; |
4237 | } | 4238 | } |
4238 | } | 4239 | } |
4239 | } | 4240 | } |
4240 | } | 4241 | } |
@@ -4246,8 +4247,8 @@ run_epoll_for_upgrade (struct MHD_Daemon *daemon) | |||
4246 | if (! is_urh_ready(pos)) | 4247 | if (! is_urh_ready(pos)) |
4247 | { | 4248 | { |
4248 | EDLL_remove (daemon->eready_urh_head, | 4249 | EDLL_remove (daemon->eready_urh_head, |
4249 | daemon->eready_urh_tail, | 4250 | daemon->eready_urh_tail, |
4250 | pos); | 4251 | pos); |
4251 | pos->in_eready_list = false; | 4252 | pos->in_eready_list = false; |
4252 | } | 4253 | } |
4253 | /* Finished forwarding? */ | 4254 | /* Finished forwarding? */ |
@@ -4287,7 +4288,7 @@ static const char * const epoll_itc_marker = "itc_marker"; | |||
4287 | */ | 4288 | */ |
4288 | static int | 4289 | static int |
4289 | MHD_epoll (struct MHD_Daemon *daemon, | 4290 | MHD_epoll (struct MHD_Daemon *daemon, |
4290 | int may_block) | 4291 | int may_block) |
4291 | { | 4292 | { |
4292 | #if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT) | 4293 | #if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT) |
4293 | static const char * const upgrade_marker = "upgrade_ptr"; | 4294 | static const char * const upgrade_marker = "upgrade_ptr"; |
@@ -4302,7 +4303,7 @@ MHD_epoll (struct MHD_Daemon *daemon, | |||
4302 | unsigned int i; | 4303 | unsigned int i; |
4303 | MHD_socket ls; | 4304 | MHD_socket ls; |
4304 | #if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT) | 4305 | #if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT) |
4305 | int run_upgraded = MHD_NO; | 4306 | bool run_upgraded = false; |
4306 | #endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT */ | 4307 | #endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT */ |
4307 | 4308 | ||
4308 | if (-1 == daemon->epoll_fd) | 4309 | if (-1 == daemon->epoll_fd) |
@@ -4318,31 +4319,31 @@ MHD_epoll (struct MHD_Daemon *daemon, | |||
4318 | event.events = EPOLLIN; | 4319 | event.events = EPOLLIN; |
4319 | event.data.ptr = daemon; | 4320 | event.data.ptr = daemon; |
4320 | if (0 != epoll_ctl (daemon->epoll_fd, | 4321 | if (0 != epoll_ctl (daemon->epoll_fd, |
4321 | EPOLL_CTL_ADD, | 4322 | EPOLL_CTL_ADD, |
4322 | ls, | 4323 | ls, |
4323 | &event)) | 4324 | &event)) |
4324 | { | 4325 | { |
4325 | #ifdef HAVE_MESSAGES | 4326 | #ifdef HAVE_MESSAGES |
4326 | MHD_DLOG (daemon, | 4327 | MHD_DLOG (daemon, |
4327 | _("Call to epoll_ctl failed: %s\n"), | 4328 | _("Call to epoll_ctl failed: %s\n"), |
4328 | MHD_socket_last_strerr_ ()); | 4329 | MHD_socket_last_strerr_ ()); |
4329 | #endif | 4330 | #endif |
4330 | return MHD_NO; | 4331 | return MHD_NO; |
4331 | } | 4332 | } |
4332 | daemon->listen_socket_in_epoll = true; | 4333 | daemon->listen_socket_in_epoll = true; |
4333 | } | 4334 | } |
4334 | if ( (daemon->was_quiesced) && | 4335 | if ( (daemon->was_quiesced) && |
4335 | (daemon->listen_socket_in_epoll) ) | 4336 | (daemon->listen_socket_in_epoll) ) |
4336 | { | 4337 | { |
4337 | if ( (0 != epoll_ctl (daemon->epoll_fd, | 4338 | if ( (0 != epoll_ctl (daemon->epoll_fd, |
4338 | EPOLL_CTL_DEL, | 4339 | EPOLL_CTL_DEL, |
4339 | ls, | 4340 | ls, |
4340 | NULL)) && | 4341 | NULL)) && |
4341 | (ENOENT != errno) ) /* ENOENT can happen due to race with | 4342 | (ENOENT != errno) ) /* ENOENT can happen due to race with |
4342 | #MHD_quiesce_daemon() */ | 4343 | #MHD_quiesce_daemon() */ |
4343 | MHD_PANIC ("Failed to remove listen FD from epoll set\n"); | 4344 | MHD_PANIC ("Failed to remove listen FD from epoll set\n"); |
4344 | daemon->listen_socket_in_epoll = false; | 4345 | daemon->listen_socket_in_epoll = false; |
4345 | } | 4346 | } |
4346 | 4347 | ||
4347 | #if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT) | 4348 | #if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT) |
4348 | if ( (! daemon->upgrade_fd_in_epoll) && | 4349 | if ( (! daemon->upgrade_fd_in_epoll) && |
@@ -4351,17 +4352,17 @@ MHD_epoll (struct MHD_Daemon *daemon, | |||
4351 | event.events = EPOLLIN | EPOLLOUT; | 4352 | event.events = EPOLLIN | EPOLLOUT; |
4352 | event.data.ptr = (void *) upgrade_marker; | 4353 | event.data.ptr = (void *) upgrade_marker; |
4353 | if (0 != epoll_ctl (daemon->epoll_fd, | 4354 | if (0 != epoll_ctl (daemon->epoll_fd, |
4354 | EPOLL_CTL_ADD, | 4355 | EPOLL_CTL_ADD, |
4355 | daemon->epoll_upgrade_fd, | 4356 | daemon->epoll_upgrade_fd, |
4356 | &event)) | 4357 | &event)) |
4357 | { | 4358 | { |
4358 | #ifdef HAVE_MESSAGES | 4359 | #ifdef HAVE_MESSAGES |
4359 | MHD_DLOG (daemon, | 4360 | MHD_DLOG (daemon, |
4360 | _("Call to epoll_ctl failed: %s\n"), | 4361 | _("Call to epoll_ctl failed: %s\n"), |
4361 | MHD_socket_last_strerr_ ()); | 4362 | MHD_socket_last_strerr_ ()); |
4362 | #endif | 4363 | #endif |
4363 | return MHD_NO; | 4364 | return MHD_NO; |
4364 | } | 4365 | } |
4365 | daemon->upgrade_fd_in_epoll = true; | 4366 | daemon->upgrade_fd_in_epoll = true; |
4366 | } | 4367 | } |
4367 | #endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT */ | 4368 | #endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT */ |
@@ -4373,10 +4374,10 @@ MHD_epoll (struct MHD_Daemon *daemon, | |||
4373 | /* we're at the connection limit, disable listen socket | 4374 | /* we're at the connection limit, disable listen socket |
4374 | for event loop for now */ | 4375 | for event loop for now */ |
4375 | if (0 != epoll_ctl (daemon->epoll_fd, | 4376 | if (0 != epoll_ctl (daemon->epoll_fd, |
4376 | EPOLL_CTL_DEL, | 4377 | EPOLL_CTL_DEL, |
4377 | ls, | 4378 | ls, |
4378 | NULL)) | 4379 | NULL)) |
4379 | MHD_PANIC (_("Failed to remove listen FD from epoll set\n")); | 4380 | MHD_PANIC (_("Failed to remove listen FD from epoll set\n")); |
4380 | daemon->listen_socket_in_epoll = false; | 4381 | daemon->listen_socket_in_epoll = false; |
4381 | } | 4382 | } |
4382 | 4383 | ||
@@ -4388,14 +4389,14 @@ MHD_epoll (struct MHD_Daemon *daemon, | |||
4388 | { | 4389 | { |
4389 | if (MHD_YES == MHD_get_timeout (daemon, | 4390 | if (MHD_YES == MHD_get_timeout (daemon, |
4390 | &timeout_ll)) | 4391 | &timeout_ll)) |
4391 | { | 4392 | { |
4392 | if (timeout_ll >= (MHD_UNSIGNED_LONG_LONG) INT_MAX) | 4393 | if (timeout_ll >= (MHD_UNSIGNED_LONG_LONG) INT_MAX) |
4393 | timeout_ms = INT_MAX; | 4394 | timeout_ms = INT_MAX; |
4394 | else | 4395 | else |
4395 | timeout_ms = (int) timeout_ll; | 4396 | timeout_ms = (int) timeout_ll; |
4396 | } | 4397 | } |
4397 | else | 4398 | else |
4398 | timeout_ms = -1; | 4399 | timeout_ms = -1; |
4399 | } | 4400 | } |
4400 | else | 4401 | else |
4401 | timeout_ms = 0; | 4402 | timeout_ms = 0; |
@@ -4414,33 +4415,33 @@ MHD_epoll (struct MHD_Daemon *daemon, | |||
4414 | { | 4415 | { |
4415 | /* update event masks */ | 4416 | /* update event masks */ |
4416 | num_events = epoll_wait (daemon->epoll_fd, | 4417 | num_events = epoll_wait (daemon->epoll_fd, |
4417 | events, | 4418 | events, |
4418 | MAX_EVENTS, | 4419 | MAX_EVENTS, |
4419 | timeout_ms); | 4420 | timeout_ms); |
4420 | if (-1 == num_events) | 4421 | if (-1 == num_events) |
4421 | { | 4422 | { |
4422 | const int err = MHD_socket_get_error_ (); | 4423 | const int err = MHD_socket_get_error_ (); |
4423 | if (MHD_SCKT_ERR_IS_EINTR_ (err)) | 4424 | if (MHD_SCKT_ERR_IS_EINTR_ (err)) |
4424 | return MHD_YES; | 4425 | return MHD_YES; |
4425 | #ifdef HAVE_MESSAGES | 4426 | #ifdef HAVE_MESSAGES |
4426 | MHD_DLOG (daemon, | 4427 | MHD_DLOG (daemon, |
4427 | _("Call to epoll_wait failed: %s\n"), | 4428 | _("Call to epoll_wait failed: %s\n"), |
4428 | MHD_socket_strerr_ (err)); | 4429 | MHD_socket_strerr_ (err)); |
4429 | #endif | 4430 | #endif |
4430 | return MHD_NO; | 4431 | return MHD_NO; |
4431 | } | 4432 | } |
4432 | for (i=0;i<(unsigned int) num_events;i++) | 4433 | for (i=0;i<(unsigned int) num_events;i++) |
4433 | { | 4434 | { |
4434 | /* First, check for the values of `ptr` that would indicate | 4435 | /* First, check for the values of `ptr` that would indicate |
4435 | that this event is not about a normal connection. */ | 4436 | that this event is not about a normal connection. */ |
4436 | if (NULL == events[i].data.ptr) | 4437 | if (NULL == events[i].data.ptr) |
4437 | continue; /* shutdown signal! */ | 4438 | continue; /* shutdown signal! */ |
4438 | #if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT) | 4439 | #if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT) |
4439 | if (upgrade_marker == events[i].data.ptr) | 4440 | if (upgrade_marker == events[i].data.ptr) |
4440 | { | 4441 | { |
4441 | /* activity on an upgraded connection, we process | 4442 | /* activity on an upgraded connection, we process |
4442 | those in a separate epoll() */ | 4443 | those in a separate epoll() */ |
4443 | run_upgraded = MHD_YES; | 4444 | run_upgraded = true; |
4444 | continue; | 4445 | continue; |
4445 | } | 4446 | } |
4446 | #endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT */ | 4447 | #endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT */ |
@@ -4451,8 +4452,8 @@ MHD_epoll (struct MHD_Daemon *daemon, | |||
4451 | MHD_itc_clear_ (daemon->itc); | 4452 | MHD_itc_clear_ (daemon->itc); |
4452 | continue; | 4453 | continue; |
4453 | } | 4454 | } |
4454 | if (daemon == events[i].data.ptr) | 4455 | if (daemon == events[i].data.ptr) |
4455 | { | 4456 | { |
4456 | /* Check for error conditions on listen socket. */ | 4457 | /* Check for error conditions on listen socket. */ |
4457 | /* FIXME: Initiate MHD_quiesce_daemon() to prevent busy waiting? */ | 4458 | /* FIXME: Initiate MHD_quiesce_daemon() to prevent busy waiting? */ |
4458 | if (0 == (events[i].events & (EPOLLERR | EPOLLHUP))) | 4459 | if (0 == (events[i].events & (EPOLLERR | EPOLLHUP))) |
@@ -4467,9 +4468,9 @@ MHD_epoll (struct MHD_Daemon *daemon, | |||
4467 | (daemon->connections < daemon->connection_limit) && | 4468 | (daemon->connections < daemon->connection_limit) && |
4468 | (! daemon->at_limit) ) | 4469 | (! daemon->at_limit) ) |
4469 | series_length++; | 4470 | series_length++; |
4470 | } | 4471 | } |
4471 | continue; | 4472 | continue; |
4472 | } | 4473 | } |
4473 | /* this is an event relating to a 'normal' connection, | 4474 | /* this is an event relating to a 'normal' connection, |
4474 | remember the event and if appropriate mark the | 4475 | remember the event and if appropriate mark the |
4475 | connection as 'eready'. */ | 4476 | connection as 'eready'. */ |
@@ -4518,7 +4519,7 @@ MHD_epoll (struct MHD_Daemon *daemon, | |||
4518 | } | 4519 | } |
4519 | 4520 | ||
4520 | #if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT) | 4521 | #if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT) |
4521 | if (MHD_YES == run_upgraded) | 4522 | if (run_upgraded) |
4522 | run_epoll_for_upgrade (daemon); | 4523 | run_epoll_for_upgrade (daemon); |
4523 | #endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT */ | 4524 | #endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT */ |
4524 | 4525 | ||
@@ -4606,22 +4607,22 @@ MHD_run (struct MHD_Daemon *daemon) | |||
4606 | (0 != (daemon->options & MHD_USE_INTERNAL_POLLING_THREAD)) ) | 4607 | (0 != (daemon->options & MHD_USE_INTERNAL_POLLING_THREAD)) ) |
4607 | return MHD_NO; | 4608 | return MHD_NO; |
4608 | if (0 != (daemon->options & MHD_USE_POLL)) | 4609 | if (0 != (daemon->options & MHD_USE_POLL)) |
4609 | { | 4610 | { |
4610 | MHD_poll (daemon, MHD_NO); | 4611 | MHD_poll (daemon, MHD_NO); |
4611 | MHD_cleanup_connections (daemon); | 4612 | MHD_cleanup_connections (daemon); |
4612 | } | 4613 | } |
4613 | #ifdef EPOLL_SUPPORT | 4614 | #ifdef EPOLL_SUPPORT |
4614 | else if (0 != (daemon->options & MHD_USE_EPOLL)) | 4615 | else if (0 != (daemon->options & MHD_USE_EPOLL)) |
4615 | { | 4616 | { |
4616 | MHD_epoll (daemon, MHD_NO); | 4617 | MHD_epoll (daemon, MHD_NO); |
4617 | MHD_cleanup_connections (daemon); | 4618 | MHD_cleanup_connections (daemon); |
4618 | } | 4619 | } |
4619 | #endif | 4620 | #endif |
4620 | else | 4621 | else |
4621 | { | 4622 | { |
4622 | MHD_select (daemon, MHD_NO); | 4623 | MHD_select (daemon, MHD_NO); |
4623 | /* MHD_select does MHD_cleanup_connections already */ | 4624 | /* MHD_select does MHD_cleanup_connections already */ |
4624 | } | 4625 | } |
4625 | return MHD_YES; | 4626 | return MHD_YES; |
4626 | } | 4627 | } |
4627 | 4628 | ||
@@ -6494,10 +6495,10 @@ thread_failed: | |||
6494 | if (daemon->upgrade_fd_in_epoll) | 6495 | if (daemon->upgrade_fd_in_epoll) |
6495 | { | 6496 | { |
6496 | if (0 != epoll_ctl (daemon->epoll_fd, | 6497 | if (0 != epoll_ctl (daemon->epoll_fd, |
6497 | EPOLL_CTL_DEL, | 6498 | EPOLL_CTL_DEL, |
6498 | daemon->epoll_upgrade_fd, | 6499 | daemon->epoll_upgrade_fd, |
6499 | NULL)) | 6500 | NULL)) |
6500 | MHD_PANIC (_("Failed to remove FD from epoll set\n")); | 6501 | MHD_PANIC (_("Failed to remove FD from epoll set\n")); |
6501 | daemon->upgrade_fd_in_epoll = false; | 6502 | daemon->upgrade_fd_in_epoll = false; |
6502 | } | 6503 | } |
6503 | #endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT */ | 6504 | #endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT */ |
diff --git a/src/microhttpd/response.c b/src/microhttpd/response.c index 035e3054..3e9fb053 100644 --- a/src/microhttpd/response.c +++ b/src/microhttpd/response.c | |||
@@ -80,9 +80,9 @@ | |||
80 | */ | 80 | */ |
81 | static int | 81 | static int |
82 | add_response_entry (struct MHD_Response *response, | 82 | add_response_entry (struct MHD_Response *response, |
83 | enum MHD_ValueKind kind, | 83 | enum MHD_ValueKind kind, |
84 | const char *header, | 84 | const char *header, |
85 | const char *content) | 85 | const char *content) |
86 | { | 86 | { |
87 | struct MHD_HTTP_Header *hdr; | 87 | struct MHD_HTTP_Header *hdr; |
88 | 88 | ||
@@ -195,7 +195,7 @@ MHD_add_response_footer (struct MHD_Response *response, | |||
195 | int | 195 | int |
196 | MHD_del_response_header (struct MHD_Response *response, | 196 | MHD_del_response_header (struct MHD_Response *response, |
197 | const char *header, | 197 | const char *header, |
198 | const char *content) | 198 | const char *content) |
199 | { | 199 | { |
200 | struct MHD_HTTP_Header *pos; | 200 | struct MHD_HTTP_Header *pos; |
201 | struct MHD_HTTP_Header *prev; | 201 | struct MHD_HTTP_Header *prev; |
@@ -280,7 +280,7 @@ MHD_get_response_headers (struct MHD_Response *response, | |||
280 | */ | 280 | */ |
281 | const char * | 281 | const char * |
282 | MHD_get_response_header (struct MHD_Response *response, | 282 | MHD_get_response_header (struct MHD_Response *response, |
283 | const char *key) | 283 | const char *key) |
284 | { | 284 | { |
285 | struct MHD_HTTP_Header *pos; | 285 | struct MHD_HTTP_Header *pos; |
286 | size_t key_size; | 286 | size_t key_size; |
@@ -558,8 +558,8 @@ free_callback (void *cls) | |||
558 | */ | 558 | */ |
559 | struct MHD_Response * | 559 | struct MHD_Response * |
560 | MHD_create_response_from_fd_at_offset (size_t size, | 560 | MHD_create_response_from_fd_at_offset (size_t size, |
561 | int fd, | 561 | int fd, |
562 | off_t offset) | 562 | off_t offset) |
563 | { | 563 | { |
564 | return MHD_create_response_from_fd_at_offset64 (size, | 564 | return MHD_create_response_from_fd_at_offset64 (size, |
565 | fd, | 565 | fd, |
@@ -627,7 +627,7 @@ MHD_create_response_from_fd_at_offset64 (uint64_t size, | |||
627 | */ | 627 | */ |
628 | struct MHD_Response * | 628 | struct MHD_Response * |
629 | MHD_create_response_from_fd (size_t size, | 629 | MHD_create_response_from_fd (size_t size, |
630 | int fd) | 630 | int fd) |
631 | { | 631 | { |
632 | return MHD_create_response_from_fd_at_offset64 (size, | 632 | return MHD_create_response_from_fd_at_offset64 (size, |
633 | fd, | 633 | fd, |
@@ -732,8 +732,8 @@ MHD_create_response_from_data (size_t size, | |||
732 | */ | 732 | */ |
733 | struct MHD_Response * | 733 | struct MHD_Response * |
734 | MHD_create_response_from_buffer (size_t size, | 734 | MHD_create_response_from_buffer (size_t size, |
735 | void *buffer, | 735 | void *buffer, |
736 | enum MHD_ResponseMemoryMode mode) | 736 | enum MHD_ResponseMemoryMode mode) |
737 | { | 737 | { |
738 | return MHD_create_response_from_data (size, | 738 | return MHD_create_response_from_data (size, |
739 | buffer, | 739 | buffer, |
@@ -754,15 +754,15 @@ MHD_create_response_from_buffer (size_t size, | |||
754 | */ | 754 | */ |
755 | _MHD_EXTERN struct MHD_Response * | 755 | _MHD_EXTERN struct MHD_Response * |
756 | MHD_create_response_from_buffer_with_free_callback (size_t size, | 756 | MHD_create_response_from_buffer_with_free_callback (size_t size, |
757 | void *buffer, | 757 | void *buffer, |
758 | MHD_ContentReaderFreeCallback crfc) | 758 | MHD_ContentReaderFreeCallback crfc) |
759 | { | 759 | { |
760 | struct MHD_Response *r; | 760 | struct MHD_Response *r; |
761 | 761 | ||
762 | r = MHD_create_response_from_data (size, | 762 | r = MHD_create_response_from_data (size, |
763 | buffer, | 763 | buffer, |
764 | MHD_YES, | 764 | MHD_YES, |
765 | MHD_NO); | 765 | MHD_NO); |
766 | if (NULL == r) | 766 | if (NULL == r) |
767 | return r; | 767 | return r; |
768 | r->crfc = crfc; | 768 | r->crfc = crfc; |
@@ -997,17 +997,17 @@ MHD_response_execute_upgrade_ (struct MHD_Response *response, | |||
997 | EPOLL_CTL_ADD, | 997 | EPOLL_CTL_ADD, |
998 | connection->socket_fd, | 998 | connection->socket_fd, |
999 | &event)) | 999 | &event)) |
1000 | { | 1000 | { |
1001 | #ifdef HAVE_MESSAGES | 1001 | #ifdef HAVE_MESSAGES |
1002 | MHD_DLOG (daemon, | 1002 | MHD_DLOG (daemon, |
1003 | _("Call to epoll_ctl failed: %s\n"), | 1003 | _("Call to epoll_ctl failed: %s\n"), |
1004 | MHD_socket_last_strerr_ ()); | 1004 | MHD_socket_last_strerr_ ()); |
1005 | #endif | 1005 | #endif |
1006 | MHD_socket_close_chk_ (sv[0]); | 1006 | MHD_socket_close_chk_ (sv[0]); |
1007 | MHD_socket_close_chk_ (sv[1]); | 1007 | MHD_socket_close_chk_ (sv[1]); |
1008 | free (urh); | 1008 | free (urh); |
1009 | return MHD_NO; | 1009 | return MHD_NO; |
1010 | } | 1010 | } |
1011 | 1011 | ||
1012 | /* Second, add our end of the UNIX socketpair() */ | 1012 | /* Second, add our end of the UNIX socketpair() */ |
1013 | event.events = EPOLLIN | EPOLLOUT | EPOLLPRI | EPOLLET; | 1013 | event.events = EPOLLIN | EPOLLOUT | EPOLLPRI | EPOLLET; |
@@ -1016,28 +1016,28 @@ MHD_response_execute_upgrade_ (struct MHD_Response *response, | |||
1016 | EPOLL_CTL_ADD, | 1016 | EPOLL_CTL_ADD, |
1017 | urh->mhd.socket, | 1017 | urh->mhd.socket, |
1018 | &event)) | 1018 | &event)) |
1019 | { | 1019 | { |
1020 | event.events = EPOLLIN | EPOLLOUT | EPOLLPRI; | 1020 | event.events = EPOLLIN | EPOLLOUT | EPOLLPRI; |
1021 | event.data.ptr = &urh->app; | 1021 | event.data.ptr = &urh->app; |
1022 | if (0 != epoll_ctl (daemon->epoll_upgrade_fd, | 1022 | if (0 != epoll_ctl (daemon->epoll_upgrade_fd, |
1023 | EPOLL_CTL_DEL, | 1023 | EPOLL_CTL_DEL, |
1024 | connection->socket_fd, | 1024 | connection->socket_fd, |
1025 | &event)) | 1025 | &event)) |
1026 | MHD_PANIC (_("Error cleaning up while handling epoll error")); | 1026 | MHD_PANIC (_("Error cleaning up while handling epoll error")); |
1027 | #ifdef HAVE_MESSAGES | 1027 | #ifdef HAVE_MESSAGES |
1028 | MHD_DLOG (daemon, | 1028 | MHD_DLOG (daemon, |
1029 | _("Call to epoll_ctl failed: %s\n"), | 1029 | _("Call to epoll_ctl failed: %s\n"), |
1030 | MHD_socket_last_strerr_ ()); | 1030 | MHD_socket_last_strerr_ ()); |
1031 | #endif | 1031 | #endif |
1032 | MHD_socket_close_chk_ (sv[0]); | 1032 | MHD_socket_close_chk_ (sv[0]); |
1033 | MHD_socket_close_chk_ (sv[1]); | 1033 | MHD_socket_close_chk_ (sv[1]); |
1034 | free (urh); | 1034 | free (urh); |
1035 | return MHD_NO; | 1035 | return MHD_NO; |
1036 | } | 1036 | } |
1037 | EDLL_insert (daemon->eready_urh_head, | 1037 | EDLL_insert (daemon->eready_urh_head, |
1038 | daemon->eready_urh_tail, | 1038 | daemon->eready_urh_tail, |
1039 | urh); | 1039 | urh); |
1040 | urh->in_eready_list = true; | 1040 | urh->in_eready_list = true; |
1041 | } | 1041 | } |
1042 | #endif /* EPOLL_SUPPORT */ | 1042 | #endif /* EPOLL_SUPPORT */ |
1043 | if (0 == (daemon->options & MHD_USE_THREAD_PER_CONNECTION) ) | 1043 | if (0 == (daemon->options & MHD_USE_THREAD_PER_CONNECTION) ) |
diff --git a/src/microhttpd/test_upgrade_large.c b/src/microhttpd/test_upgrade_large.c new file mode 100644 index 00000000..fdf2edf7 --- /dev/null +++ b/src/microhttpd/test_upgrade_large.c | |||
@@ -0,0 +1,1353 @@ | |||
1 | /* | ||
2 | This file is part of libmicrohttpd | ||
3 | Copyright (C) 2016, 2019 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_large.c | ||
23 | * @brief Testcase for libmicrohttpd upgrading a connection, | ||
24 | * modified to test the "large" corner case reported | ||
25 | * by Viet on the mailinglist in 6'2019 | ||
26 | * @author Christian Grothoff | ||
27 | * @author Karlson2k (Evgeny Grin) | ||
28 | */ | ||
29 | |||
30 | #include "mhd_options.h" | ||
31 | #include <stdlib.h> | ||
32 | #include <string.h> | ||
33 | #include <stdio.h> | ||
34 | #include <pthread.h> | ||
35 | #include <stdlib.h> | ||
36 | #include <stddef.h> | ||
37 | #ifndef WINDOWS | ||
38 | #include <unistd.h> | ||
39 | #endif | ||
40 | #ifdef HAVE_STDBOOL_H | ||
41 | #include <stdbool.h> | ||
42 | #endif /* HAVE_STDBOOL_H */ | ||
43 | |||
44 | #include "mhd_sockets.h" | ||
45 | #ifdef HAVE_NETINET_IP_H | ||
46 | #include <netinet/ip.h> | ||
47 | #endif /* HAVE_NETINET_IP_H */ | ||
48 | |||
49 | #include "platform.h" | ||
50 | #include "microhttpd.h" | ||
51 | |||
52 | #include "test_helpers.h" | ||
53 | |||
54 | #define LARGE_STRING "HelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHello" | ||
55 | |||
56 | #define LARGE_REPLY_STRING "WorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorld" | ||
57 | |||
58 | #ifdef HTTPS_SUPPORT | ||
59 | #include <gnutls/gnutls.h> | ||
60 | #include "../testcurl/https/tls_test_keys.h" | ||
61 | |||
62 | #if defined(HAVE_FORK) && defined(HAVE_WAITPID) | ||
63 | #include <sys/types.h> | ||
64 | #include <sys/wait.h> | ||
65 | #endif /* HAVE_FORK && HAVE_WAITPID */ | ||
66 | #endif /* HTTPS_SUPPORT */ | ||
67 | |||
68 | static int verbose = 0; | ||
69 | |||
70 | enum tls_tool | ||
71 | { | ||
72 | TLS_CLI_NO_TOOL = 0, | ||
73 | TLS_CLI_GNUTLS, | ||
74 | TLS_CLI_OPENSSL, | ||
75 | TLS_LIB_GNUTLS | ||
76 | }; | ||
77 | |||
78 | enum tls_tool use_tls_tool; | ||
79 | |||
80 | #if defined(HTTPS_SUPPORT) && defined(HAVE_FORK) && defined(HAVE_WAITPID) | ||
81 | /** | ||
82 | * Fork child that connects via GnuTLS-CLI to our @a port. Allows us to | ||
83 | * talk to our port over a socket in @a sp without having to worry | ||
84 | * about TLS. | ||
85 | * | ||
86 | * @param location where the socket is returned | ||
87 | * @return -1 on error, otherwise PID of TLS child process | ||
88 | */ | ||
89 | static pid_t | ||
90 | gnutlscli_connect (int *sock, | ||
91 | uint16_t port) | ||
92 | { | ||
93 | pid_t chld; | ||
94 | int sp[2]; | ||
95 | char destination[30]; | ||
96 | |||
97 | if (0 != socketpair (AF_UNIX, | ||
98 | SOCK_STREAM, | ||
99 | 0, | ||
100 | sp)) | ||
101 | return -1; | ||
102 | chld = fork (); | ||
103 | if (0 != chld) | ||
104 | { | ||
105 | *sock = sp[1]; | ||
106 | MHD_socket_close_chk_ (sp[0]); | ||
107 | return chld; | ||
108 | } | ||
109 | MHD_socket_close_chk_ (sp[1]); | ||
110 | (void) close (0); | ||
111 | (void) close (1); | ||
112 | if (-1 == dup2 (sp[0], 0)) | ||
113 | abort (); | ||
114 | if (-1 == dup2 (sp[0], 1)) | ||
115 | abort (); | ||
116 | MHD_socket_close_chk_ (sp[0]); | ||
117 | if (TLS_CLI_GNUTLS == use_tls_tool) | ||
118 | { | ||
119 | snprintf (destination, | ||
120 | sizeof(destination), | ||
121 | "%u", | ||
122 | (unsigned int) port); | ||
123 | execlp ("gnutls-cli", | ||
124 | "gnutls-cli", | ||
125 | "--insecure", | ||
126 | "-p", | ||
127 | destination, | ||
128 | "127.0.0.1", | ||
129 | (char *) NULL); | ||
130 | } | ||
131 | else if (TLS_CLI_OPENSSL == use_tls_tool) | ||
132 | { | ||
133 | snprintf (destination, | ||
134 | sizeof(destination), | ||
135 | "127.0.0.1:%u", | ||
136 | (unsigned int) port); | ||
137 | execlp ("openssl", | ||
138 | "openssl", | ||
139 | "s_client", | ||
140 | "-connect", | ||
141 | destination, | ||
142 | "-verify", | ||
143 | "1", | ||
144 | (char *) NULL); | ||
145 | } | ||
146 | _exit (1); | ||
147 | } | ||
148 | #endif /* HTTPS_SUPPORT && HAVE_FORK && HAVE_WAITPID */ | ||
149 | |||
150 | |||
151 | /** | ||
152 | * Wrapper structure for plain&TLS sockets | ||
153 | */ | ||
154 | struct wr_socket | ||
155 | { | ||
156 | /** | ||
157 | * Real network socket | ||
158 | */ | ||
159 | MHD_socket fd; | ||
160 | |||
161 | /** | ||
162 | * Type of this socket | ||
163 | */ | ||
164 | enum wr_type | ||
165 | { | ||
166 | wr_invalid = 0, | ||
167 | wr_plain = 1, | ||
168 | wr_tls = 2 | ||
169 | } t; | ||
170 | #ifdef HTTPS_SUPPORT | ||
171 | /** | ||
172 | * TLS credentials | ||
173 | */ | ||
174 | gnutls_certificate_credentials_t tls_crd; | ||
175 | |||
176 | /** | ||
177 | * TLS session. | ||
178 | */ | ||
179 | gnutls_session_t tls_s; | ||
180 | |||
181 | /** | ||
182 | * TLS handshake already succeed? | ||
183 | */ | ||
184 | bool tls_connected; | ||
185 | #endif | ||
186 | }; | ||
187 | |||
188 | |||
189 | /** | ||
190 | * Get underlying real socket. | ||
191 | * @return FD of real socket | ||
192 | */ | ||
193 | #define wr_fd(s) ((s)->fd) | ||
194 | |||
195 | |||
196 | /** | ||
197 | * Create wr_socket with plain TCP underlying socket | ||
198 | * @return created socket on success, NULL otherwise | ||
199 | */ | ||
200 | static struct wr_socket * | ||
201 | wr_create_plain_sckt(void) | ||
202 | { | ||
203 | struct wr_socket *s = malloc(sizeof(struct wr_socket)); | ||
204 | if (NULL == s) | ||
205 | return NULL; | ||
206 | s->t = wr_plain; | ||
207 | s->fd = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP); | ||
208 | if (MHD_INVALID_SOCKET != s->fd) | ||
209 | return s; | ||
210 | free(s); | ||
211 | return NULL; | ||
212 | } | ||
213 | |||
214 | |||
215 | /** | ||
216 | * Create wr_socket with TLS TCP underlying socket | ||
217 | * @return created socket on success, NULL otherwise | ||
218 | */ | ||
219 | static struct wr_socket * | ||
220 | wr_create_tls_sckt(void) | ||
221 | { | ||
222 | #ifdef HTTPS_SUPPORT | ||
223 | struct wr_socket *s = malloc(sizeof(struct wr_socket)); | ||
224 | if (NULL == s) | ||
225 | return NULL; | ||
226 | s->t = wr_tls; | ||
227 | s->tls_connected = 0; | ||
228 | s->fd = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP); | ||
229 | if (MHD_INVALID_SOCKET != s->fd) | ||
230 | { | ||
231 | if (GNUTLS_E_SUCCESS == gnutls_init (&(s->tls_s), GNUTLS_CLIENT)) | ||
232 | { | ||
233 | if (GNUTLS_E_SUCCESS == gnutls_set_default_priority (s->tls_s)) | ||
234 | { | ||
235 | if (GNUTLS_E_SUCCESS == gnutls_certificate_allocate_credentials (&(s->tls_crd))) | ||
236 | { | ||
237 | if (GNUTLS_E_SUCCESS == gnutls_credentials_set (s->tls_s, GNUTLS_CRD_CERTIFICATE, s->tls_crd)) | ||
238 | { | ||
239 | #if GNUTLS_VERSION_NUMBER+0 >= 0x030109 | ||
240 | gnutls_transport_set_int (s->tls_s, (int)(s->fd)); | ||
241 | #else /* GnuTLS before 3.1.9 */ | ||
242 | gnutls_transport_set_ptr (s->tls_s, (gnutls_transport_ptr_t)(intptr_t)(s->fd)); | ||
243 | #endif /* GnuTLS before 3.1.9 */ | ||
244 | return s; | ||
245 | } | ||
246 | gnutls_certificate_free_credentials (s->tls_crd); | ||
247 | } | ||
248 | } | ||
249 | gnutls_deinit (s->tls_s); | ||
250 | } | ||
251 | (void)MHD_socket_close_ (s->fd); | ||
252 | } | ||
253 | free(s); | ||
254 | #endif /* HTTPS_SUPPORT */ | ||
255 | return NULL; | ||
256 | } | ||
257 | |||
258 | |||
259 | /** | ||
260 | * Create wr_socket with plain TCP underlying socket | ||
261 | * from already created TCP socket. | ||
262 | * @param plain_sk real TCP socket | ||
263 | * @return created socket on success, NULL otherwise | ||
264 | */ | ||
265 | static struct wr_socket * | ||
266 | wr_create_from_plain_sckt(MHD_socket plain_sk) | ||
267 | { | ||
268 | struct wr_socket *s = malloc(sizeof(struct wr_socket)); | ||
269 | |||
270 | if (NULL == s) | ||
271 | return NULL; | ||
272 | s->t = wr_plain; | ||
273 | s->fd = plain_sk; | ||
274 | return s; | ||
275 | } | ||
276 | |||
277 | |||
278 | /** | ||
279 | * Connect socket to specified address. | ||
280 | * @param s socket to use | ||
281 | * @param addr address to connect | ||
282 | * @param length of sturcture pointed by @a addr | ||
283 | * @return zero on success, -1 otherwise. | ||
284 | */ | ||
285 | static int | ||
286 | wr_connect(struct wr_socket *s, | ||
287 | const struct sockaddr *addr, | ||
288 | int length) | ||
289 | { | ||
290 | if (0 != connect (s->fd, addr, length)) | ||
291 | return -1; | ||
292 | if (wr_plain == s->t) | ||
293 | return 0; | ||
294 | #ifdef HTTPS_SUPPORT | ||
295 | if (wr_tls == s->t) | ||
296 | { | ||
297 | /* Do not try handshake here as | ||
298 | * it require processing on MHD side and | ||
299 | * when testing with "external" polling, | ||
300 | * test will call MHD processing only | ||
301 | * after return from wr_connect(). */ | ||
302 | s->tls_connected = 0; | ||
303 | return 0; | ||
304 | } | ||
305 | #endif /* HTTPS_SUPPORT */ | ||
306 | return -1; | ||
307 | } | ||
308 | |||
309 | #ifdef HTTPS_SUPPORT | ||
310 | /* Only to be called from wr_send() and wr_recv() ! */ | ||
311 | static bool | ||
312 | wr_handshake(struct wr_socket *s) | ||
313 | { | ||
314 | int res = gnutls_handshake (s->tls_s); | ||
315 | if (GNUTLS_E_SUCCESS == res) | ||
316 | s->tls_connected = true; | ||
317 | else if (GNUTLS_E_AGAIN == res) | ||
318 | MHD_socket_set_error_ (MHD_SCKT_EAGAIN_); | ||
319 | else | ||
320 | MHD_socket_set_error_ (MHD_SCKT_ECONNABORTED_); /* hard error */ | ||
321 | return s->tls_connected; | ||
322 | } | ||
323 | #endif /* HTTPS_SUPPORT */ | ||
324 | |||
325 | |||
326 | /** | ||
327 | * Send data to remote by socket. | ||
328 | * @param s the socket to use | ||
329 | * @param buf the buffer with data to send | ||
330 | * @param len the length of data in @a buf | ||
331 | * @return number of bytes were sent if succeed, | ||
332 | * -1 if failed. Use #MHD_socket_get_error_() | ||
333 | * to get socket error. | ||
334 | */ | ||
335 | static ssize_t | ||
336 | wr_send (struct wr_socket *s, | ||
337 | const void *buf, | ||
338 | size_t len) | ||
339 | { | ||
340 | if (wr_plain == s->t) | ||
341 | return MHD_send_(s->fd, buf, len); | ||
342 | #ifdef HTTPS_SUPPORT | ||
343 | if (wr_tls == s->t) | ||
344 | { | ||
345 | ssize_t ret; | ||
346 | if (!s->tls_connected && !wr_handshake (s)) | ||
347 | return -1; | ||
348 | |||
349 | ret = gnutls_record_send (s->tls_s, buf, len); | ||
350 | if (ret > 0) | ||
351 | return ret; | ||
352 | if (GNUTLS_E_AGAIN == ret) | ||
353 | MHD_socket_set_error_ (MHD_SCKT_EAGAIN_); | ||
354 | else | ||
355 | MHD_socket_set_error_ (MHD_SCKT_ECONNABORTED_); /* hard error */ | ||
356 | } | ||
357 | #endif /* HTTPS_SUPPORT */ | ||
358 | return -1; | ||
359 | } | ||
360 | |||
361 | |||
362 | /** | ||
363 | * Receive data from remote by socket. | ||
364 | * @param s the socket to use | ||
365 | * @param buf the buffer to store received data | ||
366 | * @param len the length of @a buf | ||
367 | * @return number of bytes were received if succeed, | ||
368 | * -1 if failed. Use #MHD_socket_get_error_() | ||
369 | * to get socket error. | ||
370 | */ | ||
371 | static ssize_t | ||
372 | wr_recv (struct wr_socket *s, | ||
373 | void *buf, | ||
374 | size_t len) | ||
375 | { | ||
376 | if (wr_plain == s->t) | ||
377 | return MHD_recv_ (s->fd, buf, len); | ||
378 | #ifdef HTTPS_SUPPORT | ||
379 | if (wr_tls == s->t) | ||
380 | { | ||
381 | ssize_t ret; | ||
382 | if (!s->tls_connected && !wr_handshake (s)) | ||
383 | return -1; | ||
384 | |||
385 | ret = gnutls_record_recv (s->tls_s, buf, len); | ||
386 | if (ret > 0) | ||
387 | return ret; | ||
388 | if (GNUTLS_E_AGAIN == ret) | ||
389 | MHD_socket_set_error_ (MHD_SCKT_EAGAIN_); | ||
390 | else | ||
391 | MHD_socket_set_error_ (MHD_SCKT_ECONNABORTED_); /* hard error */ | ||
392 | } | ||
393 | #endif /* HTTPS_SUPPORT */ | ||
394 | return -1; | ||
395 | } | ||
396 | |||
397 | |||
398 | /** | ||
399 | * Close socket and release allocated resourced | ||
400 | * @param s the socket to close | ||
401 | * @return zero on succeed, -1 otherwise | ||
402 | */ | ||
403 | static int | ||
404 | wr_close (struct wr_socket *s) | ||
405 | { | ||
406 | int ret = (MHD_socket_close_(s->fd)) ? 0 : -1; | ||
407 | #ifdef HTTPS_SUPPORT | ||
408 | if (wr_tls == s->t) | ||
409 | { | ||
410 | gnutls_deinit (s->tls_s); | ||
411 | gnutls_certificate_free_credentials (s->tls_crd); | ||
412 | } | ||
413 | #endif /* HTTPS_SUPPORT */ | ||
414 | free (s); | ||
415 | return ret; | ||
416 | } | ||
417 | |||
418 | |||
419 | /** | ||
420 | * Thread we use to run the interaction with the upgraded socket. | ||
421 | */ | ||
422 | static pthread_t pt; | ||
423 | |||
424 | /** | ||
425 | * Will be set to the upgraded socket. | ||
426 | */ | ||
427 | static struct wr_socket *usock; | ||
428 | |||
429 | /** | ||
430 | * Thread we use to run the interaction with the upgraded socket. | ||
431 | */ | ||
432 | static pthread_t pt_client; | ||
433 | |||
434 | /** | ||
435 | * Flag set to 1 once the test is finished. | ||
436 | */ | ||
437 | static volatile bool done; | ||
438 | |||
439 | |||
440 | /** | ||
441 | * Callback used by MHD to notify the application about completed | ||
442 | * requests. Frees memory. | ||
443 | * | ||
444 | * @param cls client-defined closure | ||
445 | * @param connection connection handle | ||
446 | * @param con_cls value as set by the last call to | ||
447 | * the #MHD_AccessHandlerCallback | ||
448 | * @param toe reason for request termination | ||
449 | */ | ||
450 | static void | ||
451 | notify_completed_cb (void *cls, | ||
452 | struct MHD_Connection *connection, | ||
453 | void **con_cls, | ||
454 | enum MHD_RequestTerminationCode toe) | ||
455 | { | ||
456 | pthread_t* ppth = *con_cls; | ||
457 | |||
458 | (void) cls; | ||
459 | (void) connection; /* Unused. Silent compiler warning. */ | ||
460 | if ( (toe != MHD_REQUEST_TERMINATED_COMPLETED_OK) && | ||
461 | (toe != MHD_REQUEST_TERMINATED_CLIENT_ABORT) && | ||
462 | (toe != MHD_REQUEST_TERMINATED_DAEMON_SHUTDOWN) ) | ||
463 | abort (); | ||
464 | if (! pthread_equal (**((pthread_t**)con_cls), | ||
465 | pthread_self ())) | ||
466 | abort (); | ||
467 | if (NULL != ppth) | ||
468 | free (*con_cls); | ||
469 | *con_cls = NULL; | ||
470 | } | ||
471 | |||
472 | |||
473 | /** | ||
474 | * Logging callback. | ||
475 | * | ||
476 | * @param cls logging closure (NULL) | ||
477 | * @param uri access URI | ||
478 | * @param connection connection handle | ||
479 | * @return #TEST_PTR | ||
480 | */ | ||
481 | static void * | ||
482 | log_cb (void *cls, | ||
483 | const char *uri, | ||
484 | struct MHD_Connection *connection) | ||
485 | { | ||
486 | pthread_t *ppth; | ||
487 | |||
488 | (void) cls; | ||
489 | (void) connection; /* Unused. Silent compiler warning. */ | ||
490 | if (0 != strcmp (uri, | ||
491 | "/")) | ||
492 | abort (); | ||
493 | ppth = malloc (sizeof (pthread_t)); | ||
494 | if (NULL == ppth) | ||
495 | abort(); | ||
496 | *ppth = pthread_self (); | ||
497 | return (void *) ppth; | ||
498 | } | ||
499 | |||
500 | |||
501 | /** | ||
502 | * Function to check that MHD properly notifies about starting | ||
503 | * and stopping. | ||
504 | * | ||
505 | * @param cls client-defined closure | ||
506 | * @param connection connection handle | ||
507 | * @param socket_context socket-specific pointer where the | ||
508 | * client can associate some state specific | ||
509 | * to the TCP connection; note that this is | ||
510 | * different from the "con_cls" which is per | ||
511 | * HTTP request. The client can initialize | ||
512 | * during #MHD_CONNECTION_NOTIFY_STARTED and | ||
513 | * cleanup during #MHD_CONNECTION_NOTIFY_CLOSED | ||
514 | * and access in the meantime using | ||
515 | * #MHD_CONNECTION_INFO_SOCKET_CONTEXT. | ||
516 | * @param toe reason for connection notification | ||
517 | * @see #MHD_OPTION_NOTIFY_CONNECTION | ||
518 | * @ingroup request | ||
519 | */ | ||
520 | static void | ||
521 | notify_connection_cb (void *cls, | ||
522 | struct MHD_Connection *connection, | ||
523 | void **socket_context, | ||
524 | enum MHD_ConnectionNotificationCode toe) | ||
525 | { | ||
526 | static int started; | ||
527 | |||
528 | (void) cls; | ||
529 | (void) connection; /* Unused. Silent compiler warning. */ | ||
530 | switch (toe) | ||
531 | { | ||
532 | case MHD_CONNECTION_NOTIFY_STARTED: | ||
533 | if (MHD_NO != started) | ||
534 | abort (); | ||
535 | started = MHD_YES; | ||
536 | *socket_context = &started; | ||
537 | break; | ||
538 | case MHD_CONNECTION_NOTIFY_CLOSED: | ||
539 | if (MHD_YES != started) | ||
540 | abort (); | ||
541 | if (&started != *socket_context) | ||
542 | abort (); | ||
543 | *socket_context = NULL; | ||
544 | started = MHD_NO; | ||
545 | break; | ||
546 | } | ||
547 | } | ||
548 | |||
549 | |||
550 | /** | ||
551 | * Change socket to blocking. | ||
552 | * | ||
553 | * @param fd the socket to manipulate | ||
554 | * @return non-zero if succeeded, zero otherwise | ||
555 | */ | ||
556 | static void | ||
557 | make_blocking (MHD_socket fd) | ||
558 | { | ||
559 | #if defined(MHD_POSIX_SOCKETS) | ||
560 | int flags; | ||
561 | |||
562 | flags = fcntl (fd, F_GETFL); | ||
563 | if (-1 == flags) | ||
564 | return; | ||
565 | if ((flags & ~O_NONBLOCK) != flags) | ||
566 | if (-1 == fcntl (fd, F_SETFL, flags & ~O_NONBLOCK)) | ||
567 | abort (); | ||
568 | #elif defined(MHD_WINSOCK_SOCKETS) | ||
569 | unsigned long flags = 1; | ||
570 | |||
571 | ioctlsocket (fd, FIONBIO, &flags); | ||
572 | #endif /* MHD_WINSOCK_SOCKETS */ | ||
573 | |||
574 | } | ||
575 | |||
576 | |||
577 | static void | ||
578 | send_all (struct wr_socket *sock, | ||
579 | const char *text) | ||
580 | { | ||
581 | size_t len = strlen (text); | ||
582 | ssize_t ret; | ||
583 | size_t off; | ||
584 | |||
585 | make_blocking (wr_fd (sock)); | ||
586 | for (off = 0; off < len; off += ret) | ||
587 | { | ||
588 | ret = wr_send (sock, | ||
589 | &text[off], | ||
590 | len - off); | ||
591 | if (0 > ret) | ||
592 | { | ||
593 | if (MHD_SCKT_ERR_IS_EAGAIN_ (MHD_socket_get_error_ ())) | ||
594 | { | ||
595 | ret = 0; | ||
596 | continue; | ||
597 | } | ||
598 | abort (); | ||
599 | } | ||
600 | } | ||
601 | } | ||
602 | |||
603 | |||
604 | /** | ||
605 | * Read character-by-character until we | ||
606 | * get '\r\n\r\n'. | ||
607 | */ | ||
608 | static void | ||
609 | recv_hdr (struct wr_socket *sock) | ||
610 | { | ||
611 | unsigned int i; | ||
612 | char next; | ||
613 | char c; | ||
614 | ssize_t ret; | ||
615 | |||
616 | make_blocking (wr_fd (sock)); | ||
617 | next = '\r'; | ||
618 | i = 0; | ||
619 | while (i < 4) | ||
620 | { | ||
621 | ret = wr_recv (sock, | ||
622 | &c, | ||
623 | 1); | ||
624 | if (0 > ret) | ||
625 | { | ||
626 | if (MHD_SCKT_ERR_IS_EAGAIN_ (MHD_socket_get_error_ ())) | ||
627 | continue; | ||
628 | abort (); | ||
629 | } | ||
630 | if (0 == ret) | ||
631 | continue; | ||
632 | if (c == next) | ||
633 | { | ||
634 | i++; | ||
635 | if (next == '\r') | ||
636 | next = '\n'; | ||
637 | else | ||
638 | next = '\r'; | ||
639 | continue; | ||
640 | } | ||
641 | if (c == '\r') | ||
642 | { | ||
643 | i = 1; | ||
644 | next = '\n'; | ||
645 | continue; | ||
646 | } | ||
647 | i = 0; | ||
648 | next = '\r'; | ||
649 | } | ||
650 | } | ||
651 | |||
652 | |||
653 | static void | ||
654 | recv_all (struct wr_socket *sock, | ||
655 | const char *text) | ||
656 | { | ||
657 | size_t len = strlen (text); | ||
658 | char buf[len]; | ||
659 | ssize_t ret; | ||
660 | size_t off; | ||
661 | |||
662 | make_blocking (wr_fd (sock)); | ||
663 | for (off = 0; off < len; off += ret) | ||
664 | { | ||
665 | ret = wr_recv (sock, | ||
666 | &buf[off], | ||
667 | len - off); | ||
668 | if (0 > ret) | ||
669 | { | ||
670 | if (MHD_SCKT_ERR_IS_EAGAIN_ (MHD_socket_get_error_ ())) | ||
671 | { | ||
672 | ret = 0; | ||
673 | continue; | ||
674 | } | ||
675 | abort (); | ||
676 | } | ||
677 | } | ||
678 | if (0 != strncmp (text, buf, len)) | ||
679 | abort(); | ||
680 | } | ||
681 | |||
682 | |||
683 | /** | ||
684 | * Main function for the thread that runs the interaction with | ||
685 | * the upgraded socket. | ||
686 | * | ||
687 | * @param cls the handle for the upgrade | ||
688 | */ | ||
689 | static void * | ||
690 | run_usock (void *cls) | ||
691 | { | ||
692 | struct MHD_UpgradeResponseHandle *urh = cls; | ||
693 | |||
694 | send_all (usock, | ||
695 | LARGE_STRING); | ||
696 | recv_all (usock, | ||
697 | LARGE_REPLY_STRING); | ||
698 | send_all (usock, | ||
699 | "Finished"); | ||
700 | MHD_upgrade_action (urh, | ||
701 | MHD_UPGRADE_ACTION_CLOSE); | ||
702 | free (usock); | ||
703 | usock = NULL; | ||
704 | return NULL; | ||
705 | } | ||
706 | |||
707 | |||
708 | /** | ||
709 | * Main function for the thread that runs the client-side of the | ||
710 | * interaction with the upgraded socket. | ||
711 | * | ||
712 | * @param cls the client socket | ||
713 | */ | ||
714 | static void * | ||
715 | run_usock_client (void *cls) | ||
716 | { | ||
717 | struct wr_socket *sock = cls; | ||
718 | |||
719 | send_all (sock, | ||
720 | "GET / HTTP/1.1\r\nConnection: Upgrade\r\n\r\n"); | ||
721 | recv_hdr (sock); | ||
722 | recv_all (sock, | ||
723 | LARGE_STRING); | ||
724 | send_all (sock, | ||
725 | LARGE_REPLY_STRING); | ||
726 | recv_all (sock, | ||
727 | "Finished"); | ||
728 | wr_close (sock); | ||
729 | done = true; | ||
730 | return NULL; | ||
731 | } | ||
732 | |||
733 | |||
734 | /** | ||
735 | * Function called after a protocol "upgrade" response was sent | ||
736 | * successfully and the socket should now be controlled by some | ||
737 | * protocol other than HTTP. | ||
738 | * | ||
739 | * Any data already received on the socket will be made available in | ||
740 | * @e extra_in. This can happen if the application sent extra data | ||
741 | * before MHD send the upgrade response. The application should | ||
742 | * treat data from @a extra_in as if it had read it from the socket. | ||
743 | * | ||
744 | * Note that the application must not close() @a sock directly, | ||
745 | * but instead use #MHD_upgrade_action() for special operations | ||
746 | * on @a sock. | ||
747 | * | ||
748 | * Except when in 'thread-per-connection' mode, implementations | ||
749 | * of this function should never block (as it will still be called | ||
750 | * from within the main event loop). | ||
751 | * | ||
752 | * @param cls closure, whatever was given to #MHD_create_response_for_upgrade(). | ||
753 | * @param connection original HTTP connection handle, | ||
754 | * giving the function a last chance | ||
755 | * to inspect the original HTTP request | ||
756 | * @param con_cls last value left in `con_cls` of the `MHD_AccessHandlerCallback` | ||
757 | * @param extra_in if we happened to have read bytes after the | ||
758 | * HTTP header already (because the client sent | ||
759 | * more than the HTTP header of the request before | ||
760 | * we sent the upgrade response), | ||
761 | * these are the extra bytes already read from @a sock | ||
762 | * by MHD. The application should treat these as if | ||
763 | * it had read them from @a sock. | ||
764 | * @param extra_in_size number of bytes in @a extra_in | ||
765 | * @param sock socket to use for bi-directional communication | ||
766 | * with the client. For HTTPS, this may not be a socket | ||
767 | * that is directly connected to the client and thus certain | ||
768 | * operations (TCP-specific setsockopt(), getsockopt(), etc.) | ||
769 | * may not work as expected (as the socket could be from a | ||
770 | * socketpair() or a TCP-loopback). The application is expected | ||
771 | * to perform read()/recv() and write()/send() calls on the socket. | ||
772 | * The application may also call shutdown(), but must not call | ||
773 | * close() directly. | ||
774 | * @param urh argument for #MHD_upgrade_action()s on this @a connection. | ||
775 | * Applications must eventually use this callback to (indirectly) | ||
776 | * perform the close() action on the @a sock. | ||
777 | */ | ||
778 | static void | ||
779 | upgrade_cb (void *cls, | ||
780 | struct MHD_Connection *connection, | ||
781 | void *con_cls, | ||
782 | const char *extra_in, | ||
783 | size_t extra_in_size, | ||
784 | MHD_socket sock, | ||
785 | struct MHD_UpgradeResponseHandle *urh) | ||
786 | { | ||
787 | (void) cls; | ||
788 | (void) connection; | ||
789 | (void) con_cls; | ||
790 | (void) extra_in; /* Unused. Silent compiler warning. */ | ||
791 | |||
792 | usock = wr_create_from_plain_sckt (sock); | ||
793 | if (0 != extra_in_size) | ||
794 | abort (); | ||
795 | if (0 != pthread_create (&pt, | ||
796 | NULL, | ||
797 | &run_usock, | ||
798 | urh)) | ||
799 | abort (); | ||
800 | } | ||
801 | |||
802 | |||
803 | /** | ||
804 | * A client has requested the given url using the given method | ||
805 | * (#MHD_HTTP_METHOD_GET, #MHD_HTTP_METHOD_PUT, | ||
806 | * #MHD_HTTP_METHOD_DELETE, #MHD_HTTP_METHOD_POST, etc). The callback | ||
807 | * must call MHD callbacks to provide content to give back to the | ||
808 | * client and return an HTTP status code (i.e. #MHD_HTTP_OK, | ||
809 | * #MHD_HTTP_NOT_FOUND, etc.). | ||
810 | * | ||
811 | * @param cls argument given together with the function | ||
812 | * pointer when the handler was registered with MHD | ||
813 | * @param url the requested url | ||
814 | * @param method the HTTP method used (#MHD_HTTP_METHOD_GET, | ||
815 | * #MHD_HTTP_METHOD_PUT, etc.) | ||
816 | * @param version the HTTP version string (i.e. | ||
817 | * #MHD_HTTP_VERSION_1_1) | ||
818 | * @param upload_data the data being uploaded (excluding HEADERS, | ||
819 | * for a POST that fits into memory and that is encoded | ||
820 | * with a supported encoding, the POST data will NOT be | ||
821 | * given in upload_data and is instead available as | ||
822 | * part of #MHD_get_connection_values; very large POST | ||
823 | * data *will* be made available incrementally in | ||
824 | * @a upload_data) | ||
825 | * @param upload_data_size set initially to the size of the | ||
826 | * @a upload_data provided; the method must update this | ||
827 | * value to the number of bytes NOT processed; | ||
828 | * @param con_cls pointer that the callback can set to some | ||
829 | * address and that will be preserved by MHD for future | ||
830 | * calls for this request; since the access handler may | ||
831 | * be called many times (i.e., for a PUT/POST operation | ||
832 | * with plenty of upload data) this allows the application | ||
833 | * to easily associate some request-specific state. | ||
834 | * If necessary, this state can be cleaned up in the | ||
835 | * global #MHD_RequestCompletedCallback (which | ||
836 | * can be set with the #MHD_OPTION_NOTIFY_COMPLETED). | ||
837 | * Initially, `*con_cls` will be NULL. | ||
838 | * @return #MHD_YES if the connection was handled successfully, | ||
839 | * #MHD_NO if the socket must be closed due to a serios | ||
840 | * error while handling the request | ||
841 | */ | ||
842 | static int | ||
843 | ahc_upgrade (void *cls, | ||
844 | struct MHD_Connection *connection, | ||
845 | const char *url, | ||
846 | const char *method, | ||
847 | const char *version, | ||
848 | const char *upload_data, | ||
849 | size_t *upload_data_size, | ||
850 | void **con_cls) | ||
851 | { | ||
852 | struct MHD_Response *resp; | ||
853 | int ret; | ||
854 | (void) cls; | ||
855 | (void) url; | ||
856 | (void) method; /* Unused. Silent compiler warning. */ | ||
857 | (void) version; | ||
858 | (void) upload_data; | ||
859 | (void) upload_data_size; /* Unused. Silent compiler warning. */ | ||
860 | |||
861 | if (NULL == *con_cls) | ||
862 | abort (); | ||
863 | if (! pthread_equal (**((pthread_t**)con_cls), pthread_self ())) | ||
864 | abort (); | ||
865 | resp = MHD_create_response_for_upgrade (&upgrade_cb, | ||
866 | NULL); | ||
867 | MHD_add_response_header (resp, | ||
868 | MHD_HTTP_HEADER_UPGRADE, | ||
869 | "Hello World Protocol"); | ||
870 | ret = MHD_queue_response (connection, | ||
871 | MHD_HTTP_SWITCHING_PROTOCOLS, | ||
872 | resp); | ||
873 | MHD_destroy_response (resp); | ||
874 | return ret; | ||
875 | } | ||
876 | |||
877 | |||
878 | /** | ||
879 | * Run the MHD external event loop using select. | ||
880 | * | ||
881 | * @param daemon daemon to run it for | ||
882 | */ | ||
883 | static void | ||
884 | run_mhd_select_loop (struct MHD_Daemon *daemon) | ||
885 | { | ||
886 | fd_set rs; | ||
887 | fd_set ws; | ||
888 | fd_set es; | ||
889 | MHD_socket max_fd; | ||
890 | MHD_UNSIGNED_LONG_LONG to; | ||
891 | struct timeval tv; | ||
892 | |||
893 | while (! done) | ||
894 | { | ||
895 | FD_ZERO (&rs); | ||
896 | FD_ZERO (&ws); | ||
897 | FD_ZERO (&es); | ||
898 | max_fd = -1; | ||
899 | to = 1000; | ||
900 | |||
901 | if (MHD_YES != | ||
902 | MHD_get_fdset (daemon, | ||
903 | &rs, | ||
904 | &ws, | ||
905 | &es, | ||
906 | &max_fd)) | ||
907 | abort (); | ||
908 | (void) MHD_get_timeout (daemon, | ||
909 | &to); | ||
910 | if (1000 < to) | ||
911 | to = 1000; | ||
912 | tv.tv_sec = to / 1000; | ||
913 | tv.tv_usec = 1000 * (to % 1000); | ||
914 | if (0 > MHD_SYS_select_ (max_fd + 1, | ||
915 | &rs, | ||
916 | &ws, | ||
917 | &es, | ||
918 | &tv)) | ||
919 | abort (); | ||
920 | MHD_run_from_select (daemon, | ||
921 | &rs, | ||
922 | &ws, | ||
923 | &es); | ||
924 | } | ||
925 | } | ||
926 | |||
927 | #ifdef HAVE_POLL | ||
928 | |||
929 | /** | ||
930 | * Run the MHD external event loop using select. | ||
931 | * | ||
932 | * @param daemon daemon to run it for | ||
933 | */ | ||
934 | static void | ||
935 | run_mhd_poll_loop (struct MHD_Daemon *daemon) | ||
936 | { | ||
937 | (void)daemon; /* Unused. Silent compiler warning. */ | ||
938 | abort (); /* currently not implementable with existing MHD API */ | ||
939 | } | ||
940 | #endif /* HAVE_POLL */ | ||
941 | |||
942 | |||
943 | #ifdef EPOLL_SUPPORT | ||
944 | /** | ||
945 | * Run the MHD external event loop using select. | ||
946 | * | ||
947 | * @param daemon daemon to run it for | ||
948 | */ | ||
949 | static void | ||
950 | run_mhd_epoll_loop (struct MHD_Daemon *daemon) | ||
951 | { | ||
952 | const union MHD_DaemonInfo *di; | ||
953 | MHD_socket ep; | ||
954 | fd_set rs; | ||
955 | MHD_UNSIGNED_LONG_LONG to; | ||
956 | struct timeval tv; | ||
957 | int ret; | ||
958 | |||
959 | di = MHD_get_daemon_info (daemon, | ||
960 | MHD_DAEMON_INFO_EPOLL_FD); | ||
961 | ep = di->listen_fd; | ||
962 | while (! done) | ||
963 | { | ||
964 | FD_ZERO (&rs); | ||
965 | to = 1000; | ||
966 | |||
967 | FD_SET (ep, &rs); | ||
968 | (void) MHD_get_timeout (daemon, | ||
969 | &to); | ||
970 | if (1000 < to) | ||
971 | to = 1000; | ||
972 | tv.tv_sec = to / 1000; | ||
973 | tv.tv_usec = 1000 * (to % 1000); | ||
974 | ret = select (ep + 1, | ||
975 | &rs, | ||
976 | NULL, | ||
977 | NULL, | ||
978 | &tv); | ||
979 | if ( (-1 == ret) && | ||
980 | (EAGAIN != errno) && | ||
981 | (EINTR != errno) ) | ||
982 | abort (); | ||
983 | MHD_run (daemon); | ||
984 | } | ||
985 | } | ||
986 | #endif /* EPOLL_SUPPORT */ | ||
987 | |||
988 | /** | ||
989 | * Run the MHD external event loop using select. | ||
990 | * | ||
991 | * @param daemon daemon to run it for | ||
992 | */ | ||
993 | static void | ||
994 | run_mhd_loop (struct MHD_Daemon *daemon, | ||
995 | int flags) | ||
996 | { | ||
997 | if (0 == (flags & (MHD_USE_POLL | MHD_USE_EPOLL))) | ||
998 | run_mhd_select_loop (daemon); | ||
999 | #ifdef HAVE_POLL | ||
1000 | else if (0 != (flags & MHD_USE_POLL)) | ||
1001 | run_mhd_poll_loop (daemon); | ||
1002 | #endif /* HAVE_POLL */ | ||
1003 | #if EPOLL_SUPPORT | ||
1004 | else if (0 != (flags & MHD_USE_EPOLL)) | ||
1005 | run_mhd_epoll_loop (daemon); | ||
1006 | #endif | ||
1007 | else | ||
1008 | abort (); | ||
1009 | } | ||
1010 | |||
1011 | |||
1012 | static bool test_tls; | ||
1013 | |||
1014 | /** | ||
1015 | * Test upgrading a connection. | ||
1016 | * | ||
1017 | * @param flags which event loop style should be tested | ||
1018 | * @param pool size of the thread pool, 0 to disable | ||
1019 | */ | ||
1020 | static int | ||
1021 | test_upgrade (int flags, | ||
1022 | unsigned int pool) | ||
1023 | { | ||
1024 | struct MHD_Daemon *d = NULL; | ||
1025 | struct wr_socket *sock; | ||
1026 | struct sockaddr_in sa; | ||
1027 | const union MHD_DaemonInfo *real_flags; | ||
1028 | const union MHD_DaemonInfo *dinfo; | ||
1029 | #if defined(HTTPS_SUPPORT) && defined(HAVE_FORK) && defined(HAVE_WAITPID) | ||
1030 | pid_t pid = -1; | ||
1031 | #endif /* HTTPS_SUPPORT && HAVE_FORK && HAVE_WAITPID */ | ||
1032 | |||
1033 | done = false; | ||
1034 | |||
1035 | if (! test_tls) | ||
1036 | d = MHD_start_daemon (flags | MHD_USE_ERROR_LOG | MHD_ALLOW_UPGRADE, | ||
1037 | MHD_is_feature_supported(MHD_FEATURE_AUTODETECT_BIND_PORT) ? | ||
1038 | 0 : 1090, | ||
1039 | NULL, NULL, | ||
1040 | &ahc_upgrade, NULL, | ||
1041 | MHD_OPTION_CONNECTION_MEMORY_LIMIT, 512, | ||
1042 | MHD_OPTION_URI_LOG_CALLBACK, &log_cb, NULL, | ||
1043 | MHD_OPTION_NOTIFY_COMPLETED, ¬ify_completed_cb, NULL, | ||
1044 | MHD_OPTION_NOTIFY_CONNECTION, ¬ify_connection_cb, NULL, | ||
1045 | MHD_OPTION_THREAD_POOL_SIZE, pool, | ||
1046 | MHD_OPTION_END); | ||
1047 | #ifdef HTTPS_SUPPORT | ||
1048 | else | ||
1049 | d = MHD_start_daemon (flags | MHD_USE_ERROR_LOG | MHD_ALLOW_UPGRADE | MHD_USE_TLS, | ||
1050 | MHD_is_feature_supported(MHD_FEATURE_AUTODETECT_BIND_PORT) ? | ||
1051 | 0 : 1090, | ||
1052 | NULL, NULL, | ||
1053 | &ahc_upgrade, NULL, | ||
1054 | MHD_OPTION_CONNECTION_MEMORY_LIMIT, 512, | ||
1055 | MHD_OPTION_URI_LOG_CALLBACK, &log_cb, NULL, | ||
1056 | MHD_OPTION_NOTIFY_COMPLETED, ¬ify_completed_cb, NULL, | ||
1057 | MHD_OPTION_NOTIFY_CONNECTION, ¬ify_connection_cb, NULL, | ||
1058 | MHD_OPTION_HTTPS_MEM_KEY, srv_signed_key_pem, | ||
1059 | MHD_OPTION_HTTPS_MEM_CERT, srv_signed_cert_pem, | ||
1060 | MHD_OPTION_THREAD_POOL_SIZE, pool, | ||
1061 | MHD_OPTION_END); | ||
1062 | #endif /* HTTPS_SUPPORT */ | ||
1063 | if (NULL == d) | ||
1064 | return 2; | ||
1065 | real_flags = MHD_get_daemon_info (d, | ||
1066 | MHD_DAEMON_INFO_FLAGS); | ||
1067 | if (NULL == real_flags) | ||
1068 | abort (); | ||
1069 | dinfo = MHD_get_daemon_info (d, | ||
1070 | MHD_DAEMON_INFO_BIND_PORT); | ||
1071 | if ( (NULL == dinfo) || | ||
1072 | (0 == dinfo->port) ) | ||
1073 | abort (); | ||
1074 | if (!test_tls || TLS_LIB_GNUTLS == use_tls_tool) | ||
1075 | { | ||
1076 | sock = test_tls ? wr_create_tls_sckt () : wr_create_plain_sckt (); | ||
1077 | if (NULL == sock) | ||
1078 | abort (); | ||
1079 | sa.sin_family = AF_INET; | ||
1080 | sa.sin_port = htons (dinfo->port); | ||
1081 | sa.sin_addr.s_addr = htonl (INADDR_LOOPBACK); | ||
1082 | if (0 != wr_connect (sock, | ||
1083 | (struct sockaddr *) &sa, | ||
1084 | sizeof (sa))) | ||
1085 | abort (); | ||
1086 | } | ||
1087 | else | ||
1088 | { | ||
1089 | #if defined(HTTPS_SUPPORT) && defined(HAVE_FORK) && defined(HAVE_WAITPID) | ||
1090 | MHD_socket tls_fork_sock; | ||
1091 | uint16_t port; | ||
1092 | |||
1093 | /* make address sanitizer happy */ | ||
1094 | memcpy (&port, | ||
1095 | dinfo /* ->port */, | ||
1096 | sizeof (port)); | ||
1097 | if (-1 == (pid = gnutlscli_connect (&tls_fork_sock, | ||
1098 | port))) | ||
1099 | { | ||
1100 | MHD_stop_daemon (d); | ||
1101 | return 4; | ||
1102 | } | ||
1103 | |||
1104 | sock = wr_create_from_plain_sckt (tls_fork_sock); | ||
1105 | if (NULL == sock) | ||
1106 | abort (); | ||
1107 | #else /* !HTTPS_SUPPORT || !HAVE_FORK || !HAVE_WAITPID */ | ||
1108 | abort (); | ||
1109 | #endif /* !HTTPS_SUPPORT || !HAVE_FORK || !HAVE_WAITPID */ | ||
1110 | } | ||
1111 | |||
1112 | if (0 != pthread_create (&pt_client, | ||
1113 | NULL, | ||
1114 | &run_usock_client, | ||
1115 | sock)) | ||
1116 | abort (); | ||
1117 | if (0 == (flags & MHD_USE_INTERNAL_POLLING_THREAD) ) | ||
1118 | { | ||
1119 | enum MHD_FLAG flags; | ||
1120 | |||
1121 | /* make address sanitizer happy */ | ||
1122 | memcpy (&flags, | ||
1123 | real_flags /* ->flags */, | ||
1124 | sizeof (flags)); | ||
1125 | run_mhd_loop (d, flags); | ||
1126 | } | ||
1127 | pthread_join (pt_client, | ||
1128 | NULL); | ||
1129 | pthread_join (pt, | ||
1130 | NULL); | ||
1131 | #if defined(HTTPS_SUPPORT) && defined(HAVE_FORK) && defined(HAVE_WAITPID) | ||
1132 | if (test_tls && TLS_LIB_GNUTLS != use_tls_tool) | ||
1133 | waitpid (pid, NULL, 0); | ||
1134 | #endif /* HTTPS_SUPPORT && HAVE_FORK && HAVE_WAITPID */ | ||
1135 | MHD_stop_daemon (d); | ||
1136 | return 0; | ||
1137 | } | ||
1138 | |||
1139 | |||
1140 | int | ||
1141 | main (int argc, | ||
1142 | char *const *argv) | ||
1143 | { | ||
1144 | int error_count = 0; | ||
1145 | int res; | ||
1146 | |||
1147 | use_tls_tool = TLS_CLI_NO_TOOL; | ||
1148 | test_tls = has_in_name(argv[0], "_tls"); | ||
1149 | |||
1150 | verbose = 1; | ||
1151 | if (has_param(argc, argv, "-q") || | ||
1152 | has_param(argc, argv, "--quiet")) | ||
1153 | verbose = 0; | ||
1154 | |||
1155 | if (test_tls) | ||
1156 | { | ||
1157 | #ifdef HTTPS_SUPPORT | ||
1158 | if (has_param(argc, argv, "--use-gnutls-cli")) | ||
1159 | use_tls_tool = TLS_CLI_GNUTLS; | ||
1160 | else if (has_param(argc, argv, "--use-openssl")) | ||
1161 | use_tls_tool = TLS_CLI_OPENSSL; | ||
1162 | else if (has_param(argc, argv, "--use-gnutls-lib")) | ||
1163 | use_tls_tool = TLS_LIB_GNUTLS; | ||
1164 | #if defined(HAVE_FORK) && defined(HAVE_WAITPID) | ||
1165 | else if (0 == system ("gnutls-cli --version 1> /dev/null 2> /dev/null")) | ||
1166 | use_tls_tool = TLS_CLI_GNUTLS; | ||
1167 | else if (0 == system ("openssl version 1> /dev/null 2> /dev/null")) | ||
1168 | use_tls_tool = TLS_CLI_OPENSSL; | ||
1169 | #endif /* HAVE_FORK && HAVE_WAITPID */ | ||
1170 | else | ||
1171 | use_tls_tool = TLS_LIB_GNUTLS; /* Should be available as MHD use it. */ | ||
1172 | if (verbose) | ||
1173 | { | ||
1174 | switch (use_tls_tool) | ||
1175 | { | ||
1176 | case TLS_CLI_GNUTLS: | ||
1177 | printf ("GnuTLS-CLI will be used for testing.\n"); | ||
1178 | break; | ||
1179 | case TLS_CLI_OPENSSL: | ||
1180 | printf ("Command line version of OpenSSL will be used for testing.\n"); | ||
1181 | break; | ||
1182 | case TLS_LIB_GNUTLS: | ||
1183 | printf ("GnuTLS library will be used for testing.\n"); | ||
1184 | break; | ||
1185 | default: | ||
1186 | abort (); | ||
1187 | } | ||
1188 | } | ||
1189 | if ( (TLS_LIB_GNUTLS == use_tls_tool) && | ||
1190 | (GNUTLS_E_SUCCESS != gnutls_global_init()) ) | ||
1191 | abort (); | ||
1192 | |||
1193 | #else /* ! HTTPS_SUPPORT */ | ||
1194 | fprintf (stderr, "HTTPS support was disabled by configure.\n"); | ||
1195 | return 77; | ||
1196 | #endif /* ! HTTPS_SUPPORT */ | ||
1197 | } | ||
1198 | |||
1199 | /* run tests */ | ||
1200 | if (verbose) | ||
1201 | printf ("Starting HTTP \"Upgrade\" tests with %s connections.\n", | ||
1202 | test_tls ? "TLS" : "plain"); | ||
1203 | /* try external select */ | ||
1204 | res = test_upgrade (0, | ||
1205 | 0); | ||
1206 | error_count += res; | ||
1207 | if (res) | ||
1208 | fprintf (stderr, | ||
1209 | "FAILED: Upgrade with external select, return code %d.\n", | ||
1210 | res); | ||
1211 | else if (verbose) | ||
1212 | printf ("PASSED: Upgrade with external select.\n"); | ||
1213 | |||
1214 | /* Try external auto */ | ||
1215 | res = test_upgrade (MHD_USE_AUTO, | ||
1216 | 0); | ||
1217 | error_count += res; | ||
1218 | if (res) | ||
1219 | fprintf (stderr, | ||
1220 | "FAILED: Upgrade with external 'auto', return code %d.\n", | ||
1221 | res); | ||
1222 | else if (verbose) | ||
1223 | printf ("PASSED: Upgrade with external 'auto'.\n"); | ||
1224 | |||
1225 | #ifdef EPOLL_SUPPORT | ||
1226 | res = test_upgrade (MHD_USE_EPOLL, | ||
1227 | 0); | ||
1228 | error_count += res; | ||
1229 | if (res) | ||
1230 | fprintf (stderr, | ||
1231 | "FAILED: Upgrade with external select with EPOLL, return code %d.\n", | ||
1232 | res); | ||
1233 | else if (verbose) | ||
1234 | printf ("PASSED: Upgrade with external select with EPOLL.\n"); | ||
1235 | #endif | ||
1236 | |||
1237 | /* Test thread-per-connection */ | ||
1238 | res = test_upgrade (MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_THREAD_PER_CONNECTION, | ||
1239 | 0); | ||
1240 | error_count += res; | ||
1241 | if (res) | ||
1242 | fprintf (stderr, | ||
1243 | "FAILED: Upgrade with thread per connection, return code %d.\n", | ||
1244 | res); | ||
1245 | else if (verbose) | ||
1246 | printf ("PASSED: Upgrade with thread per connection.\n"); | ||
1247 | |||
1248 | res = test_upgrade (MHD_USE_AUTO | MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_THREAD_PER_CONNECTION, | ||
1249 | 0); | ||
1250 | error_count += res; | ||
1251 | if (res) | ||
1252 | fprintf (stderr, | ||
1253 | "FAILED: Upgrade with thread per connection and 'auto', return code %d.\n", | ||
1254 | res); | ||
1255 | else if (verbose) | ||
1256 | printf ("PASSED: Upgrade with thread per connection and 'auto'.\n"); | ||
1257 | #ifdef HAVE_POLL | ||
1258 | res = test_upgrade (MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_THREAD_PER_CONNECTION | MHD_USE_POLL, | ||
1259 | 0); | ||
1260 | error_count += res; | ||
1261 | if (res) | ||
1262 | fprintf (stderr, | ||
1263 | "FAILED: Upgrade with thread per connection and poll, return code %d.\n", | ||
1264 | res); | ||
1265 | else if (verbose) | ||
1266 | printf ("PASSED: Upgrade with thread per connection and poll.\n"); | ||
1267 | #endif /* HAVE_POLL */ | ||
1268 | |||
1269 | /* Test different event loops, with and without thread pool */ | ||
1270 | res = test_upgrade (MHD_USE_INTERNAL_POLLING_THREAD, | ||
1271 | 0); | ||
1272 | error_count += res; | ||
1273 | if (res) | ||
1274 | fprintf (stderr, | ||
1275 | "FAILED: Upgrade with internal select, return code %d.\n", | ||
1276 | res); | ||
1277 | else if (verbose) | ||
1278 | printf ("PASSED: Upgrade with internal select.\n"); | ||
1279 | res = test_upgrade (MHD_USE_INTERNAL_POLLING_THREAD, | ||
1280 | 2); | ||
1281 | error_count += res; | ||
1282 | if (res) | ||
1283 | fprintf (stderr, | ||
1284 | "FAILED: Upgrade with internal select with thread pool, return code %d.\n", | ||
1285 | res); | ||
1286 | else if (verbose) | ||
1287 | printf ("PASSED: Upgrade with internal select with thread pool.\n"); | ||
1288 | res = test_upgrade (MHD_USE_AUTO | MHD_USE_INTERNAL_POLLING_THREAD, | ||
1289 | 0); | ||
1290 | error_count += res; | ||
1291 | if (res) | ||
1292 | fprintf (stderr, | ||
1293 | "FAILED: Upgrade with internal 'auto' return code %d.\n", | ||
1294 | res); | ||
1295 | else if (verbose) | ||
1296 | printf ("PASSED: Upgrade with internal 'auto'.\n"); | ||
1297 | res = test_upgrade (MHD_USE_AUTO | MHD_USE_INTERNAL_POLLING_THREAD, | ||
1298 | 2); | ||
1299 | error_count += res; | ||
1300 | if (res) | ||
1301 | fprintf (stderr, | ||
1302 | "FAILED: Upgrade with internal 'auto' with thread pool, return code %d.\n", | ||
1303 | res); | ||
1304 | else if (verbose) | ||
1305 | printf ("PASSED: Upgrade with internal 'auto' with thread pool.\n"); | ||
1306 | #ifdef HAVE_POLL | ||
1307 | res = test_upgrade (MHD_USE_POLL_INTERNAL_THREAD, | ||
1308 | 0); | ||
1309 | error_count += res; | ||
1310 | if (res) | ||
1311 | fprintf (stderr, | ||
1312 | "FAILED: Upgrade with internal poll, return code %d.\n", | ||
1313 | res); | ||
1314 | else if (verbose) | ||
1315 | printf ("PASSED: Upgrade with internal poll.\n"); | ||
1316 | res = test_upgrade (MHD_USE_POLL_INTERNAL_THREAD, | ||
1317 | 2); | ||
1318 | if (res) | ||
1319 | fprintf (stderr, | ||
1320 | "FAILED: Upgrade with internal poll with thread pool, return code %d.\n", | ||
1321 | res); | ||
1322 | else if (verbose) | ||
1323 | printf ("PASSED: Upgrade with internal poll with thread pool.\n"); | ||
1324 | #endif | ||
1325 | #ifdef EPOLL_SUPPORT | ||
1326 | res = test_upgrade (MHD_USE_EPOLL_INTERNAL_THREAD, | ||
1327 | 0); | ||
1328 | if (res) | ||
1329 | fprintf (stderr, | ||
1330 | "FAILED: Upgrade with internal epoll, return code %d.\n", | ||
1331 | res); | ||
1332 | else if (verbose) | ||
1333 | printf ("PASSED: Upgrade with internal epoll.\n"); | ||
1334 | res = test_upgrade (MHD_USE_EPOLL_INTERNAL_THREAD, | ||
1335 | 2); | ||
1336 | if (res) | ||
1337 | fprintf (stderr, | ||
1338 | "FAILED: Upgrade with internal epoll, return code %d.\n", | ||
1339 | res); | ||
1340 | else if (verbose) | ||
1341 | printf ("PASSED: Upgrade with internal epoll.\n"); | ||
1342 | #endif | ||
1343 | /* report result */ | ||
1344 | if (0 != error_count) | ||
1345 | fprintf (stderr, | ||
1346 | "Error (code: %u)\n", | ||
1347 | error_count); | ||
1348 | #ifdef HTTPS_SUPPORT | ||
1349 | if (test_tls && (TLS_LIB_GNUTLS == use_tls_tool)) | ||
1350 | gnutls_global_deinit(); | ||
1351 | #endif /* HTTPS_SUPPORT */ | ||
1352 | return error_count != 0; /* 0 == pass */ | ||
1353 | } | ||