blob: 337c75d58be0c6f1b0127d5db68ff5276ae50bb9 [file] [log] [blame]
/*
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/daemon_select.c
* @brief function to run select()-based event loop of a daemon
* @author Christian Grothoff
*/
#include "internal.h"
#include "connection_add.h"
#include "connection_call_handlers.h"
#include "connection_cleanup.h"
#include "connection_finish_forward.h"
#include "daemon_select.h"
#include "daemon_epoll.h"
#include "request_resume.h"
#include "upgrade_process.h"
/**
* We defined a macro with the same name as a function we
* are implementing here. Need to undef the macro to avoid
* causing a conflict.
*/
#undef MHD_daemon_get_fdset
/**
* Obtain the `select()` sets for this daemon. Daemon's FDs will be
* added to fd_sets. To get only daemon FDs in fd_sets, call FD_ZERO
* for each fd_set before calling this function. FD_SETSIZE is assumed
* to be platform's default.
*
* This function should only be called in when MHD is configured to
* use external select with 'select()' or with 'epoll'. In the latter
* case, it will only add the single 'epoll()' file descriptor used by
* MHD to the sets. It's necessary to use #MHD_daemon_get_timeout() in
* combination with this function.
*
* This function must be called only for daemon started without
* #MHD_USE_INTERNAL_POLLING_THREAD flag.
*
* @param daemon daemon to get sets from
* @param read_fd_set read set
* @param write_fd_set write set
* @param except_fd_set except set
* @param max_fd increased to largest FD added (if larger
* than existing value); can be NULL
* @return #MHD_SC_OK on success, otherwise error code
* @ingroup event
*/
enum MHD_StatusCode
MHD_daemon_get_fdset (struct MHD_Daemon *daemon,
fd_set *read_fd_set,
fd_set *write_fd_set,
fd_set *except_fd_set,
MHD_socket *max_fd)
{
return MHD_daemon_get_fdset2 (daemon,
read_fd_set,
write_fd_set,
except_fd_set,
max_fd,
_MHD_SYS_DEFAULT_FD_SETSIZE);
}
#if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT)
/**
* Obtain the select() file descriptor sets for the
* given @a urh.
*
* @param urh upgrade handle to wait for
* @param[out] rs read set to initialize
* @param[out] ws write set to initialize
* @param[out] es except set to initialize
* @param[out] max_fd maximum FD to update
* @param fd_setsize value of FD_SETSIZE
* @return true on success, false on error
*/
static bool
urh_to_fdset (struct MHD_UpgradeResponseHandle *urh,
fd_set *rs,
fd_set *ws,
fd_set *es,
MHD_socket *max_fd,
unsigned int fd_setsize)
{
const MHD_socket conn_sckt = urh->connection->socket_fd;
const MHD_socket mhd_sckt = urh->mhd.socket;
bool res = true;
/* Do not add to 'es' only if socket is closed
* or not used anymore. */
if (MHD_INVALID_SOCKET != conn_sckt)
{
if ( (urh->in_buffer_used < urh->in_buffer_size) &&
(! MHD_add_to_fd_set_ (conn_sckt,
rs,
max_fd,
fd_setsize)) )
res = false;
if ( (0 != urh->out_buffer_used) &&
(! MHD_add_to_fd_set_ (conn_sckt,
ws,
max_fd,
fd_setsize)) )
res = false;
/* Do not monitor again for errors if error was detected before as
* error state is remembered. */
if ((0 == (urh->app.celi & MHD_EPOLL_STATE_ERROR)) &&
((0 != urh->in_buffer_size) ||
(0 != urh->out_buffer_size) ||
(0 != urh->out_buffer_used)))
MHD_add_to_fd_set_ (conn_sckt,
es,
max_fd,
fd_setsize);
}
if (MHD_INVALID_SOCKET != mhd_sckt)
{
if ( (urh->out_buffer_used < urh->out_buffer_size) &&
(! MHD_add_to_fd_set_ (mhd_sckt,
rs,
max_fd,
fd_setsize)) )
res = false;
if ( (0 != urh->in_buffer_used) &&
(! MHD_add_to_fd_set_ (mhd_sckt,
ws,
max_fd,
fd_setsize)) )
res = false;
/* Do not monitor again for errors if error was detected before as
* error state is remembered. */
if ((0 == (urh->mhd.celi & MHD_EPOLL_STATE_ERROR)) &&
((0 != urh->out_buffer_size) ||
(0 != urh->in_buffer_size) ||
(0 != urh->in_buffer_used)))
MHD_add_to_fd_set_ (mhd_sckt,
es,
max_fd,
fd_setsize);
}
return res;
}
#endif
/**
* Internal version of #MHD_daemon_get_fdset2().
*
* @param daemon daemon to get sets from
* @param read_fd_set read set
* @param write_fd_set write set
* @param except_fd_set except set
* @param max_fd increased to largest FD added (if larger
* than existing value); can be NULL
* @param fd_setsize value of FD_SETSIZE
* @return #MHD_SC_OK on success
* @ingroup event
*/
static enum MHD_StatusCode
internal_get_fdset2 (struct MHD_Daemon *daemon,
fd_set *read_fd_set,
fd_set *write_fd_set,
fd_set *except_fd_set,
MHD_socket *max_fd,
unsigned int fd_setsize)
{
struct MHD_Connection *pos;
struct MHD_Connection *posn;
enum MHD_StatusCode result = MHD_SC_OK;
MHD_socket ls;
if (daemon->shutdown)
return MHD_SC_DAEMON_ALREADY_SHUTDOWN;
ls = daemon->listen_socket;
if ( (MHD_INVALID_SOCKET != ls) &&
(! daemon->was_quiesced) &&
(! MHD_add_to_fd_set_ (ls,
read_fd_set,
max_fd,
fd_setsize)) )
result = MHD_SC_SOCKET_OUTSIDE_OF_FDSET_RANGE;
/* Add all sockets to 'except_fd_set' as well to watch for
* out-of-band data. However, ignore errors if INFO_READ
* or INFO_WRITE sockets will not fit 'except_fd_set'. */
/* Start from oldest connections. Make sense for W32 FDSETs. */
for (pos = daemon->connections_tail; NULL != pos; pos = posn)
{
posn = pos->prev;
switch (pos->request.event_loop_info)
{
case MHD_EVENT_LOOP_INFO_READ:
if (! MHD_add_to_fd_set_ (pos->socket_fd,
read_fd_set,
max_fd,
fd_setsize))
result = MHD_SC_SOCKET_OUTSIDE_OF_FDSET_RANGE;
#ifdef MHD_POSIX_SOCKETS
MHD_add_to_fd_set_ (pos->socket_fd,
except_fd_set,
max_fd,
fd_setsize);
#endif /* MHD_POSIX_SOCKETS */
break;
case MHD_EVENT_LOOP_INFO_WRITE:
if (! MHD_add_to_fd_set_ (pos->socket_fd,
write_fd_set,
max_fd,
fd_setsize))
result = MHD_SC_SOCKET_OUTSIDE_OF_FDSET_RANGE;
#ifdef MHD_POSIX_SOCKETS
MHD_add_to_fd_set_ (pos->socket_fd,
except_fd_set,
max_fd,
fd_setsize);
#endif /* MHD_POSIX_SOCKETS */
break;
case MHD_EVENT_LOOP_INFO_BLOCK:
if ( (NULL == except_fd_set) ||
! MHD_add_to_fd_set_ (pos->socket_fd,
except_fd_set,
max_fd,
fd_setsize))
result = MHD_SC_SOCKET_OUTSIDE_OF_FDSET_RANGE;
break;
case MHD_EVENT_LOOP_INFO_CLEANUP:
/* this should never happen */
break;
}
}
#ifdef MHD_WINSOCK_SOCKETS
/* W32 use limited array for fd_set so add INFO_READ/INFO_WRITE sockets
* only after INFO_BLOCK sockets to ensure that INFO_BLOCK sockets will
* not be pushed out. */
for (pos = daemon->connections_tail; NULL != pos; pos = posn)
{
posn = pos->prev;
MHD_add_to_fd_set_ (pos->socket_fd,
except_fd_set,
max_fd,
fd_setsize);
}
#endif /* MHD_WINSOCK_SOCKETS */
#if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT)
{
struct MHD_UpgradeResponseHandle *urh;
for (urh = daemon->urh_tail; NULL != urh; urh = urh->prev)
{
if (! urh_to_fdset (urh,
read_fd_set,
write_fd_set,
except_fd_set,
max_fd,
fd_setsize))
result = MHD_SC_SOCKET_OUTSIDE_OF_FDSET_RANGE;
}
}
#endif
return result;
}
/**
* Obtain the `select()` sets for this daemon. Daemon's FDs will be
* added to fd_sets. To get only daemon FDs in fd_sets, call FD_ZERO
* for each fd_set before calling this function.
*
* Passing custom FD_SETSIZE as @a fd_setsize allow usage of
* larger/smaller than platform's default fd_sets.
*
* This function should only be called in when MHD is configured to
* use external select with 'select()' or with 'epoll'. In the latter
* case, it will only add the single 'epoll' file descriptor used by
* MHD to the sets. It's necessary to use #MHD_get_timeout() in
* combination with this function.
*
* This function must be called only for daemon started
* without #MHD_USE_INTERNAL_POLLING_THREAD flag.
*
* @param daemon daemon to get sets from
* @param read_fd_set read set
* @param write_fd_set write set
* @param except_fd_set except set
* @param max_fd increased to largest FD added (if larger
* than existing value); can be NULL
* @param fd_setsize value of FD_SETSIZE
* @return #MHD_SC_OK on success, otherwise error code
* @ingroup event
*/
enum MHD_StatusCode
MHD_daemon_get_fdset2 (struct MHD_Daemon *daemon,
fd_set *read_fd_set,
fd_set *write_fd_set,
fd_set *except_fd_set,
MHD_socket *max_fd,
unsigned int fd_setsize)
{
if ( (MHD_TM_EXTERNAL_EVENT_LOOP != daemon->threading_mode) ||
(MHD_ELS_POLL == daemon->event_loop_syscall) )
return MHD_SC_CONFIGURATION_MISMATCH_FOR_GET_FDSET;
#ifdef EPOLL_SUPPORT
if (MHD_ELS_EPOLL == daemon->event_loop_syscall)
{
if (daemon->shutdown)
return MHD_SC_DAEMON_ALREADY_SHUTDOWN;
/* we're in epoll mode, use the epoll FD as a stand-in for
the entire event set */
return MHD_add_to_fd_set_ (daemon->epoll_fd,
read_fd_set,
max_fd,
fd_setsize)
? MHD_SC_OK
: MHD_SC_SOCKET_OUTSIDE_OF_FDSET_RANGE;
}
#endif
return internal_get_fdset2 (daemon,
read_fd_set,
write_fd_set,
except_fd_set,
max_fd,
fd_setsize);
}
#if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT)
/**
* Update the @a urh based on the ready FDs in
* the @a rs, @a ws, and @a es.
*
* @param urh upgrade handle to update
* @param rs read result from select()
* @param ws write result from select()
* @param es except result from select()
*/
static void
urh_from_fdset (struct MHD_UpgradeResponseHandle *urh,
const fd_set *rs,
const fd_set *ws,
const fd_set *es)
{
const MHD_socket conn_sckt = urh->connection->socket_fd;
const MHD_socket mhd_sckt = urh->mhd.socket;
/* Reset read/write ready, preserve error state. */
urh->app.celi &= (~MHD_EPOLL_STATE_READ_READY & ~MHD_EPOLL_STATE_WRITE_READY);
urh->mhd.celi &= (~MHD_EPOLL_STATE_READ_READY & ~MHD_EPOLL_STATE_WRITE_READY);
if (MHD_INVALID_SOCKET != conn_sckt)
{
if (FD_ISSET (conn_sckt, rs))
urh->app.celi |= MHD_EPOLL_STATE_READ_READY;
if (FD_ISSET (conn_sckt, ws))
urh->app.celi |= MHD_EPOLL_STATE_WRITE_READY;
if (FD_ISSET (conn_sckt, es))
urh->app.celi |= MHD_EPOLL_STATE_ERROR;
}
if ((MHD_INVALID_SOCKET != mhd_sckt))
{
if (FD_ISSET (mhd_sckt, rs))
urh->mhd.celi |= MHD_EPOLL_STATE_READ_READY;
if (FD_ISSET (mhd_sckt, ws))
urh->mhd.celi |= MHD_EPOLL_STATE_WRITE_READY;
if (FD_ISSET (mhd_sckt, es))
urh->mhd.celi |= MHD_EPOLL_STATE_ERROR;
}
}
#endif
/**
* Internal version of #MHD_run_from_select().
*
* @param daemon daemon to run select loop for
* @param read_fd_set read set
* @param write_fd_set write set
* @param except_fd_set except set
* @return #MHD_SC_OK on success
* @ingroup event
*/
static enum MHD_StatusCode
internal_run_from_select (struct MHD_Daemon *daemon,
const fd_set *read_fd_set,
const fd_set *write_fd_set,
const fd_set *except_fd_set)
{
MHD_socket ds;
struct MHD_Connection *pos;
struct MHD_Connection *prev;
#if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT)
struct MHD_UpgradeResponseHandle *urh;
struct MHD_UpgradeResponseHandle *urhn;
#endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT */
/* Reset. New value will be set when connections are processed. */
/* Note: no-op for thread-per-connection as it is always false in that mode. */
daemon->data_already_pending = false;
/* Clear ITC to avoid spinning select */
/* Do it before any other processing so new signals
will trigger select again and will be processed */
if ( (MHD_ITC_IS_VALID_ (daemon->itc)) &&
(FD_ISSET (MHD_itc_r_fd_ (daemon->itc),
read_fd_set)) )
MHD_itc_clear_ (daemon->itc);
/* select connection thread handling type */
if ( (MHD_INVALID_SOCKET != (ds = daemon->listen_socket)) &&
(! daemon->was_quiesced) &&
(FD_ISSET (ds,
read_fd_set)) )
(void) MHD_accept_connection_ (daemon);
if (MHD_TM_THREAD_PER_CONNECTION != daemon->threading_mode)
{
/* do not have a thread per connection, process all connections now */
prev = daemon->connections_tail;
while (NULL != (pos = prev))
{
prev = pos->prev;
ds = pos->socket_fd;
if (MHD_INVALID_SOCKET == ds)
continue;
MHD_connection_call_handlers_ (pos,
FD_ISSET (ds,
read_fd_set),
FD_ISSET (ds,
write_fd_set),
FD_ISSET (ds,
except_fd_set));
}
}
#if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT)
/* handle upgraded HTTPS connections */
for (urh = daemon->urh_tail; NULL != urh; urh = urhn)
{
urhn = urh->prev;
/* update urh state based on select() output */
urh_from_fdset (urh,
read_fd_set,
write_fd_set,
except_fd_set);
/* call generic forwarding function for passing data */
MHD_upgrade_response_handle_process_ (urh);
/* Finished forwarding? */
if ( (0 == urh->in_buffer_size) &&
(0 == urh->out_buffer_size) &&
(0 == urh->in_buffer_used) &&
(0 == urh->out_buffer_used) )
{
MHD_connection_finish_forward_ (urh->connection);
urh->clean_ready = true;
/* Resuming will move connection to cleanup list. */
MHD_request_resume (&urh->connection->request);
}
}
#endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT */
MHD_connection_cleanup_ (daemon);
return MHD_SC_OK;
}
#if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT)
/**
* Process upgraded connection with a select loop.
* We are in our own thread, only processing @a con
*
* @param con connection to process
*/
void
MHD_daemon_upgrade_connection_with_select_ (struct MHD_Connection *con)
{
struct MHD_UpgradeResponseHandle *urh = con->request.urh;
while ( (0 != urh->in_buffer_size) ||
(0 != urh->out_buffer_size) ||
(0 != urh->in_buffer_used) ||
(0 != urh->out_buffer_used) )
{
/* use select */
fd_set rs;
fd_set ws;
fd_set es;
MHD_socket max_fd;
int num_ready;
bool result;
FD_ZERO (&rs);
FD_ZERO (&ws);
FD_ZERO (&es);
max_fd = MHD_INVALID_SOCKET;
result = urh_to_fdset (urh,
&rs,
&ws,
&es,
&max_fd,
FD_SETSIZE);
if (! result)
{
#ifdef HAVE_MESSAGES
MHD_DLOG (con->daemon,
MHD_SC_SOCKET_OUTSIDE_OF_FDSET_RANGE,
_ ("Error preparing select.\n"));
#endif
break;
}
/* FIXME: does this check really needed? */
if (MHD_INVALID_SOCKET != max_fd)
{
struct timeval *tvp;
struct timeval tv;
if ( (con->tls_read_ready) &&
(urh->in_buffer_used < urh->in_buffer_size))
{ /* No need to wait if incoming data is already pending in TLS buffers. */
tv.tv_sec = 0;
tv.tv_usec = 0;
tvp = &tv;
}
else
tvp = NULL;
num_ready = MHD_SYS_select_ (max_fd + 1,
&rs,
&ws,
&es,
tvp);
}
else
num_ready = 0;
if (num_ready < 0)
{
const int err = MHD_socket_get_error_ ();
if (MHD_SCKT_ERR_IS_EINTR_ (err))
continue;
#ifdef HAVE_MESSAGES
MHD_DLOG (con->daemon,
MHD_SC_UNEXPECTED_SELECT_ERROR,
_ ("Error during select (%d): `%s'\n"),
err,
MHD_socket_strerr_ (err));
#endif
break;
}
urh_from_fdset (urh,
&rs,
&ws,
&es);
MHD_upgrade_response_handle_process_ (urh);
}
}
#endif
/**
* Run webserver operations. This method should be called by clients
* in combination with #MHD_get_fdset and #MHD_get_timeout() if the
* client-controlled select method is used.
*
* You can use this function instead of #MHD_run if you called
* `select()` on the result from #MHD_get_fdset. File descriptors in
* the sets that are not controlled by MHD will be ignored. Calling
* this function instead of #MHD_run is more efficient as MHD will not
* have to call `select()` again to determine which operations are
* ready.
*
* This function cannot be used with daemon started with
* #MHD_USE_INTERNAL_POLLING_THREAD flag.
*
* @param daemon daemon to run select loop for
* @param read_fd_set read set
* @param write_fd_set write set
* @param except_fd_set except set
* @return #MHD_SC_OK on success
* @ingroup event
*/
enum MHD_StatusCode
MHD_daemon_run_from_select (struct MHD_Daemon *daemon,
const fd_set *read_fd_set,
const fd_set *write_fd_set,
const fd_set *except_fd_set)
{
if ( (MHD_TM_EXTERNAL_EVENT_LOOP != daemon->threading_mode) ||
(MHD_ELS_POLL == daemon->event_loop_syscall) )
return MHD_SC_CONFIGURATION_MISMATCH_FOR_RUN_SELECT;
if (MHD_ELS_EPOLL == daemon->event_loop_syscall)
{
#ifdef EPOLL_SUPPORT
enum MHD_StatusCode sc;
sc = MHD_daemon_epoll_ (daemon,
MHD_NO);
MHD_connection_cleanup_ (daemon);
return sc;
#else /* ! EPOLL_SUPPORT */
return MHD_NO;
#endif /* ! EPOLL_SUPPORT */
}
/* Resuming external connections when using an extern mainloop */
if (! daemon->disallow_suspend_resume)
(void) MHD_resume_suspended_connections_ (daemon);
return internal_run_from_select (daemon,
read_fd_set,
write_fd_set,
except_fd_set);
}
/**
* Main internal select() call. Will compute select sets, call
* select() and then #internal_run_from_select() with the result.
*
* @param daemon daemon to run select() loop for
* @param may_block #MHD_YES if blocking, #MHD_NO if non-blocking
* @return #MHD_SC_OK on success
*/
enum MHD_StatusCode
MHD_daemon_select_ (struct MHD_Daemon *daemon,
int may_block)
{
int num_ready;
fd_set rs;
fd_set ws;
fd_set es;
MHD_socket maxsock;
struct timeval timeout;
struct timeval *tv;
MHD_UNSIGNED_LONG_LONG ltimeout;
MHD_socket ls;
enum MHD_StatusCode sc;
enum MHD_StatusCode sc2;
timeout.tv_sec = 0;
timeout.tv_usec = 0;
if (daemon->shutdown)
return MHD_SC_DAEMON_ALREADY_SHUTDOWN;
FD_ZERO (&rs);
FD_ZERO (&ws);
FD_ZERO (&es);
maxsock = MHD_INVALID_SOCKET;
sc = MHD_SC_OK;
if ( (! daemon->disallow_suspend_resume) &&
(MHD_resume_suspended_connections_ (daemon)) &&
(MHD_TM_THREAD_PER_CONNECTION != daemon->threading_mode) )
may_block = MHD_NO;
if (MHD_TM_THREAD_PER_CONNECTION != daemon->threading_mode)
{
/* single-threaded, go over everything */
if (MHD_SC_OK !=
(sc = internal_get_fdset2 (daemon,
&rs,
&ws,
&es,
&maxsock,
FD_SETSIZE)))
{
#ifdef HAVE_MESSAGES
MHD_DLOG (daemon,
sc,
_ ("Could not obtain daemon fdsets.\n"));
#endif
}
}
else
{
/* accept only, have one thread per connection */
if ( (MHD_INVALID_SOCKET != (ls = daemon->listen_socket)) &&
(! daemon->was_quiesced) &&
(! MHD_add_to_fd_set_ (ls,
&rs,
&maxsock,
FD_SETSIZE)) )
{
#ifdef HAVE_MESSAGES
MHD_DLOG (daemon,
MHD_SC_SOCKET_OUTSIDE_OF_FDSET_RANGE,
_ ("Could not add listen socket to fdset.\n"));
#endif
return MHD_SC_SOCKET_OUTSIDE_OF_FDSET_RANGE;
}
}
if ( (MHD_ITC_IS_VALID_ (daemon->itc)) &&
(! MHD_add_to_fd_set_ (MHD_itc_r_fd_ (daemon->itc),
&rs,
&maxsock,
FD_SETSIZE)) )
{
#if defined(MHD_WINSOCK_SOCKETS)
/* fdset limit reached, new connections
cannot be handled. Remove listen socket FD
from fdset and retry to add ITC FD. */
if ( (MHD_INVALID_SOCKET != (ls = daemon->listen_socket)) &&
(! daemon->was_quiesced) )
{
FD_CLR (ls,
&rs);
if (! MHD_add_to_fd_set_ (MHD_itc_r_fd_ (daemon->itc),
&rs,
&maxsock,
FD_SETSIZE))
{
#endif /* MHD_WINSOCK_SOCKETS */
sc = MHD_SC_SOCKET_OUTSIDE_OF_FDSET_RANGE;
#ifdef HAVE_MESSAGES
MHD_DLOG (daemon,
sc,
_ (
"Could not add control inter-thread communication channel FD to fdset.\n"));
#endif
#if defined(MHD_WINSOCK_SOCKETS)
}
}
#endif /* MHD_WINSOCK_SOCKETS */
}
/* Stop listening if we are at the configured connection limit */
/* If we're at the connection limit, no point in really
accepting new connections; however, make sure we do not miss
the shutdown OR the termination of an existing connection; so
only do this optimization if we have a signaling ITC in
place. */
if ( (MHD_INVALID_SOCKET != (ls = daemon->listen_socket)) &&
(MHD_ITC_IS_VALID_ (daemon->itc)) &&
( (daemon->connections == daemon->global_connection_limit) ||
(daemon->at_limit) ) )
{
FD_CLR (ls,
&rs);
}
tv = NULL;
if (MHD_SC_OK != sc)
may_block = MHD_NO;
if (MHD_NO == may_block)
{
timeout.tv_usec = 0;
timeout.tv_sec = 0;
tv = &timeout;
}
else if ( (MHD_TM_THREAD_PER_CONNECTION != daemon->threading_mode) &&
(MHD_SC_OK ==
MHD_daemon_get_timeout (daemon,
&ltimeout)) )
{
/* ltimeout is in ms */
timeout.tv_usec = (ltimeout % 1000) * 1000;
if (ltimeout / 1000 > TIMEVAL_TV_SEC_MAX)
timeout.tv_sec = TIMEVAL_TV_SEC_MAX;
else
timeout.tv_sec = (_MHD_TIMEVAL_TV_SEC_TYPE) (ltimeout / 1000);
tv = &timeout;
}
num_ready = MHD_SYS_select_ (maxsock + 1,
&rs,
&ws,
&es,
tv);
if (daemon->shutdown)
return MHD_SC_DAEMON_ALREADY_SHUTDOWN;
if (num_ready < 0)
{
const int err = MHD_socket_get_error_ ();
if (MHD_SCKT_ERR_IS_EINTR_ (err))
return sc;
#ifdef HAVE_MESSAGES
MHD_DLOG (daemon,
MHD_SC_UNEXPECTED_SELECT_ERROR,
_ ("select failed: %s\n"),
MHD_socket_strerr_ (err));
#endif
return MHD_SC_UNEXPECTED_SELECT_ERROR;
}
if (MHD_SC_OK !=
(sc2 = internal_run_from_select (daemon,
&rs,
&ws,
&es)))
return sc2;
return sc;
}
/* end of daemon_select.c */