X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=src%2Fnetwork%2Fhttpd.c;h=cd384587a50a7ff850ce07856fd7eba38f3f5213;hb=d8180bb27170d1c5be7e1804144f707b0f557b14;hp=b9ab16fa6f843e82191d1a65e649f27a84ea0630;hpb=47fd1f615a8c94a6af6d54c8b47f54ca2860152e;p=vlc diff --git a/src/network/httpd.c b/src/network/httpd.c index b9ab16fa6f..cd384587a5 100644 --- a/src/network/httpd.c +++ b/src/network/httpd.c @@ -1,7 +1,8 @@ /***************************************************************************** * httpd.c ***************************************************************************** - * Copyright (C) 2004-2005 the VideoLAN team + * Copyright (C) 2004-2006 the VideoLAN team + * Copyright © 2004-2007 Rémi Denis-Courmont * $Id$ * * Authors: Laurent Aimar @@ -22,15 +23,21 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ -#include #include +#include +#include + #ifdef ENABLE_HTTPD -#include "vlc_httpd.h" -#include "network.h" -#include "vlc_tls.h" -#include "vlc_acl.h" +#include + +#include +#include +#include +#include +#include +#include "../libvlc.h" #include #include @@ -43,19 +50,16 @@ # include #endif +#ifdef HAVE_POLL +# include +#endif + #if defined( UNDER_CE ) # include #elif defined( WIN32 ) # include -# include #else -# include /* hostent ... */ # include -/* FIXME: should not be needed */ -# include -# ifdef HAVE_ARPA_INET_H -# include /* inet_ntoa(), inet_aton() */ -# endif #endif #if defined( WIN32 ) @@ -67,6 +71,15 @@ static void httpd_ClientClean( httpd_client_t *cl ); +struct httpd_t +{ + VLC_COMMON_MEMBERS + + int i_host; + httpd_host_t **host; +}; + + /* each host run in his own thread */ struct httpd_host_t { @@ -80,7 +93,8 @@ struct httpd_host_t /* address/port and socket for listening at connections */ char *psz_hostname; int i_port; - int *fd; + int *fds; + unsigned nfd; vlc_mutex_t lock; @@ -98,6 +112,7 @@ struct httpd_host_t tls_server_t *p_tls; }; + struct httpd_url_t { httpd_host_t *host; @@ -147,8 +162,6 @@ struct httpd_client_t int i_ref; - struct sockaddr_storage sock; - int i_sock_size; int fd; int i_mode; @@ -175,63 +188,6 @@ struct httpd_client_t /***************************************************************************** * Various functions *****************************************************************************/ -/*char b64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";*/ -static void b64_decode( char *dest, char *src ) -{ - int i_level; - int last = 0; - int b64[256] = { - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 00-0F */ - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 10-1F */ - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63, /* 20-2F */ - 52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1, /* 30-3F */ - -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14, /* 40-4F */ - 15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1, /* 50-5F */ - -1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40, /* 60-6F */ - 41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1, /* 70-7F */ - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 80-8F */ - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 90-9F */ - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* A0-AF */ - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* B0-BF */ - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* C0-CF */ - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* D0-DF */ - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* E0-EF */ - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 /* F0-FF */ - }; - - for( i_level = 0; *src != '\0'; src++ ) - { - int c; - - c = b64[(unsigned int)*src]; - if( c == -1 ) - { - continue; - } - - switch( i_level ) - { - case 0: - i_level++; - break; - case 1: - *dest++ = ( last << 2 ) | ( ( c >> 4)&0x03 ); - i_level++; - break; - case 2: - *dest++ = ( ( last << 4 )&0xf0 ) | ( ( c >> 2 )&0x0f ); - i_level++; - break; - case 3: - *dest++ = ( ( last &0x03 ) << 6 ) | c; - i_level = 0; - } - last = c; - } - - *dest = '\0'; -} - static struct { const char *psz_ext; @@ -301,6 +257,119 @@ static const char *httpd_MimeFromUrl( const char *psz_url ) return "application/octet-stream"; } + +typedef struct +{ + int i_code; + const char *psz_reason; +} http_status_info; + +static const http_status_info http_reason[] = +{ + /*{ 100, "Continue" }, + { 101, "Switching Protocols" },*/ + { 200, "OK" }/*, + { 201, "Created" }, + { 202, "Accepted" }, + { 203, "Non-Authoritative Information" }, + { 204, "No Content" }, + { 205, "Reset Content" }, + { 206, "Partial Content" }, + { 250, "Low on Storage Space" }, + { 300, "Multiple Choices" }*/, + { 301, "Moved Permanently" }/*, + { 302, "Moved Temporarily" }, - aka "Found" + { 303, "See Other" }, + { 304, "Not Modified" }, + { 305, "Use Proxy" }, + { 307, "Temporary Redirect" }, + { 400, "Bad Request" }*/, + { 401, "Unauthorized" }/*, + { 402, "Payment Required" }*/, + { 403, "Forbidden" }, + { 404, "Not Found" }/*, + { 405, "Method Not Allowed" }, + { 406, "Not Acceptable" }, + { 407, "Proxy Authentication Required" }, + { 408, "Request Time-out" }, + { 409, "Conflict" }, + { 410, "Gone" }, + { 411, "Length Required" }, + { 412, "Precondition Failed" }, + { 413, "Request Entity Too Large" }, + { 414, "Request-URI Too Large" }, + { 415, "Unsupported Media Type" }, + { 416, "Requested range not satisfiable" }, + { 417, "Expectation Failed" }, + { 451, "Parameter Not Understood" }, + { 452, "Conference Not Found" }, + { 453, "Not Enough Bandwidth" }*/, + { 454, "Session Not Found" }/*, + { 455, "Method Not Valid in This State" }, + { 456, "Header Field Not Valid for Resource" }, + { 457, "Invalid Range" }, + { 458, "Parameter Is Read-Only" }, + { 459, "Aggregate operation not allowed" }, + { 460, "Only aggregate operation allowed" }*/, + { 461, "Unsupported transport" }/*, + { 462, "Destination unreachable" }*/, + { 500, "Internal Server Error" }, + { 501, "Not Implemented" }/*, + { 502, "Bad Gateway" }*/, + { 503, "Service Unavailable" }/*, + { 504, "Gateway Time-out" }, + { 505, "Protocol version not supported" }*/, + { 0, NULL } +}; + +static const char *psz_fallback_reason[] = +{ "Continue", "OK", "Found", "Client Error", "Server Error" }; + +static const char *httpd_ReasonFromCode( int i_code ) +{ + const http_status_info *p; + + for (p = http_reason; p->i_code < i_code; p++); + + if( p->i_code == i_code ) + return p->psz_reason; + + assert( ( i_code >= 100 ) && ( i_code <= 599 ) ); + return psz_fallback_reason[(i_code / 100) - 1]; +} + + +static size_t httpd_HtmlError (char **body, int code, const char *url) +{ + const char *errname = httpd_ReasonFromCode (code); + assert (errname != NULL); + + int res = asprintf (body, + "\n" + "\n" + "\n" + "\n" + "%s\n" + "\n" + "\n" + "

