]> git.sesse.net Git - vlc/blobdiff - src/network/httpd.c
Fix Server: HTTP and RTSP header syntax
[vlc] / src / network / httpd.c
index 4dce0e8ec2f758e78c76ae8fd5c4e99018ff29dc..2e95eedc38ffd0d352de06c6e87bc50101db38dc 100644 (file)
@@ -36,6 +36,7 @@
 #include <vlc_tls.h>
 #include <vlc_acl.h>
 #include <vlc_strings.h>
+#include <vlc_rand.h>
 #include "../libvlc.h"
 
 #include <string.h>
 #   include <unistd.h>
 #endif
 
-#ifdef HAVE_FCNTL_H
-#   include <fcntl.h>
-#endif
-
 #ifdef HAVE_POLL
 # include <poll.h>
 #endif
@@ -87,7 +84,7 @@ struct httpd_host_t
     httpd_t     *httpd;
 
     /* ref count */
-    int         i_ref;
+    unsigned    i_ref;
 
     /* address/port and socket for listening at connections */
     char        *psz_hostname;
@@ -95,7 +92,9 @@ struct httpd_host_t
     int         *fds;
     unsigned     nfd;
 
+    vlc_thread_t thread;
     vlc_mutex_t lock;
+    vlc_cond_t  wait;
 
     /* 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
@@ -225,8 +224,12 @@ static const struct
     { ".mpe",   "video/mpeg" },
     { ".mov",   "video/quicktime" },
     { ".moov",  "video/quicktime" },
+    { ".oga",   "audio/ogg" },
     { ".ogg",   "application/ogg" },
     { ".ogm",   "application/ogg" },
+    { ".ogv",   "video/ogg" },
+    { ".ogx",   "application/ogg" },
+    { ".spx",   "audio/ogg" },
     { ".wav",   "audio/wav" },
     { ".wma",   "audio/x-ms-wma" },
     { ".wmv",   "video/x-ms-wmv" },
@@ -360,7 +363,7 @@ static size_t httpd_HtmlError (char **body, int code, const char *url)
         "<a href=\"http://www.videolan.org\">VideoLAN</a>\n"
         "</body>\n"
         "</html>\n", errname, code, errname,
-        (url ? " (" : ""), (url ?: ""), (url ? ")" : ""));
+        (url ? " (" : ""), (url ? url : ""), (url ? ")" : ""));
 
     if (res == -1)
     {
@@ -454,7 +457,7 @@ httpd_file_t *httpd_FileNew( httpd_host_t *host,
                              const vlc_acl_t *p_acl, httpd_file_callback_t pf_fill,
                              httpd_file_sys_t *p_sys )
 {
-    httpd_file_t *file = malloc( sizeof( httpd_file_t ) );
+    httpd_file_t *file = xmalloc( sizeof( httpd_file_t ) );
 
     if( ( file->url = httpd_UrlNewUnique( host, psz_url, psz_user,
                                           psz_password, p_acl )
@@ -557,7 +560,7 @@ httpd_HandlerCallBack( httpd_callback_sys_t *p_sys, httpd_client_t *cl,
         {
             p[4] = '\0';
             answer->i_body = strlen((char*)answer->p_body) + 1;
-            answer->p_body = realloc( answer->p_body, answer->i_body );
+            answer->p_body = xrealloc( answer->p_body, answer->i_body );
         }
     }
 
@@ -571,8 +574,8 @@ httpd_HandlerCallBack( httpd_callback_sys_t *p_sys, httpd_client_t *cl,
         {
             /* Apache-style */
             i_status = strtol( (char *)&answer->p_body[8], &psz_headers, 0 );
-            if( *psz_headers ) psz_headers++;
-            if( *psz_headers ) psz_headers++;
+            if( *psz_headers == '\r' || *psz_headers == '\n' ) psz_headers++;
+            if( *psz_headers == '\n' ) psz_headers++;
             i_headers = answer->i_body - (psz_headers - (char *)answer->p_body);
         }
         else
