diff options
author | Christian Grothoff <christian@grothoff.org> | 2016-08-26 21:15:34 +0000 |
---|---|---|
committer | Christian Grothoff <christian@grothoff.org> | 2016-08-26 21:15:34 +0000 |
commit | 2669ca22a4d30949c54cead24958d24215276967 (patch) | |
tree | 4369262a3228072acf980cc624e84e1ca13cb376 | |
parent | a0dcd1ab1b234bbdfb5e2d4c92e48c7ea949cc6c (diff) |
sketching how I envision handling Upgrade
-rw-r--r-- | src/include/microhttpd.h | 12 | ||||
-rw-r--r-- | src/microhttpd/internal.h | 145 | ||||
-rw-r--r-- | src/microhttpd/response.c | 239 | ||||
-rw-r--r-- | src/microhttpd/response.h | 5 |
4 files changed, 322 insertions, 79 deletions
diff --git a/src/include/microhttpd.h b/src/include/microhttpd.h index eef50c70..27576f5f 100644 --- a/src/include/microhttpd.h +++ b/src/include/microhttpd.h @@ -2311,6 +2311,14 @@ MHD_upgrade_action (struct MHD_UpgradeResponseHandle *urh, * @param connection original HTTP connection handle, * giving the function a last chance * to inspect the original HTTP request + * @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 @@ -2324,6 +2332,8 @@ MHD_upgrade_action (struct MHD_UpgradeResponseHandle *urh, typedef void (*MHD_UpgradeHandler)(void *cls, struct MHD_Connection *connection, + const char *extra_in, + size_t extra_in_size, MHD_SOCKET sock, struct MHD_UpgradeResponseHandle *urh); @@ -2852,7 +2862,7 @@ enum MHD_FEATURE * @ingroup specialized */ _MHD_EXTERN int -MHD_is_feature_supported(enum MHD_FEATURE feature); +MHD_is_feature_supported (enum MHD_FEATURE feature); #if 0 /* keep Emacsens' auto-indent happy */ diff --git a/src/microhttpd/internal.h b/src/microhttpd/internal.h index 863def56..d6f1177c 100644 --- a/src/microhttpd/internal.h +++ b/src/microhttpd/internal.h @@ -102,68 +102,68 @@ extern void *mhd_panic_cls; * State of the socket with respect to epoll (bitmask). */ enum MHD_EpollState - { - - /** - * The socket is not involved with a defined state in epoll() right - * now. - */ - MHD_EPOLL_STATE_UNREADY = 0, - - /** - * epoll() told us that data was ready for reading, and we did - * not consume all of it yet. - */ - MHD_EPOLL_STATE_READ_READY = 1, - - /** - * epoll() told us that space was available for writing, and we did - * not consume all of it yet. - */ - MHD_EPOLL_STATE_WRITE_READY = 2, - - /** - * Is this connection currently in the 'eready' EDLL? - */ - MHD_EPOLL_STATE_IN_EREADY_EDLL = 4, - - /** - * Is this connection currently in the epoll() set? - */ - MHD_EPOLL_STATE_IN_EPOLL_SET = 8, - - /** - * Is this connection currently suspended? - */ - MHD_EPOLL_STATE_SUSPENDED = 16 - }; +{ + + /** + * The socket is not involved with a defined state in epoll() right + * now. + */ + MHD_EPOLL_STATE_UNREADY = 0, + + /** + * epoll() told us that data was ready for reading, and we did + * not consume all of it yet. + */ + MHD_EPOLL_STATE_READ_READY = 1, + + /** + * epoll() told us that space was available for writing, and we did + * not consume all of it yet. + */ + MHD_EPOLL_STATE_WRITE_READY = 2, + + /** + * Is this connection currently in the 'eready' EDLL? + */ + MHD_EPOLL_STATE_IN_EREADY_EDLL = 4, + + /** + * Is this connection currently in the epoll() set? + */ + MHD_EPOLL_STATE_IN_EPOLL_SET = 8, + + /** + * Is this connection currently suspended? + */ + MHD_EPOLL_STATE_SUSPENDED = 16 +}; /** * What is this connection waiting for? */ enum MHD_ConnectionEventLoopInfo - { - /** - * We are waiting to be able to read. - */ - MHD_EVENT_LOOP_INFO_READ = 0, +{ + /** + * We are waiting to be able to read. + */ + MHD_EVENT_LOOP_INFO_READ = 0, - /** - * We are waiting to be able to write. - */ - MHD_EVENT_LOOP_INFO_WRITE = 1, + /** + * We are waiting to be able to write. + */ + MHD_EVENT_LOOP_INFO_WRITE = 1, - /** - * We are waiting for the application to provide data. - */ - MHD_EVENT_LOOP_INFO_BLOCK = 2, + /** + * We are waiting for the application to provide data. + */ + MHD_EVENT_LOOP_INFO_BLOCK = 2, - /** - * We are finished and are awaiting cleanup. - */ - MHD_EVENT_LOOP_INFO_CLEANUP = 3 - }; + /** + * We are finished and are awaiting cleanup. + */ + MHD_EVENT_LOOP_INFO_CLEANUP = 3 +}; /** @@ -202,7 +202,8 @@ struct MHD_NonceNc */ void MHD_DLOG (const struct MHD_Daemon *daemon, - const char *format, ...); + const char *format, + ...); #endif @@ -273,6 +274,20 @@ struct MHD_Response */ MHD_ContentReaderFreeCallback crfc; +#if 0 + /** + * Application function to call once we are done sending the headers + * of the response; NULL unless this is a response created with + * #MHD_create_response_for_upgrade(). + */ + MHD_UpgradeHandler upgrade_handler; + + /** + * Closure for @e uh. + */ + void *upgrade_handler_cls; +#endif + /** * Mutex to synchronize access to @e data, @e size and * @e reference_count. @@ -569,14 +584,12 @@ struct MHD_Connection struct MHD_Response *response; /** - * The memory pool is created whenever we first read - * from the TCP stream and destroyed at the end of - * each request (and re-created for the next request). - * In the meantime, this pointer is NULL. The - * pool is used for all connection-related data - * except for the response (which maybe shared between - * connections) and the IP address (which persists - * across individual requests). + * The memory pool is created whenever we first read from the TCP + * stream and destroyed at the end of each request (and re-created + * for the next request). In the meantime, this pointer is NULL. + * The pool is used for all connection-related data except for the + * response (which maybe shared between connections) and the IP + * address (which persists across individual requests). */ struct MemoryPool *pool; @@ -598,8 +611,7 @@ struct MHD_Connection void *socket_context; /** - * Request method. Should be GET/POST/etc. Allocated - * in pool. + * Request method. Should be GET/POST/etc. Allocated in pool. */ char *method; @@ -616,9 +628,8 @@ struct MHD_Connection char *version; /** - * Buffer for reading requests. Allocated - * in pool. Actually one byte larger than - * @e read_buffer_size (if non-NULL) to allow for + * Buffer for reading requests. Allocated in pool. Actually one + * byte larger than @e read_buffer_size (if non-NULL) to allow for * 0-termination. */ char *read_buffer; diff --git a/src/microhttpd/response.c b/src/microhttpd/response.c index 3e967e68..f0ebf5c6 100644 --- a/src/microhttpd/response.c +++ b/src/microhttpd/response.c @@ -101,7 +101,8 @@ add_response_entry (struct MHD_Response *response, */ int MHD_add_response_header (struct MHD_Response *response, - const char *header, const char *content) + const char *header, + const char *content) { return add_response_entry (response, MHD_HEADER_KIND, @@ -121,7 +122,8 @@ MHD_add_response_header (struct MHD_Response *response, */ int MHD_add_response_footer (struct MHD_Response *response, - const char *footer, const char *content) + const char *footer, + const char *content) { return add_response_entry (response, MHD_FOOTER_KIND, @@ -184,7 +186,8 @@ MHD_del_response_header (struct MHD_Response *response, */ int MHD_get_response_headers (struct MHD_Response *response, - MHD_KeyValueIterator iterator, void *iterator_cls) + MHD_KeyValueIterator iterator, + void *iterator_cls) { struct MHD_HTTP_Header *pos; int numHeaders = 0; @@ -316,7 +319,10 @@ MHD_set_response_options (struct MHD_Response *response, * @return number of bytes written */ static ssize_t -file_reader (void *cls, uint64_t pos, char *buf, size_t max) +file_reader (void *cls, + uint64_t pos, + char *buf, + size_t max) { struct MHD_Response *response = cls; ssize_t n; @@ -397,7 +403,9 @@ MHD_create_response_from_fd_at_offset (size_t size, int fd, off_t offset) { - return MHD_create_response_from_fd_at_offset64 (size, fd, offset); + return MHD_create_response_from_fd_at_offset64 (size, + fd, + offset); } @@ -461,7 +469,9 @@ struct MHD_Response * MHD_create_response_from_fd (size_t size, int fd) { - return MHD_create_response_from_fd_at_offset64 (size, fd, 0); + return MHD_create_response_from_fd_at_offset64 (size, + fd, + 0); } @@ -482,7 +492,9 @@ _MHD_EXTERN struct MHD_Response * MHD_create_response_from_fd64 (uint64_t size, int fd) { - return MHD_create_response_from_fd_at_offset64 (size, fd, 0); + return MHD_create_response_from_fd_at_offset64 (size, + fd, + 0); } @@ -502,7 +514,9 @@ MHD_create_response_from_fd64 (uint64_t size, */ struct MHD_Response * MHD_create_response_from_data (size_t size, - void *data, int must_free, int must_copy) + void *data, + int must_free, + int must_copy) { struct MHD_Response *response; void *tmp; @@ -513,7 +527,7 @@ MHD_create_response_from_data (size_t size, return NULL; memset (response, 0, sizeof (struct MHD_Response)); response->fd = -1; - if (!MHD_mutex_init_ (&response->mutex)) + if (! MHD_mutex_init_ (&response->mutex)) { free (response); return NULL; @@ -563,6 +577,208 @@ MHD_create_response_from_buffer (size_t size, } +#if 0 +/** + * Handle given to the application to manage special + * actions relating to MHD responses that "upgrade" + * the HTTP protocol (i.e. to WebSockets). + */ +struct MHD_UpgradeResponseHandle +{ + + /** + * The connection for which this is an upgrade handle. Note that + * because a response may be shared over many connections, this may + * not be the only upgrade handle for the response of this connection. + */ + struct MHD_Connection *connection; + + /** + * The socket we gave to the application (r/w). + */ + MHD_SOCKET app_sock; + + /** + * If @a app_sock was a socketpair, our end of it, otherwise + * #MHD_INVALID_SOCKET; (r/w). + */ + MHD_SOCKET mhd_sock; + +}; + + +/** + * This connection-specific callback is provided by MHD to + * applications (unusual) during the #MHD_UpgradeHandler. + * It allows applications to perform 'special' actions on + * the underlying socket from the upgrade. + * + * @param urh the handle identifying the connection to perform + * the upgrade @a action on. + * @param action which action should be performed + * @param ... arguments to the action (depends on the action) + * @return #MHD_NO on error, #MHD_YES on success + */ +_MHD_EXTERN int +MHD_upgrade_action (struct MHD_UpgradeResponseHandle *urh, + enum MHD_UpgradeAction action, + ...) +{ + switch (action) + { + case MHD_UPGRADE_ACTION_CLOSE: + /* Application is done with this connection, tear it down! */ + if ( (MHD_INVALID_SOCKET != urh->app_socket) && + (0 != MHD_socket_close (urh->app_socket)) ) + MHD_PANIC ("close failed\n"); + if ( (MHD_INVALID_SOCKET != urh->mhd_socket) && + (0 != MHD_socket_close (urh->mhd_socket)) ) + MHD_PANIC ("close failed\n"); + MHD_connection_resume (urh->connection); + MHD_connection_close_ (urh->connection, + MHD_REQUEST_TERMINATED_COMPLETED_OK); + free (urh); + return MHD_YES; + case MHD_UPGRADE_ACTION_CORK: + /* FIXME: not implemented */ + return MHD_NO; + default: + /* we don't understand this one */ + return MHD_NO; + } +} + + +/** + * We are done sending the header of a given response + * to the client. Now it is time to perform the upgrade + * and hand over the connection to the application. + * + * @param response the response that was created for an upgrade + * @param connection the specific connection we are upgrading + * @return #MHD_YES on success, #MHD_NO on failure (will cause + * connection to be closed) + */ +// FIXME: This function will need to be called at the right place(s) +// in the connection processing (just after we are done sending the header) +// (for responses that have the 'upgrade_header' callback set). +int +MHD_response_execute_upgrade_ (struct MHD_Response *response, + struct MHD_Connection *connection) +{ + struct MHD_UpgradeResponseHandle *urh; + int sv[2]; + size_t rbo; + + urh = malloc (sizeof (struct MHD_UpgradeResponseHandle)); + if (NULL == urh) + return MHD_NO; +#if HTTPS_SUPPORT + if (0 != (connection->daemon->flags & MHD_USE_SSL) ) + { + /* FIXME: this is non-portable for now; W32 port pending... */ + if (0 != socketpair (AF_UNIX, + SOCK_STREAM, + 0, + sv)) + { + free (urh); + return MHD_NO; + } + urh->app_socket = sv[0]; + urh->mhd_socket = sv[1]; + urh->connection = connection; + rbo = connection->read_buffer_offset; + connection->read_buffer_offset = 0; + response->upgrade_handler (response->upgrade_handler_cls, + connection, + connection->read_buffer, + rbo, + urh->app_sock, + urh); + /* As far as MHD is concerned, this connection is + suspended; it will be resumed once we are done + in the #MHD_upgrade_action() function */ + MHD_connection_suspend (connection); + /* FIXME: also need to start some processing logic in _all_ MHD + event loops for the sv traffic! (NOT IMPLEMENTED!!!) */ + return MHD_YES; + } +#endif + urh->app_socket = MHD_INVALID_SOCKET; + urh->mhd_socket = MHD_INVALID_SOCKET; + rbo = connection->read_buffer_offset; + connection->read_buffer_offset = 0; + response->upgrade_handler (response->upgrade_handler_cls, + connection, + connection->read_buffer, + rbo, + connection->socket_fd, + urh); + /* As far as MHD is concerned, this connection is + suspended; it will be resumed once we are done + in the #MHD_upgrade_action() function */ + MHD_connection_suspend (connection); + return MHD_YES; +} + + +/** + * Create a response object that can be used for 101 UPGRADE + * responses, for example to implement WebSockets. After sending the + * response, control over the data stream is given to the callback (which + * can then, for example, start some bi-directional communication). + * If the response is queued for multiple connections, the callback + * will be called for each connection. The callback + * will ONLY be called after the response header was successfully passed + * to the OS; if there are communication errors before, the usual MHD + * connection error handling code will be performed. + * + * Setting the correct HTTP code (i.e. MHD_HTTP_SWITCHING_PROTOCOLS) + * and setting correct HTTP headers for the upgrade must be done + * manually (this way, it is possible to implement most existing + * WebSocket versions using this API; in fact, this API might be useful + * for any protocol switch, not just WebSockets). Note that + * draft-ietf-hybi-thewebsocketprotocol-00 cannot be implemented this + * way as the header "HTTP/1.1 101 WebSocket Protocol Handshake" + * cannot be generated; instead, MHD will always produce "HTTP/1.1 101 + * Switching Protocols" (if the response code 101 is used). + * + * As usual, the response object can be extended with header + * information and then be used any number of times (as long as the + * header information is not connection-specific). + * + * @param upgrade_handler function to call with the 'upgraded' socket + * @param upgrade_handler_cls closure for @a upgrade_handler + * @return NULL on error (i.e. invalid arguments, out of memory) + */ +_MHD_EXTERN struct MHD_Response * +MHD_create_response_for_upgrade (MHD_UpgradeHandler upgrade_handler, + void *upgrade_handler_cls) +{ + struct MHD_Response *response; + + if (NULL == upgrade_header) + return NULL; /* invalid request */ + response = malloc (sizeof (struct MHD_Response)); + if (NULL == response) + return NULL; + memset (response, 0, sizeof (struct MHD_Response)); + if (! MHD_mutex_init_ (&response->mutex)) + { + free (response); + return NULL; + } + urh->response = response; + response->upgrade_handler = upgrade_handler; + response->upgrade_handler_cls = upgrade_handler_cls; + response->total_size = MHD_SIZE_UNKNOWN; + response->reference_count = 1; + return response; +} +#endif + + /** * Destroy a response object and associated resources. Note that * libmicrohttpd may keep some of the resources around if the response @@ -601,6 +817,11 @@ MHD_destroy_response (struct MHD_Response *response) } +/** + * Increments the reference counter for the @a response. + * + * @param response object to modify + */ void MHD_increment_response_rc (struct MHD_Response *response) { diff --git a/src/microhttpd/response.h b/src/microhttpd/response.h index 5785ec96..a13683d1 100644 --- a/src/microhttpd/response.h +++ b/src/microhttpd/response.h @@ -28,8 +28,9 @@ #define RESPONSE_H /** - * Increment response RC. Should this be part of the - * public API? + * Increments the reference counter for the @a response. + * + * @param response object to modify */ void MHD_increment_response_rc (struct MHD_Response *response); |