%d %s%s%s%s

\n" + "
\n" + "VideoLAN\n" + "\n" + "\n", errname, code, errname, + (url ? " (" : ""), (url ?: ""), (url ? ")" : "")); + + if (res == -1) + { + *body = NULL; + return 0; + } + + return (size_t)res; +} + + /***************************************************************************** * High Level Functions: httpd_file_t *****************************************************************************/ @@ -321,6 +390,7 @@ static int httpd_FileCallBack( httpd_callback_sys_t *p_sys, httpd_client_t *cl, httpd_file_t *file = (httpd_file_t*)p_sys; uint8_t *psz_args = query->psz_args; uint8_t **pp_body, *p_body; + const char *psz_connection; int *pi_body, i_body; if( answer == NULL || query == NULL ) @@ -364,10 +434,10 @@ static int httpd_FileCallBack( httpd_callback_sys_t *p_sys, httpd_client_t *cl, } /* We respect client request */ - if( strcmp( httpd_MsgGet( &cl->query, "Connection" ), "" ) ) + psz_connection = httpd_MsgGet( &cl->query, "Connection" ); + if( psz_connection != NULL ) { - httpd_MsgAdd( answer, "Connection", - httpd_MsgGet( &cl->query, "Connection" ) ); + httpd_MsgAdd( answer, "Connection", psz_connection ); } httpd_MsgAdd( answer, "Content-Length", "%d", answer->i_body ); @@ -440,7 +510,7 @@ static int httpd_HandlerCallBack( httpd_callback_sys_t *p_sys, httpd_client_t *c { httpd_handler_t *handler = (httpd_handler_t*)p_sys; uint8_t *psz_args = query->psz_args; - char psz_remote_addr[100]; + char psz_remote_addr[NI_MAXNUMERICHOST]; if( answer == NULL || query == NULL ) { @@ -453,30 +523,8 @@ static int httpd_HandlerCallBack( httpd_callback_sys_t *p_sys, httpd_client_t *c answer->i_status = 0; answer->psz_status = NULL; - switch( cl->sock.ss_family ) - { -#ifdef HAVE_INET_PTON - case AF_INET: - inet_ntop( cl->sock.ss_family, - &((struct sockaddr_in *)&cl->sock)->sin_addr, - psz_remote_addr, sizeof(psz_remote_addr) ); - break; - case AF_INET6: - inet_ntop( cl->sock.ss_family, - &((struct sockaddr_in6 *)&cl->sock)->sin6_addr, - psz_remote_addr, sizeof(psz_remote_addr) ); - break; -#else - case AF_INET: - { - char *psz_tmp = inet_ntoa( ((struct sockaddr_in *)&cl->sock)->sin_addr ); - strncpy( psz_remote_addr, psz_tmp, sizeof(psz_remote_addr) ); - break; - } -#endif - default: - psz_remote_addr[0] = '\0'; - } + if( httpd_ClientIP( cl, psz_remote_addr ) == NULL ) + *psz_remote_addr = '\0'; handler->pf_fill( handler->p_sys, handler, query->psz_url, psz_args, query->i_type, query->p_body, query->i_body, @@ -505,7 +553,8 @@ static int httpd_HandlerCallBack( httpd_callback_sys_t *p_sys, httpd_client_t *c if( strncmp( (char *)answer->p_body, "HTTP/1.", 7 ) ) { int i_status, i_headers; - char *psz_headers, *psz_new, *psz_status; + char *psz_headers, *psz_new; + const char *psz_status; if( !strncmp( (char *)answer->p_body, "Status: ", 8 ) ) { @@ -597,7 +646,8 @@ static int httpd_RedirectCallBack( httpd_callback_sys_t *p_sys, httpd_message_t *query ) { httpd_redirect_t *rdir = (httpd_redirect_t*)p_sys; - uint8_t *p; + char *p_body; + (void)cl; if( answer == NULL || query == NULL ) { @@ -609,24 +659,8 @@ static int httpd_RedirectCallBack( httpd_callback_sys_t *p_sys, answer->i_status = 301; answer->psz_status = strdup( "Moved Permanently" ); - p = answer->p_body = malloc( 1000 + strlen( rdir->psz_dst ) ); - p += sprintf( (char *)p, - "\n" - "\n" - "\n" - "\n" - "Redirection\n" - "\n" - "\n" - "

