commit 6450f55f0c18d7a4b2fac829d9eff5d567348d19
parent 552f3aa7b23b958c8ad275fbf28682f229dbc4f7
Author: Christian Grothoff <christian@grothoff.org>
Date: Fri, 16 Feb 2018 06:30:29 +0100
add upgrade_process.c
Diffstat:
15 files changed, 517 insertions(+), 15 deletions(-)
diff --git a/src/gnutls/check_record_pending.c b/src/gnutls/check_record_pending.c
@@ -0,0 +1,6 @@
+ enum MHD_Bool
+ (*check_record_pending)(void *cls,
+ struct MHD_TLS_ConnectionState *cs);
+
+see:
+gnutls_record_check_pending (connection->tls_session)
diff --git a/src/gnutls/recv.c b/src/gnutls/recv.c
@@ -0,0 +1,5 @@
+recv:
+
+res = gnutls_record_recv (connection->tls_session,
+ &urh->in_buffer[urh->in_buffer_used],
+ buf_size);
diff --git a/src/gnutls/send.c b/src/gnutls/send.c
@@ -0,0 +1,11 @@
+ ssize_t
+ (*send)(void *cls,
+ struct MHD_TLS_ConnectionState *cs,
+ const void *buf,
+ size_t buf_size);
+
+
+see:
+ res = gnutls_record_send (connection->tls_session,
+ urh->out_buffer,
+ data_size);
diff --git a/src/gnutls/strerror.c b/src/gnutls/strerror.c
@@ -0,0 +1,6 @@
+ const char *
+ (*strerror)(void *cls,
+ int ec);
+
+see:
+gnutls_strerror (ec));
diff --git a/src/include/microhttpd2.h b/src/include/microhttpd2.h
@@ -422,6 +422,13 @@ enum MHD_StatusCode
*/
MHD_SC_POOL_MALLOC_FAILURE = 30010,
+ /**
+ * We failed to forward data from a Web socket to the
+ * application to the remote side due to the socket
+ * being closed prematurely. (May be transient.)
+ */
+ MHD_SC_UPGRADE_FORWARD_INCOMPLETE = 30011,
+
/**
* MHD does not support the requested combination of
diff --git a/src/include/microhttpd_tls.h b/src/include/microhttpd_tls.h
@@ -91,6 +91,28 @@ struct MHD_TLS_Plugin
...);
+ ssize_t
+ (*send)(void *cls,
+ struct MHD_TLS_ConnectionState *cs,
+ const void *buf,
+ size_t buf_size);
+
+
+ ssize_t
+ (*recv)(void *cls,
+ struct MHD_TLS_ConnectionState *cs,
+ void *buf,
+ size_t buf_size);
+
+
+ const char *
+ (*strerror)(void *cls,
+ int ec);
+
+ enum MHD_Bool
+ (*check_record_pending)(void *cls,
+ struct MHD_TLS_ConnectionState *cs);
+
enum MHD_Bool
(*shutdown_connection) (void *cls,
struct MHD_TLS_ConnectionState *cs);
diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am
@@ -100,6 +100,7 @@ libmicrohttpd_la_SOURCES = \
request_info.c \
request_resume.c \
sysfdsetsize.c sysfdsetsize.h \
+ upgrade_process.c upgrade_process.h \
panic.c \
version.c
diff --git a/src/lib/connection_add.c b/src/lib/connection_add.c
@@ -23,6 +23,8 @@
*/
#include "internal.h"
#include "connection_add.h"
+#include "connection_close.h"
+#include "connection_finish_forward.h"
#include "connection_update_last_activity.h"
#include "daemon_ip_limit.h"
#include "daemon_select.h"
@@ -42,7 +44,6 @@ static void
thread_main_connection_upgrade (struct MHD_Connection *con)
{
#ifdef HTTPS_SUPPORT
- struct MHD_UpgradeResponseHandle *urh = con->request.urh;
struct MHD_Daemon *daemon = con->daemon;
/* Here, we need to bi-directionally forward
diff --git a/src/lib/daemon_epoll.c b/src/lib/daemon_epoll.c
@@ -24,7 +24,9 @@
*/
#include "internal.h"
#include "daemon_epoll.h"
+#include "upgrade_process.h"
#include "request_resume.h"
+#include "connection_add.h"
#include "connection_finish_forward.h"
#ifdef EPOLL_SUPPORT
@@ -170,7 +172,7 @@ run_epoll_for_upgrade (struct MHD_Daemon *daemon)
while (NULL != (pos = prev))
{
prev = pos->prevE;
- process_urh (pos);
+ MHD_upgrade_response_handle_process_ (pos);
if (! is_urh_ready(pos))
{
EDLL_remove (daemon->eready_urh_head,
diff --git a/src/lib/daemon_ip_limit.c b/src/lib/daemon_ip_limit.c
@@ -24,6 +24,12 @@
*/
#include "internal.h"
#include "daemon_ip_limit.h"
+#if HAVE_SEARCH_H
+#include <search.h>
+#else
+#include "tsearch.h"
+#endif
+
/**
* Maintain connection count for single address.
diff --git a/src/lib/daemon_poll.c b/src/lib/daemon_poll.c
@@ -23,6 +23,7 @@
*/
#include "internal.h"
#include "daemon_poll.h"
+#include "upgrade_process.h"
#include "request_resume.h"
#include "connection_add.h"
#include "connection_finish_forward.h"
@@ -315,7 +316,7 @@ MHD_daemon_poll_all_ (struct MHD_Daemon *daemon,
urh_from_pollfd (urh,
&(p[poll_server+i]));
i += 2;
- process_urh (urh);
+ MHD_upgrade_response_handle_process_ (urh);
/* Finished forwarding? */
if ( (0 == urh->in_buffer_size) &&
(0 == urh->out_buffer_size) &&
@@ -508,7 +509,7 @@ MHD_daemon_upgrade_connection_with_poll_ (struct MHD_Connection *con)
}
urh_from_pollfd (urh,
p);
- process_urh (urh);
+ MHD_upgrade_response_handle_process_ (urh);
}
}
#endif
diff --git a/src/lib/daemon_run.c b/src/lib/daemon_run.c
@@ -49,6 +49,8 @@
enum MHD_StatusCode
MHD_daemon_run (struct MHD_Daemon *daemon)
{
+ enum MHD_StatusCode sc;
+
if (daemon->shutdown)
return MHD_SC_DAEMON_ALREADY_SHUTDOWN;
if (MHD_TM_EXTERNAL_EVENT_LOOP != daemon->threading_model)
@@ -56,22 +58,21 @@ MHD_daemon_run (struct MHD_Daemon *daemon)
switch (daemon->event_loop_syscall)
{
case MHD_ELS_POLL:
- MHD_daemon_poll_ (daemon,
- MHD_NO);
+ sc = MHD_daemon_poll_ (daemon,
+ MHD_NO);
MHD_cleanup_connections (daemon);
- break;
+ return sc;
#ifdef EPOLL_SUPPORT
case MHD_ELS_EPOLL:
- MHD_daemon_epoll_ (daemon,
- MHD_NO);
+ sc = MHD_daemon_epoll_ (daemon,
+ MHD_NO);
MHD_cleanup_connections (daemon);
- break;
+ return sc;
#endif
case MHD_ELS_SELECT:
- MHD_daemon_select_ (daemon,
- MHD_NO);
+ return MHD_daemon_select_ (daemon,
+ MHD_NO);
/* MHD_select does MHD_cleanup_connections already */
- break;
default:
return MHD_SC_CONFIGURATION_UNEXPECTED_ELS;
}
diff --git a/src/lib/daemon_select.c b/src/lib/daemon_select.c
@@ -25,6 +25,7 @@
#include "internal.h"
#include "daemon_select.h"
#include "request_resume.h"
+#include "upgrade_process.h"
#include "connection_finish_forward.h"
@@ -461,7 +462,7 @@ internal_run_from_select (struct MHD_Daemon *daemon,
write_fd_set,
except_fd_set);
/* call generic forwarding function for passing data */
- process_urh (urh);
+ MHD_upgrade_response_handle_process_ (urh);
/* Finished forwarding? */
if ( (0 == urh->in_buffer_size) &&
(0 == urh->out_buffer_size) &&
@@ -566,7 +567,7 @@ MHD_daemon_upgrade_connection_with_select_ (struct MHD_Connection *con)
&rs,
&ws,
&es);
- process_urh (urh);
+ MHD_upgrade_response_handle_process_ (urh);
}
}
#endif
diff --git a/src/lib/upgrade_process.c b/src/lib/upgrade_process.c
@@ -0,0 +1,388 @@
+/*
+ This file is part of libmicrohttpd
+ Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ 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 lib/upgrade_process.c
+ * @brief function to process upgrade activity (over TLS)
+ * @author Christian Grothoff
+ */
+#include "internal.h"
+#include "upgrade_process.h"
+
+
+#if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT)
+/**
+ * Performs bi-directional forwarding on upgraded HTTPS connections
+ * based on the readyness state stored in the @a urh handle.
+ * @remark To be called only from thread that process
+ * connection's recv(), send() and response.
+ *
+ * @param urh handle to process
+ */
+void
+MHD_upgrade_response_handle_process_ (struct MHD_UpgradeResponseHandle *urh)
+{
+ /* Help compiler to optimize:
+ * pointers to 'connection' and 'daemon' are not changed
+ * during this processing, so no need to chain dereference
+ * each time. */
+ struct MHD_Connection * const connection = urh->connection;
+ struct MHD_Daemon * const daemon = connection->daemon;
+ /* Prevent data races: use same value of 'was_closed' throughout
+ * this function. If 'was_closed' changed externally in the middle
+ * of processing - it will be processed on next iteration. */
+ bool was_closed;
+ struct MHD_TLS_Plugin *tls = daemon->tls_api;
+
+ if (daemon->shutdown)
+ {
+ /* Daemon shutting down, application will not receive any more data. */
+#ifdef HAVE_MESSAGES
+ if (! urh->was_closed)
+ {
+ MHD_DLOG (daemon,
+ MHD_SC_DAEMON_ALREADY_SHUTDOWN,
+ _("Initiated daemon shutdown while \"upgraded\" connection was not closed.\n"));
+ }
+#endif
+ urh->was_closed = true;
+ }
+ was_closed = urh->was_closed;
+ if (was_closed)
+ {
+ /* Application was closed connections: no more data
+ * can be forwarded to application socket. */
+ if (0 < urh->in_buffer_used)
+ {
+#ifdef HAVE_MESSAGES
+ MHD_DLOG (daemon,
+ MHD_SC_UPGRADE_FORWARD_INCOMPLETE,
+ _("Failed to forward to application " MHD_UNSIGNED_LONG_LONG_PRINTF \
+ " bytes of data received from remote side: application shut down socket\n"),
+ (MHD_UNSIGNED_LONG_LONG) urh->in_buffer_used);
+#endif
+
+ }
+ /* If application signaled MHD about socket closure then
+ * check for any pending data even if socket is not marked
+ * as 'ready' (signal may arrive after poll()/select()).
+ * Socketpair for forwarding is always in non-blocking mode
+ * so no risk that recv() will block the thread. */
+ if (0 != urh->out_buffer_size)
+ urh->mhd.celi |= MHD_EPOLL_STATE_READ_READY;
+ /* Discard any data received form remote. */
+ urh->in_buffer_used = 0;
+ /* Do not try to push data to application. */
+ urh->mhd.celi &= ~MHD_EPOLL_STATE_WRITE_READY;
+ /* Reading from remote client is not required anymore. */
+ urh->in_buffer_size = 0;
+ urh->app.celi &= ~MHD_EPOLL_STATE_READ_READY;
+ connection->tls_read_ready = false;
+ }
+
+ /* On some platforms (W32, possibly Darwin) failed send() (send() will always
+ * fail after remote disconnect was detected) may discard data in system
+ * buffers received by system but not yet read by recv().
+ * So, before trying send() on any socket, recv() must be performed at first
+ * otherwise last part of incoming data may be lost. */
+
+ /* If disconnect or error was detected - try to read from socket
+ * to dry data possibly pending is system buffers. */
+ if (0 != (MHD_EPOLL_STATE_ERROR & urh->app.celi))
+ urh->app.celi |= MHD_EPOLL_STATE_READ_READY;
+ if (0 != (MHD_EPOLL_STATE_ERROR & urh->mhd.celi))
+ urh->mhd.celi |= MHD_EPOLL_STATE_READ_READY;
+
+ /*
+ * handle reading from remote TLS client
+ */
+ if ( ( (0 != (MHD_EPOLL_STATE_READ_READY & urh->app.celi)) ||
+ (connection->tls_read_ready) ) &&
+ (urh->in_buffer_used < urh->in_buffer_size) )
+ {
+ ssize_t res;
+ size_t buf_size;
+
+ buf_size = urh->in_buffer_size - urh->in_buffer_used;
+ if (buf_size > SSIZE_MAX)
+ buf_size = SSIZE_MAX;
+
+ connection->tls_read_ready = false;
+ res = tls->recv (tls->cls,
+ connection->tls_cs,
+ &urh->in_buffer[urh->in_buffer_used],
+ buf_size);
+ if (0 >= res)
+ {
+ // FIXME: define GNUTLS-independent error codes!
+ if (GNUTLS_E_INTERRUPTED != res)
+ {
+ urh->app.celi &= ~MHD_EPOLL_STATE_READ_READY;
+ if (GNUTLS_E_AGAIN != res)
+ {
+ /* Unrecoverable error on socket was detected or
+ * socket was disconnected/shut down. */
+ /* Stop trying to read from this TLS socket. */
+ urh->in_buffer_size = 0;
+ }
+ }
+ }
+ else /* 0 < res */
+ {
+ urh->in_buffer_used += res;
+ if (buf_size > (size_t)res)
+ urh->app.celi &= ~MHD_EPOLL_STATE_READ_READY;
+ else if (0 < tls->check_record_pending (tls->cls,
+ connection->tls_cs))
+ connection->tls_read_ready = true;
+ }
+ if (MHD_EPOLL_STATE_ERROR ==
+ ((MHD_EPOLL_STATE_ERROR | MHD_EPOLL_STATE_READ_READY) & urh->app.celi))
+ {
+ /* Unrecoverable error on socket was detected and all
+ * pending data was read from system buffers. */
+ /* Stop trying to read from this TLS socket. */
+ urh->in_buffer_size = 0;
+ }
+ }
+
+ /*
+ * handle reading from application
+ */
+ if ( (0 != (MHD_EPOLL_STATE_READ_READY & urh->mhd.celi)) &&
+ (urh->out_buffer_used < urh->out_buffer_size) )
+ {
+ ssize_t res;
+ size_t buf_size;
+
+ buf_size = urh->out_buffer_size - urh->out_buffer_used;
+ if (buf_size > MHD_SCKT_SEND_MAX_SIZE_)
+ buf_size = MHD_SCKT_SEND_MAX_SIZE_;
+
+ res = MHD_recv_ (urh->mhd.socket,
+ &urh->out_buffer[urh->out_buffer_used],
+ buf_size);
+ if (0 >= res)
+ {
+ const int err = MHD_socket_get_error_ ();
+ if ((0 == res) ||
+ ((! MHD_SCKT_ERR_IS_EINTR_ (err)) &&
+ (! MHD_SCKT_ERR_IS_LOW_RESOURCES_(err))))
+ {
+ urh->mhd.celi &= ~MHD_EPOLL_STATE_READ_READY;
+ if ((0 == res) ||
+ (was_closed) ||
+ (0 != (MHD_EPOLL_STATE_ERROR & urh->mhd.celi)) ||
+ (! MHD_SCKT_ERR_IS_EAGAIN_ (err)))
+ {
+ /* Socket disconnect/shutdown was detected;
+ * Application signaled about closure of 'upgraded' socket;
+ * or persistent / unrecoverable error. */
+ /* Do not try to pull more data from application. */
+ urh->out_buffer_size = 0;
+ }
+ }
+ }
+ else /* 0 < res */
+ {
+ urh->out_buffer_used += res;
+ if (buf_size > (size_t)res)
+ urh->mhd.celi &= ~MHD_EPOLL_STATE_READ_READY;
+ }
+ if ( (0 == (MHD_EPOLL_STATE_READ_READY & urh->mhd.celi)) &&
+ ( (0 != (MHD_EPOLL_STATE_ERROR & urh->mhd.celi)) ||
+ (was_closed) ) )
+ {
+ /* Unrecoverable error on socket was detected and all
+ * pending data was read from system buffers. */
+ /* Do not try to pull more data from application. */
+ urh->out_buffer_size = 0;
+ }
+ }
+
+ /*
+ * handle writing to remote HTTPS client
+ */
+ if ( (0 != (MHD_EPOLL_STATE_WRITE_READY & urh->app.celi)) &&
+ (urh->out_buffer_used > 0) )
+ {
+ ssize_t res;
+ size_t data_size;
+
+ data_size = urh->out_buffer_used;
+ if (data_size > SSIZE_MAX)
+ data_size = SSIZE_MAX;
+
+ res = tls->send (tls->cls,
+ connection->tls_cs,
+ urh->out_buffer,
+ data_size);
+ if (0 >= res)
+ {
+ // FIXME: define GNUTLS-independent error codes!
+ if (GNUTLS_E_INTERRUPTED != res)
+ {
+ urh->app.celi &= ~MHD_EPOLL_STATE_WRITE_READY;
+ if (GNUTLS_E_INTERRUPTED != res)
+ {
+ /* TLS connection shut down or
+ * persistent / unrecoverable error. */
+#ifdef HAVE_MESSAGES
+ MHD_DLOG (daemon,
+ MHD_SC_UPGRADE_FORWARD_INCOMPLETE,
+ _("Failed to forward to remote client " MHD_UNSIGNED_LONG_LONG_PRINTF \
+ " bytes of data received from application: %s\n"),
+ (MHD_UNSIGNED_LONG_LONG) urh->out_buffer_used,
+ tls->strerror (tls->cls,
+ res));
+#endif
+ /* Discard any data unsent to remote. */
+ urh->out_buffer_used = 0;
+ /* Do not try to pull more data from application. */
+ urh->out_buffer_size = 0;
+ urh->mhd.celi &= ~MHD_EPOLL_STATE_READ_READY;
+ }
+ }
+ }
+ else /* 0 < res */
+ {
+ const size_t next_out_buffer_used = urh->out_buffer_used - res;
+ if (0 != next_out_buffer_used)
+ {
+ memmove (urh->out_buffer,
+ &urh->out_buffer[res],
+ next_out_buffer_used);
+ if (data_size > (size_t)res)
+ urh->app.celi &= ~MHD_EPOLL_STATE_WRITE_READY;
+ }
+ urh->out_buffer_used = next_out_buffer_used;
+ }
+ if ( (0 == urh->out_buffer_used) &&
+ (0 != (MHD_EPOLL_STATE_ERROR & urh->app.celi)) )
+ {
+ /* Unrecoverable error on socket was detected and all
+ * pending data was sent to remote. */
+ /* Do not try to send to remote anymore. */
+ urh->app.celi &= ~MHD_EPOLL_STATE_WRITE_READY;
+ /* Do not try to pull more data from application. */
+ urh->out_buffer_size = 0;
+ urh->mhd.celi &= ~MHD_EPOLL_STATE_READ_READY;
+ }
+ }
+
+ /*
+ * handle writing to application
+ */
+ if ( (0 != (MHD_EPOLL_STATE_WRITE_READY & urh->mhd.celi)) &&
+ (urh->in_buffer_used > 0) )
+ {
+ ssize_t res;
+ size_t data_size;
+
+ data_size = urh->in_buffer_used;
+ if (data_size > MHD_SCKT_SEND_MAX_SIZE_)
+ data_size = MHD_SCKT_SEND_MAX_SIZE_;
+
+ res = MHD_send_ (urh->mhd.socket,
+ urh->in_buffer,
+ data_size);
+ if (0 >= res)
+ {
+ const int err = MHD_socket_get_error_ ();
+ if ( (! MHD_SCKT_ERR_IS_EINTR_ (err)) &&
+ (! MHD_SCKT_ERR_IS_LOW_RESOURCES_(err)) )
+ {
+ urh->mhd.celi &= ~MHD_EPOLL_STATE_WRITE_READY;
+ if (! MHD_SCKT_ERR_IS_EAGAIN_ (err))
+ {
+ /* Socketpair connection shut down or
+ * persistent / unrecoverable error. */
+#ifdef HAVE_MESSAGES
+ MHD_DLOG (daemon,
+ MHD_SC_UPGRADE_FORWARD_INCOMPLETE,
+ _("Failed to forward to application " MHD_UNSIGNED_LONG_LONG_PRINTF \
+ " bytes of data received from remote side: %s\n"),
+ (MHD_UNSIGNED_LONG_LONG) urh->in_buffer_used,
+ MHD_socket_strerr_ (err));
+#endif
+ /* Discard any data received form remote. */
+ urh->in_buffer_used = 0;
+ /* Reading from remote client is not required anymore. */
+ urh->in_buffer_size = 0;
+ urh->app.celi &= ~MHD_EPOLL_STATE_READ_READY;
+ connection->tls_read_ready = false;
+ }
+ }
+ }
+ else /* 0 < res */
+ {
+ const size_t next_in_buffer_used = urh->in_buffer_used - res;
+ if (0 != next_in_buffer_used)
+ {
+ memmove (urh->in_buffer,
+ &urh->in_buffer[res],
+ next_in_buffer_used);
+ if (data_size > (size_t)res)
+ urh->mhd.celi &= ~MHD_EPOLL_STATE_WRITE_READY;
+ }
+ urh->in_buffer_used = next_in_buffer_used;
+ }
+ if ( (0 == urh->in_buffer_used) &&
+ (0 != (MHD_EPOLL_STATE_ERROR & urh->mhd.celi)) )
+ {
+ /* Do not try to push data to application. */
+ urh->mhd.celi &= ~MHD_EPOLL_STATE_WRITE_READY;
+ /* Reading from remote client is not required anymore. */
+ urh->in_buffer_size = 0;
+ urh->app.celi &= ~MHD_EPOLL_STATE_READ_READY;
+ connection->tls_read_ready = false;
+ }
+ }
+
+ /* Check whether data is present in TLS buffers
+ * and incoming forward buffer have some space. */
+ if ( (connection->tls_read_ready) &&
+ (urh->in_buffer_used < urh->in_buffer_size) &&
+ (MHD_TM_THREAD_PER_CONNECTION != daemon->threading_model) )
+ daemon->data_already_pending = true;
+
+ if ( (daemon->shutdown) &&
+ ( (0 != urh->out_buffer_size) ||
+ (0 != urh->out_buffer_used) ) )
+ {
+ /* Daemon shutting down, discard any remaining forward data. */
+#ifdef HAVE_MESSAGES
+ if (0 < urh->out_buffer_used)
+ MHD_DLOG (daemon,
+ MHD_SC_UPGRADE_FORWARD_INCOMPLETE,
+ _("Failed to forward to remote client " MHD_UNSIGNED_LONG_LONG_PRINTF \
+ " bytes of data received from application: daemon shut down\n"),
+ (MHD_UNSIGNED_LONG_LONG) urh->out_buffer_used);
+#endif
+ /* Discard any data unsent to remote. */
+ urh->out_buffer_used = 0;
+ /* Do not try to sent to remote anymore. */
+ urh->app.celi &= ~MHD_EPOLL_STATE_WRITE_READY;
+ /* Do not try to pull more data from application. */
+ urh->out_buffer_size = 0;
+ urh->mhd.celi &= ~MHD_EPOLL_STATE_READ_READY;
+ }
+}
+#endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT */
+
+/* end of upgrade_process.c */
diff --git a/src/lib/upgrade_process.h b/src/lib/upgrade_process.h
@@ -0,0 +1,44 @@
+/*
+ This file is part of libmicrohttpd
+ Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ 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 lib/upgrade_process.h
+ * @brief function to process upgrade activity (over TLS)
+ * @author Christian Grothoff
+ */
+#ifndef UPGRADE_PROCESS_H
+#define UPGRADE_PROCESS_H
+
+
+#if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT)
+/**
+ * Performs bi-directional forwarding on upgraded HTTPS connections
+ * based on the readyness state stored in the @a urh handle.
+ * @remark To be called only from thread that process
+ * connection's recv(), send() and response.
+ *
+ * @param urh handle to process
+ */
+void
+MHD_upgrade_response_handle_process_ (struct MHD_UpgradeResponseHandle *urh)
+ MHD_NONNULL(1);
+
+
+#endif
+
+#endif