aboutsummaryrefslogtreecommitdiff
path: root/src/microhttpd
diff options
context:
space:
mode:
Diffstat (limited to 'src/microhttpd')
-rw-r--r--src/microhttpd/connection.c61
-rw-r--r--src/microhttpd/connection.h10
-rw-r--r--src/microhttpd/daemon.c342
-rw-r--r--src/microhttpd/internal.h33
-rw-r--r--src/microhttpd/response.c97
-rw-r--r--src/microhttpd/test_upgrade.c5
-rw-r--r--src/microhttpd/test_upgrade_ssl.c5
7 files changed, 323 insertions, 230 deletions
diff --git a/src/microhttpd/connection.c b/src/microhttpd/connection.c
index 52b67b05..d3ccdf26 100644
--- a/src/microhttpd/connection.c
+++ b/src/microhttpd/connection.c
@@ -22,6 +22,7 @@
22 * @brief Methods for managing connections 22 * @brief Methods for managing connections
23 * @author Daniel Pittman 23 * @author Daniel Pittman
24 * @author Christian Grothoff 24 * @author Christian Grothoff
25 * @author Karlson2k (Evgeny Grin)
25 */ 26 */
26 27
27#include "internal.h" 28#include "internal.h"
@@ -527,6 +528,63 @@ MHD_connection_close_ (struct MHD_Connection *connection,
527 528
528 529
529/** 530/**
531 * Stop TLS forwarding on upgraded connection and
532 * reflect remote disconnect state to socketpair.
533 * @remark In thread-per-connection mode this function
534 * can be called from any thread, in other modes this
535 * function must be called only from thread that process
536 * daemon's select()/poll()/etc.
537 *
538 * @param connection the upgraded connection
539 */
540void
541MHD_connection_finish_forward_ (struct MHD_Connection *connection)
542{
543 struct MHD_Daemon *daemon = connection->daemon;
544 struct MHD_UpgradeResponseHandle *urh = connection->urh;
545
546 if (0 == (daemon->options & MHD_USE_TLS))
547 return; /* Nothing to do with non-TLS connection. */
548
549 if (0 == (daemon->options & MHD_USE_THREAD_PER_CONNECTION))
550 DLL_remove (daemon->urh_head,
551 daemon->urh_tail,
552 urh);
553#if EPOLL_SUPPORT
554 if ( (0 != (daemon->options & MHD_USE_EPOLL)) &&
555 (0 != epoll_ctl (daemon->epoll_upgrade_fd,
556 EPOLL_CTL_DEL,
557 connection->socket_fd,
558 NULL)) )
559 {
560 MHD_PANIC (_("Failed to remove FD from epoll set\n"));
561 }
562#endif /* EPOLL_SUPPORT */
563 if (MHD_INVALID_SOCKET != urh->mhd.socket)
564 {
565#if EPOLL_SUPPORT
566 if ( (0 != (daemon->options & MHD_USE_EPOLL)) &&
567 (0 != epoll_ctl (daemon->epoll_upgrade_fd,
568 EPOLL_CTL_DEL,
569 urh->mhd.socket,
570 NULL)) )
571 {
572 MHD_PANIC (_("Failed to remove FD from epoll set\n"));
573 }
574#endif /* EPOLL_SUPPORT */
575 /* Reflect remote disconnect to application by breaking
576 * socketpair connection. */
577 shutdown (urh->mhd.socket, SHUT_RDWR);
578 }
579 /* Socketpair sockets will remain open as they will be
580 * used with MHD_UPGRADE_ACTION_CLOSE. They will be
581 * closed by MHD_cleanup_upgraded_connection_() during
582 * connection's final cleanup.
583 */
584}
585
586
587/**
530 * A serious error occured, close the 588 * A serious error occured, close the
531 * connection (and notify the application). 589 * connection (and notify the application).
532 * 590 *
@@ -3080,8 +3138,9 @@ MHD_connection_handle_idle (struct MHD_Connection *connection)
3080 cleanup_connection (connection); 3138 cleanup_connection (connection);
3081 return MHD_NO; 3139 return MHD_NO;
3082 case MHD_CONNECTION_UPGRADE: 3140 case MHD_CONNECTION_UPGRADE:
3083 case MHD_CONNECTION_UPGRADE_CLOSED:
3084 return MHD_YES; /* keep open */ 3141 return MHD_YES; /* keep open */
3142 case MHD_CONNECTION_UPGRADE_CLOSED:
3143 return MHD_YES; /* "Upgraded" connection should be closed in special way. */
3085 default: 3144 default:
3086 EXTRA_CHECK (0); 3145 EXTRA_CHECK (0);
3087 break; 3146 break;
diff --git a/src/microhttpd/connection.h b/src/microhttpd/connection.h
index 0fce7346..a962a40c 100644
--- a/src/microhttpd/connection.h
+++ b/src/microhttpd/connection.h
@@ -22,6 +22,7 @@
22 * @brief Methods for managing connections 22 * @brief Methods for managing connections
23 * @author Daniel Pittman 23 * @author Daniel Pittman
24 * @author Christian Grothoff 24 * @author Christian Grothoff
25 * @author Karlson2k (Evgeny Grin)
25 */ 26 */
26 27
27#ifndef CONNECTION_H 28#ifndef CONNECTION_H
@@ -97,6 +98,15 @@ MHD_connection_close_ (struct MHD_Connection *connection,
97 enum MHD_RequestTerminationCode termination_code); 98 enum MHD_RequestTerminationCode termination_code);
98 99
99 100
101/**
102 * Stop TLS forwarding on upgraded connection and
103 * reflect remote disconnect state to socketpair.
104 * @param connection the upgraded connection
105 */
106void
107MHD_connection_finish_forward_ (struct MHD_Connection *connection);
108
109
100#ifdef EPOLL_SUPPORT 110#ifdef EPOLL_SUPPORT
101/** 111/**
102 * Perform epoll processing, possibly moving the connection back into 112 * Perform epoll processing, possibly moving the connection back into
diff --git a/src/microhttpd/daemon.c b/src/microhttpd/daemon.c
index 2bce6d15..622ab34e 100644
--- a/src/microhttpd/daemon.c
+++ b/src/microhttpd/daemon.c
@@ -23,6 +23,7 @@
23 * @brief A minimal-HTTP server library 23 * @brief A minimal-HTTP server library
24 * @author Daniel Pittman 24 * @author Daniel Pittman
25 * @author Christian Grothoff 25 * @author Christian Grothoff
26 * @author Karlson2k (Evgeny Grin)
26 */ 27 */
27#include "platform.h" 28#include "platform.h"
28#include "mhd_threads.h" 29#include "mhd_threads.h"
@@ -688,6 +689,7 @@ urh_to_fdset (struct MHD_UpgradeResponseHandle *urh,
688 fd_setsize)) ) 689 fd_setsize)) )
689 return MHD_NO; 690 return MHD_NO;
690 if ( (0 == (MHD_EPOLL_STATE_WRITE_READY & urh->mhd.celi)) && 691 if ( (0 == (MHD_EPOLL_STATE_WRITE_READY & urh->mhd.celi)) &&
692 (MHD_NO == urh->was_closed) &&
691 (MHD_INVALID_SOCKET != urh->mhd.socket) && 693 (MHD_INVALID_SOCKET != urh->mhd.socket) &&
692 (! MHD_add_to_fd_set_ (urh->mhd.socket, 694 (! MHD_add_to_fd_set_ (urh->mhd.socket,
693 ws, 695 ws,
@@ -695,6 +697,7 @@ urh_to_fdset (struct MHD_UpgradeResponseHandle *urh,
695 fd_setsize)) ) 697 fd_setsize)) )
696 return MHD_NO; 698 return MHD_NO;
697 if ( (urh->in_buffer_used < urh->in_buffer_size) && 699 if ( (urh->in_buffer_used < urh->in_buffer_size) &&
700 (MHD_NO == urh->was_closed) &&
698 (MHD_INVALID_SOCKET != urh->connection->socket_fd) && 701 (MHD_INVALID_SOCKET != urh->connection->socket_fd) &&
699 (! MHD_add_to_fd_set_ (urh->connection->socket_fd, 702 (! MHD_add_to_fd_set_ (urh->connection->socket_fd,
700 rs, 703 rs,
@@ -934,73 +937,19 @@ call_handlers (struct MHD_Connection *con,
934void 937void
935MHD_cleanup_upgraded_connection_ (struct MHD_Connection *connection) 938MHD_cleanup_upgraded_connection_ (struct MHD_Connection *connection)
936{ 939{
937 struct MHD_Daemon *daemon = connection->daemon;
938 struct MHD_UpgradeResponseHandle *urh = connection->urh; 940 struct MHD_UpgradeResponseHandle *urh = connection->urh;
939 941
940#if HTTPS_SUPPORT 942 /* Signal remote client the end of TLS connection by
941 if ( (NULL != urh) && 943 * gracefully closing TLS session. */
942 (0 != (daemon->options & MHD_USE_TLS)) ) 944 if (0 != (connection->daemon->options & MHD_USE_TLS))
943 { 945 gnutls_bye (connection->tls_session,
944 if (0 == (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) 946 GNUTLS_SHUT_WR);
945 DLL_remove (daemon->urh_head, 947
946 daemon->urh_tail, 948 if (MHD_INVALID_SOCKET != urh->mhd.socket)
947 urh); 949 MHD_socket_close_chk_ (urh->mhd.socket);
948#if EPOLL_SUPPORT 950
949 if (0 != (daemon->options & MHD_USE_EPOLL)) 951 if (MHD_INVALID_SOCKET != urh->app.socket)
950 { 952 MHD_socket_close_chk_ (urh->app.socket);
951 /* epoll documentation suggests that closing a FD
952 automatically removes it from the epoll set; however,
953 this is not true as if we fail to do manually remove it,
954 we are still seeing an event for this fd in epoll,
955 causing grief (use-after-free...) --- at least on my
956 system. */
957 if (0 != epoll_ctl (daemon->epoll_upgrade_fd,
958 EPOLL_CTL_DEL,
959 connection->socket_fd,
960 NULL))
961 MHD_PANIC (_("Failed to remove FD from epoll set\n"));
962 }
963#endif /* EPOLL_SUPPORT */
964 if (MHD_INVALID_SOCKET != urh->mhd.socket)
965 {
966 /* epoll documentation suggests that closing a FD
967 automatically removes it from the epoll set; however,
968 this is not true as if we fail to do manually remove it,
969 we are still seeing an event for this fd in epoll,
970 causing grief (use-after-free...) --- at least on my
971 system. */
972#if EPOLL_SUPPORT
973 if ( (0 != (daemon->options & MHD_USE_EPOLL)) &&
974 (0 != epoll_ctl (daemon->epoll_upgrade_fd,
975 EPOLL_CTL_DEL,
976 urh->mhd.socket,
977 NULL)) )
978 MHD_PANIC (_("Failed to remove FD from epoll set\n"));
979#endif /* EPOLL_SUPPORT */
980 MHD_socket_close_chk_ (urh->mhd.socket);
981 }
982 if (MHD_INVALID_SOCKET != urh->app.socket)
983 MHD_socket_close_chk_ (urh->app.socket);
984 }
985#endif /* HTTPS_SUPPORT */
986 if (0 == (daemon->options & MHD_USE_THREAD_PER_CONNECTION))
987 {
988 /* resuming the connection will indirectly close it */
989 MHD_resume_connection (connection);
990#if HTTPS_SUPPORT
991 if (0 != (daemon->options & MHD_USE_TLS))
992 {
993 gnutls_bye (connection->tls_session,
994 GNUTLS_SHUT_RDWR);
995 }
996#endif
997 }
998 else
999 {
1000 /* the thread would no longer close it */
1001 MHD_connection_close_ (connection,
1002 MHD_REQUEST_TERMINATED_COMPLETED_OK);
1003 }
1004 953
1005 connection->urh = NULL; 954 connection->urh = NULL;
1006 if (NULL != urh) 955 if (NULL != urh)
@@ -1259,14 +1208,6 @@ process_urh (struct MHD_UpgradeResponseHandle *urh)
1259 urh->app.celi &= ~MHD_EPOLL_STATE_WRITE_READY; 1208 urh->app.celi &= ~MHD_EPOLL_STATE_WRITE_READY;
1260 urh->mhd.celi &= ~MHD_EPOLL_STATE_READ_READY; 1209 urh->mhd.celi &= ~MHD_EPOLL_STATE_READ_READY;
1261 } 1210 }
1262
1263 /* cleanup connection if it was closed and all data was sent */
1264 if ( (MHD_NO != urh->was_closed) &&
1265 (0 == urh->out_buffer_size) &&
1266 (0 == urh->out_buffer_used) )
1267 {
1268 MHD_cleanup_upgraded_connection_ (urh->connection);
1269 }
1270} 1211}
1271#endif 1212#endif
1272 1213
@@ -1292,7 +1233,8 @@ thread_main_connection_upgrade (struct MHD_Connection *con)
1292 if ( (0 != (daemon->options & MHD_USE_TLS)) && 1233 if ( (0 != (daemon->options & MHD_USE_TLS)) &&
1293 (0 == (daemon->options & MHD_USE_POLL))) 1234 (0 == (daemon->options & MHD_USE_POLL)))
1294 { 1235 {
1295 while (MHD_CONNECTION_UPGRADE == con->state) 1236 while ( (MHD_CONNECTION_UPGRADE == con->state) ||
1237 (0 != urh->out_buffer_used) )
1296 { 1238 {
1297 /* use select */ 1239 /* use select */
1298 fd_set rs; 1240 fd_set rs;
@@ -1356,7 +1298,8 @@ thread_main_connection_upgrade (struct MHD_Connection *con)
1356 /* use poll() */ 1298 /* use poll() */
1357 const unsigned int timeout = UINT_MAX; 1299 const unsigned int timeout = UINT_MAX;
1358 1300
1359 while (MHD_CONNECTION_UPGRADE == con->state) 1301 while ( (MHD_CONNECTION_UPGRADE == con->state) ||
1302 (0 != urh->out_buffer_used) )
1360 { 1303 {
1361 struct pollfd p[2]; 1304 struct pollfd p[2];
1362 1305
@@ -1410,6 +1353,10 @@ thread_main_connection_upgrade (struct MHD_Connection *con)
1410#endif 1353#endif
1411 /* end HTTPS */ 1354 /* end HTTPS */
1412#endif 1355#endif
1356 /* TLS forwarding was finished. Cleanup socketpair. */
1357 MHD_connection_finish_forward_ (con);
1358 /* Do not set 'urh->clean_ready' yet as 'urh' will be used
1359 * in connection thread for a little while. */
1413} 1360}
1414 1361
1415 1362
@@ -1456,7 +1403,7 @@ thread_main_handle_connection (void *data)
1456 const unsigned int timeout = daemon->connection_timeout; 1403 const unsigned int timeout = daemon->connection_timeout;
1457 _MHD_bool was_suspended = 0; 1404 _MHD_bool was_suspended = 0;
1458 1405
1459 if (MHD_NO != con->suspended) 1406 if (MHD_NO != con->suspended && NULL == con->urh)
1460 { 1407 {
1461 /* Connection was suspended, wait for resume. */ 1408 /* Connection was suspended, wait for resume. */
1462 was_suspended = !0; 1409 was_suspended = !0;
@@ -1721,7 +1668,11 @@ thread_main_handle_connection (void *data)
1721 goto exit; 1668 goto exit;
1722 } 1669 }
1723#endif 1670#endif
1724 if (MHD_CONNECTION_UPGRADE == con->state) 1671 /* Check for 'MHD_CONNECTION_UPGRADE_CLOSED' too:
1672 * application can finish with "upgraded" connection
1673 * before this thread process it for the first time. */
1674 if ( (MHD_CONNECTION_UPGRADE == con->state) ||
1675 (MHD_CONNECTION_UPGRADE_CLOSED == con->state) )
1725 { 1676 {
1726 /* Normal HTTP processing is finished, 1677 /* Normal HTTP processing is finished,
1727 * notify application. */ 1678 * notify application. */
@@ -1734,13 +1685,17 @@ thread_main_handle_connection (void *data)
1734 con->client_aware = MHD_NO; 1685 con->client_aware = MHD_NO;
1735 1686
1736 thread_main_connection_upgrade (con); 1687 thread_main_connection_upgrade (con);
1737#if HTTPS_SUPPORT 1688 /* MHD_connection_finish_forward_() was called by thread_main_connection_upgrade(). */
1738 if (0 != (daemon->options & MHD_USE_TLS) ) 1689
1739 break; 1690 /* "Upgraded" data will not be used in this thread from this point. */
1740#endif 1691 con->urh->clean_ready = MHD_YES;
1692 /* If 'urh->was_closed' set to MHD_YES, connection will be
1693 * moved immediately to cleanup list. Otherwise connection
1694 * will stay in suspended list until 'urh' will be marked
1695 * with 'was_closed' by application. */
1696 MHD_resume_connection(con);
1741 1697
1742 /* skip usual clean up EXCEPT for the completion 1698 /* skip usual clean up */
1743 notification (which must be done in this thread)! */
1744 return (MHD_THRD_RTRN_TYPE_) 0; 1699 return (MHD_THRD_RTRN_TYPE_) 0;
1745 } 1700 }
1746 } 1701 }
@@ -2459,6 +2414,8 @@ MHD_resume_connection (struct MHD_Connection *connection)
2459/** 2414/**
2460 * Run through the suspended connections and move any that are no 2415 * Run through the suspended connections and move any that are no
2461 * longer suspended back to the active state. 2416 * longer suspended back to the active state.
2417 * @remark To be called only from thread that process
2418 * daemon's select()/poll()/etc.
2462 * 2419 *
2463 * @param daemon daemon context 2420 * @param daemon daemon context
2464 * @return #MHD_YES if a connection was actually resumed 2421 * @return #MHD_YES if a connection was actually resumed
@@ -2490,40 +2447,55 @@ resume_suspended_connections (struct MHD_Daemon *daemon)
2490 while (NULL != (pos = next)) 2447 while (NULL != (pos = next))
2491 { 2448 {
2492 next = pos->next; 2449 next = pos->next;
2493 if (MHD_NO == pos->resuming) 2450 if ( (MHD_NO == pos->resuming) ||
2451 ((NULL != pos->urh) &&
2452 ((MHD_NO == pos->urh->was_closed) || (MHD_NO == pos->urh->clean_ready))) )
2494 continue; 2453 continue;
2495 ret = MHD_YES; 2454 ret = MHD_YES;
2496 DLL_remove (daemon->suspended_connections_head, 2455 DLL_remove (daemon->suspended_connections_head,
2497 daemon->suspended_connections_tail, 2456 daemon->suspended_connections_tail,
2498 pos); 2457 pos);
2499 DLL_insert (daemon->connections_head, 2458 if (NULL == pos->urh)
2500 daemon->connections_tail,
2501 pos);
2502 if (0 == (daemon->options & MHD_USE_THREAD_PER_CONNECTION))
2503 { 2459 {
2504 if (pos->connection_timeout == daemon->connection_timeout) 2460 DLL_insert (daemon->connections_head,
2505 XDLL_insert (daemon->normal_timeout_head, 2461 daemon->connections_tail,
2506 daemon->normal_timeout_tail, 2462 pos);
2507 pos); 2463 if (0 == (daemon->options & MHD_USE_THREAD_PER_CONNECTION))
2508 else 2464 {
2509 XDLL_insert (daemon->manual_timeout_head, 2465 if (pos->connection_timeout == daemon->connection_timeout)
2510 daemon->manual_timeout_tail, 2466 XDLL_insert (daemon->normal_timeout_head,
2511 pos); 2467 daemon->normal_timeout_tail,
2512 } 2468 pos);
2469 else
2470 XDLL_insert (daemon->manual_timeout_head,
2471 daemon->manual_timeout_tail,
2472 pos);
2473 }
2513#ifdef EPOLL_SUPPORT 2474#ifdef EPOLL_SUPPORT
2514 if (0 != (daemon->options & MHD_USE_EPOLL)) 2475 if (0 != (daemon->options & MHD_USE_EPOLL))
2476 {
2477 if (0 != (pos->epoll_state & MHD_EPOLL_STATE_IN_EREADY_EDLL))
2478 MHD_PANIC ("Resumed connection was already in EREADY set\n");
2479 /* we always mark resumed connections as ready, as we
2480 might have missed the edge poll event during suspension */
2481 EDLL_insert (daemon->eready_head,
2482 daemon->eready_tail,
2483 pos);
2484 pos->epoll_state |= MHD_EPOLL_STATE_IN_EREADY_EDLL;
2485 pos->epoll_state &= ~MHD_EPOLL_STATE_SUSPENDED;
2486 }
2487#endif
2488 }
2489 else
2515 { 2490 {
2516 if (0 != (pos->epoll_state & MHD_EPOLL_STATE_IN_EREADY_EDLL)) 2491 /* Data forwarding was finished (for TLS connections) AND
2517 MHD_PANIC ("Resumed connection was already in EREADY set\n"); 2492 * application was closed upgraded connection.
2518 /* we always mark resumed connections as ready, as we 2493 * Insert connection into cleanup list. */
2519 might have missed the edge poll event during suspension */ 2494 DLL_insert (daemon->cleanup_head,
2520 EDLL_insert (daemon->eready_head, 2495 daemon->cleanup_tail,
2521 daemon->eready_tail, 2496 pos);
2522 pos); 2497
2523 pos->epoll_state |= MHD_EPOLL_STATE_IN_EREADY_EDLL;
2524 pos->epoll_state &= ~MHD_EPOLL_STATE_SUSPENDED;
2525 } 2498 }
2526#endif
2527 pos->suspended = MHD_NO; 2499 pos->suspended = MHD_NO;
2528 pos->resuming = MHD_NO; 2500 pos->resuming = MHD_NO;
2529 } 2501 }
@@ -2735,6 +2707,8 @@ MHD_accept_connection (struct MHD_Daemon *daemon)
2735 * Free resources associated with all closed connections. 2707 * Free resources associated with all closed connections.
2736 * (destroy responses, free buffers, etc.). All closed 2708 * (destroy responses, free buffers, etc.). All closed
2737 * connections are kept in the "cleanup" doubly-linked list. 2709 * connections are kept in the "cleanup" doubly-linked list.
2710 * @remark To be called only from thread that
2711 * process daemon's select()/poll()/etc.
2738 * 2712 *
2739 * @param daemon daemon to clean up 2713 * @param daemon daemon to clean up
2740 */ 2714 */
@@ -2758,6 +2732,8 @@ MHD_cleanup_connections (struct MHD_Daemon *daemon)
2758 (MHD_NO == pos->thread_joined) && 2732 (MHD_NO == pos->thread_joined) &&
2759 (! MHD_join_thread_ (pos->pid)) ) 2733 (! MHD_join_thread_ (pos->pid)) )
2760 MHD_PANIC (_("Failed to join a thread\n")); 2734 MHD_PANIC (_("Failed to join a thread\n"));
2735 if (NULL != pos->urh)
2736 MHD_cleanup_upgraded_connection_ (pos);
2761 MHD_pool_destroy (pos->pool); 2737 MHD_pool_destroy (pos->pool);
2762#if HTTPS_SUPPORT 2738#if HTTPS_SUPPORT
2763 if (NULL != pos->tls_session) 2739 if (NULL != pos->tls_session)
@@ -3014,6 +2990,17 @@ MHD_run_from_select (struct MHD_Daemon *daemon,
3014 write_fd_set); 2990 write_fd_set);
3015 /* call generic forwarding function for passing data */ 2991 /* call generic forwarding function for passing data */
3016 process_urh (urh); 2992 process_urh (urh);
2993 /* Finished forwarding? */
2994 if ( (0 == urh->in_buffer_size) &&
2995 (0 == urh->out_buffer_size) &&
2996 (0 == urh->in_buffer_used) &&
2997 (0 == urh->out_buffer_used) )
2998 {
2999 MHD_connection_finish_forward_ (urh->connection);
3000 urh->clean_ready = MHD_YES;
3001 /* Resuming will move connection to cleanup list. */
3002 MHD_resume_connection(urh->connection);
3003 }
3017 } 3004 }
3018#endif 3005#endif
3019 MHD_cleanup_connections (daemon); 3006 MHD_cleanup_connections (daemon);
@@ -3367,28 +3354,45 @@ MHD_poll_all (struct MHD_Daemon *daemon,
3367 { 3354 {
3368 if (i >= num_connections) 3355 if (i >= num_connections)
3369 break; /* connection list changed somehow, retry later ... */ 3356 break; /* connection list changed somehow, retry later ... */
3357
3358 /* Get next connection here as connection can be removed
3359 * from 'daemon->urh_head' list. */
3370 urhn = urh->next; 3360 urhn = urh->next;
3371 if (p[poll_server+i].fd != urh->connection->socket_fd) 3361 /* Check for fd mismatch. FIXME: required for safety? */
3372 continue; /* fd mismatch, something else happened, retry later ... */ 3362 if (p[poll_server+i].fd == urh->connection->socket_fd)
3373 if (0 != (p[poll_server+i].revents & POLLIN)) 3363 {
3374 urh->app.celi |= MHD_EPOLL_STATE_READ_READY; 3364 if (0 != (p[poll_server+i].revents & POLLIN))
3375 if (0 != (p[poll_server+i].revents & POLLOUT)) 3365 urh->app.celi |= MHD_EPOLL_STATE_READ_READY;
3376 urh->app.celi |= MHD_EPOLL_STATE_WRITE_READY; 3366 if (0 != (p[poll_server+i].revents & POLLOUT))
3367 urh->app.celi |= MHD_EPOLL_STATE_WRITE_READY;
3368 }
3377 i++; 3369 i++;
3378 if (p[poll_server+i].fd != urh->mhd.socket) 3370 /* Check for fd mismatch. FIXME: required for safety? */
3371 if (p[poll_server+i].fd == urh->mhd.socket)
3379 { 3372 {
3380 /* fd mismatch, something else happened, retry later ... */ 3373 if (0 != (p[poll_server+i].revents & POLLIN))
3381 /* may still be able to do something based on updates 3374 urh->mhd.celi |= MHD_EPOLL_STATE_READ_READY;
3382 to socket_fd availability */ 3375 if (0 != (p[poll_server+i].revents & POLLOUT))
3383 process_urh (urh); 3376 urh->mhd.celi |= MHD_EPOLL_STATE_WRITE_READY;
3384 continue;
3385 } 3377 }
3386 if (0 != (p[poll_server+i].revents & POLLIN))
3387 urh->mhd.celi |= MHD_EPOLL_STATE_READ_READY;
3388 if (0 != (p[poll_server+i].revents & POLLOUT))
3389 urh->mhd.celi |= MHD_EPOLL_STATE_WRITE_READY;
3390 i++; 3378 i++;
3391 process_urh (urh); 3379 process_urh (urh);
3380 /* Finished forwarding? */
3381 if ( (0 == urh->in_buffer_size) &&
3382 (0 == urh->out_buffer_size) &&
3383 (0 == urh->in_buffer_used) &&
3384 (0 == urh->out_buffer_used) )
3385 {
3386 /* MHD_connection_finish_forward_() will remove connection from
3387 * 'daemon->urh_head' list. */
3388 MHD_connection_finish_forward_ (urh->connection);
3389 urh->clean_ready = MHD_YES;
3390 /* If 'urh->was_closed' set to MHD_YES, connection will be
3391 * moved immediately to cleanup list. Otherwise connection
3392 * will stay in suspended list until 'urh' will be marked
3393 * with 'was_closed' by application. */
3394 MHD_resume_connection(urh->connection);
3395 }
3392 } 3396 }
3393#endif 3397#endif
3394 /* handle 'listen' FD */ 3398 /* handle 'listen' FD */
@@ -3530,12 +3534,11 @@ run_epoll_for_upgrade (struct MHD_Daemon *daemon)
3530{ 3534{
3531 struct epoll_event events[MAX_EVENTS]; 3535 struct epoll_event events[MAX_EVENTS];
3532 int num_events; 3536 int num_events;
3533 unsigned int i;
3534 unsigned int j;
3535 3537
3536 num_events = MAX_EVENTS; 3538 num_events = MAX_EVENTS;
3537 while (MAX_EVENTS == num_events) 3539 while (MAX_EVENTS == num_events)
3538 { 3540 {
3541 unsigned int i;
3539 /* update event masks */ 3542 /* update event masks */
3540 num_events = epoll_wait (daemon->epoll_upgrade_fd, 3543 num_events = epoll_wait (daemon->epoll_upgrade_fd,
3541 events, 3544 events,
@@ -3555,35 +3558,8 @@ run_epoll_for_upgrade (struct MHD_Daemon *daemon)
3555 } 3558 }
3556 for (i=0;i<(unsigned int) num_events;i++) 3559 for (i=0;i<(unsigned int) num_events;i++)
3557 { 3560 {
3558 struct UpgradeEpollHandle *ueh = events[i].data.ptr; 3561 struct UpgradeEpollHandle * const ueh = events[i].data.ptr;
3559 struct MHD_UpgradeResponseHandle *urh; 3562 struct MHD_UpgradeResponseHandle * const urh = ueh->urh;
3560
3561 if (NULL == ueh)
3562 continue; /* was killed, see below */
3563 urh = ueh->urh;
3564
3565 /* In case we get two events for the same upgrade handle,
3566 squash them together (otherwise the first one may
3567 cause us to free the 'urh', and the second one then
3568 causes a use-after-free). */
3569 for (j=i+1;j< (unsigned int) num_events;j++)
3570 {
3571 struct UpgradeEpollHandle *uehj = events[j].data.ptr;
3572 struct MHD_UpgradeResponseHandle *urhj;
3573
3574 if (NULL == uehj)
3575 continue; /* was killed, see below */
3576 urhj = uehj->urh;
3577
3578 if (urh == urhj) /* yep, indeed the same! */
3579 {
3580 if (0 != (events[j].events & EPOLLIN))
3581 uehj->celi |= MHD_EPOLL_STATE_READ_READY;
3582 if (0 != (events[j].events & EPOLLOUT))
3583 uehj->celi |= MHD_EPOLL_STATE_WRITE_READY;
3584 }
3585 events[j].data.ptr = NULL; /* kill this one */
3586 }
3587 3563
3588 /* Update our state based on what is ready according to epoll() */ 3564 /* Update our state based on what is ready according to epoll() */
3589 if (0 != (events[i].events & EPOLLIN)) 3565 if (0 != (events[i].events & EPOLLIN))
@@ -3591,8 +3567,21 @@ run_epoll_for_upgrade (struct MHD_Daemon *daemon)
3591 if (0 != (events[i].events & EPOLLOUT)) 3567 if (0 != (events[i].events & EPOLLOUT))
3592 ueh->celi |= MHD_EPOLL_STATE_WRITE_READY; 3568 ueh->celi |= MHD_EPOLL_STATE_WRITE_READY;
3593 3569
3594 /* shuffle data based on buffers and FD readyness */
3595 process_urh (urh); 3570 process_urh (urh);
3571 /* Finished forwarding? */
3572 if ( (0 == urh->in_buffer_size) &&
3573 (0 == urh->out_buffer_size) &&
3574 (0 == urh->in_buffer_used) &&
3575 (0 == urh->out_buffer_used) )
3576 {
3577 MHD_connection_finish_forward_ (urh->connection);
3578 urh->clean_ready = MHD_YES;
3579 /* If 'urh->was_closed' set to MHD_YES, connection will be
3580 * moved immediately to cleanup list. Otherwise connection
3581 * will stay in suspended list until 'urh' will be marked
3582 * with 'was_closed' by application. */
3583 MHD_resume_connection(urh->connection);
3584 }
3596 } 3585 }
3597 } 3586 }
3598 return MHD_YES; 3587 return MHD_YES;
@@ -5403,18 +5392,23 @@ close_all_connections (struct MHD_Daemon *daemon)
5403#ifdef HTTPS_SUPPORT 5392#ifdef HTTPS_SUPPORT
5404 struct MHD_UpgradeResponseHandle *urh; 5393 struct MHD_UpgradeResponseHandle *urh;
5405 struct MHD_UpgradeResponseHandle *urhn; 5394 struct MHD_UpgradeResponseHandle *urhn;
5395 const _MHD_bool used_tls = (0 != (daemon->options & MHD_USE_TLS));
5396 const _MHD_bool used_thr_p_c = (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION));
5406#endif /* HTTPS_SUPPORT */ 5397#endif /* HTTPS_SUPPORT */
5407 5398
5399#ifdef HTTPS_SUPPORT
5408 /* give upgraded HTTPS connections a chance to finish */ 5400 /* give upgraded HTTPS connections a chance to finish */
5409#if HTTPS_SUPPORT 5401 /* 'daemon->urh_head' is not used in thread-per-connection mode. */
5410 for (urh = daemon->urh_head; NULL != urh; urh = urhn) 5402 for (urh = daemon->urh_head; NULL != urh; urh = urhn)
5411 { 5403 {
5412 urhn = urh->next; 5404 urhn = urh->next;
5413 /* call generic forwarding function for passing data 5405 /* call generic forwarding function for passing data
5414 with chance to detect that application is done; 5406 with chance to detect that application is done. */
5415 fake read readyness just to be sure. */
5416 urh->mhd.celi |= MHD_EPOLL_STATE_READ_READY;
5417 process_urh (urh); 5407 process_urh (urh);
5408 MHD_connection_finish_forward_ (urh->connection);
5409 urh->clean_ready = MHD_YES;
5410 /* Resuming will move connection to cleanup list. */
5411 MHD_resume_connection(urh->connection);
5418 } 5412 }
5419#endif 5413#endif
5420 5414
@@ -5431,6 +5425,23 @@ close_all_connections (struct MHD_Daemon *daemon)
5431 traverse DLLs in peace... */ 5425 traverse DLLs in peace... */
5432 if (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) 5426 if (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION))
5433 MHD_mutex_lock_chk_ (&daemon->cleanup_connection_mutex); 5427 MHD_mutex_lock_chk_ (&daemon->cleanup_connection_mutex);
5428#ifdef HTTPS_SUPPORT
5429 if (used_tls && used_thr_p_c)
5430 {
5431 struct MHD_Connection * susp;
5432
5433 susp = daemon->suspended_connections_head;
5434 while (NULL != susp)
5435 {
5436 if (NULL == susp->urh) /* "Upgraded" connection? */
5437 MHD_PANIC (_("MHD_stop_daemon() called while we have suspended connections.\n"));
5438 else if (MHD_NO == susp->urh->clean_ready)
5439 shutdown (urh->app.socket, SHUT_RDWR); /* Wake thread by shutdown of app socket. */
5440 susp = susp->next;
5441 }
5442 }
5443 else /* This 'else' is combined with next 'if'. */
5444#endif /* HTTPS_SUPPORT */
5434 if (NULL != daemon->suspended_connections_head) 5445 if (NULL != daemon->suspended_connections_head)
5435 MHD_PANIC (_("MHD_stop_daemon() called while we have suspended connections.\n")); 5446 MHD_PANIC (_("MHD_stop_daemon() called while we have suspended connections.\n"));
5436 for (pos = daemon->connections_head; NULL != pos; pos = pos->next) 5447 for (pos = daemon->connections_head; NULL != pos; pos = pos->next)
@@ -5465,6 +5476,17 @@ close_all_connections (struct MHD_Daemon *daemon)
5465 pos = pos->next; 5476 pos = pos->next;
5466 } 5477 }
5467 } 5478 }
5479
5480#ifdef HTTPS_SUPPORT
5481 /* Finished threads with "upgraded" connections need to be moved
5482 * to cleanup list by resume_suspended_connections(). */
5483 if (used_tls && used_thr_p_c)
5484 {
5485 daemon->resuming = MHD_YES; /* Force check for pending resume. */
5486 resume_suspended_connections (daemon);
5487 }
5488#endif /* HTTPS_SUPPORT */
5489
5468 /* now that we're alone, move everyone to cleanup */ 5490 /* now that we're alone, move everyone to cleanup */
5469 while (NULL != (pos = daemon->connections_head)) 5491 while (NULL != (pos = daemon->connections_head))
5470 { 5492 {
diff --git a/src/microhttpd/internal.h b/src/microhttpd/internal.h
index 1f239f10..bb9974a9 100644
--- a/src/microhttpd/internal.h
+++ b/src/microhttpd/internal.h
@@ -1056,8 +1056,37 @@ struct MHD_UpgradeResponseHandle
1056 /** 1056 /**
1057 * Set to #MHD_YES after the application finished with the socket 1057 * Set to #MHD_YES after the application finished with the socket
1058 * by #MHD_UPGRADE_ACTION_CLOSE. 1058 * by #MHD_UPGRADE_ACTION_CLOSE.
1059 *
1060 * When BOTH @e was_closed (changed by command from application)
1061 * AND @e clean_ready (changed internally by MHD) are set to
1062 * #MHD_YES, function #MHD_resume_connection() will move this
1063 * connection to cleanup list.
1064 * @remark This flag could be changed from any thread.
1059 */ 1065 */
1060 int was_closed; 1066 int was_closed;
1067
1068 /**
1069 * Set to #MHD_YES if connection is ready for cleanup.
1070 *
1071 * In TLS mode functions #MHD_connection_finish_forward_() must
1072 * be called before setting this flag to #MHD_YES.
1073 *
1074 * In thread-per-connection mode #MHD_YES in this flag means
1075 * that connection's thread exited or about to exit and will
1076 * not use MHD_Connection::urh data anymore.
1077 *
1078 * In any mode #MHD_YES in this flag also means that
1079 * MHD_Connection::urh data will not be used for socketpair
1080 * forwarding and forwarding itself is finished.
1081 *
1082 * When BOTH @e was_closed (changed by command from application)
1083 * AND @e clean_ready (changed internally by MHD) are set to
1084 * #MHD_YES, function #MHD_resume_connection() will move this
1085 * connection to cleanup list.
1086 * @remark This flag could be changed from thread that process
1087 * connection's recv(), send() and response.
1088 */
1089 int clean_ready;
1061}; 1090};
1062 1091
1063 1092
@@ -1410,11 +1439,15 @@ struct MHD_Daemon
1410#if HTTPS_SUPPORT 1439#if HTTPS_SUPPORT
1411 /** 1440 /**
1412 * Head of DLL of upgrade response handles we are processing. 1441 * Head of DLL of upgrade response handles we are processing.
1442 * Used for upgraded TLS connections when thread-per-connection
1443 * is not used.
1413 */ 1444 */
1414 struct MHD_UpgradeResponseHandle *urh_head; 1445 struct MHD_UpgradeResponseHandle *urh_head;
1415 1446
1416 /** 1447 /**
1417 * Tail of DLL of upgrade response handles we are processing. 1448 * Tail of DLL of upgrade response handles we are processing.
1449 * Used for upgraded TLS connections when thread-per-connection
1450 * is not used.
1418 */ 1451 */
1419 struct MHD_UpgradeResponseHandle *urh_tail; 1452 struct MHD_UpgradeResponseHandle *urh_tail;
1420 1453
diff --git a/src/microhttpd/response.c b/src/microhttpd/response.c
index b0fffe19..db77dc6d 100644
--- a/src/microhttpd/response.c
+++ b/src/microhttpd/response.c
@@ -21,6 +21,7 @@
21 * @brief Methods for managing response objects 21 * @brief Methods for managing response objects
22 * @author Daniel Pittman 22 * @author Daniel Pittman
23 * @author Christian Grothoff 23 * @author Christian Grothoff
24 * @author Karlson2k (Evgeny Grin)
24 */ 25 */
25 26
26#define MHD_NO_DEPRECATION 1 27#define MHD_NO_DEPRECATION 1
@@ -626,66 +627,41 @@ MHD_upgrade_action (struct MHD_UpgradeResponseHandle *urh,
626 enum MHD_UpgradeAction action, 627 enum MHD_UpgradeAction action,
627 ...) 628 ...)
628{ 629{
629 struct MHD_Connection *connection = urh->connection; 630 struct MHD_Connection *connection;
630 struct MHD_Daemon *daemon = connection->daemon; 631 struct MHD_Daemon *daemon;
632 if (NULL == urh)
633 return MHD_NO;
634 connection = urh->connection;
635
636 /* Precaution checks on external data. */
637 if (NULL == connection)
638 return MHD_NO;
639 daemon = connection->daemon;
640 if (NULL == daemon)
641 return MHD_NO;
631 642
632 switch (action) 643 switch (action)
633 { 644 {
634 case MHD_UPGRADE_ACTION_CLOSE: 645 case MHD_UPGRADE_ACTION_CLOSE:
646 if (MHD_YES == urh->was_closed)
647 return MHD_NO; /* Already closed. */
648
635 /* transition to special 'closed' state for start of cleanup */ 649 /* transition to special 'closed' state for start of cleanup */
650 urh->was_closed = MHD_YES;
636 connection->state = MHD_CONNECTION_UPGRADE_CLOSED; 651 connection->state = MHD_CONNECTION_UPGRADE_CLOSED;
652 /* As soon as connection will be marked with BOTH
653 * 'urh->was_closed' AND 'urh->clean_ready', it will
654 * be moved to cleanup list by MHD_resume_connection(). */
655 MHD_resume_connection (connection);
637#if HTTPS_SUPPORT 656#if HTTPS_SUPPORT
638 if (0 != (daemon->options & MHD_USE_TLS) ) 657 if (0 != (daemon->options & MHD_USE_TLS) )
639 { 658 {
640 /* signal that app is done by shutdown() of 'app' socket */ 659 /* signal that app is done by shutdown() of 'app' socket */
660 /* Application will not use anyway this socket after this command. */
641 shutdown (urh->app.socket, 661 shutdown (urh->app.socket,
642 SHUT_RDWR); 662 SHUT_RDWR);
643 } 663 }
644#endif 664#endif /* HTTPS_SUPPORT */
645#if HTTPS_SUPPORT
646 if (0 != (daemon->options & MHD_USE_TLS) )
647 {
648 urh->was_closed = MHD_YES;
649 /* connection and urh cleanup will be done as soon as outgoing
650 * data will be sent and 'was_closed' is detected */
651 return MHD_YES;
652 }
653#endif
654 /* Application is done with this connection, tear it down! */
655 if (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION) )
656 {
657 /* need to finish connection clean up */
658 MHD_cleanup_upgraded_connection_ (connection);
659 if (MHD_CONNECTION_IN_CLEANUP != connection->state)
660 {
661#if DEBUG_CLOSE
662#ifdef HAVE_MESSAGES
663 MHD_DLOG (connection->daemon,
664 _("Processing thread terminating. Closing connection\n"));
665#endif
666#endif
667 if (MHD_CONNECTION_CLOSED != connection->state)
668 MHD_connection_close_ (connection,
669 MHD_REQUEST_TERMINATED_DAEMON_SHUTDOWN);
670 connection->idle_handler (connection);
671 }
672 if (NULL != connection->response)
673 {
674 MHD_destroy_response (connection->response);
675 connection->response = NULL;
676 }
677
678 if (MHD_INVALID_SOCKET != connection->socket_fd)
679 {
680 shutdown (connection->socket_fd,
681 SHUT_WR);
682 MHD_socket_close_chk_ (connection->socket_fd);
683 connection->socket_fd = MHD_INVALID_SOCKET;
684 }
685 return MHD_YES;
686 }
687 /* 'upgraded' resources are not needed anymore - cleanup now */
688 MHD_cleanup_upgraded_connection_ (connection);
689 return MHD_YES; 665 return MHD_YES;
690 default: 666 default:
691 /* we don't understand this one */ 667 /* we don't understand this one */
@@ -905,30 +881,25 @@ MHD_response_execute_upgrade_ (struct MHD_Response *response,
905 DLL_insert (daemon->urh_head, 881 DLL_insert (daemon->urh_head,
906 daemon->urh_tail, 882 daemon->urh_tail,
907 urh); 883 urh);
908 /* Keep reference for later removal from the DLL */
909 connection->urh = urh;
910 } 884 }
885 /* In thread-per-connection mode, thread will switch to forwarding once
886 * connection.urh is not NULL and connection.state == MHD_CONNECTION_UPGRADE.
887 */
911 } 888 }
912 else 889 else
913 { 890 {
914 urh->app.socket = MHD_INVALID_SOCKET; 891 urh->app.socket = MHD_INVALID_SOCKET;
915 urh->mhd.socket = MHD_INVALID_SOCKET; 892 urh->mhd.socket = MHD_INVALID_SOCKET;
893 /* Non-TLS connection do not hold any additional resources. */
894 urh->clean_ready = MHD_YES;
916 } 895 }
917#endif 896#endif
918 if (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION) ) 897 connection->urh = urh;
919 { 898 /* As far as MHD's event loops are concerned, this connection is
920 /* Our caller will set 'connection->state' to 899 suspended; it will be resumed once application is done by the
921 MHD_CONNECTION_UPGRADE, thereby triggering the main method 900 #MHD_upgrade_action() function */
922 of the thread to switch to bi-directional forwarding or exit. */ 901 MHD_suspend_connection (connection);
923 connection->urh = urh; 902
924 }
925 else
926 {
927 /* As far as MHD's event loops are concerned, this connection is
928 suspended; it will be resumed once we are done in the
929 #MHD_upgrade_action() function */
930 MHD_suspend_connection (connection);
931 }
932 /* hand over socket to application */ 903 /* hand over socket to application */
933 response->upgrade_handler (response->upgrade_handler_cls, 904 response->upgrade_handler (response->upgrade_handler_cls,
934 connection, 905 connection,
diff --git a/src/microhttpd/test_upgrade.c b/src/microhttpd/test_upgrade.c
index b369fd70..965ecc00 100644
--- a/src/microhttpd/test_upgrade.c
+++ b/src/microhttpd/test_upgrade.c
@@ -60,9 +60,8 @@ test_upgrade (int flags,
60 struct sockaddr_in sa; 60 struct sockaddr_in sa;
61 61
62 done = 0; 62 done = 0;
63 if (0 == (flags & MHD_USE_THREAD_PER_CONNECTION)) 63
64 flags |= MHD_USE_SUSPEND_RESUME; 64 d = MHD_start_daemon (flags | MHD_USE_DEBUG | MHD_USE_SUSPEND_RESUME,
65 d = MHD_start_daemon (flags | MHD_USE_DEBUG,
66 1080, 65 1080,
67 NULL, NULL, 66 NULL, NULL,
68 &ahc_upgrade, NULL, 67 &ahc_upgrade, NULL,
diff --git a/src/microhttpd/test_upgrade_ssl.c b/src/microhttpd/test_upgrade_ssl.c
index 07031cff..a72fad6d 100644
--- a/src/microhttpd/test_upgrade_ssl.c
+++ b/src/microhttpd/test_upgrade_ssl.c
@@ -138,9 +138,8 @@ test_upgrade (int flags,
138 pid_t pid; 138 pid_t pid;
139 139
140 done = 0; 140 done = 0;
141 if (0 == (flags & MHD_USE_THREAD_PER_CONNECTION)) 141
142 flags |= MHD_USE_SUSPEND_RESUME; 142 d = MHD_start_daemon (flags | MHD_USE_DEBUG | MHD_USE_SUSPEND_RESUME |MHD_USE_TLS,
143 d = MHD_start_daemon (flags | MHD_USE_DEBUG | MHD_USE_TLS,
144 1080, 143 1080,
145 NULL, NULL, 144 NULL, NULL,
146 &ahc_upgrade, NULL, 145 &ahc_upgrade, NULL,