You should be " - "redirected

\n" - "
\n" - "

VideoLAN\n

" - "
\n" - "\n" - "\n", rdir->psz_dst ); - answer->i_body = p - answer->p_body; + answer->i_body = httpd_HtmlError (&p_body, 301, rdir->psz_dst); + answer->p_body = (unsigned char *)p_body; /* XXX check if it's ok or we need to set an absolute url */ httpd_MsgAdd( answer, "Location", "%s", rdir->psz_dst ); @@ -718,8 +752,10 @@ static int httpd_StreamCallBack( httpd_callback_sys_t *p_sys, stream->i_buffer_pos ) { /* this client isn't fast enough */ +#if 0 fprintf( stderr, "fixing i_body_offset (old=%lld new=%lld)\n", answer->i_body_offset, stream->i_buffer_last_pos ); +#endif answer->i_body_offset = stream->i_buffer_last_pos; } @@ -935,6 +971,8 @@ httpd_host_t *httpd_HostNew( vlc_object_t *p_this, const char *psz_host, ); } +static const char psz_object_type[] = "http server"; + httpd_host_t *httpd_TLSHostNew( vlc_object_t *p_this, const char *psz_hostname, int i_port, const char *psz_cert, const char *psz_key, @@ -958,14 +996,17 @@ httpd_host_t *httpd_TLSHostNew( vlc_object_t *p_this, const char *psz_hostname, } /* to be sure to avoid multiple creation */ - var_Create( p_this->p_libvlc, "httpd_mutex", VLC_VAR_MUTEX ); - var_Get( p_this->p_libvlc, "httpd_mutex", &lockval ); + var_Create( p_this->p_libvlc_global, "httpd_mutex", VLC_VAR_MUTEX ); + var_Get( p_this->p_libvlc_global, "httpd_mutex", &lockval ); vlc_mutex_lock( lockval.p_address ); if( !(httpd = vlc_object_find( p_this, VLC_OBJECT_HTTPD, FIND_ANYWHERE )) ) { msg_Info( p_this, "creating httpd" ); - if( ( httpd = vlc_object_create( p_this, VLC_OBJECT_HTTPD ) ) == NULL ) + httpd = (httpd_t *)vlc_custom_create( p_this, sizeof (*httpd), + VLC_OBJECT_HTTPD, + psz_object_type ); + if (httpd == NULL) { vlc_mutex_unlock( lockval.p_address ); free( psz_host ); @@ -976,7 +1017,7 @@ httpd_host_t *httpd_TLSHostNew( vlc_object_t *p_this, const char *psz_hostname, httpd->host = NULL; vlc_object_yield( httpd ); - vlc_object_attach( httpd, p_this->p_vlc ); + vlc_object_attach( httpd, p_this->p_libvlc ); } /* verify if it already exist */ @@ -1025,17 +1066,23 @@ httpd_host_t *httpd_TLSHostNew( vlc_object_t *p_this, const char *psz_hostname, p_tls = NULL; /* create the new host */ - host = vlc_object_create( p_this, sizeof( httpd_host_t ) ); + host = (httpd_host_t *)vlc_custom_create( p_this, sizeof (*host), + VLC_OBJECT_HTTPD_HOST, + psz_object_type ); + if (host == NULL) + goto error; + host->httpd = httpd; vlc_mutex_init( httpd, &host->lock ); host->i_ref = 1; - host->fd = net_ListenTCP( p_this, psz_host, i_port ); - if( host->fd == NULL ) + host->fds = net_ListenTCP( p_this, psz_host, i_port ); + if( host->fds == NULL ) { msg_Err( p_this, "cannot create socket(s) for HTTP host" ); goto error; } + for (host->nfd = 0; host->fds[host->nfd] != -1; host->nfd++); host->i_port = i_port; host->psz_hostname = psz_host; @@ -1073,7 +1120,7 @@ error: if( host != NULL ) { - net_ListenClose( host->fd ); + net_ListenClose( host->fds ); vlc_mutex_destroy( &host->lock ); vlc_object_destroy( host ); } @@ -1091,7 +1138,7 @@ void httpd_HostDelete( httpd_host_t *host ) vlc_value_t lockval; int i; - var_Get( httpd->p_libvlc, "httpd_mutex", &lockval ); + var_Get( httpd->p_libvlc_global, "httpd_mutex", &lockval ); vlc_mutex_lock( lockval.p_address ); host->i_ref--; @@ -1104,14 +1151,14 @@ void httpd_HostDelete( httpd_host_t *host ) } TAB_REMOVE( httpd->i_host, httpd->host, host ); - host->b_die = 1; + vlc_object_kill( host ); vlc_thread_join( host ); msg_Dbg( host, "HTTP host removed" ); for( i = 0; i < host->i_url; i++ ) { - msg_Err( host, "url still registered:%s", host->url[i]->psz_url ); + msg_Err( host, "url still registered: %s", host->url[i]->psz_url ); } for( i = 0; i < host->i_client; i++ ) { @@ -1127,7 +1174,7 @@ void httpd_HostDelete( httpd_host_t *host ) if( host->p_tls != NULL) tls_ServerDelete( host->p_tls ); - net_ListenClose( host->fd ); + net_ListenClose( host->fds ); free( host->psz_hostname ); vlc_mutex_destroy( &host->lock ); @@ -1308,7 +1355,7 @@ void httpd_MsgClean( httpd_message_t *msg ) httpd_MsgInit( msg ); } -char *httpd_MsgGet( httpd_message_t *msg, char *name ) +const char *httpd_MsgGet( httpd_message_t *msg, const char *name ) { int i; @@ -1319,10 +1366,10 @@ char *httpd_MsgGet( httpd_message_t *msg, char *name ) return msg->value[i]; } } - return ""; + return NULL; } -void httpd_MsgAdd( httpd_message_t *msg, char *name, char *psz_value, ... ) +void httpd_MsgAdd( httpd_message_t *msg, const char *name, const char *psz_value, ... ) { va_list args; char *value = NULL; @@ -1342,14 +1389,14 @@ void httpd_MsgAdd( httpd_message_t *msg, char *name, char *psz_value, ... ) name = strdup( name ); - TAB_APPEND( msg->i_name, msg->name, name ); + TAB_APPEND( msg->i_name, msg->name, (char*)name ); TAB_APPEND( msg->i_value, msg->value, value ); } -static void httpd_ClientInit( httpd_client_t *cl ) +static void httpd_ClientInit( httpd_client_t *cl, mtime_t now ) { cl->i_state = HTTPD_CLIENT_RECEIVING; - cl->i_activity_date = mdate(); + cl->i_activity_date = now; cl->i_activity_timeout = I64C(10000000); cl->i_buffer_size = HTTPD_CL_BUFSIZE; cl->i_buffer = 0; @@ -1371,12 +1418,12 @@ void httpd_ClientModeBidir( httpd_client_t *cl ) cl->i_mode = HTTPD_CLIENT_BIDIR; } -char* httpd_ClientIP( httpd_client_t *cl, char *psz_ip ) +char* httpd_ClientIP( const httpd_client_t *cl, char *psz_ip ) { return net_GetPeerAddress( cl->fd, psz_ip, NULL ) ? NULL : psz_ip; } -char* httpd_ServerIP( httpd_client_t *cl, char *psz_ip ) +char* httpd_ServerIP( const httpd_client_t *cl, char *psz_ip ) { return net_GetSockAddress( cl->fd, psz_ip, NULL ) ? NULL : psz_ip; } @@ -1401,9 +1448,7 @@ static void httpd_ClientClean( httpd_client_t *cl ) } } -static httpd_client_t *httpd_ClientNew( int fd, struct sockaddr_storage *sock, - int i_sock_size, - tls_session_t *p_tls ) +static httpd_client_t *httpd_ClientNew( int fd, tls_session_t *p_tls, mtime_t now ) { httpd_client_t *cl = malloc( sizeof( httpd_client_t ) ); @@ -1411,12 +1456,10 @@ static httpd_client_t *httpd_ClientNew( int fd, struct sockaddr_storage *sock, cl->i_ref = 0; cl->fd = fd; - memcpy( &cl->sock, sock, sizeof( cl->sock ) ); - cl->i_sock_size = i_sock_size; cl->url = NULL; cl->p_tls = p_tls; - httpd_ClientInit( cl ); + httpd_ClientInit( cl, now ); return cl; } @@ -1482,7 +1525,7 @@ static void httpd_ClientRecv( httpd_client_t *cl ) cl->query.i_proto = HTTPD_PROTO_RTSP; cl->query.i_type = HTTPD_MSG_ANSWER; } - else if( !memcmp( cl->p_buffer, "GET", 3 ) || + else if( !memcmp( cl->p_buffer, "GET ", 4 ) || !memcmp( cl->p_buffer, "HEAD", 4 ) || !memcmp( cl->p_buffer, "POST", 4 ) ) { @@ -1553,24 +1596,25 @@ static void httpd_ClientRecv( httpd_client_t *cl ) { static const struct { - char *name; + const char *name; int i_type; int i_proto; } msg_type[] = { - { "GET", HTTPD_MSG_GET, HTTPD_PROTO_HTTP }, - { "HEAD", HTTPD_MSG_HEAD, HTTPD_PROTO_HTTP }, - { "POST", HTTPD_MSG_POST, HTTPD_PROTO_HTTP }, - - { "OPTIONS", HTTPD_MSG_OPTIONS, HTTPD_PROTO_RTSP }, - { "DESCRIBE", HTTPD_MSG_DESCRIBE, HTTPD_PROTO_RTSP }, - { "SETUP", HTTPD_MSG_SETUP, HTTPD_PROTO_RTSP }, - { "PLAY", HTTPD_MSG_PLAY, HTTPD_PROTO_RTSP }, - { "PAUSE", HTTPD_MSG_PAUSE, HTTPD_PROTO_RTSP }, - { "TEARDOWN", HTTPD_MSG_TEARDOWN, HTTPD_PROTO_RTSP }, - - { NULL, HTTPD_MSG_NONE, HTTPD_PROTO_NONE } + { "OPTIONS", HTTPD_MSG_OPTIONS, HTTPD_PROTO_RTSP }, + { "DESCRIBE", HTTPD_MSG_DESCRIBE, HTTPD_PROTO_RTSP }, + { "SETUP", HTTPD_MSG_SETUP, HTTPD_PROTO_RTSP }, + { "PLAY", HTTPD_MSG_PLAY, HTTPD_PROTO_RTSP }, + { "PAUSE", HTTPD_MSG_PAUSE, HTTPD_PROTO_RTSP }, + { "GET_PARAMETER", HTTPD_MSG_GETPARAMETER, HTTPD_PROTO_RTSP }, + { "TEARDOWN", HTTPD_MSG_TEARDOWN, HTTPD_PROTO_RTSP }, + + { "GET", HTTPD_MSG_GET, HTTPD_PROTO_HTTP }, + { "HEAD", HTTPD_MSG_HEAD, HTTPD_PROTO_HTTP }, + { "POST", HTTPD_MSG_POST, HTTPD_PROTO_HTTP }, + + { NULL, HTTPD_MSG_NONE, HTTPD_PROTO_NONE } }; int i; @@ -1742,7 +1786,6 @@ static void httpd_ClientRecv( httpd_client_t *cl ) cl->i_state = HTTPD_CLIENT_DEAD; } } - cl->i_activity_date = mdate(); /* XXX: for QT I have to disable timeout. Try to find why */ if( cl->query.i_proto == HTTPD_PROTO_RTSP ) @@ -1825,7 +1868,6 @@ static void httpd_ClientSend( httpd_client_t *cl ) cl->i_buffer_size - cl->i_buffer ); if( i_len >= 0 ) { - cl->i_activity_date = mdate(); cl->i_buffer += i_len; if( cl->i_buffer >= cl->i_buffer_size ) @@ -1916,24 +1958,11 @@ static void httpd_ClientTlsHsOut( httpd_client_t *cl ) static void httpd_HostThread( httpd_host_t *host ) { tls_session_t *p_tls = NULL; - - stats_Create( host, "client_connections", STATS_CLIENT_CONNECTIONS, - VLC_VAR_INTEGER, STATS_COUNTER ); - stats_Create( host, "active_connections", STATS_ACTIVE_CONNECTIONS, - VLC_VAR_INTEGER, STATS_COUNTER ); + counter_t *p_total_counter = stats_CounterCreate( host, VLC_VAR_INTEGER, STATS_COUNTER ); + counter_t *p_active_counter = stats_CounterCreate( host, VLC_VAR_INTEGER, STATS_COUNTER ); 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 = 0; - int i_ret; - int i_client; - int b_low_delay = 0; - if( host->i_url <= 0 ) { /* 0.2s */ @@ -1941,52 +1970,55 @@ static void httpd_HostThread( httpd_host_t *host ) continue; } - /* built a set of handle to select */ - FD_ZERO( &fds_read ); - FD_ZERO( &fds_write ); - - i_handle_max = -1; - - 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 */ if( ( p_tls == NULL ) && ( host->p_tls != NULL ) ) 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 */ 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]; - if( cl->i_ref < 0 || ( cl->i_ref == 0 && ( cl->i_state == HTTPD_CLIENT_DEAD || ( 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 ); - stats_UpdateInteger( host, STATS_ACTIVE_CONNECTIONS, -1, NULL ); + stats_UpdateInteger( host, p_active_counter, -1, NULL ); TAB_REMOVE( host->i_client, host->client, cl ); free( cl ); i_client--; 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 ) ) { - FD_SET( cl->fd, &fds_read ); - i_handle_max = __MAX( i_handle_max, cl->fd ); + pufd->events = POLLIN; } else if( ( cl->i_state == HTTPD_CLIENT_SENDING ) || ( cl->i_state == HTTPD_CLIENT_TLS_HS_OUT ) ) { - FD_SET( cl->fd, &fds_write ); - i_handle_max = __MAX( i_handle_max, cl->fd ); + pufd->events = POLLOUT; } else if( cl->i_state == HTTPD_CLIENT_RECEIVE_DONE ) { @@ -2029,6 +2061,7 @@ static void httpd_HostThread( httpd_host_t *host ) } else if( i_msg == HTTPD_MSG_OPTIONS ) { + const char *psz_cseq; int i_cseq; /* unimplemented */ @@ -2041,7 +2074,11 @@ static void httpd_HostThread( httpd_host_t *host ) answer->i_body = 0; answer->p_body = NULL; - i_cseq = atoi( httpd_MsgGet( query, "Cseq" ) ); + psz_cseq = httpd_MsgGet( query, "Cseq" ); + if( psz_cseq ) + i_cseq = atoi( psz_cseq ); + else + i_cseq = 0; httpd_MsgAdd( answer, "Cseq", "%d", i_cseq ); httpd_MsgAdd( answer, "Server", "VLC Server" ); httpd_MsgAdd( answer, "Public", "DESCRIBE, SETUP, " @@ -2062,7 +2099,7 @@ static void httpd_HostThread( httpd_host_t *host ) } else { - uint8_t *p; + char *p; /* unimplemented */ answer->i_proto = query->i_proto ; @@ -2071,24 +2108,8 @@ static void httpd_HostThread( httpd_host_t *host ) answer->i_status = 501; answer->psz_status = strdup( "Unimplemented" ); - p = answer->p_body = malloc( 1000 ); - - p += sprintf( (char *)p, - "" - "\n" - "\n" - "\n" - "Error 501\n" - "\n" - "\n" - "

