X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=src%2Fnetwork%2Fhttpd.c;h=6e1af91c6a5fa196a58aaa5f084ff83035213023;hb=f485214f09dd284cbb85674e937fbbb0a6032a2e;hp=b9ab16fa6f843e82191d1a65e649f27a84ea0630;hpb=47fd1f615a8c94a6af6d54c8b47f54ca2860152e;p=vlc diff --git a/src/network/httpd.c b/src/network/httpd.c index b9ab16fa6f..6e1af91c6a 100644 --- a/src/network/httpd.c +++ b/src/network/httpd.c @@ -1,7 +1,7 @@ /***************************************************************************** * httpd.c ***************************************************************************** - * Copyright (C) 2004-2005 the VideoLAN team + * Copyright (C) 2004-2006 the VideoLAN team * $Id$ * * Authors: Laurent Aimar @@ -22,11 +22,15 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ -#include #include +#include +#include + #ifdef ENABLE_HTTPD +#include + #include "vlc_httpd.h" #include "network.h" #include "vlc_tls.h" @@ -47,15 +51,8 @@ # 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,37 +64,6 @@ static void httpd_ClientClean( httpd_client_t *cl ); -/* each host run in his own thread */ -struct httpd_host_t -{ - VLC_COMMON_MEMBERS - - httpd_t *httpd; - - /* ref count */ - int i_ref; - - /* address/port and socket for listening at connections */ - char *psz_hostname; - int i_port; - int *fd; - - vlc_mutex_t lock; - - /* all registered url (becarefull that 2 httpd_url_t could point at the same url) - * This will slow down the url research but make my live easier - * All url will have their cb trigger, but only the first one can answer - * */ - int i_url; - httpd_url_t **url; - - int i_client; - httpd_client_t **client; - - /* TLS data */ - tls_server_t *p_tls; -}; - struct httpd_url_t { httpd_host_t *host; @@ -301,6 +267,88 @@ static const char *httpd_MimeFromUrl( const char *psz_url ) return "application/octet-stream"; } +#if 0 +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]; +} +#endif + /***************************************************************************** * High Level Functions: httpd_file_t *****************************************************************************/ @@ -321,6 +369,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; + char *psz_connection = NULL; int *pi_body, i_body; if( answer == NULL || query == NULL ) @@ -364,10 +413,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 +489,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 +502,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, @@ -597,7 +624,7 @@ 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; if( answer == NULL || query == NULL ) { @@ -609,8 +636,7 @@ 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, + answer->i_body = asprintf( &p_body, "\n" "\n" @@ -619,14 +645,14 @@ static int httpd_RedirectCallBack( httpd_callback_sys_t *p_sys, "Redirection\n" "\n" "\n" - "

You should be " + "

You should be " "redirected

\n" "
\n" "

VideoLAN\n

