2007-02-28 03:40:23 +00:00
/*
* HTTP protocol for ffmpeg client
2009-01-19 15:46:40 +00:00
* Copyright (c) 2000, 2001 Fabrice Bellard
2007-02-28 03:40:23 +00:00
*
* This file is part of FFmpeg.
*
* FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
2008-05-09 11:56:36 +00:00
#include "libavutil/avstring.h"
2007-02-28 03:40:23 +00:00
#include "avformat.h"
2010-03-14 23:59:48 +00:00
#include "internal.h"
2007-02-28 03:40:23 +00:00
#include "network.h"
2010-06-08 10:26:16 +00:00
#include "http.h"
2007-11-22 02:27:39 +00:00
#include "os_support.h"
2010-03-24 22:32:05 +00:00
#include "httpauth.h"
2011-03-31 16:25:10 +02:00
#include "url.h"
2010-09-26 14:25:22 +00:00
#include "libavutil/opt.h"
2007-02-28 03:40:23 +00:00
2013-07-23 04:07:10 +08:00
#if CONFIG_ZLIB
#include <zlib.h>
#endif
2007-04-07 14:09:20 +00:00
/* XXX: POST protocol is not completely implemented because ffmpeg uses
only a subset of it. */
2007-02-28 03:40:23 +00:00
2012-09-22 21:17:36 +01:00
/* The IO buffer size is unrelated to the max URL size in itself, but needs
* to be large enough to fit the full request headers (including long
* path names).
*/
#define BUFFER_SIZE MAX_URL_SIZE
2007-02-28 03:40:23 +00:00
#define MAX_REDIRECTS 8
typedef struct {
2010-06-22 14:13:55 +00:00
const AVClass * class ;
2007-02-28 03:40:23 +00:00
URLContext * hd ;
unsigned char buffer [ BUFFER_SIZE ], * buf_ptr , * buf_end ;
int line_count ;
int http_code ;
2009-06-23 15:38:53 +00:00
int64_t chunksize ; /**< Used if "Transfer-Encoding: chunked" otherwise -1. */
2012-10-23 11:13:42 +02:00
char * content_type ;
2011-09-28 21:07:12 +02:00
char * user_agent ;
2008-10-03 10:16:29 +00:00
int64_t off , filesize ;
2013-06-26 00:53:26 +02:00
int icy_data_read ; ///< how much data was read since last ICY metadata packet
int icy_metaint ; ///< after how many bytes of read data a new metadata packet will be found
2010-08-19 14:50:40 +00:00
char location [ MAX_URL_SIZE ];
2010-03-24 22:32:05 +00:00
HTTPAuthState auth_state ;
2011-11-11 11:21:42 +02:00
HTTPAuthState proxy_auth_state ;
2011-11-07 11:43:13 +02:00
char * headers ;
2010-08-09 08:14:48 +00:00
int willclose ; /**< Set if the server correctly handles Connection: close and will close the connection after feeding us the content. */
2012-10-02 22:22:44 +01:00
int seekable ; /**< Control seekability, 0 = disable, 1 = enable, -1 = probe. */
2011-11-10 11:03:35 +02:00
int chunked_post ;
2012-05-21 11:26:40 +02:00
int end_chunked_post ; /**< A flag which indicates if the end of chunked encoding has been sent. */
2012-05-21 11:27:10 +02:00
int end_header ; /**< A flag which indicates we have finished to read POST reply. */
2012-05-28 15:03:19 +02:00
int multiple_requests ; /**< A flag which indicates if we use persistent connections. */
2012-05-30 11:27:18 +02:00
uint8_t * post_data ;
int post_datalen ;
2012-06-03 18:34:53 +02:00
int is_akamai ;
2012-12-10 01:00:16 +01:00
char * mime_type ;
2013-01-13 21:32:57 -05:00
char * cookies ; ///< holds newline (\n) delimited Set-Cookie header field values (without the "Set-Cookie: " field name)
2013-06-26 00:53:26 +02:00
int icy ;
char * icy_metadata_headers ;
char * icy_metadata_packet ;
2013-07-23 04:07:10 +08:00
#if CONFIG_ZLIB
int compressed ;
z_stream inflate_stream ;
uint8_t * inflate_buffer ;
#endif
2011-11-07 11:06:50 +02:00
AVDictionary * chained_options ;
2007-02-28 03:40:23 +00:00
} HTTPContext ;
2010-06-22 14:13:55 +00:00
#define OFFSET(x) offsetof(HTTPContext, x)
2011-11-10 09:34:58 +01:00
#define D AV_OPT_FLAG_DECODING_PARAM
#define E AV_OPT_FLAG_ENCODING_PARAM
2013-06-17 17:03:15 +02:00
#define DEFAULT_USER_AGENT "Lavf/" AV_STRINGIFY(LIBAVFORMAT_VERSION)
2010-06-22 14:13:55 +00:00
static const AVOption options [] = {
2013-01-11 11:35:31 +01:00
{ "seekable" , "control seekability of connection" , OFFSET ( seekable ), AV_OPT_TYPE_INT , {. i64 = - 1 }, - 1 , 1 , D },
2012-08-31 13:22:31 +03:00
{ "chunked_post" , "use chunked transfer-encoding for posts" , OFFSET ( chunked_post ), AV_OPT_TYPE_INT , {. i64 = 1 }, 0 , 1 , E },
2013-01-11 11:35:31 +01:00
{ "headers" , "set custom HTTP headers, can override built in default headers" , OFFSET ( headers ), AV_OPT_TYPE_STRING , { 0 }, 0 , 0 , D | E },
2012-10-23 11:13:42 +02:00
{ "content_type" , "force a content type" , OFFSET ( content_type ), AV_OPT_TYPE_STRING , { 0 }, 0 , 0 , D | E },
2013-04-19 14:44:58 +02:00
{ "user-agent" , "override User-Agent header" , OFFSET ( user_agent ), AV_OPT_TYPE_STRING , {. str = DEFAULT_USER_AGENT }, 0 , 0 , D },
2012-08-31 13:22:31 +03:00
{ "multiple_requests" , "use persistent connections" , OFFSET ( multiple_requests ), AV_OPT_TYPE_INT , {. i64 = 0 }, 0 , 1 , D | E },
2013-01-11 11:35:31 +01:00
{ "post_data" , "set custom HTTP post data" , OFFSET ( post_data ), AV_OPT_TYPE_BINARY , . flags = D | E },
{ "mime_type" , "set MIME type" , OFFSET ( mime_type ), AV_OPT_TYPE_STRING , { 0 }, 0 , 0 , 0 },
2013-01-13 21:32:57 -05:00
{ "cookies" , "set cookies to be sent in applicable future requests, use newline delimited Set-Cookie HTTP field value syntax" , OFFSET ( cookies ), AV_OPT_TYPE_STRING , { 0 }, 0 , 0 , 0 },
2013-06-26 00:53:26 +02:00
{ "icy" , "request ICY metadata" , OFFSET ( icy ), AV_OPT_TYPE_INT , {. i64 = 0 }, 0 , 1 , D },
{ "icy_metadata_headers" , "return ICY metadata headers" , OFFSET ( icy_metadata_headers ), AV_OPT_TYPE_STRING , { 0 }, 0 , 0 , 0 },
{ "icy_metadata_packet" , "return current ICY metadata packet" , OFFSET ( icy_metadata_packet ), AV_OPT_TYPE_STRING , { 0 }, 0 , 0 , 0 },
2010-06-22 14:13:55 +00:00
{ NULL }
};
2011-11-05 12:54:01 +01:00
#define HTTP_CLASS(flavor)\
static const AVClass flavor ## _context_class = {\
.class_name = #flavor,\
.item_name = av_default_item_name,\
.option = options,\
.version = LIBAVUTIL_VERSION_INT,\
2011-12-11 17:07:04 +00:00
}
2010-06-22 14:13:55 +00:00
2011-11-05 12:54:01 +01:00
HTTP_CLASS ( http );
HTTP_CLASS ( https );
2011-11-11 11:21:42 +02:00
static int http_connect ( URLContext * h , const char * path , const char * local_path ,
const char * hoststr , const char * auth ,
const char * proxyauth , int * new_location );
2007-02-28 03:40:23 +00:00
2010-06-21 19:40:30 +00:00
void ff_http_init_auth_state ( URLContext * dest , const URLContext * src )
{
memcpy ( & (( HTTPContext * ) dest -> priv_data ) -> auth_state ,
& (( HTTPContext * ) src -> priv_data ) -> auth_state , sizeof ( HTTPAuthState ));
2011-11-11 11:21:42 +02:00
memcpy ( & (( HTTPContext * ) dest -> priv_data ) -> proxy_auth_state ,
& (( HTTPContext * ) src -> priv_data ) -> proxy_auth_state ,
sizeof ( HTTPAuthState ));
2010-06-21 19:40:30 +00:00
}
2007-02-28 03:40:23 +00:00
/* return non zero if error */
2011-11-07 11:06:50 +02:00
static int http_open_cnx ( URLContext * h , AVDictionary ** options )
2007-02-28 03:40:23 +00:00
{
2011-11-11 11:21:42 +02:00
const char * path , * proxy_path , * lower_proto = "tcp" , * local_path ;
2011-02-06 00:20:26 +02:00
char hostname [ 1024 ], hoststr [ 1024 ], proto [ 10 ];
2011-12-01 11:24:23 +02:00
char auth [ 1024 ], proxyauth [ 1024 ] = "" ;
2012-09-22 21:17:36 +01:00
char path1 [ MAX_URL_SIZE ];
char buf [ 1024 ], urlbuf [ MAX_URL_SIZE ];
2012-03-12 14:00:16 +02:00
int port , use_proxy , err , location_changed = 0 , redirects = 0 , attempts = 0 ;
2011-11-11 11:21:42 +02:00
HTTPAuthType cur_auth_type , cur_proxy_auth_type ;
2007-02-28 03:40:23 +00:00
HTTPContext * s = h -> priv_data ;
/* fill the dest addr */
redo :
/* needed in any case to build the host string */
2011-02-06 00:20:26 +02:00
av_url_split ( proto , sizeof ( proto ), auth , sizeof ( auth ),
hostname , sizeof ( hostname ), & port ,
2010-03-08 09:05:03 +00:00
path1 , sizeof ( path1 ), s -> location );
2010-03-05 22:35:21 +00:00
ff_url_join ( hoststr , sizeof ( hoststr ), NULL , NULL , hostname , port , NULL );
2007-02-28 03:40:23 +00:00
2013-02-27 11:13:47 +02:00
proxy_path = getenv ( "http_proxy" );
use_proxy = ! ff_http_match_no_proxy ( getenv ( "no_proxy" ), hostname ) &&
proxy_path != NULL && av_strstart ( proxy_path , "http://" , NULL );
2011-11-10 14:55:18 +02:00
if ( ! strcmp ( proto , "https" )) {
lower_proto = "tls" ;
2011-11-10 14:57:15 +02:00
use_proxy = 0 ;
2011-11-10 14:55:18 +02:00
if ( port < 0 )
port = 443 ;
}
if ( port < 0 )
port = 80 ;
2011-11-11 11:21:42 +02:00
if ( path1 [ 0 ] == '\0' )
path = "/" ;
else
path = path1 ;
local_path = path ;
2007-02-28 03:40:23 +00:00
if ( use_proxy ) {
2011-11-11 11:21:42 +02:00
/* Reassemble the request URL without auth string - we don't
* want to leak the auth to the proxy. */
ff_url_join ( urlbuf , sizeof ( urlbuf ), proto , NULL , hostname , port , "%s" ,
path1 );
path = urlbuf ;
av_url_split ( NULL , 0 , proxyauth , sizeof ( proxyauth ),
hostname , sizeof ( hostname ), & port , NULL , 0 , proxy_path );
2007-02-28 03:40:23 +00:00
}
2011-02-06 00:20:26 +02:00
ff_url_join ( buf , sizeof ( buf ), lower_proto , NULL , hostname , port , NULL );
2007-02-28 03:40:23 +00:00
2012-05-28 15:03:54 +02:00
if ( ! s -> hd ) {
2012-06-01 16:30:01 +03:00
err = ffurl_open ( & s -> hd , buf , AVIO_FLAG_READ_WRITE ,
2011-11-07 11:06:50 +02:00
& h -> interrupt_callback , options );
2012-05-28 15:03:54 +02:00
if ( err < 0 )
goto fail ;
}
2010-03-24 22:32:05 +00:00
cur_auth_type = s -> auth_state . auth_type ;
2011-11-11 11:21:42 +02:00
cur_proxy_auth_type = s -> auth_state . auth_type ;
if ( http_connect ( h , path , local_path , hoststr , auth , proxyauth , & location_changed ) < 0 )
2007-02-28 03:40:23 +00:00
goto fail ;
2012-03-12 14:00:16 +02:00
attempts ++ ;
2010-03-24 22:32:05 +00:00
if ( s -> http_code == 401 ) {
2012-03-12 14:00:16 +02:00
if (( cur_auth_type == HTTP_AUTH_NONE || s -> auth_state . stale ) &&
s -> auth_state . auth_type != HTTP_AUTH_NONE && attempts < 4 ) {
2012-06-01 19:52:35 +02:00
ffurl_closep ( & s -> hd );
2010-03-24 22:32:05 +00:00
goto redo ;
} else
goto fail ;
}
2011-11-11 11:21:42 +02:00
if ( s -> http_code == 407 ) {
2012-03-12 14:00:16 +02:00
if (( cur_proxy_auth_type == HTTP_AUTH_NONE || s -> proxy_auth_state . stale ) &&
s -> proxy_auth_state . auth_type != HTTP_AUTH_NONE && attempts < 4 ) {
2012-06-01 19:52:35 +02:00
ffurl_closep ( & s -> hd );
2011-11-11 11:21:42 +02:00
goto redo ;
} else
goto fail ;
}
2010-11-02 20:37:01 +00:00
if (( s -> http_code == 301 || s -> http_code == 302 || s -> http_code == 303 || s -> http_code == 307 )
&& location_changed == 1 ) {
2007-02-28 03:40:23 +00:00
/* url moved, get next */
2012-06-01 19:52:35 +02:00
ffurl_closep ( & s -> hd );
2007-02-28 03:40:23 +00:00
if ( redirects ++ >= MAX_REDIRECTS )
2007-07-19 15:23:32 +00:00
return AVERROR ( EIO );
2012-03-12 14:03:46 +02:00
/* Restart the authentication process with the new target, which
* might use a different auth mechanism. */
memset ( & s -> auth_state , 0 , sizeof ( s -> auth_state ));
2012-03-12 14:00:16 +02:00
attempts = 0 ;
2007-02-28 03:40:23 +00:00
location_changed = 0 ;
goto redo ;
}
return 0 ;
fail :
2012-06-01 16:30:01 +03:00
if ( s -> hd )
2012-06-01 19:52:35 +02:00
ffurl_closep ( & s -> hd );
2007-07-19 15:23:32 +00:00
return AVERROR ( EIO );
2007-02-28 03:40:23 +00:00
}
2012-05-28 15:03:54 +02:00
int ff_http_do_new_request ( URLContext * h , const char * uri )
{
HTTPContext * s = h -> priv_data ;
2011-11-07 11:06:50 +02:00
AVDictionary * options = NULL ;
int ret ;
2012-05-28 15:03:54 +02:00
s -> off = 0 ;
2013-06-26 00:53:26 +02:00
s -> icy_data_read = 0 ;
2012-05-28 15:03:54 +02:00
av_strlcpy ( s -> location , uri , sizeof ( s -> location ));
2011-11-07 11:06:50 +02:00
av_dict_copy ( & options , s -> chained_options , 0 );
ret = http_open_cnx ( h , & options );
av_dict_free ( & options );
return ret ;
2012-05-28 15:03:54 +02:00
}
2011-11-07 11:06:50 +02:00
static int http_open ( URLContext * h , const char * uri , int flags ,
AVDictionary ** options )
2007-02-28 03:40:23 +00:00
{
2010-06-22 14:12:34 +00:00
HTTPContext * s = h -> priv_data ;
2011-11-07 11:06:50 +02:00
int ret ;
2007-02-28 03:40:23 +00:00
2012-10-02 22:22:44 +01:00
if ( s -> seekable == 1 )
h -> is_streamed = 0 ;
else
h -> is_streamed = 1 ;
2007-02-28 03:40:23 +00:00
s -> filesize = - 1 ;
2010-08-19 14:50:40 +00:00
av_strlcpy ( s -> location , uri , sizeof ( s -> location ));
2011-11-07 11:06:50 +02:00
if ( options )
av_dict_copy ( & s -> chained_options , * options , 0 );
2007-02-28 03:40:23 +00:00
2011-11-07 11:43:13 +02:00
if ( s -> headers ) {
int len = strlen ( s -> headers );
if ( len < 2 || strcmp ( " \r\n " , s -> headers + len - 2 ))
2011-11-09 01:14:05 +02:00
av_log ( h , AV_LOG_WARNING , "No trailing CRLF found in HTTP header. \n " );
2011-11-07 11:43:13 +02:00
}
2011-11-07 11:06:50 +02:00
ret = http_open_cnx ( h , options );
if ( ret < 0 )
av_dict_free ( & s -> chained_options );
return ret ;
2007-02-28 03:40:23 +00:00
}
static int http_getc ( HTTPContext * s )
{
int len ;
if ( s -> buf_ptr >= s -> buf_end ) {
2011-03-31 16:31:43 +02:00
len = ffurl_read ( s -> hd , s -> buffer , BUFFER_SIZE );
2007-02-28 03:40:23 +00:00
if ( len < 0 ) {
2012-05-30 11:52:11 +02:00
return len ;
2007-02-28 03:40:23 +00:00
} else if ( len == 0 ) {
return - 1 ;
} else {
s -> buf_ptr = s -> buffer ;
s -> buf_end = s -> buffer + len ;
}
}
return * s -> buf_ptr ++ ;
}
2009-06-06 16:44:21 +00:00
static int http_get_line ( HTTPContext * s , char * line , int line_size )
{
int ch ;
char * q ;
q = line ;
for (;;) {
ch = http_getc ( s );
if ( ch < 0 )
2012-05-30 11:52:11 +02:00
return ch ;
2009-06-06 16:44:21 +00:00
if ( ch == '\n' ) {
/* process line */
if ( q > line && q [ - 1 ] == '\r' )
q -- ;
* q = '\0' ;
return 0 ;
} else {
if (( q - line ) < line_size - 1 )
* q ++ = ch ;
}
}
}
2007-02-28 03:40:23 +00:00
static int process_line ( URLContext * h , char * line , int line_count ,
int * new_location )
{
HTTPContext * s = h -> priv_data ;
2010-07-16 14:15:37 +00:00
char * tag , * p , * end ;
2013-07-04 17:18:52 +08:00
char redirected_location [ MAX_URL_SIZE ];
2007-02-28 03:40:23 +00:00
/* end of header */
2012-05-21 11:27:10 +02:00
if ( line [ 0 ] == '\0' ) {
s -> end_header = 1 ;
2007-02-28 03:40:23 +00:00
return 0 ;
2012-05-21 11:27:10 +02:00
}
2007-02-28 03:40:23 +00:00
p = line ;
if ( line_count == 0 ) {
2013-03-03 11:17:50 +01:00
while ( ! av_isspace ( * p ) && * p != '\0' )
2007-02-28 03:40:23 +00:00
p ++ ;
2013-03-03 11:17:50 +01:00
while ( av_isspace ( * p ))
2007-02-28 03:40:23 +00:00
p ++ ;
2010-07-16 14:15:37 +00:00
s -> http_code = strtol ( p , & end , 10 );
2009-06-06 17:32:59 +00:00
2011-01-29 17:46:18 +01:00
av_dlog ( NULL , "http_code=%d \n " , s -> http_code );
2009-06-06 17:32:59 +00:00
2010-03-24 22:32:05 +00:00
/* error codes are 4xx and 5xx, but regard 401 as a success, so we
* don't abort until all headers have been parsed. */
2011-11-10 18:21:17 +02:00
if ( s -> http_code >= 400 && s -> http_code < 600 && ( s -> http_code != 401
2011-11-11 11:21:42 +02:00
|| s -> auth_state . auth_type != HTTP_AUTH_NONE ) &&
( s -> http_code != 407 || s -> proxy_auth_state . auth_type != HTTP_AUTH_NONE )) {
2010-07-16 14:15:37 +00:00
end += strspn ( end , SPACE_CHARS );
2011-04-20 11:40:29 +02:00
av_log ( h , AV_LOG_WARNING , "HTTP error %d %s \n " ,
2010-07-16 14:15:37 +00:00
s -> http_code , end );
2007-03-06 13:38:41 +00:00
return - 1 ;
2010-07-16 14:15:37 +00:00
}
2007-02-28 03:40:23 +00:00
} else {
while ( * p != '\0' && * p != ':' )
p ++ ;
if ( * p != ':' )
return 1 ;
* p = '\0' ;
tag = line ;
p ++ ;
2013-03-03 11:17:50 +01:00
while ( av_isspace ( * p ))
2007-02-28 03:40:23 +00:00
p ++ ;
2011-11-02 20:17:25 +01:00
if ( ! av_strcasecmp ( tag , "Location" )) {
2013-07-04 17:18:52 +08:00
ff_make_absolute_url ( redirected_location , sizeof ( redirected_location ), s -> location , p );
av_strlcpy ( s -> location , redirected_location , sizeof ( s -> location ));
2007-02-28 03:40:23 +00:00
* new_location = 1 ;
2011-11-02 20:17:25 +01:00
} else if ( ! av_strcasecmp ( tag , "Content-Length" ) && s -> filesize == - 1 ) {
2012-06-18 14:02:43 +01:00
s -> filesize = strtoll ( p , NULL , 10 );
2011-11-02 20:17:25 +01:00
} else if ( ! av_strcasecmp ( tag , "Content-Range" )) {
2007-02-28 03:40:23 +00:00
/* "bytes $from-$to/$document_size" */
const char * slash ;
if ( ! strncmp ( p , "bytes " , 6 )) {
p += 6 ;
2012-06-18 14:02:43 +01:00
s -> off = strtoll ( p , NULL , 10 );
2007-02-28 03:40:23 +00:00
if (( slash = strchr ( p , '/' )) && strlen ( slash ) > 0 )
2012-06-18 14:02:43 +01:00
s -> filesize = strtoll ( slash + 1 , NULL , 10 );
2007-02-28 03:40:23 +00:00
}
2012-10-02 22:22:44 +01:00
if ( s -> seekable == - 1 && ( ! s -> is_akamai || s -> filesize != 2147483647 ))
2012-06-03 18:34:53 +02:00
h -> is_streamed = 0 ; /* we _can_ in fact seek */
2012-10-02 22:22:44 +01:00
} else if ( ! av_strcasecmp ( tag , "Accept-Ranges" ) && ! strncmp ( p , "bytes" , 5 ) && s -> seekable == - 1 ) {
2011-09-03 22:29:07 +02:00
h -> is_streamed = 0 ;
2011-11-02 20:17:25 +01:00
} else if ( ! av_strcasecmp ( tag , "Transfer-Encoding" ) && ! av_strncasecmp ( p , "chunked" , 7 )) {
2009-06-23 15:38:53 +00:00
s -> filesize = - 1 ;
s -> chunksize = 0 ;
2011-11-02 20:17:25 +01:00
} else if ( ! av_strcasecmp ( tag , "WWW-Authenticate" )) {
2010-03-24 22:32:05 +00:00
ff_http_auth_handle_header ( & s -> auth_state , tag , p );
2011-11-02 20:17:25 +01:00
} else if ( ! av_strcasecmp ( tag , "Authentication-Info" )) {
2010-03-24 22:32:05 +00:00
ff_http_auth_handle_header ( & s -> auth_state , tag , p );
2011-11-11 11:21:42 +02:00
} else if ( ! av_strcasecmp ( tag , "Proxy-Authenticate" )) {
ff_http_auth_handle_header ( & s -> proxy_auth_state , tag , p );
2011-11-02 20:17:25 +01:00
} else if ( ! av_strcasecmp ( tag , "Connection" )) {
2010-08-09 08:14:48 +00:00
if ( ! strcmp ( p , "close" ))
s -> willclose = 1 ;
2012-06-03 18:34:53 +02:00
} else if ( ! av_strcasecmp ( tag , "Server" ) && ! av_strcasecmp ( p , "AkamaiGHost" )) {
s -> is_akamai = 1 ;
2012-12-10 23:58:26 +01:00
} else if ( ! av_strcasecmp ( tag , "Content-Type" )) {
2012-12-10 01:00:16 +01:00
av_free ( s -> mime_type ); s -> mime_type = av_strdup ( p );
2013-01-13 21:32:57 -05:00
} else if ( ! av_strcasecmp ( tag , "Set-Cookie" )) {
if ( ! s -> cookies ) {
if ( ! ( s -> cookies = av_strdup ( p )))
return AVERROR ( ENOMEM );
} else {
char * tmp = s -> cookies ;
size_t str_size = strlen ( tmp ) + strlen ( p ) + 2 ;
if ( ! ( s -> cookies = av_malloc ( str_size ))) {
s -> cookies = tmp ;
return AVERROR ( ENOMEM );
}
snprintf ( s -> cookies , str_size , "%s \n %s" , tmp , p );
av_free ( tmp );
}
2013-06-26 00:53:26 +02:00
} else if ( ! av_strcasecmp ( tag , "Icy-MetaInt" )) {
s -> icy_metaint = strtoll ( p , NULL , 10 );
} else if ( ! av_strncasecmp ( tag , "Icy-" , 4 )) {
// Concat all Icy- header lines
char * buf = av_asprintf ( "%s%s: %s \n " ,
s -> icy_metadata_headers ? s -> icy_metadata_headers : "" , tag , p );
if ( ! buf )
return AVERROR ( ENOMEM );
av_freep ( & s -> icy_metadata_headers );
s -> icy_metadata_headers = buf ;
2013-07-23 04:07:10 +08:00
} else if ( ! av_strcasecmp ( tag , "Content-Encoding" )) {
if ( ! av_strncasecmp ( p , "gzip" , 4 ) || ! av_strncasecmp ( p , "deflate" , 7 )) {
#if CONFIG_ZLIB
s -> compressed = 1 ;
inflateEnd ( & s -> inflate_stream );
2013-07-23 04:07:10 +08:00
if ( inflateInit2 ( & s -> inflate_stream , 32 + 15 ) != Z_OK ) {
av_log ( h , AV_LOG_WARNING , "Error during zlib initialisation: %s \n " ,
2013-07-23 04:07:10 +08:00
s -> inflate_stream . msg );
2013-07-23 04:07:10 +08:00
return AVERROR ( ENOSYS );
}
if ( zlibCompileFlags () & ( 1 << 17 )) {
av_log ( h , AV_LOG_WARNING , "Your zlib was compiled without gzip support. \n " );
return AVERROR ( ENOSYS );
}
2013-07-23 04:07:10 +08:00
#else
2013-07-23 04:07:10 +08:00
av_log ( h , AV_LOG_WARNING , "Compressed (%s) content, need zlib with gzip support \n " , p );
return AVERROR ( ENOSYS );
2013-07-23 04:07:10 +08:00
#endif
2013-07-23 04:07:10 +08:00
} else if ( ! av_strncasecmp ( p , "identity" , 8 )) {
// The normal, no-encoding case (although servers shouldn't include
// the header at all if this is the case).
} else {
av_log ( h , AV_LOG_WARNING , "Unknown content coding: %s \n " , p );
return AVERROR ( ENOSYS );
2013-07-23 04:07:10 +08:00
}
2007-02-28 03:40:23 +00:00
}
}
return 1 ;
}
2013-01-13 21:32:57 -05:00
/**
* Create a string containing cookie values for use as a HTTP cookie header
* field value for a particular path and domain from the cookie values stored in
* the HTTP protocol context. The cookie string is stored in *cookies.
*
* @return a negative value if an error condition occurred, 0 otherwise
*/
static int get_cookies ( HTTPContext * s , char ** cookies , const char * path ,
const char * domain )
{
// cookie strings will look like Set-Cookie header field values. Multiple
// Set-Cookie fields will result in multiple values delimited by a newline
int ret = 0 ;
char * next , * cookie , * set_cookies = av_strdup ( s -> cookies ), * cset_cookies = set_cookies ;
if ( ! set_cookies ) return AVERROR ( EINVAL );
* cookies = NULL ;
while (( cookie = av_strtok ( set_cookies , " \n " , & next ))) {
int domain_offset = 0 ;
char * param , * next_param , * cdomain = NULL , * cpath = NULL , * cvalue = NULL ;
set_cookies = NULL ;
while (( param = av_strtok ( cookie , "; " , & next_param ))) {
cookie = NULL ;
if ( ! av_strncasecmp ( "path=" , param , 5 )) {
2013-01-20 20:25:30 -05:00
av_free ( cpath );
2013-01-13 21:32:57 -05:00
cpath = av_strdup ( & param [ 5 ]);
} else if ( ! av_strncasecmp ( "domain=" , param , 7 )) {
2013-01-20 20:25:30 -05:00
av_free ( cdomain );
2013-01-13 21:32:57 -05:00
cdomain = av_strdup ( & param [ 7 ]);
} else if ( ! av_strncasecmp ( "secure" , param , 6 ) ||
! av_strncasecmp ( "comment" , param , 7 ) ||
! av_strncasecmp ( "max-age" , param , 7 ) ||
! av_strncasecmp ( "version" , param , 7 )) {
// ignore Comment, Max-Age, Secure and Version
} else {
2013-01-20 20:25:30 -05:00
av_free ( cvalue );
2013-01-13 21:32:57 -05:00
cvalue = av_strdup ( param );
}
}
2013-06-10 10:52:40 +02:00
if ( ! cdomain )
cdomain = av_strdup ( domain );
2013-01-13 21:32:57 -05:00
// ensure all of the necessary values are valid
if ( ! cdomain || ! cpath || ! cvalue ) {
av_log ( s , AV_LOG_WARNING ,
"Invalid cookie found, no value, path or domain specified \n " );
goto done_cookie ;
}
// check if the request path matches the cookie path
if ( av_strncasecmp ( path , cpath , strlen ( cpath )))
goto done_cookie ;
// the domain should be at least the size of our cookie domain
domain_offset = strlen ( domain ) - strlen ( cdomain );
if ( domain_offset < 0 )
goto done_cookie ;
// match the cookie domain
if ( av_strcasecmp ( & domain [ domain_offset ], cdomain ))
goto done_cookie ;
// cookie parameters match, so copy the value
if ( !* cookies ) {
if ( ! ( * cookies = av_strdup ( cvalue ))) {
ret = AVERROR ( ENOMEM );
goto done_cookie ;
}
} else {
char * tmp = * cookies ;
size_t str_size = strlen ( cvalue ) + strlen ( * cookies ) + 3 ;
if ( ! ( * cookies = av_malloc ( str_size ))) {
ret = AVERROR ( ENOMEM );
goto done_cookie ;
}
snprintf ( * cookies , str_size , "%s; %s" , tmp , cvalue );
av_free ( tmp );
}
done_cookie :
av_free ( cdomain );
av_free ( cpath );
av_free ( cvalue );
if ( ret < 0 ) {
if ( * cookies ) av_freep ( cookies );
av_free ( cset_cookies );
return ret ;
}
}
av_free ( cset_cookies );
return 0 ;
}
2010-06-08 10:26:16 +00:00
static inline int has_header ( const char * str , const char * header )
{
/* header + 2 to skip over CRLF prefix. (make sure you have one!) */
2011-11-07 11:43:13 +02:00
if ( ! str )
return 0 ;
2010-06-08 10:26:16 +00:00
return av_stristart ( str , header + 2 , NULL ) || av_stristr ( str , header );
}
2012-05-20 16:20:56 +02:00
static int http_read_header ( URLContext * h , int * new_location )
{
HTTPContext * s = h -> priv_data ;
2012-09-22 21:17:36 +01:00
char line [ MAX_URL_SIZE ];
2012-05-20 16:20:56 +02:00
int err = 0 ;
2012-06-17 21:19:41 +03:00
s -> chunksize = - 1 ;
2012-05-20 16:20:56 +02:00
for (;;) {
2012-05-30 11:52:11 +02:00
if (( err = http_get_line ( s , line , sizeof ( line ))) < 0 )
return err ;
2012-05-20 16:20:56 +02:00
av_dlog ( NULL , "header='%s' \n " , line );
err = process_line ( h , line , s -> line_count , new_location );
if ( err < 0 )
return err ;
if ( err == 0 )
break ;
s -> line_count ++ ;
}
return err ;
}
2011-11-11 11:21:42 +02:00
static int http_connect ( URLContext * h , const char * path , const char * local_path ,
const char * hoststr , const char * auth ,
const char * proxyauth , int * new_location )
2007-02-28 03:40:23 +00:00
{
HTTPContext * s = h -> priv_data ;
2009-06-06 16:44:21 +00:00
int post , err ;
2012-09-22 21:17:36 +01:00
char headers [ 4096 ] = "" ;
2011-11-11 11:21:42 +02:00
char * authstr = NULL , * proxyauthstr = NULL ;
2008-10-03 10:16:29 +00:00
int64_t off = s -> off ;
2010-06-08 10:26:16 +00:00
int len = 0 ;
2011-11-11 11:21:42 +02:00
const char * method ;
2007-02-28 03:40:23 +00:00
/* send http header */
2011-04-15 16:42:09 +02:00
post = h -> flags & AVIO_FLAG_WRITE ;
2012-05-30 11:27:18 +02:00
if ( s -> post_data ) {
/* force POST method and disable chunked encoding when
* custom HTTP post data is set */
post = 1 ;
s -> chunked_post = 0 ;
}
2011-11-11 11:21:42 +02:00
method = post ? "POST" : "GET" ;
authstr = ff_http_auth_create_response ( & s -> auth_state , auth , local_path ,
method );
proxyauthstr = ff_http_auth_create_response ( & s -> proxy_auth_state , proxyauth ,
local_path , method );
2010-06-08 10:26:16 +00:00
/* set default headers if needed */
if ( ! has_header ( s -> headers , " \r\n User-Agent: " ))
2011-09-28 21:07:12 +02:00
len += av_strlcatf ( headers + len , sizeof ( headers ) - len ,
2013-04-04 12:18:02 +02:00
"User-Agent: %s \r\n " , s -> user_agent );
2010-06-08 10:26:16 +00:00
if ( ! has_header ( s -> headers , " \r\n Accept: " ))
len += av_strlcpy ( headers + len , "Accept: */* \r\n " ,
sizeof ( headers ) - len );
2012-10-02 22:22:44 +01:00
// Note: we send this on purpose even when s->off is 0 when we're probing,
2012-09-23 15:26:56 +02:00
// since it allows us to detect more reliably if a (non-conforming)
// server supports seeking by analysing the reply headers.
2012-10-02 22:22:44 +01:00
if ( ! has_header ( s -> headers , " \r\n Range: " ) && ! post && ( s -> off > 0 || s -> seekable == - 1 ))
2010-06-08 10:26:16 +00:00
len += av_strlcatf ( headers + len , sizeof ( headers ) - len ,
2010-06-08 11:06:36 +00:00
"Range: bytes=%" PRId64 "- \r\n " , s -> off );
2012-05-28 15:03:19 +02:00
if ( ! has_header ( s -> headers , " \r\n Connection: " )) {
if ( s -> multiple_requests ) {
len += av_strlcpy ( headers + len , "Connection: keep-alive \r\n " ,
sizeof ( headers ) - len );
} else {
len += av_strlcpy ( headers + len , "Connection: close \r\n " ,
sizeof ( headers ) - len );
}
}
2010-06-08 10:26:16 +00:00
if ( ! has_header ( s -> headers , " \r\n Host: " ))
len += av_strlcatf ( headers + len , sizeof ( headers ) - len ,
"Host: %s \r\n " , hoststr );
2012-05-30 11:27:18 +02:00
if ( ! has_header ( s -> headers , " \r\n Content-Length: " ) && s -> post_data )
len += av_strlcatf ( headers + len , sizeof ( headers ) - len ,
"Content-Length: %d \r\n " , s -> post_datalen );
2012-10-23 11:13:42 +02:00
if ( ! has_header ( s -> headers , " \r\n Content-Type: " ) && s -> content_type )
len += av_strlcatf ( headers + len , sizeof ( headers ) - len ,
"Content-Type: %s \r\n " , s -> content_type );
2013-01-13 21:32:57 -05:00
if ( ! has_header ( s -> headers , " \r\n Cookie: " ) && s -> cookies ) {
char * cookies = NULL ;
if ( ! get_cookies ( s , & cookies , path , hoststr )) {
len += av_strlcatf ( headers + len , sizeof ( headers ) - len ,
"Cookie: %s \r\n " , cookies );
av_free ( cookies );
}
}
2013-06-26 00:53:26 +02:00
if ( ! has_header ( s -> headers , " \r\n Icy-MetaData: " ) && s -> icy ) {
len += av_strlcatf ( headers + len , sizeof ( headers ) - len ,
"Icy-MetaData: %d \r\n " , 1 );
}
2010-06-08 10:26:16 +00:00
/* now add in custom headers */
2011-11-07 11:43:13 +02:00
if ( s -> headers )
av_strlcpy ( headers + len , s -> headers , sizeof ( headers ) - len );
2010-06-08 10:26:16 +00:00
2007-02-28 03:40:23 +00:00
snprintf ( s -> buffer , sizeof ( s -> buffer ),
"%s %s HTTP/1.1 \r\n "
2010-03-24 22:32:05 +00:00
"%s"
2010-06-08 10:26:16 +00:00
"%s"
2010-01-12 16:36:00 +00:00
"%s"
2011-11-11 11:21:42 +02:00
"%s%s"
2007-02-28 03:40:23 +00:00
" \r\n " ,
2011-11-11 11:21:42 +02:00
method ,
2007-02-28 03:40:23 +00:00
path ,
2011-11-10 11:03:35 +02:00
post && s -> chunked_post ? "Transfer-Encoding: chunked \r\n " : "" ,
2010-06-08 10:26:16 +00:00
headers ,
2011-11-11 11:21:42 +02:00
authstr ? authstr : "" ,
proxyauthstr ? "Proxy-" : "" , proxyauthstr ? proxyauthstr : "" );
2007-02-28 03:40:23 +00:00
2010-03-24 22:32:05 +00:00
av_freep ( & authstr );
2011-11-11 11:21:42 +02:00
av_freep ( & proxyauthstr );
2012-05-30 11:52:11 +02:00
if (( err = ffurl_write ( s -> hd , s -> buffer , strlen ( s -> buffer ))) < 0 )
return err ;
2007-02-28 03:40:23 +00:00
2012-05-30 11:27:18 +02:00
if ( s -> post_data )
if (( err = ffurl_write ( s -> hd , s -> post_data , s -> post_datalen )) < 0 )
return err ;
2007-02-28 03:40:23 +00:00
/* init input buffer */
s -> buf_ptr = s -> buffer ;
s -> buf_end = s -> buffer ;
s -> line_count = 0 ;
s -> off = 0 ;
2013-06-26 00:53:26 +02:00
s -> icy_data_read = 0 ;
2007-03-12 14:51:18 +00:00
s -> filesize = - 1 ;
2010-08-09 08:14:48 +00:00
s -> willclose = 0 ;
2012-05-21 11:26:40 +02:00
s -> end_chunked_post = 0 ;
2012-05-28 15:03:54 +02:00
s -> end_header = 0 ;
2012-05-30 11:27:18 +02:00
if ( post && ! s -> post_data ) {
2010-06-04 06:35:12 +00:00
/* Pretend that it did work. We didn't read any header yet, since
* we've still to send the POST data, but the code calling this
* function will check http_code after we return. */
s -> http_code = 200 ;
2007-02-28 03:40:23 +00:00
return 0 ;
}
/* wait for header */
2012-05-20 16:20:56 +02:00
err = http_read_header ( h , new_location );
if ( err < 0 )
return err ;
2007-02-28 03:40:23 +00:00
return ( off == s -> off ) ? 0 : - 1 ;
}
2011-11-10 14:52:50 +02:00
static int http_buf_read ( URLContext * h , uint8_t * buf , int size )
2007-02-28 03:40:23 +00:00
{
HTTPContext * s = h -> priv_data ;
int len ;
2011-11-10 14:52:50 +02:00
/* read bytes from input buffer first */
len = s -> buf_end - s -> buf_ptr ;
if ( len > 0 ) {
if ( len > size )
len = size ;
memcpy ( buf , s -> buf_ptr , len );
s -> buf_ptr += len ;
} else {
if ( ! s -> willclose && s -> filesize >= 0 && s -> off >= s -> filesize )
return AVERROR_EOF ;
len = ffurl_read ( s -> hd , buf , size );
}
if ( len > 0 ) {
s -> off += len ;
2013-06-26 00:53:26 +02:00
s -> icy_data_read += len ;
2011-11-10 14:52:50 +02:00
if ( s -> chunksize > 0 )
s -> chunksize -= len ;
}
return len ;
}
2013-07-23 04:07:10 +08:00
#if CONFIG_ZLIB
#define DECOMPRESS_BUF_SIZE (256 * 1024)
static int http_buf_read_compressed ( URLContext * h , uint8_t * buf , int size )
{
HTTPContext * s = h -> priv_data ;
int ret ;
if ( ! s -> inflate_buffer ) {
s -> inflate_buffer = av_malloc ( DECOMPRESS_BUF_SIZE );
if ( ! s -> inflate_buffer )
return AVERROR ( ENOMEM );
}
if ( s -> inflate_stream . avail_in == 0 ) {
int read = http_buf_read ( h , s -> inflate_buffer , DECOMPRESS_BUF_SIZE );
if ( read <= 0 )
return read ;
s -> inflate_stream . next_in = s -> inflate_buffer ;
s -> inflate_stream . avail_in = read ;
}
s -> inflate_stream . avail_out = size ;
s -> inflate_stream . next_out = buf ;
ret = inflate ( & s -> inflate_stream , Z_SYNC_FLUSH );
if ( ret != Z_OK && ret != Z_STREAM_END )
2013-07-23 04:07:10 +08:00
av_log ( h , AV_LOG_WARNING , "inflate return value: %d, %s \n " , ret , s -> inflate_stream . msg );
2013-07-23 04:07:10 +08:00
return size - s -> inflate_stream . avail_out ;
}
#endif
2011-11-10 14:52:50 +02:00
static int http_read ( URLContext * h , uint8_t * buf , int size )
{
HTTPContext * s = h -> priv_data ;
2012-05-21 11:27:10 +02:00
int err , new_location ;
2012-06-17 21:15:32 +03:00
if ( ! s -> hd )
return AVERROR_EOF ;
2012-05-21 11:27:10 +02:00
2012-06-17 21:19:41 +03:00
if ( s -> end_chunked_post && ! s -> end_header ) {
err = http_read_header ( h , & new_location );
if ( err < 0 )
return err ;
2012-05-21 11:27:10 +02:00
}
2007-02-28 03:40:23 +00:00
2009-06-23 15:38:53 +00:00
if ( s -> chunksize >= 0 ) {
if ( ! s -> chunksize ) {
char line [ 32 ];
for (;;) {
do {
2012-05-30 11:52:11 +02:00
if (( err = http_get_line ( s , line , sizeof ( line ))) < 0 )
return err ;
2009-06-23 15:38:53 +00:00
} while ( !* line ); /* skip CR LF from last chunk */
s -> chunksize = strtoll ( line , NULL , 16 );
2011-01-29 17:46:18 +01:00
av_dlog ( NULL , "Chunked encoding data size: %" PRId64 "' \n " , s -> chunksize );
2009-06-23 15:38:53 +00:00
if ( ! s -> chunksize )
return 0 ;
break ;
}
}
size = FFMIN ( size , s -> chunksize );
}
2013-06-26 00:53:26 +02:00
if ( s -> icy_metaint > 0 ) {
int remaining = s -> icy_metaint - s -> icy_data_read ; /* until next metadata packet */
if ( ! remaining ) {
// The metadata packet is variable sized. It has a 1 byte header
// which sets the length of the packet (divided by 16). If it's 0,
// the metadata doesn't change. After the packet, icy_metaint bytes
// of normal data follow.
int ch = http_getc ( s );
if ( ch < 0 )
return ch ;
if ( ch > 0 ) {
char data [ 255 * 16 + 1 ];
int n ;
int ret ;
ch *= 16 ;
for ( n = 0 ; n < ch ; n ++ )
data [ n ] = http_getc ( s );
data [ ch + 1 ] = 0 ;
if (( ret = av_opt_set ( s , "icy_metadata_packet" , data , 0 )) < 0 )
return ret ;
}
s -> icy_data_read = 0 ;
remaining = s -> icy_metaint ;
}
size = FFMIN ( size , remaining );
}
2013-07-23 04:07:10 +08:00
#if CONFIG_ZLIB
if ( s -> compressed )
return http_buf_read_compressed ( h , buf , size );
#endif
2011-11-10 14:52:50 +02:00
return http_buf_read ( h , buf , size );
2007-02-28 03:40:23 +00:00
}
/* used only when posting data */
2010-06-01 07:46:23 +00:00
static int http_write ( URLContext * h , const uint8_t * buf , int size )
2007-02-28 03:40:23 +00:00
{
2010-06-08 11:48:03 +00:00
char temp [ 11 ] = "" ; /* 32-bit hex + CRLF + nul */
2010-01-12 16:36:00 +00:00
int ret ;
char crlf [] = " \r\n " ;
2007-02-28 03:40:23 +00:00
HTTPContext * s = h -> priv_data ;
2010-01-12 16:36:00 +00:00
2011-11-10 11:03:35 +02:00
if ( ! s -> chunked_post ) {
2010-06-21 18:40:53 +00:00
/* non-chunked data is sent without any special encoding */
2011-03-31 16:48:01 +02:00
return ffurl_write ( s -> hd , buf , size );
2010-01-12 16:36:00 +00:00
}
/* silently ignore zero-size data since chunk encoding that would
* signal EOF */
if ( size > 0 ) {
/* upload data using chunked encoding */
2010-06-21 19:02:35 +00:00
snprintf ( temp , sizeof ( temp ), "%x \r\n " , size );
2010-01-12 16:36:00 +00:00
2011-03-31 16:48:01 +02:00
if (( ret = ffurl_write ( s -> hd , temp , strlen ( temp ))) < 0 ||
( ret = ffurl_write ( s -> hd , buf , size )) < 0 ||
( ret = ffurl_write ( s -> hd , crlf , sizeof ( crlf ) - 1 )) < 0 )
2010-01-12 16:36:00 +00:00
return ret ;
}
return size ;
2007-02-28 03:40:23 +00:00
}
2012-05-21 11:26:40 +02:00
static int http_shutdown ( URLContext * h , int flags )
2007-02-28 03:40:23 +00:00
{
2010-01-12 16:36:00 +00:00
int ret = 0 ;
char footer [] = "0 \r\n\r\n " ;
2007-02-28 03:40:23 +00:00
HTTPContext * s = h -> priv_data ;
2010-01-12 16:36:00 +00:00
/* signal end of chunked encoding if used */
2012-05-21 11:26:40 +02:00
if (( flags & AVIO_FLAG_WRITE ) && s -> chunked_post ) {
2011-03-31 16:48:01 +02:00
ret = ffurl_write ( s -> hd , footer , sizeof ( footer ) - 1 );
2010-01-12 16:36:00 +00:00
ret = ret > 0 ? 0 : ret ;
2012-05-21 11:26:40 +02:00
s -> end_chunked_post = 1 ;
}
return ret ;
}
static int http_close ( URLContext * h )
{
int ret = 0 ;
HTTPContext * s = h -> priv_data ;
2013-07-23 04:07:10 +08:00
#if CONFIG_ZLIB
inflateEnd ( & s -> inflate_stream );
av_freep ( & s -> inflate_buffer );
#endif
2012-05-21 11:26:40 +02:00
if ( ! s -> end_chunked_post ) {
/* Close the write direction by sending the end of chunked encoding. */
ret = http_shutdown ( h , h -> flags );
2010-01-12 16:36:00 +00:00
}
2010-06-08 11:18:22 +00:00
if ( s -> hd )
2012-06-01 14:48:17 +02:00
ffurl_closep ( & s -> hd );
2011-11-07 11:06:50 +02:00
av_dict_free ( & s -> chained_options );
2010-01-12 16:36:00 +00:00
return ret ;
2007-02-28 03:40:23 +00:00
}
2008-10-03 10:16:29 +00:00
static int64_t http_seek ( URLContext * h , int64_t off , int whence )
2007-02-28 03:40:23 +00:00
{
HTTPContext * s = h -> priv_data ;
URLContext * old_hd = s -> hd ;
2008-10-03 10:16:29 +00:00
int64_t old_off = s -> off ;
2010-01-13 23:27:52 +00:00
uint8_t old_buf [ BUFFER_SIZE ];
int old_buf_size ;
2011-11-07 11:06:50 +02:00
AVDictionary * options = NULL ;
2007-02-28 03:40:23 +00:00
if ( whence == AVSEEK_SIZE )
return s -> filesize ;
else if (( s -> filesize == - 1 && whence == SEEK_END ) || h -> is_streamed )
return - 1 ;
/* we save the old context in case the seek fails */
2010-01-13 23:27:52 +00:00
old_buf_size = s -> buf_end - s -> buf_ptr ;
memcpy ( old_buf , s -> buf_ptr , old_buf_size );
2007-02-28 03:40:23 +00:00
s -> hd = NULL ;
if ( whence == SEEK_CUR )
off += s -> off ;
else if ( whence == SEEK_END )
off += s -> filesize ;
s -> off = off ;
/* if it fails, continue on old connection */
2011-11-07 11:06:50 +02:00
av_dict_copy ( & options , s -> chained_options , 0 );
if ( http_open_cnx ( h , & options ) < 0 ) {
av_dict_free ( & options );
2010-01-13 23:27:52 +00:00
memcpy ( s -> buffer , old_buf , old_buf_size );
s -> buf_ptr = s -> buffer ;
s -> buf_end = s -> buffer + old_buf_size ;
2007-02-28 03:40:23 +00:00
s -> hd = old_hd ;
s -> off = old_off ;
return - 1 ;
}
2011-11-07 11:06:50 +02:00
av_dict_free ( & options );
2011-03-31 17:36:06 +02:00
ffurl_close ( old_hd );
2007-02-28 03:40:23 +00:00
return off ;
}
2009-03-03 17:04:51 +00:00
static int
http_get_file_handle ( URLContext * h )
{
HTTPContext * s = h -> priv_data ;
2011-03-31 17:51:24 +02:00
return ffurl_get_file_handle ( s -> hd );
2009-03-03 17:04:51 +00:00
}
2011-02-06 00:20:26 +02:00
#if CONFIG_HTTP_PROTOCOL
2011-01-25 22:03:28 +00:00
URLProtocol ff_http_protocol = {
2011-04-08 07:41:47 +02:00
. name = "http" ,
2011-11-07 11:06:50 +02:00
. url_open2 = http_open ,
2011-04-08 07:41:47 +02:00
. url_read = http_read ,
. url_write = http_write ,
. url_seek = http_seek ,
. url_close = http_close ,
2009-03-03 17:04:51 +00:00
. url_get_file_handle = http_get_file_handle ,
2012-05-21 11:26:40 +02:00
. url_shutdown = http_shutdown ,
2011-04-08 07:41:47 +02:00
. priv_data_size = sizeof ( HTTPContext ),
2011-11-05 12:54:01 +01:00
. priv_data_class = & http_context_class ,
2011-12-30 11:38:05 +02:00
. flags = URL_PROTOCOL_FLAG_NETWORK ,
2007-02-28 03:40:23 +00:00
};
2011-02-06 00:20:26 +02:00
#endif
#if CONFIG_HTTPS_PROTOCOL
URLProtocol ff_https_protocol = {
. name = "https" ,
2011-11-07 11:06:50 +02:00
. url_open2 = http_open ,
2011-02-06 00:20:26 +02:00
. url_read = http_read ,
. url_write = http_write ,
. url_seek = http_seek ,
. url_close = http_close ,
. url_get_file_handle = http_get_file_handle ,
2012-06-01 16:36:20 +03:00
. url_shutdown = http_shutdown ,
2011-02-06 00:20:26 +02:00
. priv_data_size = sizeof ( HTTPContext ),
2011-11-05 12:54:01 +01:00
. priv_data_class = & https_context_class ,
2011-12-30 11:38:05 +02:00
. flags = URL_PROTOCOL_FLAG_NETWORK ,
2011-02-06 00:20:26 +02:00
};
#endif
2011-11-10 14:53:16 +02:00
#if CONFIG_HTTPPROXY_PROTOCOL
static int http_proxy_close ( URLContext * h )
{
HTTPContext * s = h -> priv_data ;
if ( s -> hd )
2012-06-01 14:48:17 +02:00
ffurl_closep ( & s -> hd );
2011-11-10 14:53:16 +02:00
return 0 ;
}
static int http_proxy_open ( URLContext * h , const char * uri , int flags )
{
HTTPContext * s = h -> priv_data ;
char hostname [ 1024 ], hoststr [ 1024 ];
char auth [ 1024 ], pathbuf [ 1024 ], * path ;
2012-05-20 16:20:56 +02:00
char lower_url [ 100 ];
2012-03-12 14:00:16 +02:00
int port , ret = 0 , attempts = 0 ;
2011-11-10 14:53:16 +02:00
HTTPAuthType cur_auth_type ;
char * authstr ;
2012-05-20 16:20:56 +02:00
int new_loc ;
2011-11-10 14:53:16 +02:00
2012-10-02 22:22:44 +01:00
if ( s -> seekable == 1 )
h -> is_streamed = 0 ;
else
h -> is_streamed = 1 ;
2011-11-10 14:53:16 +02:00
av_url_split ( NULL , 0 , auth , sizeof ( auth ), hostname , sizeof ( hostname ), & port ,
pathbuf , sizeof ( pathbuf ), uri );
ff_url_join ( hoststr , sizeof ( hoststr ), NULL , NULL , hostname , port , NULL );
path = pathbuf ;
if ( * path == '/' )
path ++ ;
ff_url_join ( lower_url , sizeof ( lower_url ), "tcp" , NULL , hostname , port ,
NULL );
redo :
ret = ffurl_open ( & s -> hd , lower_url , AVIO_FLAG_READ_WRITE ,
& h -> interrupt_callback , NULL );
if ( ret < 0 )
return ret ;
authstr = ff_http_auth_create_response ( & s -> proxy_auth_state , auth ,
path , "CONNECT" );
snprintf ( s -> buffer , sizeof ( s -> buffer ),
"CONNECT %s HTTP/1.1 \r\n "
"Host: %s \r\n "
"Connection: close \r\n "
"%s%s"
" \r\n " ,
path ,
hoststr ,
authstr ? "Proxy-" : "" , authstr ? authstr : "" );
av_freep ( & authstr );
if (( ret = ffurl_write ( s -> hd , s -> buffer , strlen ( s -> buffer ))) < 0 )
goto fail ;
s -> buf_ptr = s -> buffer ;
s -> buf_end = s -> buffer ;
s -> line_count = 0 ;
s -> filesize = - 1 ;
cur_auth_type = s -> proxy_auth_state . auth_type ;
2012-05-20 16:20:56 +02:00
/* Note: This uses buffering, potentially reading more than the
* HTTP header. If tunneling a protocol where the server starts
* the conversation, we might buffer part of that here, too.
* Reading that requires using the proper ffurl_read() function
* on this URLContext, not using the fd directly (as the tls
* protocol does). This shouldn't be an issue for tls though,
* since the client starts the conversation there, so there
* is no extra data that we might buffer up here.
*/
ret = http_read_header ( h , & new_loc );
if ( ret < 0 )
goto fail ;
2011-11-10 14:53:16 +02:00
2012-03-12 14:00:16 +02:00
attempts ++ ;
if ( s -> http_code == 407 &&
( cur_auth_type == HTTP_AUTH_NONE || s -> proxy_auth_state . stale ) &&
s -> proxy_auth_state . auth_type != HTTP_AUTH_NONE && attempts < 2 ) {
2012-06-01 14:48:17 +02:00
ffurl_closep ( & s -> hd );
2011-11-10 14:53:16 +02:00
goto redo ;
}
if ( s -> http_code < 400 )
return 0 ;
ret = AVERROR ( EIO );
fail :
http_proxy_close ( h );
return ret ;
}
static int http_proxy_write ( URLContext * h , const uint8_t * buf , int size )
{
HTTPContext * s = h -> priv_data ;
return ffurl_write ( s -> hd , buf , size );
}
URLProtocol ff_httpproxy_protocol = {
. name = "httpproxy" ,
. url_open = http_proxy_open ,
. url_read = http_buf_read ,
. url_write = http_proxy_write ,
. url_close = http_proxy_close ,
. url_get_file_handle = http_get_file_handle ,
. priv_data_size = sizeof ( HTTPContext ),
2011-12-30 11:38:05 +02:00
. flags = URL_PROTOCOL_FLAG_NETWORK ,
2011-11-10 14:53:16 +02:00
};
#endif