501 Unimplemented

\n" - "
\n" - "VideoLAN\n" - "\n" - "\n" ); - - answer->i_body = p - answer->p_body; + answer->i_body = httpd_HtmlError (&p, 501, NULL); + answer->p_body = (uint8_t *)p; httpd_MsgAdd( answer, "Content-Length", "%d", answer->i_body ); cl->i_buffer = -1; /* Force the creation of the answer in httpd_ClientSend */ @@ -2099,10 +2120,9 @@ static void httpd_HostThread( httpd_host_t *host ) { vlc_bool_t b_auth_failed = VLC_FALSE; vlc_bool_t b_hosts_failed = VLC_FALSE; - int i; /* 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]; @@ -2129,28 +2149,23 @@ static void httpd_HostThread( httpd_host_t *host ) if( answer && ( *url->psz_user || *url->psz_password ) ) { /* create the headers */ - char *b64 = httpd_MsgGet( query, "Authorization" ); /* BASIC id */ - char *auth; + const char *b64 = httpd_MsgGet( query, "Authorization" ); /* BASIC id */ + char *auth = NULL; char *id; asprintf( &id, "%s:%s", url->psz_user, url->psz_password ); - auth = malloc( strlen(b64) + 1 ); - - if( !strncasecmp( b64, "BASIC", 5 ) ) + if( b64 != NULL + && !strncasecmp( b64, "BASIC", 5 ) ) { b64 += 5; while( *b64 == ' ' ) { b64++; } - b64_decode( auth, b64 ); - } - else - { - strcpy( auth, "" ); + auth = vlc_b64_decode( b64 ); } - if( strcmp( id, auth ) ) + if( (auth == NULL) || strcmp( id, auth ) ) { httpd_MsgAdd( answer, "WWW-Authenticate", "Basic realm=\"%s\"", url->psz_user ); /* We fail for all url */ @@ -2187,79 +2202,37 @@ static void httpd_HostThread( httpd_host_t *host ) if( answer ) { - uint8_t *p; + char *p; answer->i_proto = query->i_proto; answer->i_type = HTTPD_MSG_ANSWER; answer->i_version= 0; - p = answer->p_body = malloc( 1000 + strlen(query->psz_url) ); if( b_hosts_failed ) { answer->i_status = 403; answer->psz_status = strdup( "Forbidden" ); - - /* FIXME: lots of code duplication */ - p += sprintf( (char *)p, - "" - "\n" - "\n" - "\n" - "Error 403\n" - "\n" - "\n" - "

403 Forbidden (%s)

\n" - "
\n" - "VideoLAN\n" - "\n" - "\n", query->psz_url ); } else if( b_auth_failed ) { answer->i_status = 401; answer->psz_status = strdup( "Authorization Required" ); - - p += sprintf( (char *)p, - "" - "\n" - "\n" - "\n" - "Error 401\n" - "\n" - "\n" - "

401 Authorization Required (%s)

\n" - "
\n" - "VideoLAN\n" - "\n" - "\n", query->psz_url ); } else { /* no url registered */ answer->i_status = 404; answer->psz_status = strdup( "Not found" ); - - p += sprintf( (char *)p, - "" - "\n" - "\n" - "\n" - "Error 404\n" - "\n" - "\n" - "

404 Resource not found(%s)

\n" - "
\n" - "VideoLAN\n" - "\n" - "\n", query->psz_url ); } - answer->i_body = p - answer->p_body; + answer->i_body = httpd_HtmlError (&p, + answer->i_status, + query->psz_url); + answer->p_body = (uint8_t *)p; + cl->i_buffer = -1; /* Force the creation of the answer in httpd_ClientSend */ httpd_MsgAdd( answer, "Content-Length", "%d", answer->i_body ); + httpd_MsgAdd( answer, "Content-Type", "%s", "text/html" ); } cl->i_state = HTTPD_CLIENT_SENDING; @@ -2269,13 +2242,29 @@ static void httpd_HostThread( httpd_host_t *host ) { if( cl->i_mode == HTTPD_CLIENT_FILE || cl->answer.i_body_offset == 0 ) { + const char *psz_connection = httpd_MsgGet( &cl->answer, "Connection" ); + const char *psz_query = httpd_MsgGet( &cl->query, "Connection" ); + vlc_bool_t b_connection = VLC_FALSE; + vlc_bool_t b_keepalive = VLC_FALSE; + vlc_bool_t b_query = VLC_FALSE; + cl->url = NULL; - if( ( cl->query.i_proto == HTTPD_PROTO_HTTP && - ( ( cl->answer.i_version == 0 && !strcasecmp( httpd_MsgGet( &cl->answer, "Connection" ), "Keep-Alive" ) ) || - ( cl->answer.i_version == 1 && strcasecmp( httpd_MsgGet( &cl->answer, "Connection" ), "Close" ) ) ) ) || - ( cl->query.i_proto == HTTPD_PROTO_RTSP && - strcasecmp( httpd_MsgGet( &cl->query, "Connection" ), "Close" ) && - strcasecmp( httpd_MsgGet( &cl->answer, "Connection" ), "Close" ) ) ) + if( psz_connection ) + { + b_connection = ( strcasecmp( psz_connection, "Close" ) == 0 ); + b_keepalive = ( strcasecmp( psz_connection, "Keep-Alive" ) == 0 ); + } + + if( psz_query ) + { + b_query = ( strcasecmp( psz_query, "Close" ) == 0 ); + } + + if( ( ( cl->query.i_proto == HTTPD_PROTO_HTTP ) && + ( ( cl->answer.i_version == 0 && b_keepalive ) || + ( cl->answer.i_version == 1 && !b_connection ) ) ) || + ( ( cl->query.i_proto == HTTPD_PROTO_RTSP ) && + !b_query && !b_connection ) ) { httpd_MsgClean( &cl->query ); httpd_MsgInit( &cl->query ); @@ -2331,7 +2320,7 @@ static void httpd_HostThread( httpd_host_t *host ) &cl->answer, &cl->query ); 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->p_buffer = cl->answer.p_body; cl->i_buffer_size = cl->answer.i_body; @@ -2350,104 +2339,45 @@ static void httpd_HostThread( httpd_host_t *host ) if( cl->i_mode == HTTPD_CLIENT_BIDIR && cl->i_state == HTTPD_CLIENT_SENDING ) { - FD_SET( cl->fd, &fds_read ); - i_handle_max = __MAX( i_handle_max, cl->fd ); + pufd->events |= POLLIN; } + + if (pufd->events != 0) + nfd++; } vlc_mutex_unlock( &host->lock ); /* we will wait 100ms or 20ms (not too big 'cause HTTPD_CLIENT_WAITING) */ - timeout.tv_sec = 0; - 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) ) - { -#if defined(WIN32) || defined(UNDER_CE) - msg_Warn( host, "cannot select sockets (%d)", WSAGetLastError( ) ); -#else - msg_Warn( host, "cannot select sockets : %s", strerror( errno ) ); -#endif - msleep( 1000 ); - continue; - } - else if( i_ret <= 0 ) + switch( poll( ufd, nfd, b_low_delay ? 20 : 100) ) { - continue; - } - - /* accept new connections */ - for( i_fd = 0; (fd = host->fd[i_fd]) != -1; i_fd++ ) - { - if( FD_ISSET( fd, &fds_read ) ) - { - socklen_t i_sock_size = sizeof( struct sockaddr_storage ); - struct sockaddr_storage sock; - - fd = accept( fd, (struct sockaddr *)&sock, &i_sock_size ); - msg_Info( host, "Accepting" ); - if( fd >= 0 ) + case -1: + if (errno != EINTR) { - int i_state = 0; - - /* set this new socket non-block */ -#if defined( WIN32 ) || defined( UNDER_CE ) - { - unsigned long i_dummy = 1; - ioctlsocket( fd, FIONBIO, &i_dummy ); - } -#else - fcntl( fd, F_SETFL, O_NONBLOCK ); -#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 ) - { - httpd_client_t *cl; - stats_UpdateInteger( host, STATS_CLIENT_CONNECTIONS, - 1, NULL ); - stats_UpdateInteger( host, STATS_ACTIVE_CONNECTIONS, 1, - NULL ); - cl = httpd_ClientNew( fd, &sock, i_sock_size, p_tls ); - p_tls = NULL; - vlc_mutex_lock( &host->lock ); - TAB_APPEND( host->i_client, host->client, cl ); - vlc_mutex_unlock( &host->lock ); - - if( i_state != 0 ) - cl->i_state = i_state; // override state for TLS - } + /* This is most likely a bug */ + msg_Err( host, "polling error: %s", strerror (errno)); + msleep( 1000 ); } - } + case 0: + continue; } - /* now try all others socket */ + /* Handle client sockets */ vlc_mutex_lock( &host->lock ); - for( i_client = 0; i_client < host->i_client; i_client++ ) + now = mdate(); + for( int i_client = 0; i_client < host->i_client; i_client++ ) { httpd_client_t *cl = host->client[i_client]; + const struct pollfd *pufd = &ufd[host->nfd + i_client]; + + assert( pufd < &ufd[sizeof(ufd) / sizeof(ufd[0])] ); + + if( cl->fd != pufd->fd ) + continue; // we were not waiting for this client + if( pufd->revents == 0 ) + continue; // no event received + + cl->i_activity_date = now; + if( cl->i_state == HTTPD_CLIENT_RECEIVING ) { httpd_ClientRecv( cl ); @@ -2467,16 +2397,76 @@ static void httpd_HostThread( httpd_host_t *host ) if( cl->i_mode == HTTPD_CLIENT_BIDIR && cl->i_state == HTTPD_CLIENT_SENDING && - FD_ISSET( cl->fd, &fds_read ) ) + (pufd->revents & POLLIN) ) { cl->b_read_waiting = VLC_TRUE; } } vlc_mutex_unlock( &host->lock ); + + /* Handle server sockets (accept new connections) */ + for( nfd = 0; nfd < host->nfd; nfd++ ) + { + httpd_client_t *cl; + int i_state = -1; + + assert (ufd[nfd].fd == host->fds[nfd]); + + if( ufd[nfd].revents == 0 ) + continue; + + /* */ + int kludge[] = { ufd[nfd].fd, -1 }; + int fd = net_Accept( host, kludge, 0 ); + if( fd < 0 ) + continue; + + 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( (p_tls == NULL) != (host->p_tls == NULL) ) + break; // wasted TLS session, cannot accept() anymore + } + + stats_UpdateInteger( host, p_total_counter, 1, NULL ); + stats_UpdateInteger( host, p_active_counter, 1, NULL ); + cl = httpd_ClientNew( fd, p_tls, now ); + p_tls = NULL; + vlc_mutex_lock( &host->lock ); + TAB_APPEND( host->i_client, host->client, cl ); + vlc_mutex_unlock( &host->lock ); + if( i_state != -1 ) + cl->i_state = i_state; // override state for TLS + + if (host->p_tls != NULL) + break; // cannot accept further without new TLS session + } + } if( p_tls != NULL ) tls_ServerSessionClose( p_tls ); + if( p_total_counter ) + stats_CounterClean( p_total_counter ); + if( p_active_counter ) + stats_CounterClean( p_active_counter ); } #else /* ENABLE_HTTPD */ @@ -2600,13 +2590,13 @@ void httpd_MsgInit ( httpd_message_t *a ) { } -void httpd_MsgAdd ( httpd_message_t *a, char *b, char *c, ... ) +void httpd_MsgAdd ( httpd_message_t *a, const char *b, const char *c, ... ) { } -char *httpd_MsgGet ( httpd_message_t *a, char *b ) +const char *httpd_MsgGet ( httpd_message_t *a, const char *b ) { - return NULL; + return ""; } void httpd_MsgClean( httpd_message_t *a )