" "
\n" "\n" "\n", rdir->psz_dst ); - answer->i_body = p - answer->p_body; + 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 +744,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; } @@ -958,8 +986,8 @@ 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 )) ) @@ -976,7 +1004,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,7 +1053,7 @@ 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 = vlc_object_create( p_this, VLC_OBJECT_HTTPD_HOST ); host->httpd = httpd; vlc_mutex_init( httpd, &host->lock ); host->i_ref = 1; @@ -1091,7 +1119,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--; @@ -1111,7 +1139,7 @@ void httpd_HostDelete( httpd_host_t *host ) 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++ ) { @@ -1319,7 +1347,7 @@ 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, ... ) @@ -1371,12 +1399,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; } @@ -1482,7 +1510,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 ) ) { @@ -1559,18 +1587,19 @@ static void httpd_ClientRecv( httpd_client_t *cl ) } 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; @@ -1917,10 +1946,10 @@ 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 ); + host->p_total_counter = stats_CounterCreate( host, + VLC_VAR_INTEGER, STATS_COUNTER ); + host->p_active_counter = stats_CounterCreate( host, + VLC_VAR_INTEGER, STATS_COUNTER ); while( !host->b_die ) { @@ -1970,7 +1999,7 @@ static void httpd_HostThread( httpd_host_t *host ) cl->i_activity_date+cl->i_activity_timeout < mdate()) ) ) ) { httpd_ClientClean( cl ); - stats_UpdateInteger( host, STATS_ACTIVE_CONNECTIONS, -1, NULL ); + stats_UpdateInteger( host, host->p_active_counter, -1, NULL ); TAB_REMOVE( host->i_client, host->client, cl ); free( cl ); i_client--; @@ -2029,6 +2058,7 @@ static void httpd_HostThread( httpd_host_t *host ) } else if( i_msg == HTTPD_MSG_OPTIONS ) { + char *psz_cseq = NULL; int i_cseq; /* unimplemented */ @@ -2041,7 +2071,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, " @@ -2134,9 +2168,11 @@ static void httpd_HostThread( httpd_host_t *host ) char *id; asprintf( &id, "%s:%s", url->psz_user, url->psz_password ); - auth = malloc( strlen(b64) + 1 ); + if( b64 ) auth = malloc( strlen(b64) + 1 ); + else auth = malloc( strlen("") + 1 ); - if( !strncasecmp( b64, "BASIC", 5 ) ) + if( b64 != NULL + && !strncasecmp( b64, "BASIC", 5 ) ) { b64 += 5; while( *b64 == ' ' ) @@ -2201,7 +2237,7 @@ static void httpd_HostThread( httpd_host_t *host ) /* FIXME: lots of code duplication */ p += sprintf( (char *)p, - "" + "" "\n" "\n" @@ -2211,7 +2247,7 @@ static void httpd_HostThread( httpd_host_t *host ) "\n" "

403 Forbidden (%s)

\n" "
\n" - "VideoLAN\n" + "

VideoLAN

\n" "\n" "\n", query->psz_url ); } @@ -2221,7 +2257,7 @@ static void httpd_HostThread( httpd_host_t *host ) answer->psz_status = strdup( "Authorization Required" ); p += sprintf( (char *)p, - "" + "" "\n" "\n" @@ -2231,7 +2267,7 @@ static void httpd_HostThread( httpd_host_t *host ) "\n" "

401 Authorization Required (%s)

\n" "
\n" - "VideoLAN\n" + "

VideoLAN

\n" "\n" "\n", query->psz_url ); } @@ -2242,7 +2278,7 @@ static void httpd_HostThread( httpd_host_t *host ) answer->psz_status = strdup( "Not found" ); p += sprintf( (char *)p, - "" + "" "\n" "\n" @@ -2252,7 +2288,7 @@ static void httpd_HostThread( httpd_host_t *host ) "\n" "

404 Resource not found(%s)

\n" "
\n" - "VideoLAN\n" + "

VideoLAN

\n" "\n" "\n", query->psz_url ); } @@ -2260,6 +2296,7 @@ static void httpd_HostThread( httpd_host_t *host ) answer->i_body = p - answer->p_body; 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 +2306,29 @@ static void httpd_HostThread( httpd_host_t *host ) { if( cl->i_mode == HTTPD_CLIENT_FILE || cl->answer.i_body_offset == 0 ) { + char *psz_connection = httpd_MsgGet( &cl->answer, "Connection" ); + 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 ); @@ -2365,11 +2418,7 @@ static void httpd_HostThread( httpd_host_t *host ) 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 + msg_Warn( host, "select error: %s", net_strerror( net_errno ) ); msleep( 1000 ); continue; } @@ -2387,10 +2436,13 @@ static void httpd_HostThread( httpd_host_t *host ) struct sockaddr_storage sock; fd = accept( fd, (struct sockaddr *)&sock, &i_sock_size ); - msg_Info( host, "Accepting" ); + if( fd >= 0 ) { - int i_state = 0; + int i_state = 1; + + setsockopt( fd, SOL_SOCKET, SO_REUSEADDR, &i_state, sizeof (i_state)); + i_state = 0; /* set this new socket non-block */ #if defined( WIN32 ) || defined( UNDER_CE ) @@ -2399,9 +2451,20 @@ static void httpd_HostThread( httpd_host_t *host ) ioctlsocket( fd, FIONBIO, &i_dummy ); } #else - fcntl( fd, F_SETFL, O_NONBLOCK ); -#endif + 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 ) + { + net_Close( fd ); + fd = -1; + } + else +#endif if( p_tls != NULL) { switch ( tls_ServerSessionHandshake( p_tls, fd ) ) @@ -2426,11 +2489,14 @@ static void httpd_HostThread( httpd_host_t *host ) if( fd >= 0 ) { httpd_client_t *cl; - stats_UpdateInteger( host, STATS_CLIENT_CONNECTIONS, + char ip[NI_MAXNUMERICHOST]; + stats_UpdateInteger( host, host->p_total_counter, + 1, NULL ); + stats_UpdateInteger( host, host->p_active_counter, 1, NULL ); - stats_UpdateInteger( host, STATS_ACTIVE_CONNECTIONS, 1, - NULL ); cl = httpd_ClientNew( fd, &sock, i_sock_size, 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 );