From 1ffcc4e53a3ef5a02c8d1f1d2f835484411281d4 Mon Sep 17 00:00:00 2001 From: Denis Charmet Date: Sat, 15 Feb 2014 23:15:49 +0100 Subject: [PATCH] Allow custom HTTP headers for httpd_Stream This will end up useful for cubemap implementation and may allow to put mms stuff out of the core --- include/vlc_httpd.h | 14 ++-- src/libvlccore.sym | 1 + src/network/httpd.c | 172 +++++++++++++++++++++++++++++++++----------- 3 files changed, 141 insertions(+), 46 deletions(-) diff --git a/include/vlc_httpd.h b/include/vlc_httpd.h index 9eb0b155fa..852a7a8c15 100644 --- a/include/vlc_httpd.h +++ b/include/vlc_httpd.h @@ -71,6 +71,12 @@ VLC_API httpd_host_t *vlc_rtsp_HostNew( vlc_object_t * ) VLC_USED; /* delete a host */ VLC_API void httpd_HostDelete( httpd_host_t * ); +typedef struct +{ + char * name; + char * value; +} httpd_header; + typedef struct httpd_message_t { httpd_client_t *cl; /* NULL if not throught a connection e vlc internal */ @@ -89,10 +95,8 @@ typedef struct httpd_message_t uint8_t *psz_args; /* options */ - int i_name; - char **name; - int i_value; - char **value; + size_t i_headers; + httpd_header *p_headers; /* body */ int64_t i_body_offset; @@ -139,7 +143,7 @@ VLC_API httpd_stream_t * httpd_StreamNew( httpd_host_t *, const char *psz_url, c VLC_API void httpd_StreamDelete( httpd_stream_t * ); VLC_API int httpd_StreamHeader( httpd_stream_t *, uint8_t *p_data, int i_data ); VLC_API int httpd_StreamSend( httpd_stream_t *, const block_t *p_block ); - +VLC_API int httpd_StreamSetHTTPHeaders(httpd_stream_t *, httpd_header *, size_t); /* Msg functions facilities */ VLC_API void httpd_MsgAdd( httpd_message_t *, const char *psz_name, const char *psz_value, ... ) VLC_FORMAT( 3, 4 ); diff --git a/src/libvlccore.sym b/src/libvlccore.sym index 1ed906a36c..f6f83002c5 100644 --- a/src/libvlccore.sym +++ b/src/libvlccore.sym @@ -162,6 +162,7 @@ httpd_StreamDelete httpd_StreamHeader httpd_StreamNew httpd_StreamSend +httpd_StreamSetHTTPHeaders httpd_UrlCatch httpd_UrlDelete httpd_UrlNew diff --git a/src/network/httpd.c b/src/network/httpd.c index 37e10c323e..6f48e71375 100644 --- a/src/network/httpd.c +++ b/src/network/httpd.c @@ -645,6 +645,10 @@ struct httpd_stream_t uint8_t *p_buffer; /* buffer */ int64_t i_buffer_pos; /* absolute position from begining */ int64_t i_buffer_last_pos; /* a new connection will start with that */ + + /* custom headers */ + size_t i_http_headers; + httpd_header * p_http_headers; }; static int httpd_StreamCallBack( httpd_callback_sys_t *p_sys, @@ -729,6 +733,25 @@ static int httpd_StreamCallBack( httpd_callback_sys_t *p_sys, answer->i_status = 200; + bool b_has_content_type = false; + bool b_has_cache_control = false; + + vlc_mutex_lock( &stream->lock ); + for( size_t i = 0; i < stream->i_http_headers; i++ ) + { + if( strncasecmp( stream->p_http_headers[i].name, "Content-Length", 14 ) ) + { + httpd_MsgAdd( answer, stream->p_http_headers[i].name, + stream->p_http_headers[i].value ); + + if( !strncasecmp( stream->p_http_headers[i].name, "Content-Type", 12 ) ) + b_has_content_type = true; + else if( !strncasecmp( stream->p_http_headers[i].name, "Cache-Control", 13 ) ) + b_has_cache_control = true; + } + } + vlc_mutex_unlock( &stream->lock ); + if( query->i_type != HTTPD_MSG_HEAD ) { cl->b_stream_mode = true; @@ -749,17 +772,17 @@ static int httpd_StreamCallBack( httpd_callback_sys_t *p_sys, } else { - httpd_MsgAdd( answer, "Content-Length", "%d", 0 ); + httpd_MsgAdd( answer, "Content-Length", "0" ); answer->i_body_offset = 0; } + /* FIXME: move to http access_output */ if( !strcmp( stream->psz_mime, "video/x-ms-asf-stream" ) ) { bool b_xplaystream = false; int i; - httpd_MsgAdd( answer, "Content-type", "%s", - "application/octet-stream" ); + httpd_MsgAdd( answer, "Content-type", "application/octet-stream" ); httpd_MsgAdd( answer, "Server", "Cougar 4.1.0.3921" ); httpd_MsgAdd( answer, "Pragma", "no-cache" ); httpd_MsgAdd( answer, "Pragma", "client-id=%lu", @@ -767,10 +790,10 @@ static int httpd_StreamCallBack( httpd_callback_sys_t *p_sys, httpd_MsgAdd( answer, "Pragma", "features=\"broadcast\"" ); /* Check if there is a xPlayStrm=1 */ - for( i = 0; i < query->i_name; i++ ) + for( i = 0; i < query->i_headers; i++ ) { - if( !strcasecmp( query->name[i], "Pragma" ) && - strstr( query->value[i], "xPlayStrm=1" ) ) + if( !strcasecmp( query->p_headers[i].name, "Pragma" ) && + strstr( query->p_headers[i].value, "xPlayStrm=1" ) ) { b_xplaystream = true; } @@ -781,11 +804,12 @@ static int httpd_StreamCallBack( httpd_callback_sys_t *p_sys, answer->i_body_offset = 0; } } - else + else if( !b_has_content_type ) { - httpd_MsgAdd( answer, "Content-type", "%s", stream->psz_mime ); + httpd_MsgAdd( answer, "Content-type", stream->psz_mime ); } - httpd_MsgAdd( answer, "Cache-Control", "%s", "no-cache" ); + if( !b_has_cache_control ) + httpd_MsgAdd( answer, "Cache-Control", "no-cache" ); return VLC_SUCCESS; } } @@ -821,6 +845,8 @@ httpd_stream_t *httpd_StreamNew( httpd_host_t *host, stream->i_buffer_last_pos = 1; stream->b_has_keyframes = false; stream->i_last_keyframe_seen_pos = 0; + stream->i_http_headers = 0; + stream->p_http_headers = NULL; httpd_UrlCatch( stream->url, HTTPD_MSG_HEAD, httpd_StreamCallBack, (httpd_callback_sys_t*)stream ); @@ -896,6 +922,12 @@ int httpd_StreamSend( httpd_stream_t *stream, const block_t *p_block ) void httpd_StreamDelete( httpd_stream_t *stream ) { httpd_UrlDelete( stream->url ); + for( size_t i = 0; i < stream->i_http_headers; i++ ) + { + free( stream->p_http_headers[i].name ); + free( stream->p_http_headers[i].value ); + } + free( stream->p_http_headers ); vlc_mutex_destroy( &stream->lock ); free( stream->psz_mime ); free( stream->p_header ); @@ -1238,10 +1270,8 @@ static void httpd_MsgInit( httpd_message_t *msg ) msg->psz_url = NULL; msg->psz_args = NULL; - msg->i_name = 0; - msg->name = NULL; - msg->i_value = 0; - msg->value = NULL; + msg->i_headers = 0; + msg->p_headers = NULL; msg->i_body_offset = 0; msg->i_body = 0; @@ -1252,22 +1282,25 @@ static void httpd_MsgClean( httpd_message_t *msg ) { free( msg->psz_url ); free( msg->psz_args ); - for (int i = 0; i < msg->i_name; i++) { - free( msg->name[i] ); - free( msg->value[i] ); + for( size_t i = 0; i < msg->i_headers; i++ ) + { + free( msg->p_headers[i].name ); + free( msg->p_headers[i].value ); } - free( msg->name ); - free( msg->value ); + free( msg->p_headers ); free( msg->p_body ); httpd_MsgInit( msg ); } const char *httpd_MsgGet( const httpd_message_t *msg, const char *name ) { - for (int i = 0; i < msg->i_name; i++ ) - if( !strcasecmp( msg->name[i], name )) - return msg->value[i]; - + for( size_t i = 0; i < msg->i_headers; i++ ) + { + if( !strcasecmp( msg->p_headers[i].name, name )) + { + return msg->p_headers[i].value; + } + } return NULL; } @@ -1290,9 +1323,19 @@ void httpd_MsgAdd( httpd_message_t *msg, const char *name, const char *psz_value free( value ); return; } - - TAB_APPEND( msg->i_name, msg->name, (char*)name ); - TAB_APPEND( msg->i_value, msg->value, value ); + httpd_header * p_tmp = realloc( msg->p_headers, sizeof(httpd_header) * (msg->i_headers + 1)); + if(p_tmp) + { + msg->p_headers = p_tmp; + msg->p_headers[msg->i_headers].name = name; + msg->p_headers[msg->i_headers].value = value; + msg->i_headers++; + } + else + { + free(name); + free(value); + } } static void httpd_ClientInit( httpd_client_t *cl, mtime_t now ) @@ -1687,23 +1730,16 @@ static void httpd_ClientRecv( httpd_client_t *cl ) if( ( colon = strchr( line, ':' ) ) ) { - char *name; - char *value; - *colon++ = '\0'; while( *colon == ' ' ) { colon++; } - name = strdup( line ); - value = strdup( colon ); - - TAB_APPEND( cl->query.i_name, cl->query.name, name ); - TAB_APPEND( cl->query.i_value,cl->query.value,value); + httpd_MsgAdd( &cl->query, line, colon ); - if( !strcasecmp( name, "Content-Length" ) ) + if( !strcasecmp( line, "Content-Length" ) ) { - cl->query.i_body = atol( value ); + cl->query.i_body = atol( colon ); } } @@ -1820,10 +1856,10 @@ static void httpd_ClientSend( httpd_client_t *cl ) const char *psz_status = httpd_ReasonFromCode( cl->answer.i_status ); i_size = strlen( "HTTP/1.") + 10 + 10 + strlen( psz_status ) + 5; - for( i = 0; i < cl->answer.i_name; i++ ) + for( i = 0; i < cl->answer.i_headers; i++ ) { - i_size += strlen( cl->answer.name[i] ) + 2 + - strlen( cl->answer.value[i] ) + 2; + i_size += strlen( cl->answer.p_headers[i].name ) + 2 + + strlen( cl->answer.p_headers[i].value ) + 2; } if( cl->i_buffer_size < i_size ) @@ -1838,10 +1874,10 @@ static void httpd_ClientSend( httpd_client_t *cl ) cl->answer.i_proto == HTTPD_PROTO_HTTP ? "HTTP/1" : "RTSP/1", cl->answer.i_version, cl->answer.i_status, psz_status ); - for( i = 0; i < cl->answer.i_name; i++ ) + for( i = 0; i < cl->answer.i_headers; i++ ) { - p += sprintf( p, "%s: %s\r\n", cl->answer.name[i], - cl->answer.value[i] ); + p += sprintf( p, "%s: %s\r\n", cl->answer.p_headers[i].name, + cl->answer.p_headers[i].value ); } p += sprintf( p, "\r\n" ); @@ -2324,3 +2360,57 @@ static void* httpd_HostThread( void *data ) vlc_mutex_unlock( &host->lock ); return NULL; } + +int httpd_StreamSetHTTPHeaders(httpd_stream_t * p_stream, httpd_header * p_headers, size_t i_headers ) +{ + if( !p_stream ) + return VLC_EGENERIC; + + vlc_mutex_lock( &p_stream->lock ); + if( p_stream->p_http_headers ) + { + for( size_t i = 0; i < p_stream->i_http_headers; i++) + { + free( p_stream->p_http_headers[i].name ); + free( p_stream->p_http_headers[i].value ); + } + free( p_stream->p_http_headers ); + p_stream->p_http_headers = NULL; + p_stream->i_http_headers = 0; + } + + if( !p_headers || !i_headers ) + { + vlc_mutex_unlock( &p_stream->lock ); + return VLC_SUCCESS; + } + + p_stream->p_http_headers = malloc(sizeof(httpd_header) * i_headers ); + if( !p_stream->p_http_headers ) + { + vlc_mutex_unlock( &p_stream->lock ); + return VLC_ENOMEM; + } + + size_t j = 0; + for( size_t i = 0; i < i_headers; i++ ) + { + if( unlikely( !p_headers[i].name || !p_headers[i].value ) ) + continue; + + p_stream->p_http_headers[j].name = strdup( p_headers[i].name ); + p_stream->p_http_headers[j].value = strdup( p_headers[i].value ); + + if( unlikely( !p_stream->p_http_headers[j].name || + !p_stream->p_http_headers[j].value ) ) + { + free( p_stream->p_http_headers[j].name ); + free( p_stream->p_http_headers[j].value ); + break; + } + j++; + } + p_stream->i_http_headers = j; + vlc_mutex_unlock( &p_stream->lock ); + return VLC_SUCCESS; +}