aboutsummaryrefslogtreecommitdiff
path: root/src/microhttpd
diff options
context:
space:
mode:
Diffstat (limited to 'src/microhttpd')
-rw-r--r--src/microhttpd/Makefile.am2
-rw-r--r--src/microhttpd/connection.c4
-rw-r--r--src/microhttpd/daemon.c546
-rw-r--r--src/microhttpd/internal.h26
-rw-r--r--src/microhttpd/mhd_locks.h47
-rw-r--r--src/microhttpd/mhd_sem.c138
-rw-r--r--src/microhttpd/response.c78
7 files changed, 659 insertions, 182 deletions
diff --git a/src/microhttpd/Makefile.am b/src/microhttpd/Makefile.am
index 6973af8a..9a32f598 100644
--- a/src/microhttpd/Makefile.am
+++ b/src/microhttpd/Makefile.am
@@ -63,7 +63,7 @@ libmicrohttpd_la_SOURCES = \
63 sysfdsetsize.c sysfdsetsize.h \ 63 sysfdsetsize.c sysfdsetsize.h \
64 mhd_str.c mhd_str.h \ 64 mhd_str.c mhd_str.h \
65 mhd_threads.c mhd_threads.h \ 65 mhd_threads.c mhd_threads.h \
66 mhd_locks.h \ 66 mhd_locks.h mhd_sem.c \
67 mhd_sockets.c mhd_sockets.h \ 67 mhd_sockets.c mhd_sockets.h \
68 mhd_itc.c mhd_itc.h \ 68 mhd_itc.c mhd_itc.h \
69 mhd_compat.c mhd_compat.h \ 69 mhd_compat.c mhd_compat.h \
diff --git a/src/microhttpd/connection.c b/src/microhttpd/connection.c
index e3880e8a..0c21bc57 100644
--- a/src/microhttpd/connection.c
+++ b/src/microhttpd/connection.c
@@ -413,7 +413,8 @@ MHD_set_connection_value (struct MHD_Connection *connection,
413 */ 413 */
414const char * 414const char *
415MHD_lookup_connection_value (struct MHD_Connection *connection, 415MHD_lookup_connection_value (struct MHD_Connection *connection,
416 enum MHD_ValueKind kind, const char *key) 416 enum MHD_ValueKind kind,
417 const char *key)
417{ 418{
418 struct MHD_HTTP_Header *pos; 419 struct MHD_HTTP_Header *pos;
419 420
@@ -2772,7 +2773,6 @@ MHD_connection_handle_idle (struct MHD_Connection *connection)
2772 /* Buffering for flushable socket was already enabled*/ 2773 /* Buffering for flushable socket was already enabled*/
2773 if (MHD_NO == socket_flush_possible (connection)) 2774 if (MHD_NO == socket_flush_possible (connection))
2774 socket_start_no_buffering (connection); 2775 socket_start_no_buffering (connection);
2775
2776 break; 2776 break;
2777 } 2777 }
2778 /* not ready, no socket action */ 2778 /* not ready, no socket action */
diff --git a/src/microhttpd/daemon.c b/src/microhttpd/daemon.c
index 791e17d4..a298a1a4 100644
--- a/src/microhttpd/daemon.c
+++ b/src/microhttpd/daemon.c
@@ -636,6 +636,79 @@ MHD_get_fdset (struct MHD_Daemon *daemon,
636 636
637 637
638/** 638/**
639 * Obtain the select() file descriptor sets for the
640 * given @a urh.
641 *
642 * @param urh upgrade handle to wait for
643 * @param[out] rs read set to initialize
644 * @param[out] ws write set to initialize
645 * @param[out] max_fd maximum FD to update
646 * @param fd_setsize value of FD_SETSIZE
647 * @return #MHD_YES on success, #MHD_NO on error
648 */
649static int
650urh_to_fdset (struct MHD_UpgradeResponseHandle *urh,
651 fd_set *rs,
652 fd_set *ws,
653 MHD_socket *max_fd,
654 unsigned int fd_setsize)
655{
656 if ( (0 == (MHD_EPOLL_STATE_READ_READY & urh->mhd.celi)) &&
657 (! MHD_add_to_fd_set_ (urh->mhd.socket,
658 rs,
659 max_fd,
660 fd_setsize)) )
661 return MHD_NO;
662 if ( (0 != (MHD_EPOLL_STATE_WRITE_READY & urh->mhd.celi)) &&
663 (! MHD_add_to_fd_set_ (urh->mhd.socket,
664 ws,
665 max_fd,
666 fd_setsize)) )
667 return MHD_NO;
668 if ( (0 != (MHD_EPOLL_STATE_READ_READY & urh->app.celi)) &&
669 (! MHD_add_to_fd_set_ (urh->connection->socket_fd,
670 rs,
671 max_fd,
672 fd_setsize)) )
673 return MHD_NO;
674 if ( (0 != (MHD_EPOLL_STATE_WRITE_READY & urh->app.celi)) &&
675 (! MHD_add_to_fd_set_ (urh->connection->socket_fd,
676 ws,
677 max_fd,
678 fd_setsize)) )
679 return MHD_NO;
680 return MHD_YES;
681}
682
683
684/**
685 * Update the @a urh based on the ready FDs in the @a rs and @a ws.
686 *
687 * @param urh upgrade handle to update
688 * @param rs read result from select()
689 * @param ws write result from select()
690 */
691static void
692urh_from_fdset (struct MHD_UpgradeResponseHandle *urh,
693 const fd_set *rs,
694 const fd_set *ws)
695{
696 if (FD_ISSET (urh->connection->socket_fd,
697 rs))
698 urh->app.celi |= MHD_EPOLL_STATE_READ_READY;
699 if (FD_ISSET (urh->connection->socket_fd,
700 ws))
701 urh->app.celi |= MHD_EPOLL_STATE_WRITE_READY;
702 if (FD_ISSET (urh->mhd.socket,
703 rs))
704 urh->mhd.celi |= MHD_EPOLL_STATE_READ_READY;
705 if (FD_ISSET (urh->mhd.socket,
706 ws))
707 urh->mhd.celi |= MHD_EPOLL_STATE_WRITE_READY;
708}
709
710
711/**
639 * Obtain the `select()` sets for this daemon. 712 * Obtain the `select()` sets for this daemon.
640 * Daemon's FDs will be added to fd_sets. To get only 713 * Daemon's FDs will be added to fd_sets. To get only
641 * daemon FDs in fd_sets, call FD_ZERO for each fd_set 714 * daemon FDs in fd_sets, call FD_ZERO for each fd_set
@@ -733,29 +806,12 @@ MHD_get_fdset2 (struct MHD_Daemon *daemon,
733 } 806 }
734 for (urh = daemon->urh_head; NULL != urh; urh = urh->next) 807 for (urh = daemon->urh_head; NULL != urh; urh = urh->next)
735 { 808 {
736 if ( (0 == (MHD_EPOLL_STATE_READ_READY & urh->mhd.celi)) && 809 if (MHD_NO ==
737 (! MHD_add_to_fd_set_ (urh->mhd.socket, 810 urh_to_fdset (urh,
738 read_fd_set, 811 read_fd_set,
739 max_fd, 812 write_fd_set,
740 fd_setsize)) ) 813 max_fd,
741 result = MHD_NO; 814 fd_setsize))
742 if ( (0 != (MHD_EPOLL_STATE_WRITE_READY & urh->mhd.celi)) &&
743 (! MHD_add_to_fd_set_ (urh->mhd.socket,
744 write_fd_set,
745 max_fd,
746 fd_setsize)) )
747 result = MHD_NO;
748 if ( (0 != (MHD_EPOLL_STATE_READ_READY & urh->app.celi)) &&
749 (! MHD_add_to_fd_set_ (urh->connection->socket_fd,
750 read_fd_set,
751 max_fd,
752 fd_setsize)) )
753 result = MHD_NO;
754 if ( (0 != (MHD_EPOLL_STATE_WRITE_READY & urh->app.celi)) &&
755 (! MHD_add_to_fd_set_ (urh->connection->socket_fd,
756 write_fd_set,
757 max_fd,
758 fd_setsize)) )
759 result = MHD_NO; 815 result = MHD_NO;
760 } 816 }
761#if DEBUG_CONNECT 817#if DEBUG_CONNECT
@@ -825,6 +881,251 @@ call_handlers (struct MHD_Connection *con,
825} 881}
826 882
827 883
884#if HTTPS_SUPPORT
885/**
886 * Performs bi-directional forwarding on upgraded HTTPS connections
887 * based on the readyness state stored in the @a urh handle.
888 *
889 * @param urh handle to process
890 */
891static void
892process_urh (struct MHD_UpgradeResponseHandle *urh)
893{
894 /* handle reading from TLS client and writing to application */
895 if ( (0 != (MHD_EPOLL_STATE_READ_READY & urh->app.celi)) &&
896 (urh->in_buffer_off < urh->in_buffer_size) )
897 {
898 ssize_t res;
899
900 res = gnutls_record_recv (urh->connection->tls_session,
901 &urh->in_buffer[urh->in_buffer_off],
902 urh->in_buffer_size - urh->in_buffer_off);
903 if ( (GNUTLS_E_AGAIN == res) ||
904 (GNUTLS_E_INTERRUPTED == res) )
905 {
906 urh->app.celi &= ~MHD_EPOLL_STATE_READ_READY;
907 }
908 else if (res > 0)
909 {
910 urh->in_buffer_off += res;
911 }
912 }
913 if ( (0 != (MHD_EPOLL_STATE_WRITE_READY & urh->mhd.celi)) &&
914 (urh->in_buffer_off > 0) )
915 {
916 size_t res;
917
918 res = write (urh->mhd.socket,
919 urh->in_buffer,
920 urh->in_buffer_off);
921 if (-1 == res)
922 {
923 /* FIXME: differenciate by errno? */
924 urh->mhd.celi &= ~MHD_EPOLL_STATE_WRITE_READY;
925 }
926 else
927 {
928 if (urh->in_buffer_off != res)
929 {
930 memmove (urh->in_buffer,
931 &urh->in_buffer[res],
932 urh->in_buffer_off - res);
933 urh->in_buffer_off -= res;
934 }
935 else
936 {
937 urh->in_buffer_off = 0;
938 }
939 }
940 }
941
942 /* handle reading from application and writing to HTTPS client */
943 if ( (0 != (MHD_EPOLL_STATE_READ_READY & urh->mhd.celi)) &&
944 (urh->out_buffer_off < urh->out_buffer_size) )
945 {
946 size_t res;
947
948 res = read (urh->mhd.socket,
949 &urh->out_buffer[urh->out_buffer_off],
950 urh->out_buffer_size - urh->out_buffer_off);
951 if (-1 == res)
952 {
953 /* FIXME: differenciate by errno? */
954 urh->mhd.celi &= ~MHD_EPOLL_STATE_READ_READY;
955 }
956 else
957 {
958 urh->out_buffer_off += res;
959 }
960 }
961 if ( (0 != (MHD_EPOLL_STATE_WRITE_READY & urh->app.celi)) &&
962 (urh->out_buffer_off > 0) )
963 {
964 ssize_t res;
965
966 res = gnutls_record_send (urh->connection->tls_session,
967 urh->out_buffer,
968 urh->out_buffer_off);
969 if ( (GNUTLS_E_AGAIN == res) ||
970 (GNUTLS_E_INTERRUPTED == res) )
971 {
972 urh->app.celi &= ~MHD_EPOLL_STATE_WRITE_READY;
973 }
974 else if (res > 0)
975 {
976 if (urh->out_buffer_off != res)
977 {
978 memmove (urh->out_buffer,
979 &urh->out_buffer[res],
980 urh->out_buffer_off - res);
981 urh->out_buffer_off -= res;
982 }
983 else
984 {
985 urh->out_buffer_off = 0;
986 }
987 }
988 }
989}
990#endif
991
992
993/**
994 * Main function of the thread that handles an individual connection
995 * after it was "upgraded" when #MHD_USE_THREAD_PER_CONNECTION is set.
996 *
997 * @param con the connection this thread will handle
998 */
999static void
1000thread_main_connection_upgrade (struct MHD_Connection *con)
1001{
1002 struct MHD_Daemon *daemon = con->daemon;
1003
1004 if (0 == (daemon->options & MHD_USE_SSL))
1005 {
1006 /* Here, we need to block until the application
1007 signals us that it is done with the socket */
1008 MHD_semaphore_down (con->upgrade_sem);
1009 MHD_semaphore_destroy (con->upgrade_sem);
1010 con->upgrade_sem = NULL;
1011 return;
1012 }
1013#if HTTPS_SUPPORT
1014 {
1015 struct MHD_UpgradeResponseHandle *urh = con->urh;
1016
1017 /* Here, we need to bi-directionally forward
1018 until the application tells us that it is done
1019 with the socket; */
1020 if (0 == (daemon->options & MHD_USE_POLL))
1021 {
1022 while (MHD_CONNECTION_UPGRADE == con->state)
1023 {
1024 /* use select */
1025 fd_set rs;
1026 fd_set ws;
1027 MHD_socket max_fd;
1028 int num_ready;
1029 int result;
1030
1031 FD_ZERO (&rs);
1032 FD_ZERO (&ws);
1033 max_fd = MHD_INVALID_SOCKET;
1034 result = urh_to_fdset (urh,
1035 &rs,
1036 &ws,
1037 &max_fd,
1038 FD_SETSIZE);
1039 if (MHD_NO == result)
1040 {
1041#ifdef HAVE_MESSAGES
1042 MHD_DLOG (con->daemon,
1043 "Error preparing select\n");
1044#endif
1045 break;
1046 }
1047 num_ready = MHD_SYS_select_ (max_fd + 1,
1048 &rs,
1049 &ws,
1050 NULL,
1051 NULL);
1052 if (num_ready < 0)
1053 {
1054 const int err = MHD_socket_get_error_();
1055
1056 if (MHD_SCKT_ERR_IS_EINTR_(err))
1057 continue;
1058#ifdef HAVE_MESSAGES
1059 MHD_DLOG (con->daemon,
1060 "Error during select (%d): `%s'\n",
1061 err,
1062 MHD_socket_strerr_ (err));
1063#endif
1064 break;
1065 }
1066 urh_from_fdset (urh,
1067 &rs,
1068 &ws);
1069 process_urh (urh);
1070 }
1071 }
1072#ifdef HAVE_POLL
1073 else
1074 {
1075 /* use poll() */
1076 struct pollfd p[2];
1077 const unsigned int timeout = UINT_MAX;
1078
1079 p[0].fd = urh->connection->socket_fd;
1080 p[1].fd = urh->mhd.socket;
1081 while (MHD_CONNECTION_UPGRADE == con->state)
1082 {
1083 if (0 == (MHD_EPOLL_STATE_READ_READY & urh->app.celi))
1084 p[0].events |= POLLIN;
1085 if (0 == (MHD_EPOLL_STATE_WRITE_READY & urh->app.celi))
1086 p[0].events |= POLLOUT;
1087 if (0 == (MHD_EPOLL_STATE_READ_READY & urh->mhd.celi))
1088 p[1].events |= POLLIN;
1089 if (0 == (MHD_EPOLL_STATE_WRITE_READY & urh->mhd.celi))
1090 p[1].events |= POLLOUT;
1091
1092 if (MHD_sys_poll_ (p,
1093 2,
1094 timeout) < 0)
1095 {
1096 const int err = MHD_socket_get_error_ ();
1097
1098 if (MHD_SCKT_ERR_IS_EINTR_ (err))
1099 continue;
1100#ifdef HAVE_MESSAGES
1101 MHD_DLOG (con->daemon,
1102 "Error during poll: `%s'\n",
1103 MHD_socket_strerr_ (err));
1104#endif
1105 break;
1106 }
1107 if (0 != (p[0].revents & POLLIN))
1108 urh->app.celi |= MHD_EPOLL_STATE_READ_READY;
1109 if (0 != (p[0].revents & POLLOUT))
1110 urh->app.celi |= MHD_EPOLL_STATE_WRITE_READY;
1111 if (0 != (p[1].revents & POLLIN))
1112 urh->mhd.celi |= MHD_EPOLL_STATE_READ_READY;
1113 if (0 != (p[1].revents & POLLOUT))
1114 urh->mhd.celi |= MHD_EPOLL_STATE_WRITE_READY;
1115 process_urh (urh);
1116 }
1117 }
1118 /* end POLL */
1119#endif
1120 /* end HTTPS */
1121#else
1122 /* HTTPS option set, but compiled without HTTPS */
1123 MHD_PANIC ("This should not be possible\n");
1124#endif
1125 }
1126}
1127
1128
828/** 1129/**
829 * Main function of the thread that handles an individual 1130 * Main function of the thread that handles an individual
830 * connection when #MHD_USE_THREAD_PER_CONNECTION is set. 1131 * connection when #MHD_USE_THREAD_PER_CONNECTION is set.
@@ -836,6 +1137,7 @@ static MHD_THRD_RTRN_TYPE_ MHD_THRD_CALL_SPEC_
836thread_main_handle_connection (void *data) 1137thread_main_handle_connection (void *data)
837{ 1138{
838 struct MHD_Connection *con = data; 1139 struct MHD_Connection *con = data;
1140 struct MHD_Daemon *daemon = con->daemon;
839 int num_ready; 1141 int num_ready;
840 fd_set rs; 1142 fd_set rs;
841 fd_set ws; 1143 fd_set ws;
@@ -844,7 +1146,7 @@ thread_main_handle_connection (void *data)
844 struct timeval *tvp; 1146 struct timeval *tvp;
845 time_t now; 1147 time_t now;
846#if WINDOWS 1148#if WINDOWS
847 MHD_pipe spipe = con->daemon->wpipe[0]; 1149 MHD_pipe spipe = daemon->wpipe[0];
848#ifdef HAVE_POLL 1150#ifdef HAVE_POLL
849 int extra_slot; 1151 int extra_slot;
850#endif /* HAVE_POLL */ 1152#endif /* HAVE_POLL */
@@ -855,11 +1157,13 @@ thread_main_handle_connection (void *data)
855#ifdef HAVE_POLL 1157#ifdef HAVE_POLL
856 struct pollfd p[1 + EXTRA_SLOTS]; 1158 struct pollfd p[1 + EXTRA_SLOTS];
857#endif 1159#endif
1160#undef EXTRA_SLOTS
858 1161
859 while ( (MHD_YES != con->daemon->shutdown) && 1162 while ( (MHD_YES != daemon->shutdown) &&
860 (MHD_CONNECTION_CLOSED != con->state) ) 1163 (MHD_CONNECTION_CLOSED != con->state) )
861 { 1164 {
862 unsigned const int timeout = con->daemon->connection_timeout; 1165 const unsigned int timeout = daemon->connection_timeout;
1166
863 tvp = NULL; 1167 tvp = NULL;
864#if HTTPS_SUPPORT 1168#if HTTPS_SUPPORT
865 if (MHD_YES == con->tls_read_ready) 1169 if (MHD_YES == con->tls_read_ready)
@@ -870,7 +1174,8 @@ thread_main_handle_connection (void *data)
870 tvp = &tv; 1174 tvp = &tv;
871 } 1175 }
872#endif 1176#endif
873 if (NULL == tvp && timeout > 0) 1177 if ( (NULL == tvp) &&
1178 (timeout > 0) )
874 { 1179 {
875 now = MHD_monotonic_sec_counter(); 1180 now = MHD_monotonic_sec_counter();
876 if (now - con->last_activity > timeout) 1181 if (now - con->last_activity > timeout)
@@ -884,35 +1189,48 @@ thread_main_handle_connection (void *data)
884 if (seconds_left > TIMEVAL_TV_SEC_MAX) 1189 if (seconds_left > TIMEVAL_TV_SEC_MAX)
885 tv.tv_sec = TIMEVAL_TV_SEC_MAX; 1190 tv.tv_sec = TIMEVAL_TV_SEC_MAX;
886 else 1191 else
887 tv.tv_sec = (_MHD_TIMEVAL_TV_SEC_TYPE)seconds_left; 1192 tv.tv_sec = (_MHD_TIMEVAL_TV_SEC_TYPE) seconds_left;
888#endif /* _WIN32 */ 1193#endif /* _WIN32 */
889 } 1194 }
890 tv.tv_usec = 0; 1195 tv.tv_usec = 0;
891 tvp = &tv; 1196 tvp = &tv;
892 } 1197 }
893 if (0 == (con->daemon->options & MHD_USE_POLL)) 1198 if (0 == (daemon->options & MHD_USE_POLL))
894 { 1199 {
895 /* use select */ 1200 /* use select */
896 int err_state = 0; 1201 int err_state = 0;
1202
897 FD_ZERO (&rs); 1203 FD_ZERO (&rs);
898 FD_ZERO (&ws); 1204 FD_ZERO (&ws);
899 maxsock = MHD_INVALID_SOCKET; 1205 maxsock = MHD_INVALID_SOCKET;
900 switch (con->event_loop_info) 1206 switch (con->event_loop_info)
901 { 1207 {
902 case MHD_EVENT_LOOP_INFO_READ: 1208 case MHD_EVENT_LOOP_INFO_READ:
903 if (!MHD_add_to_fd_set_ (con->socket_fd, &rs, &maxsock, FD_SETSIZE)) 1209 if (! MHD_add_to_fd_set_ (con->socket_fd,
1210 &rs,
1211 &maxsock,
1212 FD_SETSIZE))
904 err_state = 1; 1213 err_state = 1;
905 break; 1214 break;
906 case MHD_EVENT_LOOP_INFO_WRITE: 1215 case MHD_EVENT_LOOP_INFO_WRITE:
907 if (!MHD_add_to_fd_set_ (con->socket_fd, &ws, &maxsock, FD_SETSIZE)) 1216 if (! MHD_add_to_fd_set_ (con->socket_fd,
1217 &ws,
1218 &maxsock,
1219 FD_SETSIZE))
908 err_state = 1; 1220 err_state = 1;
909 if ( (con->read_buffer_size > con->read_buffer_offset) && 1221 if ( (con->read_buffer_size > con->read_buffer_offset) &&
910 (!MHD_add_to_fd_set_ (con->socket_fd, &rs, &maxsock, FD_SETSIZE)) ) 1222 (! MHD_add_to_fd_set_ (con->socket_fd,
1223 &rs,
1224 &maxsock,
1225 FD_SETSIZE)) )
911 err_state = 1; 1226 err_state = 1;
912 break; 1227 break;
913 case MHD_EVENT_LOOP_INFO_BLOCK: 1228 case MHD_EVENT_LOOP_INFO_BLOCK:
914 if ( (con->read_buffer_size > con->read_buffer_offset) && 1229 if ( (con->read_buffer_size > con->read_buffer_offset) &&
915 (!MHD_add_to_fd_set_ (con->socket_fd, &rs, &maxsock, FD_SETSIZE)) ) 1230 (! MHD_add_to_fd_set_ (con->socket_fd,
1231 &rs,
1232 &maxsock,
1233 FD_SETSIZE)) )
916 err_state = 1; 1234 err_state = 1;
917 tv.tv_sec = 0; 1235 tv.tv_sec = 0;
918 tv.tv_usec = 0; 1236 tv.tv_usec = 0;
@@ -925,7 +1243,10 @@ thread_main_handle_connection (void *data)
925#if WINDOWS 1243#if WINDOWS
926 if (MHD_INVALID_PIPE_ != spipe) 1244 if (MHD_INVALID_PIPE_ != spipe)
927 { 1245 {
928 if (!MHD_add_to_fd_set_ (spipe, &rs, &maxsock, FD_SETSIZE)) 1246 if (! MHD_add_to_fd_set_ (spipe,
1247 &rs,
1248 &maxsock,
1249 FD_SETSIZE))
929 err_state = 1; 1250 err_state = 1;
930 } 1251 }
931#endif 1252#endif
@@ -938,10 +1259,15 @@ thread_main_handle_connection (void *data)
938 goto exit; 1259 goto exit;
939 } 1260 }
940 1261
941 num_ready = MHD_SYS_select_ (maxsock + 1, &rs, &ws, NULL, tvp); 1262 num_ready = MHD_SYS_select_ (maxsock + 1,
1263 &rs,
1264 &ws,
1265 NULL,
1266 tvp);
942 if (num_ready < 0) 1267 if (num_ready < 0)
943 { 1268 {
944 const int err = MHD_socket_get_error_(); 1269 const int err = MHD_socket_get_error_();
1270
945 if (MHD_SCKT_ERR_IS_EINTR_(err)) 1271 if (MHD_SCKT_ERR_IS_EINTR_(err))
946 continue; 1272 continue;
947#ifdef HAVE_MESSAGES 1273#ifdef HAVE_MESSAGES
@@ -960,8 +1286,10 @@ thread_main_handle_connection (void *data)
960#endif 1286#endif
961 if (MHD_NO == 1287 if (MHD_NO ==
962 call_handlers (con, 1288 call_handlers (con,
963 FD_ISSET (con->socket_fd, &rs), 1289 FD_ISSET (con->socket_fd,
964 FD_ISSET (con->socket_fd, &ws), 1290 &rs),
1291 FD_ISSET (con->socket_fd,
1292 &ws),
965 MHD_NO)) 1293 MHD_NO))
966 goto exit; 1294 goto exit;
967 } 1295 }
@@ -969,7 +1297,9 @@ thread_main_handle_connection (void *data)
969 else 1297 else
970 { 1298 {
971 /* use poll */ 1299 /* use poll */
972 memset (&p, 0, sizeof (p)); 1300 memset (&p,
1301 0,
1302 sizeof (p));
973 p[0].fd = con->socket_fd; 1303 p[0].fd = con->socket_fd;
974 switch (con->event_loop_info) 1304 switch (con->event_loop_info)
975 { 1305 {
@@ -1004,11 +1334,11 @@ thread_main_handle_connection (void *data)
1004#endif 1334#endif
1005 if (MHD_sys_poll_ (p, 1335 if (MHD_sys_poll_ (p,
1006#if WINDOWS 1336#if WINDOWS
1007 1 + extra_slot, 1337 1 + extra_slot,
1008#else 1338#else
1009 1, 1339 1,
1010#endif 1340#endif
1011 (NULL == tvp) ? -1 : tv.tv_sec * 1000) < 0) 1341 (NULL == tvp) ? -1 : tv.tv_sec * 1000) < 0)
1012 { 1342 {
1013 if (MHD_SCKT_LAST_ERR_IS_(MHD_SCKT_EINTR_)) 1343 if (MHD_SCKT_LAST_ERR_IS_(MHD_SCKT_EINTR_))
1014 continue; 1344 continue;
@@ -1033,6 +1363,11 @@ thread_main_handle_connection (void *data)
1033 goto exit; 1363 goto exit;
1034 } 1364 }
1035#endif 1365#endif
1366 if (MHD_CONNECTION_UPGRADE == con->state)
1367 {
1368 thread_main_connection_upgrade (con);
1369 break;
1370 }
1036 } 1371 }
1037 if (MHD_CONNECTION_IN_CLEANUP != con->state) 1372 if (MHD_CONNECTION_IN_CLEANUP != con->state)
1038 { 1373 {
@@ -1054,14 +1389,15 @@ exit:
1054 con->response = NULL; 1389 con->response = NULL;
1055 } 1390 }
1056 1391
1057 if (NULL != con->daemon->notify_connection) 1392 if (NULL != daemon->notify_connection)
1058 con->daemon->notify_connection (con->daemon->notify_connection_cls, 1393 con->daemon->notify_connection (daemon->notify_connection_cls,
1059 con, 1394 con,
1060 &con->socket_context, 1395 &con->socket_context,
1061 MHD_CONNECTION_NOTIFY_CLOSED); 1396 MHD_CONNECTION_NOTIFY_CLOSED);
1062 if (MHD_INVALID_SOCKET != con->socket_fd) 1397 if (MHD_INVALID_SOCKET != con->socket_fd)
1063 { 1398 {
1064 shutdown (con->socket_fd, SHUT_WR); 1399 shutdown (con->socket_fd,
1400 SHUT_WR);
1065 if (0 != MHD_socket_close_ (con->socket_fd)) 1401 if (0 != MHD_socket_close_ (con->socket_fd))
1066 MHD_PANIC ("close failed\n"); 1402 MHD_PANIC ("close failed\n");
1067 con->socket_fd = MHD_INVALID_SOCKET; 1403 con->socket_fd = MHD_INVALID_SOCKET;
@@ -1433,7 +1769,7 @@ internal_add_connection (struct MHD_Daemon *daemon,
1433 { 1769 {
1434 /* in turbo mode, we assume that non-blocking was already set 1770 /* in turbo mode, we assume that non-blocking was already set
1435 by 'accept4' or whoever calls 'MHD_add_connection' */ 1771 by 'accept4' or whoever calls 'MHD_add_connection' */
1436 if (!MHD_socket_nonblocking_ (connection->socket_fd)) 1772 if (! MHD_socket_nonblocking_ (connection->socket_fd))
1437 { 1773 {
1438#ifdef HAVE_MESSAGES 1774#ifdef HAVE_MESSAGES
1439 MHD_DLOG (connection->daemon, 1775 MHD_DLOG (connection->daemon,
@@ -2168,115 +2504,6 @@ MHD_get_timeout (struct MHD_Daemon *daemon,
2168} 2504}
2169 2505
2170 2506
2171#if HTTPS_SUPPORT
2172/**
2173 * Performs bi-directional forwarding on upgraded HTTPS connections
2174 * based on the readyness state stored in the @a urh handle.
2175 *
2176 * @param urh handle to process
2177 */
2178static void
2179process_urh (struct MHD_UpgradeResponseHandle *urh)
2180{
2181 /* handle reading from TLS client and writing to application */
2182 if ( (0 != (MHD_EPOLL_STATE_READ_READY & urh->app.celi)) &&
2183 (urh->in_buffer_off < urh->in_buffer_size) )
2184 {
2185 ssize_t res;
2186
2187 res = gnutls_record_recv (urh->connection->tls_session,
2188 &urh->in_buffer[urh->in_buffer_off],
2189 urh->in_buffer_size - urh->in_buffer_off);
2190 if ( (GNUTLS_E_AGAIN == res) ||
2191 (GNUTLS_E_INTERRUPTED == res) )
2192 {
2193 urh->app.celi &= ~MHD_EPOLL_STATE_READ_READY;
2194 }
2195 else if (res > 0)
2196 {
2197 urh->in_buffer_off += res;
2198 }
2199 }
2200 if ( (0 != (MHD_EPOLL_STATE_WRITE_READY & urh->mhd.celi)) &&
2201 (urh->in_buffer_off > 0) )
2202 {
2203 size_t res;
2204
2205 res = write (urh->mhd.socket,
2206 urh->in_buffer,
2207 urh->in_buffer_off);
2208 if (-1 == res)
2209 {
2210 /* FIXME: differenciate by errno? */
2211 urh->mhd.celi &= ~MHD_EPOLL_STATE_WRITE_READY;
2212 }
2213 else
2214 {
2215 if (urh->in_buffer_off != res)
2216 {
2217 memmove (urh->in_buffer,
2218 &urh->in_buffer[res],
2219 urh->in_buffer_off - res);
2220 urh->in_buffer_off -= res;
2221 }
2222 else
2223 {
2224 urh->in_buffer_off = 0;
2225 }
2226 }
2227 }
2228
2229 /* handle reading from application and writing to HTTPS client */
2230 if ( (0 != (MHD_EPOLL_STATE_READ_READY & urh->mhd.celi)) &&
2231 (urh->out_buffer_off < urh->out_buffer_size) )
2232 {
2233 size_t res;
2234
2235 res = read (urh->mhd.socket,
2236 &urh->out_buffer[urh->out_buffer_off],
2237 urh->out_buffer_size - urh->out_buffer_off);
2238 if (-1 == res)
2239 {
2240 /* FIXME: differenciate by errno? */
2241 urh->mhd.celi &= ~MHD_EPOLL_STATE_READ_READY;
2242 }
2243 else
2244 {
2245 urh->out_buffer_off += res;
2246 }
2247 }
2248 if ( (0 != (MHD_EPOLL_STATE_WRITE_READY & urh->app.celi)) &&
2249 (urh->out_buffer_off > 0) )
2250 {
2251 ssize_t res;
2252
2253 res = gnutls_record_send (urh->connection->tls_session,
2254 urh->out_buffer,
2255 urh->out_buffer_off);
2256 if ( (GNUTLS_E_AGAIN == res) ||
2257 (GNUTLS_E_INTERRUPTED == res) )
2258 {
2259 urh->app.celi &= ~MHD_EPOLL_STATE_WRITE_READY;
2260 }
2261 else if (res > 0)
2262 {
2263 if (urh->out_buffer_off != res)
2264 {
2265 memmove (urh->out_buffer,
2266 &urh->out_buffer[res],
2267 urh->out_buffer_off - res);
2268 urh->out_buffer_off -= res;
2269 }
2270 else
2271 {
2272 urh->out_buffer_off = 0;
2273 }
2274 }
2275 }
2276}
2277#endif
2278
2279
2280/** 2507/**
2281 * Run webserver operations. This method should be called by clients 2508 * Run webserver operations. This method should be called by clients
2282 * in combination with #MHD_get_fdset if the client-controlled select 2509 * in combination with #MHD_get_fdset if the client-controlled select
@@ -2362,14 +2589,9 @@ MHD_run_from_select (struct MHD_Daemon *daemon,
2362 for (urh = daemon->urh_head; NULL != urh; urh = urh->next) 2589 for (urh = daemon->urh_head; NULL != urh; urh = urh->next)
2363 { 2590 {
2364 /* update urh state based on select() output */ 2591 /* update urh state based on select() output */
2365 if (FD_ISSET (urh->connection->socket_fd, read_fd_set)) 2592 urh_from_fdset (urh,
2366 urh->app.celi |= MHD_EPOLL_STATE_READ_READY; 2593 read_fd_set,
2367 if (FD_ISSET (urh->connection->socket_fd, write_fd_set)) 2594 write_fd_set);
2368 urh->app.celi |= MHD_EPOLL_STATE_WRITE_READY;
2369 if (FD_ISSET (urh->mhd.socket, read_fd_set))
2370 urh->mhd.celi |= MHD_EPOLL_STATE_READ_READY;
2371 if (FD_ISSET (urh->mhd.socket, write_fd_set))
2372 urh->mhd.celi |= MHD_EPOLL_STATE_WRITE_READY;
2373 /* call generic forwarding function for passing data */ 2595 /* call generic forwarding function for passing data */
2374 process_urh (urh); 2596 process_urh (urh);
2375 } 2597 }
diff --git a/src/microhttpd/internal.h b/src/microhttpd/internal.h
index 00843729..7a1e58e1 100644
--- a/src/microhttpd/internal.h
+++ b/src/microhttpd/internal.h
@@ -480,7 +480,14 @@ enum MHD_CONNECTION_STATE
480 * Connection was "upgraded" and socket is now under the 480 * Connection was "upgraded" and socket is now under the
481 * control of the application. 481 * control of the application.
482 */ 482 */
483 MHD_CONNECTION_UPGRADE = MHD_TLS_CONNECTION_INIT + 1 483 MHD_CONNECTION_UPGRADE = MHD_TLS_CONNECTION_INIT + 1,
484
485 /**
486 * Connection was "upgraded" and subsequently closed
487 * by the application. We now need to do our own
488 * internal cleanup.
489 */
490 MHD_CONNECTION_UPGRADE_CLOSED = MHD_TLS_CONNECTION_INIT + 1
484 491
485}; 492};
486 493
@@ -854,6 +861,23 @@ struct MHD_Connection
854 861
855#if HTTPS_SUPPORT 862#if HTTPS_SUPPORT
856 /** 863 /**
864 * If this connection was upgraded and if we are using
865 * #MHD_USE_THREAD_PER_CONNECTION, this points to the
866 * upgrade response details such that the
867 * #thread_main_connection_upgrade()-logic can perform
868 * the bi-directional forwarding.
869 */
870 struct MHD_UpgradeResponseHandle *urh;
871
872 /**
873 * If this connection was upgraded and if we are using
874 * #MHD_USE_THREAD_PER_CONNECTION without encryption,
875 * this points to the semaphore we use to signal termination
876 * to the thread handling the connection.
877 */
878 struct MHD_Semaphore *upgrade_sem;
879
880 /**
857 * State required for HTTPS/SSL/TLS support. 881 * State required for HTTPS/SSL/TLS support.
858 */ 882 */
859 gnutls_session_t tls_session; 883 gnutls_session_t tls_session;
diff --git a/src/microhttpd/mhd_locks.h b/src/microhttpd/mhd_locks.h
index cf10c0d1..1d8376f0 100644
--- a/src/microhttpd/mhd_locks.h
+++ b/src/microhttpd/mhd_locks.h
@@ -22,8 +22,9 @@
22 * @file microhttpd/mhd_locks.h 22 * @file microhttpd/mhd_locks.h
23 * @brief Header for platform-independent locks abstraction 23 * @brief Header for platform-independent locks abstraction
24 * @author Karlson2k (Evgeny Grin) 24 * @author Karlson2k (Evgeny Grin)
25 * @author Christian Grothoff
25 * 26 *
26 * Provides basic abstraction for locks and mutex. 27 * Provides basic abstraction for locks/mutex and semaphores.
27 * Any functions can be implemented as macro on some platforms 28 * Any functions can be implemented as macro on some platforms
28 * unless explicitly marked otherwise. 29 * unless explicitly marked otherwise.
29 * Any function argument can be skipped in macro, so avoid 30 * Any function argument can be skipped in macro, so avoid
@@ -147,4 +148,48 @@
147#define MHD_mutex_unlock_(pmutex) (LeaveCriticalSection((pmutex)), !0) 148#define MHD_mutex_unlock_(pmutex) (LeaveCriticalSection((pmutex)), !0)
148#endif 149#endif
149 150
151
152/**
153 * A semaphore.
154 */
155struct MHD_Semaphore;
156
157
158/**
159 * Create a semaphore with an initial counter of @a init
160 *
161 * @param init initial counter
162 * @return the semaphore, NULL on error
163 */
164struct MHD_Semaphore *
165MHD_semaphore_create (unsigned int init);
166
167
168/**
169 * Count down the semaphore, block if necessary.
170 *
171 * @param sem semaphore to count down.
172 */
173void
174MHD_semaphore_down (struct MHD_Semaphore *sem);
175
176
177/**
178 * Increment the semaphore.
179 *
180 * @param sem semaphore to increment.
181 */
182void
183MHD_semaphore_up (struct MHD_Semaphore *sem);
184
185
186/**
187 * Destroys the semaphore.
188 *
189 * @param sem semaphore to destroy.
190 */
191void
192MHD_semaphore_destroy (struct MHD_Semaphore *sem);
193
194
150#endif /* ! MHD_LOCKS_H */ 195#endif /* ! MHD_LOCKS_H */
diff --git a/src/microhttpd/mhd_sem.c b/src/microhttpd/mhd_sem.c
new file mode 100644
index 00000000..fdd5dbe4
--- /dev/null
+++ b/src/microhttpd/mhd_sem.c
@@ -0,0 +1,138 @@
1/*
2 This file is part of libmicrohttpd
3 Copyright (C) 2016 Christian Grothoff
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with this library; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18
19*/
20
21/**
22 * @file microhttpd/mhd_sem.c
23 * @brief implementation of semaphores
24 * @author Christian Grothoff
25 */
26#include "internal.h"
27#include "mhd_locks.h"
28
29/**
30 * A semaphore.
31 */
32struct MHD_Semaphore
33{
34 /**
35 * Mutex we use internally.
36 */
37 pthread_mutex_t mutex;
38
39 /**
40 * Condition variable used to implement the semaphore.
41 */
42 pthread_cond_t cv;
43
44 /**
45 * Current value of the semaphore.
46 */
47 unsigned int counter;
48};
49
50
51/**
52 * Create a semaphore with an initial counter of @a init
53 *
54 * @param init initial counter
55 * @return the semaphore, NULL on error
56 */
57struct MHD_Semaphore *
58MHD_semaphore_create (unsigned int init)
59{
60 struct MHD_Semaphore *sem;
61
62 sem = malloc (sizeof (struct MHD_Semaphore));
63 if (NULL == sem)
64 return NULL;
65 sem->counter = init;
66 if (0 != pthread_mutex_init (&sem->mutex,
67 NULL))
68 {
69 free (sem);
70 return NULL;
71 }
72 if (0 != pthread_cond_init (&sem->cv,
73 NULL))
74 {
75 (void) pthread_mutex_destroy (&sem->mutex);
76 free (sem);
77 return NULL;
78 }
79 return sem;
80}
81
82
83/**
84 * Count down the semaphore, block if necessary.
85 *
86 * @param sem semaphore to count down.
87 */
88void
89MHD_semaphore_down (struct MHD_Semaphore *sem)
90{
91 if (! pthread_mutex_lock (&sem->mutex))
92 MHD_PANIC ("pthread_mutex_lock for semaphore failed\n");
93 while (0 == sem->counter)
94 {
95 if (0 != pthread_cond_wait (&sem->cv,
96 &sem->mutex))
97 MHD_PANIC ("pthread_cond_wait failed\n");
98 }
99 sem->counter--;
100 if (! pthread_mutex_unlock (&sem->mutex))
101 MHD_PANIC ("pthread_mutex_unlock for semaphore failed\n");
102}
103
104
105/**
106 * Increment the semaphore.
107 *
108 * @param sem semaphore to increment.
109 */
110void
111MHD_semaphore_up (struct MHD_Semaphore *sem)
112{
113 if (! pthread_mutex_lock (&sem->mutex))
114 MHD_PANIC ("pthread_mutex_lock for semaphore failed\n");
115 sem->counter++;
116 pthread_cond_signal (&sem->cv);
117 if (! pthread_mutex_unlock (&sem->mutex))
118 MHD_PANIC ("pthread_mutex_unlock for semaphore failed\n");
119}
120
121
122/**
123 * Destroys the semaphore.
124 *
125 * @param sem semaphore to destroy.
126 */
127void
128MHD_semaphore_destroy (struct MHD_Semaphore *sem)
129{
130 if (0 != pthread_cond_destroy (&sem->cv))
131 MHD_PANIC ("pthread_cond_destroy failed\n");
132 if (0 != pthread_mutex_destroy (&sem->mutex))
133 MHD_PANIC ("pthread_mutex_destroy failed\n");
134 free (sem);
135}
136
137
138/* end of mhd_sem.c */
diff --git a/src/microhttpd/response.c b/src/microhttpd/response.c
index 670be983..ca729765 100644
--- a/src/microhttpd/response.c
+++ b/src/microhttpd/response.c
@@ -605,7 +605,23 @@ MHD_upgrade_action (struct MHD_UpgradeResponseHandle *urh,
605 switch (action) 605 switch (action)
606 { 606 {
607 case MHD_UPGRADE_ACTION_CLOSE: 607 case MHD_UPGRADE_ACTION_CLOSE:
608 /* transition to special 'closed' state for start of cleanup */
609 connection->state = MHD_CONNECTION_UPGRADE_CLOSED;
608 /* Application is done with this connection, tear it down! */ 610 /* Application is done with this connection, tear it down! */
611 if (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION) )
612 {
613 if (0 == (daemon->options & MHD_USE_SSL) )
614 {
615 /* just need to signal the thread that we are done */
616 MHD_semaphore_up (connection->upgrade_sem);
617 }
618 else
619 {
620 /* signal thread by shutdown() of 'app' socket */
621 shutdown (urh->app.socket, SHUT_RDWR);
622 }
623 return MHD_YES;
624 }
609#if HTTPS_SUPPORT 625#if HTTPS_SUPPORT
610 if (0 != (daemon->options & MHD_USE_SSL) ) 626 if (0 != (daemon->options & MHD_USE_SSL) )
611 { 627 {
@@ -658,6 +674,9 @@ MHD_upgrade_action (struct MHD_UpgradeResponseHandle *urh,
658 case MHD_UPGRADE_ACTION_CORK: 674 case MHD_UPGRADE_ACTION_CORK:
659 /* FIXME: not implemented */ 675 /* FIXME: not implemented */
660 return MHD_NO; 676 return MHD_NO;
677 case MHD_UPGRADE_ACTION_FLUSH:
678 /* FIXME: not implemented */
679 return MHD_NO;
661 default: 680 default:
662 /* we don't understand this one */ 681 /* we don't understand this one */
663 return MHD_NO; 682 return MHD_NO;
@@ -784,11 +803,6 @@ MHD_response_execute_upgrade_ (struct MHD_Response *response,
784 rbo, 803 rbo,
785 urh->app.socket, 804 urh->app.socket,
786 urh); 805 urh);
787 /* As far as MHD is concerned, this connection is
788 suspended; it will be resumed once we are done
789 in the #MHD_upgrade_action() function */
790 MHD_suspend_connection (connection);
791
792 /* Launch IO processing by the event loop */ 806 /* Launch IO processing by the event loop */
793 if (0 != (daemon->options & MHD_USE_EPOLL)) 807 if (0 != (daemon->options & MHD_USE_EPOLL))
794 { 808 {
@@ -846,12 +860,25 @@ MHD_response_execute_upgrade_ (struct MHD_Response *response,
846 return MHD_NO; 860 return MHD_NO;
847 } 861 }
848 } 862 }
849 863 if (0 == (daemon->options & MHD_USE_THREAD_PER_CONNECTION) )
850 /* This takes care of most event loops: simply add to DLL */ 864 {
851 DLL_insert (daemon->urh_head, 865 /* As far as MHD's event loops are concerned, this connection
852 daemon->urh_tail, 866 is suspended; it will be resumed once we are done in the
853 urh); 867 #MHD_upgrade_action() function */
854 /* FIXME: None of the above will not work (yet) for thread-per-connection processing */ 868 MHD_suspend_connection (connection);
869 /* This takes care of further processing for most event loops:
870 simply add to DLL for bi-direcitonal processing */
871 DLL_insert (daemon->urh_head,
872 daemon->urh_tail,
873 urh);
874 }
875 else
876 {
877 /* Our caller will set 'connection->state' to
878 MHD_CONNECTION_UPGRADE, thereby triggering the main method
879 of the thread to switch to bi-directional forwarding. */
880 connection->urh = urh;
881 }
855 return MHD_YES; 882 return MHD_YES;
856 } 883 }
857 urh->app.socket = MHD_INVALID_SOCKET; 884 urh->app.socket = MHD_INVALID_SOCKET;
@@ -864,10 +891,31 @@ MHD_response_execute_upgrade_ (struct MHD_Response *response,
864 rbo, 891 rbo,
865 connection->socket_fd, 892 connection->socket_fd,
866 urh); 893 urh);
867 /* As far as MHD is concerned, this connection is 894 if (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION) )
868 suspended; it will be resumed once we are done 895 {
869 in the #MHD_upgrade_action() function */ 896 /* Need to give the thread something to block on... */
870 MHD_suspend_connection (connection); 897 connection->upgrade_sem = MHD_semaphore_create (0);
898 if (NULL == connection->upgrade_sem)
899 {
900#ifdef HAVE_MESSAGES
901 MHD_DLOG (daemon,
902 "Failed to create semaphore for upgrade handling\n");
903#endif
904 MHD_connection_close_ (connection,
905 MHD_REQUEST_TERMINATED_WITH_ERROR);
906 return MHD_NO;
907 }
908 /* Our caller will set 'connection->state' to
909 MHD_CONNECTION_UPGRADE, thereby triggering the
910 main method of the thread to block on the semaphore. */
911 }
912 else
913 {
914 /* As far as MHD's event loops are concerned, this connection is
915 suspended; it will be resumed once we are done in the
916 #MHD_upgrade_action() function */
917 MHD_suspend_connection (connection);
918 }
871 return MHD_YES; 919 return MHD_YES;
872} 920}
873 921