Commit 51d5c44d authored by Rémi Denis-Courmont's avatar Rémi Denis-Courmont

- Use poll() instead of select to allow breaking the FD_SETSIZE barrier

- Poll the system clock only once per loop (yes, it DOES matter),
- Fix rare bug with TLS and multiple listening sockets,
- Do not try to read/write to client socket with no pending events.
...and of course introduce dozens of new bugs.

This SHOULD speed httpd up a bit.
parent 8468203d
...@@ -48,6 +48,10 @@ ...@@ -48,6 +48,10 @@
# include <fcntl.h> # include <fcntl.h>
#endif #endif
#ifdef HAVE_POLL
# include <poll.h>
#endif
#if defined( UNDER_CE ) #if defined( UNDER_CE )
# include <winsock.h> # include <winsock.h>
#elif defined( WIN32 ) #elif defined( WIN32 )
...@@ -87,7 +91,8 @@ struct httpd_host_t ...@@ -87,7 +91,8 @@ struct httpd_host_t
/* address/port and socket for listening at connections */ /* address/port and socket for listening at connections */
char *psz_hostname; char *psz_hostname;
int i_port; int i_port;
int *fd; int *fds;
unsigned nfd;
/* Statistics */ /* Statistics */
counter_t *p_active_counter; counter_t *p_active_counter;
...@@ -1131,12 +1136,13 @@ httpd_host_t *httpd_TLSHostNew( vlc_object_t *p_this, const char *psz_hostname, ...@@ -1131,12 +1136,13 @@ httpd_host_t *httpd_TLSHostNew( vlc_object_t *p_this, const char *psz_hostname,
vlc_mutex_init( httpd, &host->lock ); vlc_mutex_init( httpd, &host->lock );
host->i_ref = 1; host->i_ref = 1;
host->fd = net_ListenTCP( p_this, psz_host, i_port ); host->fds = net_ListenTCP( p_this, psz_host, i_port );
if( host->fd == NULL ) if( host->fds == NULL )
{ {
msg_Err( p_this, "cannot create socket(s) for HTTP host" ); msg_Err( p_this, "cannot create socket(s) for HTTP host" );
goto error; goto error;
} }
for (host->nfd = 0; host->fds[host->nfd] != -1; host->nfd++);
host->i_port = i_port; host->i_port = i_port;
host->psz_hostname = psz_host; host->psz_hostname = psz_host;
...@@ -1174,7 +1180,7 @@ error: ...@@ -1174,7 +1180,7 @@ error:
if( host != NULL ) if( host != NULL )
{ {
net_ListenClose( host->fd ); net_ListenClose( host->fds );
vlc_mutex_destroy( &host->lock ); vlc_mutex_destroy( &host->lock );
vlc_object_destroy( host ); vlc_object_destroy( host );
} }
...@@ -1228,7 +1234,7 @@ void httpd_HostDelete( httpd_host_t *host ) ...@@ -1228,7 +1234,7 @@ void httpd_HostDelete( httpd_host_t *host )
if( host->p_tls != NULL) if( host->p_tls != NULL)
tls_ServerDelete( host->p_tls ); tls_ServerDelete( host->p_tls );
net_ListenClose( host->fd ); net_ListenClose( host->fds );
free( host->psz_hostname ); free( host->psz_hostname );
vlc_mutex_destroy( &host->lock ); vlc_mutex_destroy( &host->lock );
...@@ -2026,16 +2032,6 @@ static void httpd_HostThread( httpd_host_t *host ) ...@@ -2026,16 +2032,6 @@ static void httpd_HostThread( httpd_host_t *host )
while( !host->b_die ) while( !host->b_die )
{ {
struct timeval timeout;
fd_set fds_read;
fd_set fds_write;
/* FIXME: (too) many int variables */
int fd, i_fd;
int i_handle_max = -1;
int i_ret;
int i_client;
int b_low_delay = 0;
if( host->i_url <= 0 ) if( host->i_url <= 0 )
{ {
/* 0.2s */ /* 0.2s */
...@@ -2043,31 +2039,31 @@ static void httpd_HostThread( httpd_host_t *host ) ...@@ -2043,31 +2039,31 @@ static void httpd_HostThread( httpd_host_t *host )
continue; continue;
} }
/* built a set of handle to select */
FD_ZERO( &fds_read );
FD_ZERO( &fds_write );
for( i_fd = 0; (fd = host->fd[i_fd]) != -1; i_fd++ )
{
FD_SET( fd, &fds_read );
if( fd > i_handle_max )
i_handle_max = fd;
}
/* prepare a new TLS session */ /* prepare a new TLS session */
if( ( p_tls == NULL ) && ( host->p_tls != NULL ) ) if( ( p_tls == NULL ) && ( host->p_tls != NULL ) )
p_tls = tls_ServerSessionPrepare( host->p_tls ); p_tls = tls_ServerSessionPrepare( host->p_tls );
struct pollfd ufd[host->nfd + host->i_client];
unsigned nfd;
for (nfd = 0; nfd < host->nfd; nfd++)
{
ufd[nfd].fd = host->fds[nfd];
ufd[nfd].events = POLLIN;
ufd[nfd].revents = 0;
}
/* add all socket that should be read/write and close dead connection */ /* add all socket that should be read/write and close dead connection */
vlc_mutex_lock( &host->lock ); vlc_mutex_lock( &host->lock );
for( i_client = 0; i_client < host->i_client; i_client++ ) mtime_t now = mdate();
vlc_bool_t b_low_delay = VLC_FALSE;
for(int i_client = 0; i_client < host->i_client; i_client++ )
{ {
httpd_client_t *cl = host->client[i_client]; httpd_client_t *cl = host->client[i_client];
if( cl->i_ref < 0 || ( cl->i_ref == 0 && if( cl->i_ref < 0 || ( cl->i_ref == 0 &&
( cl->i_state == HTTPD_CLIENT_DEAD || ( cl->i_state == HTTPD_CLIENT_DEAD ||
( cl->i_activity_timeout > 0 && ( cl->i_activity_timeout > 0 &&
cl->i_activity_date+cl->i_activity_timeout < mdate()) ) ) ) cl->i_activity_date+cl->i_activity_timeout < now) ) ) )
{ {
httpd_ClientClean( cl ); httpd_ClientClean( cl );
stats_UpdateInteger( host, host->p_active_counter, -1, NULL ); stats_UpdateInteger( host, host->p_active_counter, -1, NULL );
...@@ -2076,17 +2072,22 @@ static void httpd_HostThread( httpd_host_t *host ) ...@@ -2076,17 +2072,22 @@ static void httpd_HostThread( httpd_host_t *host )
i_client--; i_client--;
continue; continue;
} }
else if( ( cl->i_state == HTTPD_CLIENT_RECEIVING )
struct pollfd *pufd = ufd + nfd;
assert (pufd < ufd + (sizeof (ufd) / sizeof (ufd[0])));
pufd->fd = cl->fd;
pufd->events = pufd->revents = 0;
if( ( cl->i_state == HTTPD_CLIENT_RECEIVING )
|| ( cl->i_state == HTTPD_CLIENT_TLS_HS_IN ) ) || ( cl->i_state == HTTPD_CLIENT_TLS_HS_IN ) )
{ {
FD_SET( cl->fd, &fds_read ); pufd->events = POLLIN;
i_handle_max = __MAX( i_handle_max, cl->fd );
} }
else if( ( cl->i_state == HTTPD_CLIENT_SENDING ) else if( ( cl->i_state == HTTPD_CLIENT_SENDING )
|| ( cl->i_state == HTTPD_CLIENT_TLS_HS_OUT ) ) || ( cl->i_state == HTTPD_CLIENT_TLS_HS_OUT ) )
{ {
FD_SET( cl->fd, &fds_write ); pufd->events = POLLOUT;
i_handle_max = __MAX( i_handle_max, cl->fd );
} }
else if( cl->i_state == HTTPD_CLIENT_RECEIVE_DONE ) else if( cl->i_state == HTTPD_CLIENT_RECEIVE_DONE )
{ {
...@@ -2188,10 +2189,9 @@ static void httpd_HostThread( httpd_host_t *host ) ...@@ -2188,10 +2189,9 @@ static void httpd_HostThread( httpd_host_t *host )
{ {
vlc_bool_t b_auth_failed = VLC_FALSE; vlc_bool_t b_auth_failed = VLC_FALSE;
vlc_bool_t b_hosts_failed = VLC_FALSE; vlc_bool_t b_hosts_failed = VLC_FALSE;
int i;
/* Search the url and trigger callbacks */ /* Search the url and trigger callbacks */
for( i = 0; i < host->i_url; i++ ) for(int i = 0; i < host->i_url; i++ )
{ {
httpd_url_t *url = host->url[i]; httpd_url_t *url = host->url[i];
...@@ -2394,7 +2394,7 @@ static void httpd_HostThread( httpd_host_t *host ) ...@@ -2394,7 +2394,7 @@ static void httpd_HostThread( httpd_host_t *host )
&cl->answer, &cl->query ); &cl->answer, &cl->query );
if( cl->answer.i_type != HTTPD_MSG_NONE ) if( cl->answer.i_type != HTTPD_MSG_NONE )
{ {
/* we have new data, so reenter send mode */ /* we have new data, so re-enter send mode */
cl->i_buffer = 0; cl->i_buffer = 0;
cl->p_buffer = cl->answer.p_body; cl->p_buffer = cl->answer.p_body;
cl->i_buffer_size = cl->answer.i_body; cl->i_buffer_size = cl->answer.i_body;
...@@ -2413,117 +2413,103 @@ static void httpd_HostThread( httpd_host_t *host ) ...@@ -2413,117 +2413,103 @@ static void httpd_HostThread( httpd_host_t *host )
if( cl->i_mode == HTTPD_CLIENT_BIDIR && if( cl->i_mode == HTTPD_CLIENT_BIDIR &&
cl->i_state == HTTPD_CLIENT_SENDING ) cl->i_state == HTTPD_CLIENT_SENDING )
{ {
FD_SET( cl->fd, &fds_read ); pufd->events |= POLLIN;
i_handle_max = __MAX( i_handle_max, cl->fd );
} }
if (pufd->events != 0)
nfd++;
} }
vlc_mutex_unlock( &host->lock ); vlc_mutex_unlock( &host->lock );
/* we will wait 100ms or 20ms (not too big 'cause HTTPD_CLIENT_WAITING) */ /* we will wait 100ms or 20ms (not too big 'cause HTTPD_CLIENT_WAITING) */
timeout.tv_sec = 0; switch (poll (ufd, nfd, b_low_delay ? 20 : 100))
timeout.tv_usec = b_low_delay ? 20000 : 100000;
i_ret = select( i_handle_max + 1,
&fds_read, &fds_write, NULL, &timeout );
if( (i_ret == -1) && (errno != EINTR) )
{ {
msg_Warn( host, "select error: %s", net_strerror( net_errno ) ); case -1:
msleep( 1000 ); if (errno != EINTR)
continue; {
} /* This is most likely a bug */
else if( i_ret <= 0 ) msg_Err( host, "polling error: %s", strerror (errno));
{ msleep( 1000 );
continue; }
case 0:
continue;
} }
/* accept new connections */ /* accept new connections */
for( i_fd = 0; (fd = host->fd[i_fd]) != -1; i_fd++ ) for (nfd = 0; nfd < host->nfd; nfd++)
{ {
if( FD_ISSET( fd, &fds_read ) ) assert (ufd[nfd].fd == host->fds[nfd]);
{
socklen_t i_sock_size = sizeof( struct sockaddr_storage );
struct sockaddr_storage sock;
fd = accept( fd, (struct sockaddr *)&sock, &i_sock_size ); if (ufd[nfd].revents == 0)
continue;
if( fd >= 0 ) struct sockaddr_storage addr;
{ socklen_t addrlen = sizeof (addr);
int i_state = 1;
setsockopt( fd, SOL_SOCKET, SO_REUSEADDR, &i_state, sizeof (i_state)); int fd = accept (ufd[nfd].fd, (struct sockaddr *)&addr, &addrlen);
i_state = 0; if (fd == -1)
continue;
/* set this new socket non-block */ net_SetupSocket (fd);
#if defined( WIN32 ) || defined( UNDER_CE )
{
unsigned long i_dummy = 1;
ioctlsocket( fd, FIONBIO, &i_dummy );
}
#else
fcntl( fd, F_SETFD, FD_CLOEXEC );
{
int i_val = fcntl( fd, F_GETFL );
fcntl( fd, F_SETFL,
O_NONBLOCK | ((i_val != -1) ? i_val : 0) );
}
if( fd >= FD_SETSIZE ) int i_state = 0;
{
net_Close( fd );
fd = -1;
}
else
#endif
if( p_tls != NULL)
{
switch ( tls_ServerSessionHandshake( p_tls, fd ) )
{
case -1:
msg_Err( host, "Rejecting TLS connection" );
net_Close( fd );
fd = -1;
p_tls = NULL;
break;
case 1: /* missing input - most likely */
i_state = HTTPD_CLIENT_TLS_HS_IN;
break;
case 2: /* missing output */
i_state = HTTPD_CLIENT_TLS_HS_OUT;
break;
}
}
if( fd >= 0 ) if (p_tls != NULL)
{ {
httpd_client_t *cl; switch (tls_ServerSessionHandshake (p_tls, fd))
char ip[NI_MAXNUMERICHOST]; {
stats_UpdateInteger( host, host->p_total_counter, case -1:
1, NULL ); msg_Err( host, "Rejecting TLS connection" );
stats_UpdateInteger( host, host->p_active_counter, net_Close (fd);
1, NULL ); fd = -1;
cl = httpd_ClientNew( fd, &sock, i_sock_size, p_tls );
httpd_ClientIP( cl, ip );
msg_Dbg( host, "Connection from %s", ip );
p_tls = NULL; p_tls = NULL;
vlc_mutex_lock( &host->lock ); break;
TAB_APPEND( host->i_client, host->client, cl );
vlc_mutex_unlock( &host->lock );
if( i_state != 0 ) case 1: /* missing input - most likely */
cl->i_state = i_state; // override state for TLS i_state = HTTPD_CLIENT_TLS_HS_IN;
} break;
case 2: /* missing output */
i_state = HTTPD_CLIENT_TLS_HS_OUT;
break;
} }
if (p_tls == NULL)
break; // wasted TLS session, cannot accept() anymore
} }
httpd_client_t *cl;
char ip[NI_MAXNUMERICHOST];
stats_UpdateInteger( host, host->p_total_counter, 1, NULL );
stats_UpdateInteger( host, host->p_active_counter, 1, NULL );
cl = httpd_ClientNew( fd, &addr, addrlen, p_tls );
httpd_ClientIP( cl, ip );
msg_Dbg( host, "Connection from %s", ip );
p_tls = NULL;
vlc_mutex_lock( &host->lock );
TAB_APPEND( host->i_client, host->client, cl );
vlc_mutex_unlock( &host->lock );
cl->i_state = i_state; // override state for TLS
break; // cannot accept more than one because of TLS
} }
/* now try all others socket */ /* now try all others socket */
vlc_mutex_lock( &host->lock ); vlc_mutex_lock( &host->lock );
for( i_client = 0; i_client < host->i_client; i_client++ )
for( int i_client = 0; i_client < host->i_client; i_client++ )
{ {
httpd_client_t *cl = host->client[i_client]; httpd_client_t *cl = host->client[i_client];
const struct pollfd *pufd = ufd + nfd;
assert (pufd < ufd + (sizeof (ufd) / sizeof (ufd[0])));
if (cl->fd != pufd->fd)
continue; // we were not waiting for this client
nfd++;
if (pufd->revents == 0)
continue; // no event received
if( cl->i_state == HTTPD_CLIENT_RECEIVING ) if( cl->i_state == HTTPD_CLIENT_RECEIVING )
{ {
httpd_ClientRecv( cl ); httpd_ClientRecv( cl );
...@@ -2543,7 +2529,7 @@ static void httpd_HostThread( httpd_host_t *host ) ...@@ -2543,7 +2529,7 @@ static void httpd_HostThread( httpd_host_t *host )
if( cl->i_mode == HTTPD_CLIENT_BIDIR && if( cl->i_mode == HTTPD_CLIENT_BIDIR &&
cl->i_state == HTTPD_CLIENT_SENDING && cl->i_state == HTTPD_CLIENT_SENDING &&
FD_ISSET( cl->fd, &fds_read ) ) (pufd->revents & POLLIN) )
{ {
cl->b_read_waiting = VLC_TRUE; cl->b_read_waiting = VLC_TRUE;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment