]> git.sesse.net Git - vlc/blobdiff - src/network/httpd.c
avoid mere whitespaces when hostname is not specified
[vlc] / src / network / httpd.c
index 18f7acf4788b0ec3a7f56aa034821966b2bd8647..9d7d57406c62e0390591eb9383c6a81b02ecd2a0 100644 (file)
@@ -302,9 +302,9 @@ static const http_status_info http_reason[] =
     { 452, "Conference not found" },
     { 453, "Not enough bandwidth" },*/
     { 454, "Session not found" },
-  /*{ 455, "Method not valid in this State" },
+  /*{ 455, "Method not valid in this State" },*/
     { 456, "Header field not valid for resource" },
-    { 457, "Invalid range" },
+  /*{ 457, "Invalid range" },
     { 458, "Read-only parameter" },*/
     { 459, "Aggregate operation not allowed" },
     { 460, "Non-aggregate operation not allowed" },
@@ -314,8 +314,8 @@ static const http_status_info http_reason[] =
     { 501, "Not implemented" },
   /*{ 502, "Bad gateway" },*/
     { 503, "Service unavailable" },
-  /*{ 504, "Gateway time-out" },
-    { 505, "Protocol version not supported" },*/
+  /*{ 504, "Gateway time-out" },*/
+    { 505, "Protocol version not supported" },
     { 551, "Option not supported" },
     { 999, "" }
 };
@@ -399,7 +399,7 @@ httpd_FileCallBack( httpd_callback_sys_t *p_sys, httpd_client_t *cl,
         return VLC_SUCCESS;
     }
     answer->i_proto  = HTTPD_PROTO_HTTP;
-    answer->i_version= (query->i_version > 1) ? 1 : query->i_version;
+    answer->i_version= 1;
     answer->i_type   = HTTPD_MSG_ANSWER;
 
     answer->i_status = 200;