@@ -585,7 +588,7 @@ httpd_HandlerCallBack( httpd_callback_sys_t *p_sys, httpd_client_t *cl,
         psz_status = httpd_ReasonFromCode( i_status );
         answer->i_body = sizeof("HTTP/1.0 xxx \r\n")
                         + strlen(psz_status) + i_headers - 1;
-        psz_new = (char *)malloc( answer->i_body + 1);
+        psz_new = (char *)xmalloc( answer->i_body + 1);
         sprintf( psz_new, "HTTP/1.0 %03d %s\r\n", i_status, psz_status );
         memcpy( &psz_new[strlen(psz_new)], psz_headers, i_headers );
         free( answer->p_body );
@@ -602,7 +605,7 @@ httpd_handler_t *httpd_HandlerNew( httpd_host_t *host, const char *psz_url,
                                    httpd_handler_callback_t pf_fill,
                                    httpd_handler_sys_t *p_sys )
 {
-    httpd_handler_t *handler = malloc( sizeof( httpd_handler_t ) );
+    httpd_handler_t *handler = xmalloc( sizeof( httpd_handler_t ) );
 
     if( ( handler->url = httpd_UrlNewUnique( host, psz_url, psz_user,
                                              psz_password, p_acl )
@@ -673,7 +676,7 @@ static int httpd_RedirectCallBack( httpd_callback_sys_t *p_sys,
 httpd_redirect_t *httpd_RedirectNew( httpd_host_t *host, const char *psz_url_dst,
                                      const char *psz_url_src )
 {
-    httpd_redirect_t *rdir = malloc( sizeof( httpd_redirect_t ) );
+    httpd_redirect_t *rdir = xmalloc( sizeof( httpd_redirect_t ) );
 
     if( !( rdir->url = httpd_UrlNewUnique( host, psz_url_src, NULL, NULL, NULL ) ) )
     {
@@ -779,7 +782,7 @@ static int httpd_StreamCallBack( httpd_callback_sys_t *p_sys,
         answer->i_type   = HTTPD_MSG_ANSWER;
 
         answer->i_body = i_write;
-        answer->p_body = malloc( i_write );
+        answer->p_body = xmalloc( i_write );
         memcpy( answer->p_body, &stream->p_buffer[i_pos], i_write );
 
         answer->i_body_offset += i_write;
@@ -802,7 +805,7 @@ static int httpd_StreamCallBack( httpd_callback_sys_t *p_sys,
             if( stream->i_header > 0 )
             {
                 answer->i_body = stream->i_header;
-                answer->p_body = malloc( stream->i_header );
+                answer->p_body = xmalloc( stream->i_header );
                 memcpy( answer->p_body, stream->p_header, stream->i_header );
             }
             answer->i_body_offset = stream->i_buffer_last_pos;
@@ -823,7 +826,8 @@ static int httpd_StreamCallBack( httpd_callback_sys_t *p_sys,
                           "application/octet-stream" );
             httpd_MsgAdd( answer, "Server", "Cougar 4.1.0.3921" );
             httpd_MsgAdd( answer, "Pragma", "no-cache" );
-            httpd_MsgAdd( answer, "Pragma", "client-id=%d", rand()&0x7fff );
+            httpd_MsgAdd( answer, "Pragma", "client-id=%lu",
+                          vlc_mrand48()&0x7fff );
             httpd_MsgAdd( answer, "Pragma", "features=\"broadcast\"" );
 
             /* Check if there is a xPlayStrm=1 */
@@ -855,7 +859,7 @@ httpd_stream_t *httpd_StreamNew( httpd_host_t *host,
                                  const char *psz_user, const char *psz_password,
                                  const vlc_acl_t *p_acl )
 {
-    httpd_stream_t *stream = malloc( sizeof( httpd_stream_t ) );
+    httpd_stream_t *stream = xmalloc( sizeof( httpd_stream_t ) );
 
     if( ( stream->url = httpd_UrlNewUnique( host, psz_url, psz_user,
                                             psz_password, p_acl )
@@ -876,7 +880,7 @@ httpd_stream_t *httpd_StreamNew( httpd_host_t *host,
     stream->i_header = 0;
     stream->p_header = NULL;
     stream->i_buffer_size = 5000000;    /* 5 Mo per stream */
-    stream->p_buffer = malloc( stream->i_buffer_size );
+    stream->p_buffer = xmalloc( stream->i_buffer_size );
     /* We set to 1 to make life simpler
      * (this way i_body_offset can never be 0) */
     stream->i_buffer_pos = 1;
@@ -901,7 +905,7 @@ int httpd_StreamHeader( httpd_stream_t *stream, uint8_t *p_data, int i_data )
     stream->i_header = i_data;
     if( i_data > 0 )
     {
-        stream->p_header = malloc( i_data );
+        stream->p_header = xmalloc( i_data );
         memcpy( stream->p_header, p_data, i_data );
     }
     vlc_mutex_unlock( &stream->lock );
@@ -958,7 +962,7 @@ void httpd_StreamDelete( httpd_stream_t *stream )
 /*****************************************************************************
  * Low level
  *****************************************************************************/
-static void* httpd_HostThread( vlc_object_t * );
+static void* httpd_HostThread( void * );
 
 /* create a new host */
 httpd_host_t *httpd_HostNew( vlc_object_t *p_this, const char *psz_host,
@@ -969,6 +973,7 @@ httpd_host_t *httpd_HostNew( vlc_object_t *p_this, const char *psz_host,
 }
 
 static const char psz_object_type[] = "http server";
+static vlc_mutex_t httpd_mutex = VLC_STATIC_MUTEX;
 
 httpd_host_t *httpd_TLSHostNew( vlc_object_t *p_this, const char *psz_hostname,
                                 int i_port,
@@ -979,7 +984,7 @@ httpd_host_t *httpd_TLSHostNew( vlc_object_t *p_this, const char *psz_hostname,
     httpd_host_t *host;
     tls_server_t *p_tls;
     char *psz_host;
-    vlc_value_t  lockval, ptrval;
+    vlc_value_t  ptrval;
     int i;
 
     if( psz_hostname == NULL )
@@ -990,9 +995,7 @@ httpd_host_t *httpd_TLSHostNew( vlc_object_t *p_this, const char *psz_hostname,
         return NULL;
 
     /* 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 );
-    vlc_mutex_lock( lockval.p_address );
+    vlc_mutex_lock( &httpd_mutex );
     httpd = libvlc_priv (p_this->p_libvlc)->p_httpd;
 
     if( httpd == NULL )
@@ -1003,7 +1006,7 @@ httpd_host_t *httpd_TLSHostNew( vlc_object_t *p_this, const char *psz_hostname,
                                               psz_object_type );
         if( httpd == NULL )
         {
-            vlc_mutex_unlock( lockval.p_address );
+            vlc_mutex_unlock( &httpd_mutex );
             free( psz_host );
             return NULL;
         }
@@ -1013,7 +1016,6 @@ httpd_host_t *httpd_TLSHostNew( vlc_object_t *p_this, const char *psz_hostname,
 
         ptrval.p_address = httpd;
         libvlc_priv (p_this->p_libvlc)->p_httpd = httpd;
-        vlc_object_hold( httpd );
         vlc_object_attach( httpd, p_this->p_libvlc );
     }
 
@@ -1028,10 +1030,15 @@ httpd_host_t *httpd_TLSHostNew( vlc_object_t *p_this, const char *psz_hostname,
          || strcmp( host->psz_hostname, psz_hostname ) )
             continue;
 
-        /* yep found */
+        /* Increase existing matching host reference count.
+         * The reference count is written under both the global httpd and the
+         * host lock. It is read with either or both locks held. The global
+         * lock is always acquired first. */
+        vlc_mutex_lock( &host->lock );
         host->i_ref++;
+        vlc_mutex_unlock( &host->lock );
 
-        vlc_mutex_unlock( lockval.p_address );
+        vlc_mutex_unlock( &httpd_mutex );
         return host;
     }
 
@@ -1069,19 +1076,13 @@ httpd_host_t *httpd_TLSHostNew( vlc_object_t *p_this, const char *psz_hostname,
     if (host == NULL)
         goto error;
 
-    vlc_object_lock( host );
-    if( vlc_object_waitpipe( VLC_OBJECT( host ) ) == -1 )
-    {
-        msg_Err( host, "signaling pipe error: %m" );
-        vlc_object_unlock( host );
-        goto error;
-    }
-    vlc_object_unlock( host );
-
     host->httpd = httpd;
     vlc_mutex_init( &host->lock );
+    vlc_cond_init( &host->wait );
     host->i_ref = 1;
 
+    vlc_object_attach( host, p_this );
+
     host->fds = net_ListenTCP( p_this, psz_host, i_port );
     if( host->fds == NULL )
     {
@@ -1090,6 +1091,12 @@ httpd_host_t *httpd_TLSHostNew( vlc_object_t *p_this, const char *psz_hostname,
     }
     for (host->nfd = 0; host->fds[host->nfd] != -1; host->nfd++);
 
+    if( vlc_object_waitpipe( VLC_OBJECT( host ) ) == -1 )
+    {
+        msg_Err( host, "signaling pipe error: %m" );
+        goto error;
+    }
+
     host->i_port = i_port;
     host->psz_hostname = psz_host;
 
@@ -1101,8 +1108,8 @@ httpd_host_t *httpd_TLSHostNew( vlc_object_t *p_this, const char *psz_hostname,
     host->p_tls = p_tls;
 
     /* create the thread */
-    if( vlc_thread_create( host, "httpd host thread", httpd_HostThread,
-                           VLC_THREAD_PRIORITY_LOW, false ) )
+    if( vlc_clone( &host->thread, httpd_HostThread, host,
+                   VLC_THREAD_PRIORITY_LOW ) )
     {
         msg_Err( p_this, "cannot spawn http host thread" );
         goto error;
@@ -1110,7 +1117,7 @@ httpd_host_t *httpd_TLSHostNew( vlc_object_t *p_this, const char *psz_hostname,
 
     /* now add it to httpd */
     TAB_APPEND( httpd->i_host, httpd->host, host );
-    vlc_mutex_unlock( lockval.p_address );
+    vlc_mutex_unlock( &httpd_mutex );
 
     return host;
 
@@ -1120,14 +1127,13 @@ error:
     {
         libvlc_priv (httpd->p_libvlc)->p_httpd = NULL;
         vlc_object_release( httpd );
-        vlc_object_detach( httpd );
-        vlc_object_release( httpd );
     }
-    vlc_mutex_unlock( lockval.p_address );
+    vlc_mutex_unlock( &httpd_mutex );
 
     if( host != NULL )
     {
         net_ListenClose( host->fds );
+        vlc_cond_destroy( &host->wait );
         vlc_mutex_destroy( &host->lock );
         vlc_object_release( host );
     }
@@ -1142,24 +1148,30 @@ error:
 void httpd_HostDelete( httpd_host_t *host )
 {
     httpd_t *httpd = host->httpd;
-    vlc_value_t lockval;
     int i;
+    bool delete = false;
 
-    var_Get( httpd->p_libvlc, "httpd_mutex", &lockval );
-    vlc_mutex_lock( lockval.p_address );
+    vlc_mutex_lock( &httpd_mutex );
 
+    vlc_mutex_lock( &host->lock );
     host->i_ref--;
-    if( host->i_ref > 0 )
+    if( host->i_ref == 0 )
+    {
+        vlc_cond_signal( &host->wait );
+        delete = true;
+    }
+    vlc_mutex_unlock( &host->lock );
+    if( !delete )
     {
         /* still used */
-        vlc_mutex_unlock( lockval.p_address );
-        msg_Dbg( host, "httpd_HostDelete: host still used" );
+        vlc_mutex_unlock( &httpd_mutex );
+        msg_Dbg( host, "httpd_HostDelete: host still in use" );
         return;
     }
     TAB_REMOVE( httpd->i_host, httpd->host, host );
 
     vlc_object_kill( host );
-    vlc_thread_join( host );
+    vlc_join( host->thread, NULL );
 
     msg_Dbg( host, "HTTP host removed" );
 
@@ -1184,20 +1196,18 @@ void httpd_HostDelete( httpd_host_t *host )
     net_ListenClose( host->fds );
     free( host->psz_hostname );
 
+    vlc_cond_destroy( &host->wait );
     vlc_mutex_destroy( &host->lock );
     vlc_object_release( host );
 
-    vlc_object_release( httpd );
     if( httpd->i_host <= 0 )
     {
-        msg_Dbg( httpd, "no host left, stopping httpd" );
+        msg_Dbg( httpd, "no hosts left, stopping httpd" );
 
         libvlc_priv (httpd->p_libvlc)->p_httpd = NULL;
-        vlc_object_detach( httpd );
         vlc_object_release( httpd );
-
     }
-    vlc_mutex_unlock( lockval.p_address );
+    vlc_mutex_unlock( &httpd_mutex );
 }
 
 /* register a new url */
@@ -1225,7 +1235,7 @@ static httpd_url_t *httpd_UrlNewPrivate( httpd_host_t *host, const char *psz_url
         }
     }
 
-    url = malloc( sizeof( httpd_url_t ) );
+    url = xmalloc( sizeof( httpd_url_t ) );
     url->host = host;
 
     vlc_mutex_init( &url->lock );
@@ -1240,6 +1250,7 @@ static httpd_url_t *httpd_UrlNewPrivate( httpd_host_t *host, const char *psz_url
     }
 
     TAB_APPEND( host->i_url, host->url, url );
+    vlc_cond_signal( &host->wait );
     vlc_mutex_unlock( &host->lock );
 
     return url;
@@ -1392,7 +1403,7 @@ static void httpd_ClientInit( httpd_client_t *cl, mtime_t now )
     cl->i_activity_timeout = INT64_C(10000000);
     cl->i_buffer_size = HTTPD_CL_BUFSIZE;
     cl->i_buffer = 0;
-    cl->p_buffer = malloc( cl->i_buffer_size );
+    cl->p_buffer = xmalloc( cl->i_buffer_size );
     cl->i_mode   = HTTPD_CLIENT_FILE;
     cl->b_read_waiting = false;
 
@@ -1540,7 +1551,7 @@ static void httpd_ClientRecv( httpd_client_t *cl )
             cl->query.i_type  = HTTPD_MSG_CHANNEL;
             cl->query.i_channel = cl->p_buffer[1];
             cl->query.i_body  = (cl->p_buffer[2] << 8)|cl->p_buffer[3];
-            cl->query.p_body  = malloc( cl->query.i_body );
+            cl->query.p_body  = xmalloc( cl->query.i_body );
             cl->i_buffer      -= 4;
             memcpy( cl->query.p_body, cl->p_buffer + 4, cl->i_buffer );
         }
@@ -1821,7 +1832,7 @@ static void httpd_ClientRecv( httpd_client_t *cl )
                     /* TODO Mhh, handle the case client will only send a
                      * request and close the connection
                      * to mark and of body (probably only RTSP) */
-                    cl->query.p_body = malloc( cl->query.i_body );
+                    cl->query.p_body = xmalloc( cl->query.i_body );
                     cl->i_buffer = 0;
                 }
                 else
@@ -1909,7 +1920,7 @@ static void httpd_ClientSend( httpd_client_t *cl )
         {
             cl->i_buffer_size = i_size;
             free( cl->p_buffer );
-            cl->p_buffer = malloc( i_size );
+            cl->p_buffer = xmalloc( i_size );
         }
         p = (char *)cl->p_buffer;
 
@@ -2022,31 +2033,16 @@ static void httpd_ClientTlsHsOut( httpd_client_t *cl )
     }
 }
 
-static void* httpd_HostThread( vlc_object_t *p_this )
+static void* httpd_HostThread( void *data )
 {
-    httpd_host_t *host = (httpd_host_t *)p_this;
+    httpd_host_t *host = data;
     tls_session_t *p_tls = NULL;
     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 );
-    int evfd;
-    bool b_die;
-    int canc = vlc_savecancel ();
-
-retry:
-    vlc_object_lock( host );
-    evfd = vlc_object_waitpipe( VLC_OBJECT( host ) );
-    b_die = !vlc_object_alive( host );
-    vlc_object_unlock( host );
+    int evfd = vlc_object_waitpipe( VLC_OBJECT( host ) );
 
-    while( !b_die )
+    for( ;; )
     {
-        if( host->i_url <= 0 )
-        {
-            /* 0.2s (FIXME: use a condition variable) */
-            msleep( 200000 );
-            goto retry;
-        }
-
         /* prepare a new TLS session */
         if( ( p_tls == NULL ) && ( host->p_tls != NULL ) )
             p_tls = tls_ServerSessionPrepare( host->p_tls );
@@ -2062,6 +2058,9 @@ retry:
 
         /* add all socket that should be read/write and close dead connection */
         vlc_mutex_lock( &host->lock );
+        while( host->i_url <= 0 && host->i_ref > 0 )
+            vlc_cond_wait( &host->wait, &host->lock );
+
         mtime_t now = mdate();
         bool b_low_delay = false;
 
@@ -2145,7 +2144,7 @@ retry:
                     answer->i_body = 0;
                     answer->p_body = NULL;
 
-                    httpd_MsgAdd( answer, "Server", "%s", PACKAGE_STRING );
+                    httpd_MsgAdd( answer, "Server", "VLC/%s", VERSION );
                     httpd_MsgAdd( answer, "Content-Length", "0" );
 
                     switch( query->i_proto )
@@ -2264,8 +2263,7 @@ retry:
                                     {
                                         httpd_MsgAdd( answer,
                                                       "WWW-Authenticate",
-                                                      "Basic realm=\"%s\"",
-                                                      url->psz_user );
+                                                      "Basic realm=\"VLC stream\"" );
                                         /* We fail for all url */
                                         b_auth_failed = true;
                                         free( user );
@@ -2365,7 +2363,7 @@ retry:
                         cl->i_buffer = 0;
                         cl->i_buffer_size = 1000;
                         free( cl->p_buffer );
-                        cl->p_buffer = malloc( cl->i_buffer_size );
+                        cl->p_buffer = xmalloc( cl->i_buffer_size );
                         cl->i_state = HTTPD_CLIENT_RECEIVING;
                     }
                     else
@@ -2383,7 +2381,7 @@ retry:
                     cl->i_buffer = 0;
                     cl->i_buffer_size = 1000;
                     free( cl->p_buffer );
-                    cl->p_buffer = malloc( cl->i_buffer_size );
+                    cl->p_buffer = xmalloc( cl->i_buffer_size );
                     cl->i_state = HTTPD_CLIENT_RECEIVING;
                     cl->b_read_waiting = false;
                 }
@@ -2456,14 +2454,8 @@ retry:
                 continue;
         }
 
-        vlc_object_lock( host );
         if( ufd[nfd - 1].revents )
-        {
-            b_die = !vlc_object_alive( host );
-            if( !b_die )
-                vlc_object_wait( host );
-        }
-        vlc_object_unlock( host );
+            break;
 
         /* Handle client sockets */
         vlc_mutex_lock( &host->lock );
@@ -2523,11 +2515,12 @@ retry:
                 continue;
 
             /* */
-            fd = accept (fd, NULL, NULL);
+            fd = vlc_accept (fd, NULL, NULL, true);
             if (fd == -1)
                 continue;
+            setsockopt (fd, SOL_SOCKET, SO_REUSEADDR,
+                        &(int){ 1 }, sizeof(int));
 
-            net_SetupSocket (fd);
             if( p_tls != NULL )
             {
                 switch( tls_ServerSessionHandshake( p_tls, fd ) )
@@ -2574,6 +2567,5 @@ retry:
         stats_CounterClean( p_total_counter );
     if( p_active_counter )
         stats_CounterClean( p_active_counter );
-    vlc_restorecancel (canc);
     return NULL;
 }