diff options
author | Christian Grothoff <christian@grothoff.org> | 2016-09-03 22:32:29 +0000 |
---|---|---|
committer | Christian Grothoff <christian@grothoff.org> | 2016-09-03 22:32:29 +0000 |
commit | 352a07b01018a4379f4fd83b8277730f044afede (patch) | |
tree | e2e7624f083b3ae8a9adee3f9c36e216bc2a885e | |
parent | 6fa19ae05f226451db78316919908c44d6444ac4 (diff) |
-first, crazy-pants version of test and implementation of HTTPS upgrade; FLUSH API still needs to change dramatically, neither the ioctl nor the busy waiting are really acceptable here
-rw-r--r-- | src/microhttpd/Makefile.am | 9 | ||||
-rw-r--r-- | src/microhttpd/connection.c | 29 | ||||
-rw-r--r-- | src/microhttpd/daemon.c | 26 | ||||
-rw-r--r-- | src/microhttpd/mhd_sockets.c | 17 | ||||
-rw-r--r-- | src/microhttpd/response.c | 65 | ||||
-rw-r--r-- | src/microhttpd/test_upgrade.c | 11 | ||||
-rw-r--r-- | src/microhttpd/test_upgrade_ssl.c | 472 |
7 files changed, 590 insertions, 39 deletions
diff --git a/src/microhttpd/Makefile.am b/src/microhttpd/Makefile.am index 9a32f598..b60ff81e 100644 --- a/src/microhttpd/Makefile.am +++ b/src/microhttpd/Makefile.am @@ -146,6 +146,10 @@ check_PROGRAMS = \ test_daemon \ test_upgrade +if ENABLE_HTTPS + check_PROGRAMS += test_upgrade_ssl +endif + if HAVE_POSTPROCESSOR check_PROGRAMS += \ test_postprocessor \ @@ -171,6 +175,11 @@ test_upgrade_SOURCES = \ test_upgrade_LDADD = \ $(top_builddir)/src/microhttpd/libmicrohttpd.la +test_upgrade_ssl_SOURCES = \ + test_upgrade_ssl.c +test_upgrade_ssl_LDADD = \ + $(top_builddir)/src/microhttpd/libmicrohttpd.la + test_postprocessor_SOURCES = \ test_postprocessor.c test_postprocessor_CPPFLAGS = \ diff --git a/src/microhttpd/connection.c b/src/microhttpd/connection.c index 0c21bc57..16477538 100644 --- a/src/microhttpd/connection.c +++ b/src/microhttpd/connection.c @@ -2354,7 +2354,8 @@ MHD_connection_handle_write (struct MHD_Connection *connection) (void) MHD_mutex_unlock_ (&response->mutex); if (ret < 0) { - if (MHD_SCKT_ERR_IS_EINTR_ (err) || MHD_SCKT_ERR_IS_EAGAIN_ (err)) + if (MHD_SCKT_ERR_IS_EINTR_ (err) || + MHD_SCKT_ERR_IS_EAGAIN_ (err)) return MHD_YES; #ifdef HAVE_MESSAGES MHD_DLOG (connection->daemon, @@ -2515,13 +2516,17 @@ MHD_connection_handle_idle (struct MHD_Connection *connection) } break; } - if (MHD_NO == parse_initial_message_line (connection, line, line_len)) - CONNECTION_CLOSE_ERROR (connection, NULL); + if (MHD_NO == parse_initial_message_line (connection, + line, + line_len)) + CONNECTION_CLOSE_ERROR (connection, + NULL); else connection->state = MHD_CONNECTION_URL_RECEIVED; continue; case MHD_CONNECTION_URL_RECEIVED: - line = get_next_header_line (connection, NULL); + line = get_next_header_line (connection, + NULL); if (NULL == line) { if (MHD_CONNECTION_URL_RECEIVED != connection->state) @@ -2539,7 +2544,8 @@ MHD_connection_handle_idle (struct MHD_Connection *connection) connection->state = MHD_CONNECTION_HEADERS_RECEIVED; continue; } - if (MHD_NO == process_header_line (connection, line)) + if (MHD_NO == process_header_line (connection, + line)) { transmit_error_response (connection, MHD_HTTP_BAD_REQUEST, @@ -2645,7 +2651,8 @@ MHD_connection_handle_idle (struct MHD_Connection *connection) } break; case MHD_CONNECTION_BODY_RECEIVED: - line = get_next_header_line (connection, NULL); + line = get_next_header_line (connection, + NULL); if (NULL == line) { if (connection->state != MHD_CONNECTION_BODY_RECEIVED) @@ -2689,7 +2696,9 @@ MHD_connection_handle_idle (struct MHD_Connection *connection) break; } if (MHD_NO == - process_broken_line (connection, line, MHD_FOOTER_KIND)) + process_broken_line (connection, + line, + MHD_FOOTER_KIND)) continue; if (0 == line[0]) { @@ -3057,17 +3066,17 @@ MHD_get_connection_info (struct MHD_Connection *connection, { #if HTTPS_SUPPORT case MHD_CONNECTION_INFO_CIPHER_ALGO: - if (connection->tls_session == NULL) + if (NULL == connection->tls_session) return NULL; connection->cipher = gnutls_cipher_get (connection->tls_session); return (const union MHD_ConnectionInfo *) &connection->cipher; case MHD_CONNECTION_INFO_PROTOCOL: - if (connection->tls_session == NULL) + if (NULL == connection->tls_session) return NULL; connection->protocol = gnutls_protocol_get_version (connection->tls_session); return (const union MHD_ConnectionInfo *) &connection->protocol; case MHD_CONNECTION_INFO_GNUTLS_SESSION: - if (connection->tls_session == NULL) + if (NULL == connection->tls_session) return NULL; return (const union MHD_ConnectionInfo *) &connection->tls_session; #endif diff --git a/src/microhttpd/daemon.c b/src/microhttpd/daemon.c index a298a1a4..62cd3ce8 100644 --- a/src/microhttpd/daemon.c +++ b/src/microhttpd/daemon.c @@ -659,19 +659,19 @@ urh_to_fdset (struct MHD_UpgradeResponseHandle *urh, max_fd, fd_setsize)) ) return MHD_NO; - if ( (0 != (MHD_EPOLL_STATE_WRITE_READY & urh->mhd.celi)) && + if ( (0 == (MHD_EPOLL_STATE_WRITE_READY & urh->mhd.celi)) && (! MHD_add_to_fd_set_ (urh->mhd.socket, ws, max_fd, fd_setsize)) ) return MHD_NO; - if ( (0 != (MHD_EPOLL_STATE_READ_READY & urh->app.celi)) && + if ( (0 == (MHD_EPOLL_STATE_READ_READY & urh->app.celi)) && (! MHD_add_to_fd_set_ (urh->connection->socket_fd, rs, max_fd, fd_setsize)) ) return MHD_NO; - if ( (0 != (MHD_EPOLL_STATE_WRITE_READY & urh->app.celi)) && + if ( (0 == (MHD_EPOLL_STATE_WRITE_READY & urh->app.celi)) && (! MHD_add_to_fd_set_ (urh->connection->socket_fd, ws, max_fd, @@ -2639,10 +2639,17 @@ MHD_select (struct MHD_Daemon *daemon, may_block = MHD_NO; /* single-threaded, go over everything */ - if (MHD_NO == MHD_get_fdset2 (daemon, &rs, &ws, &es, &maxsock, FD_SETSIZE)) + if (MHD_NO == + MHD_get_fdset2 (daemon, + &rs, + &ws, + &es, + &maxsock, + FD_SETSIZE)) { #ifdef HAVE_MESSAGES - MHD_DLOG (daemon, "Could not obtain daemon fdsets"); + MHD_DLOG (daemon, + "Could not obtain daemon fdsets"); #endif err_state = MHD_YES; } @@ -2657,7 +2664,8 @@ MHD_select (struct MHD_Daemon *daemon, FD_SETSIZE)) ) { #ifdef HAVE_MESSAGES - MHD_DLOG (daemon, "Could not add listen socket to fdset"); + MHD_DLOG (daemon, + "Could not add listen socket to fdset"); #endif return MHD_NO; } @@ -2726,7 +2734,11 @@ MHD_select (struct MHD_Daemon *daemon, timeout.tv_sec = (_MHD_TIMEVAL_TV_SEC_TYPE)(ltimeout / 1000); tv = &timeout; } - num_ready = MHD_SYS_select_ (maxsock + 1, &rs, &ws, &es, tv); + num_ready = MHD_SYS_select_ (maxsock + 1, + &rs, + &ws, + &es, + tv); if (MHD_YES == daemon->shutdown) return MHD_NO; if (num_ready < 0) diff --git a/src/microhttpd/mhd_sockets.c b/src/microhttpd/mhd_sockets.c index 1023017f..1187a2cd 100644 --- a/src/microhttpd/mhd_sockets.c +++ b/src/microhttpd/mhd_sockets.c @@ -337,16 +337,21 @@ MHD_add_to_fd_set_ (MHD_socket fd, MHD_socket *max_fd, unsigned int fd_setsize) { - if (NULL == set || MHD_INVALID_SOCKET == fd) + if ( (NULL == set) || + (MHD_INVALID_SOCKET == fd) ) return 0; - if (!MHD_SCKT_FD_FITS_FDSET_SETSIZE_(fd, set, fd_setsize)) + if (! MHD_SCKT_FD_FITS_FDSET_SETSIZE_ (fd, + set, + fd_setsize)) return 0; - MHD_SCKT_ADD_FD_TO_FDSET_SETSIZE_(fd, set, fd_setsize); + MHD_SCKT_ADD_FD_TO_FDSET_SETSIZE_(fd, + set, + fd_setsize); if ( (NULL != max_fd) && - ((fd > *max_fd) || (MHD_INVALID_SOCKET == *max_fd)) ) + ( (fd > *max_fd) || + (MHD_INVALID_SOCKET == *max_fd) ) ) *max_fd = fd; - - return !0; + return ! 0; } diff --git a/src/microhttpd/response.c b/src/microhttpd/response.c index ca729765..aa79196d 100644 --- a/src/microhttpd/response.c +++ b/src/microhttpd/response.c @@ -16,7 +16,6 @@ License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - /** * @file response.c * @brief Methods for managing response objects @@ -34,6 +33,7 @@ #include "connection.h" #include "memorypool.h" +#include <sys/ioctl.h> #if defined(_WIN32) && defined(MHD_W32_MUTEX_) #ifndef WIN32_LEAN_AND_MEAN @@ -202,7 +202,9 @@ MHD_get_response_headers (struct MHD_Response *response, numHeaders++; if ((NULL != iterator) && (MHD_YES != iterator (iterator_cls, - pos->kind, pos->header, pos->value))) + pos->kind, + pos->header, + pos->value))) break; } return numHeaders; @@ -337,16 +339,23 @@ file_reader (void *cls, return MHD_CONTENT_READER_END_WITH_ERROR; /* seek to required position is not possible */ #if defined(HAVE_LSEEK64) - if (lseek64 (response->fd, offset64, SEEK_SET) != offset64) + if (lseek64 (response->fd, + offset64, + SEEK_SET) != offset64) return MHD_CONTENT_READER_END_WITH_ERROR; /* can't seek to required position */ #elif defined(HAVE___LSEEKI64) - if (_lseeki64 (response->fd, offset64, SEEK_SET) != offset64) + if (_lseeki64 (response->fd, + offset64, + SEEK_SET) != offset64) return MHD_CONTENT_READER_END_WITH_ERROR; /* can't seek to required position */ #else /* !HAVE___LSEEKI64 */ - if (sizeof(off_t) < sizeof(uint64_t) && offset64 > (uint64_t)INT32_MAX) + if ( (sizeof(off_t) < sizeof (uint64_t)) && + (offset64 > (uint64_t)INT32_MAX) ) return MHD_CONTENT_READER_END_WITH_ERROR; /* seek to required position is not possible */ - if (lseek (response->fd, (off_t)offset64, SEEK_SET) != (off_t)offset64) + if (lseek (response->fd, + (off_t) offset64, + SEEK_SET) != (off_t) offset64) return MHD_CONTENT_READER_END_WITH_ERROR; /* can't seek to required position */ #endif @@ -354,12 +363,16 @@ file_reader (void *cls, if (max > SSIZE_MAX) max = SSIZE_MAX; - n = read (response->fd, buf, max); + n = read (response->fd, + buf, + max); #else /* _WIN32 */ if (max > INT32_MAX) max = INT32_MAX; - n = read (response->fd, buf, (unsigned int)max); + n = read (response->fd, + buf, + (unsigned int)max); #endif /* _WIN32 */ if (0 == n) @@ -438,8 +451,10 @@ MHD_create_response_from_fd_at_offset64 (uint64_t size, struct MHD_Response *response; #if !defined(HAVE___LSEEKI64) && !defined(HAVE_LSEEK64) - if (sizeof(uint64_t) > sizeof(off_t) && - (size > (uint64_t)INT32_MAX || offset > (uint64_t)INT32_MAX || (size + offset) >= (uint64_t)INT32_MAX)) + if ( (sizeof(uint64_t) > sizeof(off_t)) && + ( (size > (uint64_t)INT32_MAX) || + (offset > (uint64_t)INT32_MAX) || + ((size + offset) >= (uint64_t)INT32_MAX) ) ) return NULL; #endif if ( ((int64_t)size < 0) || @@ -530,7 +545,9 @@ MHD_create_response_from_data (size_t size, return NULL; if (NULL == (response = malloc (sizeof (struct MHD_Response)))) return NULL; - memset (response, 0, sizeof (struct MHD_Response)); + memset (response, + 0, + sizeof (struct MHD_Response)); response->fd = -1; if (! MHD_mutex_init_ (&response->mutex)) { @@ -675,8 +692,30 @@ MHD_upgrade_action (struct MHD_UpgradeResponseHandle *urh, /* FIXME: not implemented */ return MHD_NO; case MHD_UPGRADE_ACTION_FLUSH: - /* FIXME: not implemented */ - return MHD_NO; +#if HTTPS_SUPPORT + if (0 != (daemon->options & MHD_USE_SSL)) + { + int avail; + + /* First, check that our pipe is empty, to be sure we do + have it all in the buffer. */ + if ( (0 == +#if WINDOWS + ioctlsocket +#else + ioctl +#endif + (urh->mhd.socket, + FIONREAD, + &avail)) && + (0 != avail) ) + return MHD_NO; + /* then, refuse 'flush' unless our buffer is empty */ + if (0 != urh->out_buffer_off) + return MHD_NO; + } +#endif + return MHD_YES; default: /* we don't understand this one */ return MHD_NO; diff --git a/src/microhttpd/test_upgrade.c b/src/microhttpd/test_upgrade.c index 2288d927..469dc581 100644 --- a/src/microhttpd/test_upgrade.c +++ b/src/microhttpd/test_upgrade.c @@ -298,7 +298,9 @@ test_upgrade_internal_select () sa.sin_family = AF_INET; sa.sin_port = htons (1080); sa.sin_addr.s_addr = htonl (INADDR_LOOPBACK); - if (0 != connect (sock, (struct sockaddr *) &sa, sizeof (sa))) + if (0 != connect (sock, + (struct sockaddr *) &sa, + sizeof (sa))) abort (); send_all (sock, "GET / HTTP/1.1\r\nConnection: Upgrade\r\n\r\n"); @@ -316,12 +318,15 @@ test_upgrade_internal_select () int -main (int argc, char *const *argv) +main (int argc, + char *const *argv) { int errorCount = 0; errorCount += test_upgrade_internal_select (); if (errorCount != 0) - fprintf (stderr, "Error (code: %u)\n", errorCount); + fprintf (stderr, + "Error (code: %u)\n", + errorCount); return errorCount != 0; /* 0 == pass */ } diff --git a/src/microhttpd/test_upgrade_ssl.c b/src/microhttpd/test_upgrade_ssl.c new file mode 100644 index 00000000..5c663dd9 --- /dev/null +++ b/src/microhttpd/test_upgrade_ssl.c @@ -0,0 +1,472 @@ +/* + This file is part of libmicrohttpd + Copyright (C) 2016 Christian Grothoff + + libmicrohttpd is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, or (at your + option) any later version. + + libmicrohttpd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with libmicrohttpd; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +/** + * @file test_upgrade_ssl.c + * @brief Testcase for libmicrohttpd upgrading a connection + * @author Christian Grothoff + */ + +#include "platform.h" +#include "microhttpd.h" +#include <stdlib.h> +#include <string.h> +#include <stdio.h> + +#ifndef WINDOWS +#include <unistd.h> +#endif + +#include <sys/socket.h> +#include <sys/wait.h> +#include <netinet/in.h> +#include <netinet/ip.h> +#include <pthread.h> +#include "mhd_sockets.h" + +#include "../testcurl/https/tls_test_keys.h" + + +/** + * Thread we use to run the interaction with the upgraded socket. + */ +static pthread_t pt; + +/** + * Will be set to the upgraded socket. + */ +static MHD_socket usock; + +/** + * Fork child that connects via OpenSSL to our @a port. Allows us to + * talk to our port over a socket in @a sp without having to worry + * about TLS. + * + * @param location where the socket is returned + * @return -1 on error, otherwise PID of SSL child process + */ +static pid_t +openssl_connect (int *sock, + uint16_t port) +{ + pid_t chld; + int sp[2]; + char destination[30]; + + if (0 != socketpair (AF_UNIX, + SOCK_STREAM, + 0, + sp)) + return -1; + chld = fork (); + if (0 != chld) + { + *sock = sp[1]; + MHD_socket_close_ (sp[0]); + return chld; + } + MHD_socket_close_ (sp[1]); + (void) close (0); + (void) close (1); + dup2 (sp[0], 0); + dup2 (sp[0], 1); + close (sp[0]); + sprintf (destination, + "localhost:%u", + (unsigned int) port); + execlp ("openssl", + "openssl", + "s_client", + "-connect", + destination, + "-verify", + "0", + // "-quiet", + (char *) NULL); + _exit (1); +} + + +/** + * Change itc FD options to be non-blocking. + * + * @param fd the FD to manipulate + * @return non-zero if succeeded, zero otherwise + */ +static void +make_blocking (MHD_socket fd) +{ + int flags; + + flags = fcntl (fd, F_GETFL); + if (-1 == flags) + return; + if ((flags & ~O_NONBLOCK) != flags) + fcntl (fd, F_SETFL, flags & ~O_NONBLOCK); +} + + +static void +send_all (MHD_socket sock, + const char *text) +{ + size_t len = strlen (text); + ssize_t ret; + + make_blocking (sock); + for (size_t off = 0; off < len; off += ret) + { + ret = write (sock, + &text[off], + len - off); + if (-1 == ret) + { + if (EAGAIN == errno) + { + ret = 0; + continue; + } + abort (); + } + } +} + + +/** + * Read character-by-character until we + * get '\r\n\r\n'. + */ +static void +recv_hdr (MHD_socket sock) +{ + unsigned int i; + char next; + char c; + ssize_t ret; + + make_blocking (sock); + next = '\r'; + i = 0; + while (i < 4) + { + ret = read (sock, + &c, + 1); + if (0 == ret) + abort (); /* this is fatal */ + if (-1 == ret) + { + if (EAGAIN == errno) + { + ret = 0; + continue; + } + abort (); + } + if (0 == ret) + continue; + if (c == next) + { + i++; + if (next == '\r') + next = '\n'; + else + next = '\r'; + continue; + } + if (c == '\r') + { + i = 1; + next = '\n'; + continue; + } + i = 0; + next = '\r'; + } +} + + +static void +recv_all (MHD_socket sock, + const char *text) +{ + size_t len = strlen (text); + char buf[len]; + ssize_t ret; + + make_blocking (sock); + for (size_t off = 0; off < len; off += ret) + { + ret = read (sock, + &buf[off], + len - off); + if (0 == ret) + abort (); /* this is fatal */ + if (-1 == ret) + { + if (EAGAIN == errno) + { + ret = 0; + continue; + } + abort (); + } + } + if (0 != strncmp (text, buf, len)) + abort(); +} + + +/** + * Main function for the thread that runs the interaction with + * the upgraded socket. + * + * @param cls the handle for the upgrade + */ +static void * +run_usock (void *cls) +{ + struct MHD_UpgradeResponseHandle *urh = cls; + + fprintf (stderr, + "Sending `Hello'\n"); + send_all (usock, + "Hello"); + fprintf (stderr, + "Receiving `World'\n"); + recv_all (usock, + "World"); + fprintf (stderr, + "Sending `Finished'\n"); + send_all (usock, + "Finished"); + fprintf (stderr, + "Closing socket\n"); + while (MHD_NO == + MHD_upgrade_action (urh, + MHD_UPGRADE_ACTION_FLUSH)) + usleep (1000); + MHD_upgrade_action (urh, + MHD_UPGRADE_ACTION_CLOSE); + fprintf (stderr, + "Thread terminating\n"); + return NULL; +} + + +/** + * Function called after a protocol "upgrade" response was sent + * successfully and the socket should now be controlled by some + * protocol other than HTTP. + * + * Any data received on the socket will be made available in + * 'data_in'. The function should update 'data_in_size' to + * reflect the number of bytes consumed from 'data_in' (the remaining + * bytes will be made available in the next call to the handler). + * + * Any data that should be transmitted on the socket should be + * stored in 'data_out'. '*data_out_size' is initially set to + * the available buffer space in 'data_out'. It should be set to + * the number of bytes stored in 'data_out' (which can be zero). + * + * The return value is a BITMASK that indicates how the function + * intends to interact with the event loop. It can request to be + * notified for reading, writing, request to UNCORK the send buffer + * (which MHD is allowed to ignore, if it is not possible to uncork on + * the local platform), to wait for the 'external' select loop to + * trigger another round. It is also possible to specify "no events" + * to terminate the connection; in this case, the + * #MHD_RequestCompletedCallback will be called and all resources of + * the connection will be released. + * + * Except when in 'thread-per-connection' mode, implementations + * of this function should never block (as it will still be called + * from within the main event loop). + * + * @param cls closure, whatever was given to #MHD_create_response_for_upgrade(). + * @param connection original HTTP connection handle, + * giving the function a last chance + * to inspect the original HTTP request + * @param con_cls last value left in `*con_cls` in the `MHD_AccessHandlerCallback` + * @param extra_in if we happened to have read bytes after the + * HTTP header already (because the client sent + * more than the HTTP header of the request before + * we sent the upgrade response), + * these are the extra bytes already read from @a sock + * by MHD. The application should treat these as if + * it had read them from @a sock. + * @param extra_in_size number of bytes in @a extra_in + * @param sock socket to use for bi-directional communication + * with the client. For HTTPS, this may not be a socket + * that is directly connected to the client and thus certain + * operations (TCP-specific setsockopt(), getsockopt(), etc.) + * may not work as expected (as the socket could be from a + * socketpair() or a TCP-loopback) + * @param urh argument for #MHD_upgrade_action()s on this @a connection. + * Applications must eventually use this function to perform the + * close() action on the @a sock. + */ +static void +upgrade_cb (void *cls, + struct MHD_Connection *connection, + void *con_cls, + const char *extra_in, + size_t extra_in_size, + MHD_socket sock, + struct MHD_UpgradeResponseHandle *urh) +{ + usock = sock; + if (0 != extra_in_size) + abort (); + pthread_create (&pt, + NULL, + &run_usock, + urh); +} + + +/** + * A client has requested the given url using the given method + * (#MHD_HTTP_METHOD_GET, #MHD_HTTP_METHOD_PUT, + * #MHD_HTTP_METHOD_DELETE, #MHD_HTTP_METHOD_POST, etc). The callback + * must call MHD callbacks to provide content to give back to the + * client and return an HTTP status code (i.e. #MHD_HTTP_OK, + * #MHD_HTTP_NOT_FOUND, etc.). + * + * @param cls argument given together with the function + * pointer when the handler was registered with MHD + * @param url the requested url + * @param method the HTTP method used (#MHD_HTTP_METHOD_GET, + * #MHD_HTTP_METHOD_PUT, etc.) + * @param version the HTTP version string (i.e. + * #MHD_HTTP_VERSION_1_1) + * @param upload_data the data being uploaded (excluding HEADERS, + * for a POST that fits into memory and that is encoded + * with a supported encoding, the POST data will NOT be + * given in upload_data and is instead available as + * part of #MHD_get_connection_values; very large POST + * data *will* be made available incrementally in + * @a upload_data) + * @param upload_data_size set initially to the size of the + * @a upload_data provided; the method must update this + * value to the number of bytes NOT processed; + * @param con_cls pointer that the callback can set to some + * address and that will be preserved by MHD for future + * calls for this request; since the access handler may + * be called many times (i.e., for a PUT/POST operation + * with plenty of upload data) this allows the application + * to easily associate some request-specific state. + * If necessary, this state can be cleaned up in the + * global #MHD_RequestCompletedCallback (which + * can be set with the #MHD_OPTION_NOTIFY_COMPLETED). + * Initially, `*con_cls` will be NULL. + * @return #MHD_YES if the connection was handled successfully, + * #MHD_NO if the socket must be closed due to a serios + * error while handling the request + */ +static int +ahc_upgrade (void *cls, + struct MHD_Connection *connection, + const char *url, + const char *method, + const char *version, + const char *upload_data, + size_t *upload_data_size, + void **con_cls) +{ + struct MHD_Response *resp; + int ret; + + resp = MHD_create_response_for_upgrade (&upgrade_cb, + NULL); + MHD_add_response_header (resp, + MHD_HTTP_HEADER_UPGRADE, + "Hello World Protocol"); + ret = MHD_queue_response (connection, + MHD_HTTP_SWITCHING_PROTOCOLS, + resp); + MHD_destroy_response (resp); + return ret; +} + + +static int +test_upgrade_internal_select () +{ + struct MHD_Daemon *d; + MHD_socket sock; + pid_t pid; + + d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG | MHD_USE_SUSPEND_RESUME | MHD_USE_TLS, + 1080, + NULL, NULL, + &ahc_upgrade, NULL, + MHD_OPTION_HTTPS_MEM_KEY, srv_signed_key_pem, + MHD_OPTION_HTTPS_MEM_CERT, srv_signed_cert_pem, + MHD_OPTION_END); + if (NULL == d) + return 2; + if (-1 == (pid = openssl_connect (&sock, 1080))) + { + MHD_stop_daemon (d); + return 4; + } + + send_all (sock, + "GET / HTTP/1.1\r\nConnection: Upgrade\r\n\r\n"); + recv_hdr (sock); + recv_all (sock, + "Hello"); + fprintf (stderr, + "Received `Hello'\n"); + send_all (sock, + "World"); + fprintf (stderr, + "Sent `World'\n"); + recv_all (sock, + "Finished"); + fprintf (stderr, + "Received `Finished'\n"); + MHD_socket_close_ (sock); + pthread_join (pt, + NULL); + fprintf (stderr, + "Joined helper thread\n"); + waitpid (pid, NULL, 0); + MHD_stop_daemon (d); + return 0; +} + + +int +main (int argc, + char *const *argv) +{ + int errorCount = 0; + + if (0 != system ("openssl version 1> /dev/null")) + return 77; /* openssl not available, can't run the test */ + errorCount += test_upgrade_internal_select (); + if (errorCount != 0) + fprintf (stderr, + "Error (code: %u)\n", + errorCount); + return errorCount != 0; /* 0 == pass */ +} |