@@ -437,7 +437,7 @@ httpd_FileCallBack( httpd_callback_sys_t *p_sys, httpd_client_t *cl,
     psz_connection = httpd_MsgGet( &cl->query, "Connection" );
     if( psz_connection != NULL )
     {
-        httpd_MsgAdd( answer, "Connection", psz_connection );
+        httpd_MsgAdd( answer, "Connection", "%s", psz_connection );
     }
 
     httpd_MsgAdd( answer, "Content-Length", "%d", answer->i_body );
@@ -484,14 +484,18 @@ httpd_file_t *httpd_FileNew( httpd_host_t *host,
     return file;
 }
 
-void httpd_FileDelete( httpd_file_t *file )
+httpd_file_sys_t *httpd_FileDelete( httpd_file_t *file )
 {
+    httpd_file_sys_t *p_sys = file->p_sys;
+
     httpd_UrlDelete( file->url );
 
     free( file->psz_url );
     free( file->psz_mime );
 
     free( file );
+
+    return p_sys;
 }
 
 /*****************************************************************************
@@ -535,6 +539,8 @@ httpd_HandlerCallBack( httpd_callback_sys_t *p_sys, httpd_client_t *cl,
     if( query->i_type == HTTPD_MSG_HEAD )
     {
         char *p = (char *)answer->p_body;
+
+        /* Looks for end of header (i.e. one empty line) */
         while ( (p = strchr( p, '\r' )) != NULL )
         {
             if( p[1] && p[1] == '\n' && p[2] && p[2] == '\r'
@@ -543,6 +549,7 @@ httpd_HandlerCallBack( httpd_callback_sys_t *p_sys, httpd_client_t *cl,
                 break;
             }
         }
+
         if( p != NULL )
         {
             p[4] = '\0';
@@ -615,10 +622,12 @@ httpd_handler_t *httpd_HandlerNew( httpd_host_t *host, const char *psz_url,
     return handler;
 }
 
-void httpd_HandlerDelete( httpd_handler_t *handler )
+httpd_handler_sys_t *httpd_HandlerDelete( httpd_handler_t *handler )
 {
+    httpd_handler_sys_t *p_sys = handler->p_sys;
     httpd_UrlDelete( handler->url );
     free( handler );
+    return p_sys;
 }
 
 /*****************************************************************************
@@ -643,7 +652,7 @@ static int httpd_RedirectCallBack( httpd_callback_sys_t *p_sys,
         return VLC_SUCCESS;
     }
     answer->i_proto  = HTTPD_PROTO_HTTP;
-    answer->i_version= (query->i_version > 1) ? 1 : query->i_version;
+    answer->i_version= 1;
     answer->i_type   = HTTPD_MSG_ANSWER;
     answer->i_status = 301;
 
@@ -1059,6 +1068,15 @@ 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( httpd, &host->lock );
     host->i_ref = 1;
@@ -1185,6 +1203,8 @@ static httpd_url_t *httpd_UrlNewPrivate( httpd_host_t *host, const char *psz_url
     httpd_url_t *url;
     int         i;
 
+    assert( psz_url != NULL );
+
     vlc_mutex_lock( &host->lock );
     if( b_check )
     {
@@ -1468,55 +1488,88 @@ static int httpd_NetSend( httpd_client_t *cl, const uint8_t *p, int i_len )
     return send( cl->fd, p, i_len, 0 );
 }
 
+
+static const struct
+{
+    const char name[16];
+    int  i_type;
+    int  i_proto;
+}
+msg_type[] =
+{
+    { "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 },
+    { "",              HTTPD_MSG_NONE,         HTTPD_PROTO_NONE }
+};
+
+
 static void httpd_ClientRecv( httpd_client_t *cl )
 {
     int i_len;
 
+    /* ignore leading whites */
+    if( ( cl->query.i_proto == HTTPD_PROTO_NONE ) &&
+        ( cl->i_buffer == 0 ) )
+    {
+        unsigned char c;
+
+        i_len = httpd_NetRecv( cl, &c, 1 );
+
+        if( ( i_len > 0 ) && ( strchr( "\r\n\t ", c ) == NULL ) )
+        {
+            cl->p_buffer[0] = c;
+            cl->i_buffer++;
+        }
+    }
+    else
     if( cl->query.i_proto == HTTPD_PROTO_NONE )
     {
-        /* enough to see if it's rtp over rtsp or RTSP/HTTP */
+        /* enough to see if it's Interleaved RTP over RTSP or RTSP/HTTP */
         i_len = httpd_NetRecv( cl, &cl->p_buffer[cl->i_buffer],
-                               4 - cl->i_buffer );
+                               7 - cl->i_buffer );
         if( i_len > 0 )
         {
             cl->i_buffer += i_len;
         }
 
-        if( cl->i_buffer >= 4 )
+        if( ( cl->i_buffer >= 4 ) && ( cl->p_buffer[0] == '$' ) )
         {
-            /*fprintf( stderr, "peek=%4.4s\n", cl->p_buffer );*/
-            /* detect type */
-            if( cl->p_buffer[0] == '$' )
-            {
-                /* RTSP (rtp over rtsp) */
-                cl->query.i_proto = HTTPD_PROTO_RTSP;
-                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->i_buffer      = 0;
-            }
-            else if( !memcmp( cl->p_buffer, "HTTP", 4 ) )
+            /* Interleaved RTP over RTSP */
+            cl->query.i_proto = HTTPD_PROTO_RTSP;
+            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->i_buffer      -= 4;
+            memcpy( cl->query.p_body, cl->p_buffer + 4, cl->i_buffer );
+        }
+        else
+        /* The smallest legal request is 7 bytes ("GET /\r\n"),
+         * this is the maximum we can ask at this point. */
+        if( cl->i_buffer >= 7 )
+        {
+            if( !memcmp( cl->p_buffer, "HTTP/1.", 7 ) )
             {
                 cl->query.i_proto = HTTPD_PROTO_HTTP;
                 cl->query.i_type  = HTTPD_MSG_ANSWER;
             }
-            else if( !memcmp( cl->p_buffer, "RTSP", 4 ) )
+            else if( !memcmp( cl->p_buffer, "RTSP/1.", 7 ) )
             {
                 cl->query.i_proto = HTTPD_PROTO_RTSP;
                 cl->query.i_type  = HTTPD_MSG_ANSWER;
             }
-            else if( !memcmp( cl->p_buffer, "GET ", 4 ) ||
-                     !memcmp( cl->p_buffer, "HEAD", 4 ) ||
-                     !memcmp( cl->p_buffer, "POST", 4 ) )
-            {
-                cl->query.i_proto = HTTPD_PROTO_HTTP;
-                cl->query.i_type  = HTTPD_MSG_NONE;
-            }
             else
             {
-                cl->query.i_proto = HTTPD_PROTO_RTSP;
+                /* We need the full request line to determine the protocol. */
+                cl->query.i_proto = HTTPD_PROTO_HTTP0;
                 cl->query.i_type  = HTTPD_MSG_NONE;
             }
         }
@@ -1540,6 +1593,19 @@ static void httpd_ClientRecv( httpd_client_t *cl )
         /* we are reading a header -> char by char */
         for( ;; )
         {
+            if( cl->i_buffer == cl->i_buffer_size )
+            {
+                uint8_t *newbuf = realloc( cl->p_buffer, cl->i_buffer_size + 1024 );
+                if( newbuf == NULL )
+                {
+                    i_len = 0;
+                    break;
+                }
+
+                cl->p_buffer = newbuf;
+                cl->i_buffer_size += 1024;
+            }
+
             i_len = httpd_NetRecv (cl, &cl->p_buffer[cl->i_buffer], 1 );
             if( i_len <= 0 )
             {
@@ -1547,11 +1613,74 @@ static void httpd_ClientRecv( httpd_client_t *cl )
             }
             cl->i_buffer++;
 
-            if( cl->i_buffer + 1 >= cl->i_buffer_size )
+            if( ( cl->query.i_proto == HTTPD_PROTO_HTTP0 )
+             && ( cl->p_buffer[cl->i_buffer - 1] == '\n' ) )
             {
-                cl->i_buffer_size += 1024;
-                cl->p_buffer = realloc( cl->p_buffer, cl->i_buffer_size );
+                /* Request line is now complete */
+                const char *p = memchr( cl->p_buffer, ' ', cl->i_buffer );
+                size_t len;
+
+                assert( cl->query.i_type == HTTPD_MSG_NONE );
+
+                if( p == NULL ) /* no URI: evil guy */
+                {
+                    i_len = 0; /* drop connection */
+                    break;
+                }
+
+                do
+                    p++; /* skips extra spaces */
+                while( *p == ' ' );
+
+                p = memchr( p, ' ', ((char *)cl->p_buffer) + cl->i_buffer - p );
+                if( p == NULL ) /* no explicit protocol: HTTP/0.9 */
+                {
+                    i_len = 0; /* not supported currently -> drop */
+                    break;
+                }
+
+                do
+                    p++; /* skips extra spaces ever again */
+                while( *p == ' ' );
+
+                len = ((char *)cl->p_buffer) + cl->i_buffer - p;
+                if( len < 7 ) /* foreign protocol */
+                    i_len = 0; /* I don't understand -> drop */
+                else
+                if( memcmp( p, "HTTP/1.", 7 ) == 0 )
+                {
+                    cl->query.i_proto = HTTPD_PROTO_HTTP;
+                    cl->query.i_version = atoi( p + 7 );
+                }
+                else
+                if( memcmp( p, "RTSP/1.", 7 ) == 0 )
+                {
+                    cl->query.i_proto = HTTPD_PROTO_RTSP;
+                    cl->query.i_version = atoi( p + 7 );
+                }
+                else
+                if( memcmp( p, "HTTP/", 5 ) == 0 )
+                {
+                    const uint8_t sorry[] =
+                       "HTTP/1.1 505 Unknown HTTP version\r\n\r\n";
+                    httpd_NetSend( cl, sorry, sizeof( sorry ) - 1 );
+                    i_len = 0; /* drop */
+                }
+                else
+                if( memcmp( p, "RTSP/", 5 ) == 0 )
+                {
+                    const uint8_t sorry[] =
+                        "RTSP/1.0 505 Unknown RTSP version\r\n\r\n";
+                    httpd_NetSend( cl, sorry, sizeof( sorry ) - 1 );
+                    i_len = 0; /* drop */
+                }
+                else /* yet another foreign protocol */
+                    i_len = 0;
+
+                if( i_len == 0 )
+                    break;
             }
+
             if( ( cl->i_buffer >= 2 && !memcmp( &cl->p_buffer[cl->i_buffer-2], "\n\n", 2 ) )||
                 ( cl->i_buffer >= 4 && !memcmp( &cl->p_buffer[cl->i_buffer-4], "\r\n\r\n", 4 ) ) )
             {
@@ -1573,28 +1702,6 @@ static void httpd_ClientRecv( httpd_client_t *cl )
                 }
                 else
                 {
-                    static const struct
-                    {
-                        const char name[16];
-                        int  i_type;
-                        int  i_proto;
-                    }
-                    msg_type[] =
-                    {
-                        { "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 },
-
-                        { "",               HTTPD_MSG_NONE,         HTTPD_PROTO_NONE }
-                    };
                     unsigned i;
 
                     p = NULL;
@@ -1607,7 +1714,7 @@ static void httpd_ClientRecv( httpd_client_t *cl )
                         if( !strncmp( (char *)cl->p_buffer, msg_type[i].name,
                                       strlen( msg_type[i].name ) ) )
                         {
-                            p = (char *)&cl->p_buffer[strlen((char *)msg_type[i].name) + 1 ];
+                            p = (char *)&cl->p_buffer[strlen(msg_type[i].name) + 1 ];
                             cl->query.i_type = msg_type[i].i_type;
                             if( cl->query.i_proto != msg_type[i].i_proto )
                             {
@@ -1656,23 +1763,6 @@ static void httpd_ClientRecv( httpd_client_t *cl )
                             *p3++ = '\0';
                             cl->query.psz_args = (uint8_t *)strdup( p3 );
                         }
-                        if( p2 )
-                        {
-                            while( *p2 == ' ' )
-                            {
-                                p2++;
-                            }
-                            if( !strncasecmp( p2, "HTTP/1.", 7 ) )
-                            {
-                                cl->query.i_proto = HTTPD_PROTO_HTTP;
-                                cl->query.i_version = atoi( p2+7 );
-                            }
-                            else if( !strncasecmp( p2, "RTSP/1.", 7 ) )
-                            {
-                                cl->query.i_proto = HTTPD_PROTO_RTSP;
-                                cl->query.i_version = atoi( p2+7 );
-                            }
-                        }
                         p = p2;
                     }
                 }
@@ -1731,7 +1821,8 @@ static void httpd_ClientRecv( httpd_client_t *cl )
                 }
                 if( cl->query.i_body > 0 )
                 {
-                    /* TODO Mhh, handle the case client will only send a request and close the connection
+                    /* 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->i_buffer = 0;
@@ -1939,12 +2030,19 @@ static void httpd_HostThread( httpd_host_t *host )
     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;
+    vlc_bool_t b_die;
+
+    vlc_object_lock( host );
+    evfd = vlc_object_waitpipe( VLC_OBJECT( host ) );
+    b_die = !vlc_object_alive( host );
+    vlc_object_unlock( host );
 
-    while( !host->b_die )
+    while( !b_die )
     {
         if( host->i_url <= 0 )
         {
-            /* 0.2s */
+            /* 0.2s (FIXME: use a condition variable) */
             msleep( 200000 );
             continue;
         }
@@ -1953,7 +2051,7 @@ static void httpd_HostThread( httpd_host_t *host )
         if( ( p_tls == NULL ) && ( host->p_tls != NULL ) )
             p_tls = tls_ServerSessionPrepare( host->p_tls );
 
-        struct pollfd ufd[host->nfd + host->i_client];
+        struct pollfd ufd[host->nfd + host->i_client + 1];
         unsigned nfd;
         for( nfd = 0; nfd < host->nfd; nfd++ )
         {
@@ -2053,6 +2151,7 @@ static void httpd_HostThread( httpd_host_t *host )
                     switch( query->i_proto )
                     {
                         case HTTPD_PROTO_HTTP:
+                            answer->i_version = 1;
                             httpd_MsgAdd( answer, "Allow",
                                           "GET,HEAD,POST,OPTIONS" );
                             break;
@@ -2078,8 +2177,8 @@ static void httpd_HostThread( httpd_host_t *host )
 
                             httpd_MsgAdd( answer, "Public", "DESCRIBE,SETUP,"
                                           "TEARDOWN,PLAY,PAUSE,GET_PARAMETER" );
+                            break;
                         }
-                        break;
                     }
 
                     cl->i_buffer = -1;  /* Force the creation of the answer in
@@ -2322,11 +2421,6 @@ static void httpd_HostThread( httpd_host_t *host )
                     cl->answer.i_body = 0;
                     cl->i_state = HTTPD_CLIENT_SENDING;
                 }
-                else
-                {
-                    /* we shouldn't wait too long */
-                    b_low_delay = VLC_TRUE;
-                }
             }
 
             /* Special for BIDIR mode we also check reading */
@@ -2338,23 +2432,35 @@ static void httpd_HostThread( httpd_host_t *host )
 
             if (pufd->events != 0)
                 nfd++;
+            else
+                b_low_delay = VLC_TRUE;
         }
         vlc_mutex_unlock( &host->lock );
 
-        /* we will wait 100ms or 20ms (not too big 'cause HTTPD_CLIENT_WAITING) */
-        switch( poll( ufd, nfd, b_low_delay ? 20 : 100) )
+        ufd[nfd].fd = evfd;
+        ufd[nfd].events = POLLIN;
+        ufd[nfd].revents = 0;
+        nfd++;
+
+        /* we will wait 20ms (not too big) if HTTPD_CLIENT_WAITING */
+        switch( poll( ufd, nfd, b_low_delay ? 20 : -1) )
         {
             case -1:
                 if (errno != EINTR)
                 {
-                    /* This is most likely a bug */
-                    msg_Err( host, "polling error: %s", strerror (errno));
-                    msleep( 1000 );
+                    /* Kernel on low memory or a bug: pace */
+                    msg_Err( host, "polling error: %m" );
+                    msleep( 100000 );
                 }
             case 0:
                 continue;
         }
 
+        vlc_object_lock( host );
+        if( ufd[nfd - 1].revents )
+            b_die = vlc_object_wait( host );
+        vlc_object_unlock( host );
+
         /* Handle client sockets */
         vlc_mutex_lock( &host->lock );
         now = mdate();
@@ -2403,18 +2509,19 @@ static void httpd_HostThread( httpd_host_t *host )
         {
             httpd_client_t *cl;
             int i_state = -1;
+            int fd = ufd[nfd].fd;
 
-            assert (ufd[nfd].fd == host->fds[nfd]);
+            assert (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 )
+            fd = accept (fd, NULL, NULL);
+            if (fd == -1)
                 continue;
 
+            net_SetupSocket (fd);
             if( p_tls != NULL )
             {
                 switch( tls_ServerSessionHandshake( p_tls, fd ) )