2007-02-28 05:40:23 +02:00
|
|
|
/*
|
2011-10-30 19:59:01 +03:00
|
|
|
* HTTP protocol for avconv client
|
2009-01-19 17:46:40 +02:00
|
|
|
* Copyright (c) 2000, 2001 Fabrice Bellard
|
2007-02-28 05:40:23 +02:00
|
|
|
*
|
2011-03-18 19:35:10 +02:00
|
|
|
* This file is part of Libav.
|
2007-02-28 05:40:23 +02:00
|
|
|
*
|
2011-03-18 19:35:10 +02:00
|
|
|
* Libav is free software; you can redistribute it and/or
|
2007-02-28 05:40:23 +02:00
|
|
|
* 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.
|
|
|
|
*
|
2011-03-18 19:35:10 +02:00
|
|
|
* Libav is distributed in the hope that it will be useful,
|
2007-02-28 05:40:23 +02:00
|
|
|
* 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
|
2011-03-18 19:35:10 +02:00
|
|
|
* License along with Libav; if not, write to the Free Software
|
2007-02-28 05:40:23 +02:00
|
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
|
*/
|
2008-05-09 14:56:36 +03:00
|
|
|
|
|
|
|
#include "libavutil/avstring.h"
|
2007-02-28 05:40:23 +02:00
|
|
|
#include "avformat.h"
|
2010-03-15 01:59:48 +02:00
|
|
|
#include "internal.h"
|
2007-02-28 05:40:23 +02:00
|
|
|
#include "network.h"
|
2010-06-08 13:26:16 +03:00
|
|
|
#include "http.h"
|
2007-11-22 04:27:39 +02:00
|
|
|
#include "os_support.h"
|
2010-03-25 00:32:05 +02:00
|
|
|
#include "httpauth.h"
|
2011-03-31 17:25:10 +03:00
|
|
|
#include "url.h"
|
2010-09-26 17:25:22 +03:00
|
|
|
#include "libavutil/opt.h"
|
2007-02-28 05:40:23 +02:00
|
|
|
|
2013-07-22 23:07:10 +03:00
|
|
|
#if CONFIG_ZLIB
|
|
|
|
#include <zlib.h>
|
|
|
|
#endif
|
|
|
|
|
2011-10-30 19:59:01 +03:00
|
|
|
/* XXX: POST protocol is not completely implemented because avconv uses
|
2007-04-07 17:09:20 +03:00
|
|
|
only a subset of it. */
|
2007-02-28 05:40:23 +02:00
|
|
|
|
2012-09-22 23:17:36 +03: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 05:40:23 +02:00
|
|
|
#define MAX_REDIRECTS 8
|
|
|
|
|
|
|
|
typedef struct {
|
2010-06-22 17:13:55 +03:00
|
|
|
const AVClass *class;
|
2007-02-28 05:40:23 +02:00
|
|
|
URLContext *hd;
|
|
|
|
unsigned char buffer[BUFFER_SIZE], *buf_ptr, *buf_end;
|
|
|
|
int line_count;
|
|
|
|
int http_code;
|
2009-06-23 18:38:53 +03:00
|
|
|
int64_t chunksize; /**< Used if "Transfer-Encoding: chunked" otherwise -1. */
|
2008-10-03 13:16:29 +03:00
|
|
|
int64_t off, filesize;
|
2013-11-21 12:25:16 +03:00
|
|
|
char *location;
|
2010-03-25 00:32:05 +02:00
|
|
|
HTTPAuthState auth_state;
|
2011-11-11 12:21:42 +03:00
|
|
|
HTTPAuthState proxy_auth_state;
|
2011-11-07 12:43:13 +03:00
|
|
|
char *headers;
|
2010-08-09 11:14:48 +03:00
|
|
|
int willclose; /**< Set if the server correctly handles Connection: close and will close the connection after feeding us the content. */
|
http: Change the chunksize AVOption into chunked_post
The chunksize internal variable has two different uses - for
reading, it's the amount of data left of the current chunk
(or -1 if the server doesn't send data in chunked mode), where
it's only an internal state variable. For writing, it's used
to decide whether to enable chunked encoding (by default), by
using the value 0, or disable chunked encoding (value -1).
This, while consistent, doesn't make much sense to expose
as an AVOption. This splits the usage of the internal variable
into two variables, chunksize which is used for reading (as
before), and chunked_post which is the user-settable option,
with the values 0 and 1, where 1 is default.
Signed-off-by: Martin Storsjö <martin@martin.st>
2011-11-10 12:03:35 +03:00
|
|
|
int chunked_post;
|
2012-05-21 12:26:40 +03:00
|
|
|
int end_chunked_post; /**< A flag which indicates if the end of chunked encoding has been sent. */
|
2012-05-21 12:27:10 +03:00
|
|
|
int end_header; /**< A flag which indicates we have finished to read POST reply. */
|
2012-05-28 16:03:19 +03:00
|
|
|
int multiple_requests; /**< A flag which indicates if we use persistent connections. */
|
2012-05-30 12:27:18 +03:00
|
|
|
uint8_t *post_data;
|
|
|
|
int post_datalen;
|
2013-07-22 23:07:10 +03:00
|
|
|
#if CONFIG_ZLIB
|
|
|
|
int compressed;
|
|
|
|
z_stream inflate_stream;
|
|
|
|
uint8_t *inflate_buffer;
|
|
|
|
#endif
|
2011-11-07 12:06:50 +03:00
|
|
|
AVDictionary *chained_options;
|
2013-10-09 13:24:40 +03:00
|
|
|
int send_expect_100;
|
2007-02-28 05:40:23 +02:00
|
|
|
} HTTPContext;
|
|
|
|
|
2010-06-22 17:13:55 +03:00
|
|
|
#define OFFSET(x) offsetof(HTTPContext, x)
|
2011-11-10 11:34:58 +03:00
|
|
|
#define D AV_OPT_FLAG_DECODING_PARAM
|
|
|
|
#define E AV_OPT_FLAG_ENCODING_PARAM
|
2010-06-22 17:13:55 +03:00
|
|
|
static const AVOption options[] = {
|
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 },
|
2011-11-10 11:34:58 +03:00
|
|
|
{"headers", "custom HTTP headers, can override built in default headers", OFFSET(headers), AV_OPT_TYPE_STRING, { 0 }, 0, 0, D|E },
|
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 },
|
2012-05-30 12:27:18 +03:00
|
|
|
{"post_data", "custom HTTP post data", OFFSET(post_data), AV_OPT_TYPE_BINARY, .flags = D|E },
|
2013-10-09 12:14:02 +03:00
|
|
|
{"auth_type", "HTTP authentication type", OFFSET(auth_state.auth_type), AV_OPT_TYPE_INT, {.i64 = HTTP_AUTH_NONE}, HTTP_AUTH_NONE, HTTP_AUTH_BASIC, D|E, "auth_type" },
|
|
|
|
{"none", "No auth method set, autodetect", 0, AV_OPT_TYPE_CONST, {.i64 = HTTP_AUTH_NONE}, 0, 0, D|E, "auth_type" },
|
|
|
|
{"basic", "HTTP basic authentication", 0, AV_OPT_TYPE_CONST, {.i64 = HTTP_AUTH_BASIC}, 0, 0, D|E, "auth_type" },
|
2013-11-20 15:53:56 +03:00
|
|
|
{"send_expect_100", "Force sending an Expect: 100-continue header for POST", OFFSET(send_expect_100), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, E },
|
2013-11-21 12:25:16 +03:00
|
|
|
{"location", "The actual location of the data received", OFFSET(location), AV_OPT_TYPE_STRING, { 0 }, 0, 0, D|E },
|
2010-06-22 17:13:55 +03:00
|
|
|
{NULL}
|
|
|
|
};
|
2011-11-05 14:54:01 +03: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 20:07:04 +03:00
|
|
|
}
|
2010-06-22 17:13:55 +03:00
|
|
|
|
2011-11-05 14:54:01 +03:00
|
|
|
HTTP_CLASS(http);
|
|
|
|
HTTP_CLASS(https);
|
|
|
|
|
2011-11-11 12:21:42 +03: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 05:40:23 +02:00
|
|
|
|
2010-06-21 22:40:30 +03: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 12:21:42 +03:00
|
|
|
memcpy(&((HTTPContext*)dest->priv_data)->proxy_auth_state,
|
|
|
|
&((HTTPContext*)src->priv_data)->proxy_auth_state,
|
|
|
|
sizeof(HTTPAuthState));
|
2010-06-21 22:40:30 +03:00
|
|
|
}
|
|
|
|
|
2007-02-28 05:40:23 +02:00
|
|
|
/* return non zero if error */
|
2011-11-07 12:06:50 +03:00
|
|
|
static int http_open_cnx(URLContext *h, AVDictionary **options)
|
2007-02-28 05:40:23 +02:00
|
|
|
{
|
2011-11-11 12:21:42 +03: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 12:24:23 +03:00
|
|
|
char auth[1024], proxyauth[1024] = "";
|
2012-09-22 23:17:36 +03:00
|
|
|
char path1[MAX_URL_SIZE];
|
|
|
|
char buf[1024], urlbuf[MAX_URL_SIZE];
|
2012-03-12 15:00:16 +03:00
|
|
|
int port, use_proxy, err, location_changed = 0, redirects = 0, attempts = 0;
|
2011-11-11 12:21:42 +03:00
|
|
|
HTTPAuthType cur_auth_type, cur_proxy_auth_type;
|
2007-02-28 05:40:23 +02: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 11:05:03 +02:00
|
|
|
path1, sizeof(path1), s->location);
|
2010-03-06 00:35:21 +02:00
|
|
|
ff_url_join(hoststr, sizeof(hoststr), NULL, NULL, hostname, port, NULL);
|
2007-02-28 05:40:23 +02:00
|
|
|
|
2013-02-27 12:13:47 +03: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 15:55:18 +03:00
|
|
|
if (!strcmp(proto, "https")) {
|
|
|
|
lower_proto = "tls";
|
2011-11-10 15:57:15 +03:00
|
|
|
use_proxy = 0;
|
2011-11-10 15:55:18 +03:00
|
|
|
if (port < 0)
|
|
|
|
port = 443;
|
|
|
|
}
|
|
|
|
if (port < 0)
|
|
|
|
port = 80;
|
|
|
|
|
2011-11-11 12:21:42 +03:00
|
|
|
if (path1[0] == '\0')
|
|
|
|
path = "/";
|
|
|
|
else
|
|
|
|
path = path1;
|
|
|
|
local_path = path;
|
2007-02-28 05:40:23 +02:00
|
|
|
if (use_proxy) {
|
2011-11-11 12:21:42 +03: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 05:40:23 +02:00
|
|
|
}
|
|
|
|
|
2011-02-06 00:20:26 +02:00
|
|
|
ff_url_join(buf, sizeof(buf), lower_proto, NULL, hostname, port, NULL);
|
2007-02-28 05:40:23 +02:00
|
|
|
|
2012-05-28 16:03:54 +03: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 12:06:50 +03:00
|
|
|
&h->interrupt_callback, options);
|
2012-05-28 16:03:54 +03:00
|
|
|
if (err < 0)
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2010-03-25 00:32:05 +02:00
|
|
|
cur_auth_type = s->auth_state.auth_type;
|
2011-11-11 12:21:42 +03: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 05:40:23 +02:00
|
|
|
goto fail;
|
2012-03-12 15:00:16 +03:00
|
|
|
attempts++;
|
2010-03-25 00:32:05 +02:00
|
|
|
if (s->http_code == 401) {
|
2012-03-12 15:00:16 +03: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 16:30:01 +03:00
|
|
|
ffurl_close(s->hd);
|
|
|
|
s->hd = NULL;
|
2010-03-25 00:32:05 +02:00
|
|
|
goto redo;
|
|
|
|
} else
|
|
|
|
goto fail;
|
|
|
|
}
|
2011-11-11 12:21:42 +03:00
|
|
|
if (s->http_code == 407) {
|
2012-03-12 15:00:16 +03: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 16:30:01 +03:00
|
|
|
ffurl_close(s->hd);
|
|
|
|
s->hd = NULL;
|
2011-11-11 12:21:42 +03:00
|
|
|
goto redo;
|
|
|
|
} else
|
|
|
|
goto fail;
|
|
|
|
}
|
2010-11-02 22:37:01 +02:00
|
|
|
if ((s->http_code == 301 || s->http_code == 302 || s->http_code == 303 || s->http_code == 307)
|
|
|
|
&& location_changed == 1) {
|
2007-02-28 05:40:23 +02:00
|
|
|
/* url moved, get next */
|
2012-06-01 16:30:01 +03:00
|
|
|
ffurl_close(s->hd);
|
|
|
|
s->hd = NULL;
|
2007-02-28 05:40:23 +02:00
|
|
|
if (redirects++ >= MAX_REDIRECTS)
|
2007-07-19 18:23:32 +03:00
|
|
|
return AVERROR(EIO);
|
2012-03-12 15:03:46 +03: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 15:00:16 +03:00
|
|
|
attempts = 0;
|
2007-02-28 05:40:23 +02:00
|
|
|
location_changed = 0;
|
|
|
|
goto redo;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
fail:
|
2012-06-01 16:30:01 +03:00
|
|
|
if (s->hd)
|
|
|
|
ffurl_close(s->hd);
|
2010-06-08 14:18:22 +03:00
|
|
|
s->hd = NULL;
|
2007-07-19 18:23:32 +03:00
|
|
|
return AVERROR(EIO);
|
2007-02-28 05:40:23 +02:00
|
|
|
}
|
|
|
|
|
2012-05-28 16:03:54 +03:00
|
|
|
int ff_http_do_new_request(URLContext *h, const char *uri)
|
|
|
|
{
|
|
|
|
HTTPContext *s = h->priv_data;
|
2011-11-07 12:06:50 +03:00
|
|
|
AVDictionary *options = NULL;
|
|
|
|
int ret;
|
2012-05-28 16:03:54 +03:00
|
|
|
|
|
|
|
s->off = 0;
|
2013-11-21 12:25:16 +03:00
|
|
|
av_free(s->location);
|
|
|
|
s->location = av_strdup(uri);
|
|
|
|
if (!s->location)
|
|
|
|
return AVERROR(ENOMEM);
|
2012-05-28 16:03:54 +03:00
|
|
|
|
2011-11-07 12:06:50 +03:00
|
|
|
av_dict_copy(&options, s->chained_options, 0);
|
|
|
|
ret = http_open_cnx(h, &options);
|
|
|
|
av_dict_free(&options);
|
|
|
|
return ret;
|
2012-05-28 16:03:54 +03:00
|
|
|
}
|
|
|
|
|
2011-11-07 12:06:50 +03:00
|
|
|
static int http_open(URLContext *h, const char *uri, int flags,
|
|
|
|
AVDictionary **options)
|
2007-02-28 05:40:23 +02:00
|
|
|
{
|
2010-06-22 17:12:34 +03:00
|
|
|
HTTPContext *s = h->priv_data;
|
2011-11-07 12:06:50 +03:00
|
|
|
int ret;
|
2007-02-28 05:40:23 +02:00
|
|
|
|
|
|
|
h->is_streamed = 1;
|
|
|
|
|
|
|
|
s->filesize = -1;
|
2013-11-21 12:25:16 +03:00
|
|
|
s->location = av_strdup(uri);
|
|
|
|
if (!s->location)
|
|
|
|
return AVERROR(ENOMEM);
|
2011-11-07 12:06:50 +03:00
|
|
|
if (options)
|
|
|
|
av_dict_copy(&s->chained_options, *options, 0);
|
2007-02-28 05:40:23 +02:00
|
|
|
|
2011-11-07 12:43:13 +03:00
|
|
|
if (s->headers) {
|
|
|
|
int len = strlen(s->headers);
|
|
|
|
if (len < 2 || strcmp("\r\n", s->headers + len - 2))
|
2011-11-09 02:14:05 +03:00
|
|
|
av_log(h, AV_LOG_WARNING, "No trailing CRLF found in HTTP header.\n");
|
2011-11-07 12:43:13 +03:00
|
|
|
}
|
|
|
|
|
2011-11-07 12:06:50 +03:00
|
|
|
ret = http_open_cnx(h, options);
|
|
|
|
if (ret < 0)
|
|
|
|
av_dict_free(&s->chained_options);
|
|
|
|
return ret;
|
2007-02-28 05:40:23 +02:00
|
|
|
}
|
|
|
|
static int http_getc(HTTPContext *s)
|
|
|
|
{
|
|
|
|
int len;
|
|
|
|
if (s->buf_ptr >= s->buf_end) {
|
2011-03-31 17:31:43 +03:00
|
|
|
len = ffurl_read(s->hd, s->buffer, BUFFER_SIZE);
|
2007-02-28 05:40:23 +02:00
|
|
|
if (len < 0) {
|
2012-05-30 12:52:11 +03:00
|
|
|
return len;
|
2007-02-28 05:40:23 +02: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 19:44:21 +03: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 12:52:11 +03:00
|
|
|
return ch;
|
2009-06-06 19:44:21 +03: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 05:40:23 +02:00
|
|
|
static int process_line(URLContext *h, char *line, int line_count,
|
|
|
|
int *new_location)
|
|
|
|
{
|
|
|
|
HTTPContext *s = h->priv_data;
|
2010-07-16 17:15:37 +03:00
|
|
|
char *tag, *p, *end;
|
2007-02-28 05:40:23 +02:00
|
|
|
|
|
|
|
/* end of header */
|
2012-05-21 12:27:10 +03:00
|
|
|
if (line[0] == '\0') {
|
|
|
|
s->end_header = 1;
|
2007-02-28 05:40:23 +02:00
|
|
|
return 0;
|
2012-05-21 12:27:10 +03:00
|
|
|
}
|
2007-02-28 05:40:23 +02:00
|
|
|
|
|
|
|
p = line;
|
|
|
|
if (line_count == 0) {
|
2013-03-03 13:17:50 +03:00
|
|
|
while (!av_isspace(*p) && *p != '\0')
|
2007-02-28 05:40:23 +02:00
|
|
|
p++;
|
2013-03-03 13:17:50 +03:00
|
|
|
while (av_isspace(*p))
|
2007-02-28 05:40:23 +02:00
|
|
|
p++;
|
2010-07-16 17:15:37 +03:00
|
|
|
s->http_code = strtol(p, &end, 10);
|
2009-06-06 20:32:59 +03:00
|
|
|
|
2011-01-29 18:46:18 +02:00
|
|
|
av_dlog(NULL, "http_code=%d\n", s->http_code);
|
2009-06-06 20:32:59 +03:00
|
|
|
|
2010-03-25 00:32:05 +02: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 19:21:17 +03:00
|
|
|
if (s->http_code >= 400 && s->http_code < 600 && (s->http_code != 401
|
2011-11-11 12:21:42 +03: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 17:15:37 +03:00
|
|
|
end += strspn(end, SPACE_CHARS);
|
2011-04-20 12:40:29 +03:00
|
|
|
av_log(h, AV_LOG_WARNING, "HTTP error %d %s\n",
|
2010-07-16 17:15:37 +03:00
|
|
|
s->http_code, end);
|
2007-03-06 15:38:41 +02:00
|
|
|
return -1;
|
2010-07-16 17:15:37 +03:00
|
|
|
}
|
2007-02-28 05:40:23 +02:00
|
|
|
} else {
|
|
|
|
while (*p != '\0' && *p != ':')
|
|
|
|
p++;
|
|
|
|
if (*p != ':')
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
*p = '\0';
|
|
|
|
tag = line;
|
|
|
|
p++;
|
2013-03-03 13:17:50 +03:00
|
|
|
while (av_isspace(*p))
|
2007-02-28 05:40:23 +02:00
|
|
|
p++;
|
2011-11-02 22:17:25 +03:00
|
|
|
if (!av_strcasecmp(tag, "Location")) {
|
2013-11-21 12:25:16 +03:00
|
|
|
char redirected_location[MAX_URL_SIZE], *new_loc;
|
2013-07-04 12:18:52 +03:00
|
|
|
ff_make_absolute_url(redirected_location, sizeof(redirected_location),
|
|
|
|
s->location, p);
|
2013-11-21 12:25:16 +03:00
|
|
|
new_loc = av_strdup(redirected_location);
|
|
|
|
if (!new_loc)
|
|
|
|
return AVERROR(ENOMEM);
|
|
|
|
av_free(s->location);
|
|
|
|
s->location = new_loc;
|
2007-02-28 05:40:23 +02:00
|
|
|
*new_location = 1;
|
2011-11-02 22:17:25 +03:00
|
|
|
} else if (!av_strcasecmp (tag, "Content-Length") && s->filesize == -1) {
|
2012-06-18 16:02:43 +03:00
|
|
|
s->filesize = strtoll(p, NULL, 10);
|
2011-11-02 22:17:25 +03:00
|
|
|
} else if (!av_strcasecmp (tag, "Content-Range")) {
|
2007-02-28 05:40:23 +02:00
|
|
|
/* "bytes $from-$to/$document_size" */
|
|
|
|
const char *slash;
|
|
|
|
if (!strncmp (p, "bytes ", 6)) {
|
|
|
|
p += 6;
|
2012-06-18 16:02:43 +03:00
|
|
|
s->off = strtoll(p, NULL, 10);
|
2007-02-28 05:40:23 +02:00
|
|
|
if ((slash = strchr(p, '/')) && strlen(slash) > 0)
|
2012-06-18 16:02:43 +03:00
|
|
|
s->filesize = strtoll(slash+1, NULL, 10);
|
2007-02-28 05:40:23 +02:00
|
|
|
}
|
|
|
|
h->is_streamed = 0; /* we _can_ in fact seek */
|
2011-11-02 22:17:25 +03:00
|
|
|
} else if (!av_strcasecmp(tag, "Accept-Ranges") && !strncmp(p, "bytes", 5)) {
|
2011-09-04 11:56:24 +03:00
|
|
|
h->is_streamed = 0;
|
2011-11-02 22:17:25 +03:00
|
|
|
} else if (!av_strcasecmp (tag, "Transfer-Encoding") && !av_strncasecmp(p, "chunked", 7)) {
|
2009-06-23 18:38:53 +03:00
|
|
|
s->filesize = -1;
|
|
|
|
s->chunksize = 0;
|
2011-11-02 22:17:25 +03:00
|
|
|
} else if (!av_strcasecmp (tag, "WWW-Authenticate")) {
|
2010-03-25 00:32:05 +02:00
|
|
|
ff_http_auth_handle_header(&s->auth_state, tag, p);
|
2011-11-02 22:17:25 +03:00
|
|
|
} else if (!av_strcasecmp (tag, "Authentication-Info")) {
|
2010-03-25 00:32:05 +02:00
|
|
|
ff_http_auth_handle_header(&s->auth_state, tag, p);
|
2011-11-11 12:21:42 +03:00
|
|
|
} else if (!av_strcasecmp (tag, "Proxy-Authenticate")) {
|
|
|
|
ff_http_auth_handle_header(&s->proxy_auth_state, tag, p);
|
2011-11-02 22:17:25 +03:00
|
|
|
} else if (!av_strcasecmp (tag, "Connection")) {
|
2010-08-09 11:14:48 +03:00
|
|
|
if (!strcmp(p, "close"))
|
|
|
|
s->willclose = 1;
|
2013-07-22 23:07:10 +03: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);
|
|
|
|
if (inflateInit2(&s->inflate_stream, 32 + 15) != Z_OK) {
|
|
|
|
av_log(h, AV_LOG_WARNING, "Error during zlib initialisation: %s\n",
|
|
|
|
s->inflate_stream.msg);
|
|
|
|
return AVERROR(ENOSYS);
|
|
|
|
}
|
|
|
|
if (zlibCompileFlags() & (1 << 17)) {
|
|
|
|
av_log(h, AV_LOG_WARNING, "Your zlib was compiled without gzip support.\n");
|
|
|
|
return AVERROR(ENOSYS);
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
av_log(h, AV_LOG_WARNING, "Compressed (%s) content, need zlib with gzip support\n", p);
|
|
|
|
return AVERROR(ENOSYS);
|
|
|
|
#endif
|
|
|
|
} 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);
|
|
|
|
}
|
2007-02-28 05:40:23 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2010-06-08 13:26:16 +03: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 12:43:13 +03:00
|
|
|
if (!str)
|
|
|
|
return 0;
|
2010-06-08 13:26:16 +03:00
|
|
|
return av_stristart(str, header + 2, NULL) || av_stristr(str, header);
|
|
|
|
}
|
|
|
|
|
2012-05-20 17:20:56 +03:00
|
|
|
static int http_read_header(URLContext *h, int *new_location)
|
|
|
|
{
|
|
|
|
HTTPContext *s = h->priv_data;
|
2012-09-22 23:17:36 +03:00
|
|
|
char line[MAX_URL_SIZE];
|
2012-05-20 17:20:56 +03:00
|
|
|
int err = 0;
|
|
|
|
|
2012-06-17 21:19:41 +03:00
|
|
|
s->chunksize = -1;
|
|
|
|
|
2012-05-20 17:20:56 +03:00
|
|
|
for (;;) {
|
2012-05-30 12:52:11 +03:00
|
|
|
if ((err = http_get_line(s, line, sizeof(line))) < 0)
|
|
|
|
return err;
|
2012-05-20 17:20:56 +03: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 12:21:42 +03: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 05:40:23 +02:00
|
|
|
{
|
|
|
|
HTTPContext *s = h->priv_data;
|
2009-06-06 19:44:21 +03:00
|
|
|
int post, err;
|
2010-06-08 14:06:36 +03:00
|
|
|
char headers[1024] = "";
|
2011-11-11 12:21:42 +03:00
|
|
|
char *authstr = NULL, *proxyauthstr = NULL;
|
2008-10-03 13:16:29 +03:00
|
|
|
int64_t off = s->off;
|
2010-06-08 13:26:16 +03:00
|
|
|
int len = 0;
|
2011-11-11 12:21:42 +03:00
|
|
|
const char *method;
|
2013-10-09 13:24:40 +03:00
|
|
|
int send_expect_100 = 0;
|
2007-02-28 05:40:23 +02:00
|
|
|
|
|
|
|
|
|
|
|
/* send http header */
|
2011-04-15 17:42:09 +03:00
|
|
|
post = h->flags & AVIO_FLAG_WRITE;
|
2012-05-30 12:27:18 +03: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 12:21:42 +03: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);
|
2013-10-09 13:24:40 +03:00
|
|
|
if (post && !s->post_data) {
|
|
|
|
send_expect_100 = s->send_expect_100;
|
|
|
|
/* The user has supplied authentication but we don't know the auth type,
|
|
|
|
* send Expect: 100-continue to get the 401 response including the
|
|
|
|
* WWW-Authenticate header, or an 100 continue if no auth actually
|
|
|
|
* is needed. */
|
2013-10-14 01:56:32 +03:00
|
|
|
if (auth && *auth &&
|
|
|
|
s->auth_state.auth_type == HTTP_AUTH_NONE &&
|
2013-10-09 13:24:40 +03:00
|
|
|
s->http_code != 401)
|
|
|
|
send_expect_100 = 1;
|
|
|
|
}
|
2010-06-08 13:26:16 +03:00
|
|
|
|
|
|
|
/* set default headers if needed */
|
|
|
|
if (!has_header(s->headers, "\r\nUser-Agent: "))
|
|
|
|
len += av_strlcatf(headers + len, sizeof(headers) - len,
|
|
|
|
"User-Agent: %s\r\n", LIBAVFORMAT_IDENT);
|
|
|
|
if (!has_header(s->headers, "\r\nAccept: "))
|
|
|
|
len += av_strlcpy(headers + len, "Accept: */*\r\n",
|
|
|
|
sizeof(headers) - len);
|
2011-11-10 12:09:26 +03:00
|
|
|
if (!has_header(s->headers, "\r\nRange: ") && !post)
|
2010-06-08 13:26:16 +03:00
|
|
|
len += av_strlcatf(headers + len, sizeof(headers) - len,
|
2010-06-08 14:06:36 +03:00
|
|
|
"Range: bytes=%"PRId64"-\r\n", s->off);
|
2013-10-09 13:24:40 +03:00
|
|
|
if (send_expect_100 && !has_header(s->headers, "\r\nExpect: "))
|
|
|
|
len += av_strlcatf(headers + len, sizeof(headers) - len,
|
|
|
|
"Expect: 100-continue\r\n");
|
2012-05-28 16:03:19 +03:00
|
|
|
|
|
|
|
if (!has_header(s->headers, "\r\nConnection: ")) {
|
|
|
|
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 13:26:16 +03:00
|
|
|
if (!has_header(s->headers, "\r\nHost: "))
|
|
|
|
len += av_strlcatf(headers + len, sizeof(headers) - len,
|
|
|
|
"Host: %s\r\n", hoststr);
|
2012-05-30 12:27:18 +03:00
|
|
|
if (!has_header(s->headers, "\r\nContent-Length: ") && s->post_data)
|
|
|
|
len += av_strlcatf(headers + len, sizeof(headers) - len,
|
|
|
|
"Content-Length: %d\r\n", s->post_datalen);
|
2010-06-08 13:26:16 +03:00
|
|
|
|
|
|
|
/* now add in custom headers */
|
2011-11-07 12:43:13 +03:00
|
|
|
if (s->headers)
|
|
|
|
av_strlcpy(headers + len, s->headers, sizeof(headers) - len);
|
2010-06-08 13:26:16 +03:00
|
|
|
|
2007-02-28 05:40:23 +02:00
|
|
|
snprintf(s->buffer, sizeof(s->buffer),
|
|
|
|
"%s %s HTTP/1.1\r\n"
|
2010-03-25 00:32:05 +02:00
|
|
|
"%s"
|
2010-06-08 13:26:16 +03:00
|
|
|
"%s"
|
2010-01-12 18:36:00 +02:00
|
|
|
"%s"
|
2011-11-11 12:21:42 +03:00
|
|
|
"%s%s"
|
2007-02-28 05:40:23 +02:00
|
|
|
"\r\n",
|
2011-11-11 12:21:42 +03:00
|
|
|
method,
|
2007-02-28 05:40:23 +02:00
|
|
|
path,
|
http: Change the chunksize AVOption into chunked_post
The chunksize internal variable has two different uses - for
reading, it's the amount of data left of the current chunk
(or -1 if the server doesn't send data in chunked mode), where
it's only an internal state variable. For writing, it's used
to decide whether to enable chunked encoding (by default), by
using the value 0, or disable chunked encoding (value -1).
This, while consistent, doesn't make much sense to expose
as an AVOption. This splits the usage of the internal variable
into two variables, chunksize which is used for reading (as
before), and chunked_post which is the user-settable option,
with the values 0 and 1, where 1 is default.
Signed-off-by: Martin Storsjö <martin@martin.st>
2011-11-10 12:03:35 +03:00
|
|
|
post && s->chunked_post ? "Transfer-Encoding: chunked\r\n" : "",
|
2010-06-08 13:26:16 +03:00
|
|
|
headers,
|
2011-11-11 12:21:42 +03:00
|
|
|
authstr ? authstr : "",
|
|
|
|
proxyauthstr ? "Proxy-" : "", proxyauthstr ? proxyauthstr : "");
|
2007-02-28 05:40:23 +02:00
|
|
|
|
2010-03-25 00:32:05 +02:00
|
|
|
av_freep(&authstr);
|
2011-11-11 12:21:42 +03:00
|
|
|
av_freep(&proxyauthstr);
|
2012-05-30 12:52:11 +03:00
|
|
|
if ((err = ffurl_write(s->hd, s->buffer, strlen(s->buffer))) < 0)
|
|
|
|
return err;
|
2007-02-28 05:40:23 +02:00
|
|
|
|
2012-05-30 12:27:18 +03:00
|
|
|
if (s->post_data)
|
|
|
|
if ((err = ffurl_write(s->hd, s->post_data, s->post_datalen)) < 0)
|
|
|
|
return err;
|
|
|
|
|
2007-02-28 05:40:23 +02:00
|
|
|
/* init input buffer */
|
|
|
|
s->buf_ptr = s->buffer;
|
|
|
|
s->buf_end = s->buffer;
|
|
|
|
s->line_count = 0;
|
|
|
|
s->off = 0;
|
2007-03-12 16:51:18 +02:00
|
|
|
s->filesize = -1;
|
2010-08-09 11:14:48 +03:00
|
|
|
s->willclose = 0;
|
2012-05-21 12:26:40 +03:00
|
|
|
s->end_chunked_post = 0;
|
2012-05-28 16:03:54 +03:00
|
|
|
s->end_header = 0;
|
2013-10-09 13:24:40 +03:00
|
|
|
if (post && !s->post_data && !send_expect_100) {
|
2010-06-04 09:35:12 +03: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 05:40:23 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* wait for header */
|
2012-05-20 17:20:56 +03:00
|
|
|
err = http_read_header(h, new_location);
|
|
|
|
if (err < 0)
|
|
|
|
return err;
|
2007-02-28 05:40:23 +02:00
|
|
|
|
|
|
|
return (off == s->off) ? 0 : -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-11-10 15:52:50 +03:00
|
|
|
static int http_buf_read(URLContext *h, uint8_t *buf, int size)
|
2007-02-28 05:40:23 +02:00
|
|
|
{
|
|
|
|
HTTPContext *s = h->priv_data;
|
|
|
|
int len;
|
2011-11-10 15:52:50 +03: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;
|
|
|
|
if (s->chunksize > 0)
|
|
|
|
s->chunksize -= len;
|
|
|
|
}
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
2013-07-22 23:07:10 +03: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)
|
|
|
|
av_log(h, AV_LOG_WARNING, "inflate return value: %d, %s\n", ret, s->inflate_stream.msg);
|
|
|
|
|
|
|
|
return size - s->inflate_stream.avail_out;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2011-11-10 15:52:50 +03:00
|
|
|
static int http_read(URLContext *h, uint8_t *buf, int size)
|
|
|
|
{
|
|
|
|
HTTPContext *s = h->priv_data;
|
2012-05-21 12:27:10 +03:00
|
|
|
int err, new_location;
|
|
|
|
|
2012-06-17 21:15:32 +03:00
|
|
|
if (!s->hd)
|
|
|
|
return AVERROR_EOF;
|
|
|
|
|
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 12:27:10 +03:00
|
|
|
}
|
2007-02-28 05:40:23 +02:00
|
|
|
|
2009-06-23 18:38:53 +03:00
|
|
|
if (s->chunksize >= 0) {
|
|
|
|
if (!s->chunksize) {
|
|
|
|
char line[32];
|
|
|
|
|
|
|
|
for(;;) {
|
|
|
|
do {
|
2012-05-30 12:52:11 +03:00
|
|
|
if ((err = http_get_line(s, line, sizeof(line))) < 0)
|
|
|
|
return err;
|
2009-06-23 18:38:53 +03:00
|
|
|
} while (!*line); /* skip CR LF from last chunk */
|
|
|
|
|
|
|
|
s->chunksize = strtoll(line, NULL, 16);
|
|
|
|
|
2011-01-29 18:46:18 +02:00
|
|
|
av_dlog(NULL, "Chunked encoding data size: %"PRId64"'\n", s->chunksize);
|
2009-06-23 18:38:53 +03:00
|
|
|
|
|
|
|
if (!s->chunksize)
|
|
|
|
return 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
size = FFMIN(size, s->chunksize);
|
|
|
|
}
|
2013-07-22 23:07:10 +03:00
|
|
|
#if CONFIG_ZLIB
|
|
|
|
if (s->compressed)
|
|
|
|
return http_buf_read_compressed(h, buf, size);
|
|
|
|
#endif
|
2011-11-10 15:52:50 +03:00
|
|
|
return http_buf_read(h, buf, size);
|
2007-02-28 05:40:23 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* used only when posting data */
|
2010-06-01 10:46:23 +03:00
|
|
|
static int http_write(URLContext *h, const uint8_t *buf, int size)
|
2007-02-28 05:40:23 +02:00
|
|
|
{
|
2010-06-08 14:48:03 +03:00
|
|
|
char temp[11] = ""; /* 32-bit hex + CRLF + nul */
|
2010-01-12 18:36:00 +02:00
|
|
|
int ret;
|
|
|
|
char crlf[] = "\r\n";
|
2007-02-28 05:40:23 +02:00
|
|
|
HTTPContext *s = h->priv_data;
|
2010-01-12 18:36:00 +02:00
|
|
|
|
http: Change the chunksize AVOption into chunked_post
The chunksize internal variable has two different uses - for
reading, it's the amount of data left of the current chunk
(or -1 if the server doesn't send data in chunked mode), where
it's only an internal state variable. For writing, it's used
to decide whether to enable chunked encoding (by default), by
using the value 0, or disable chunked encoding (value -1).
This, while consistent, doesn't make much sense to expose
as an AVOption. This splits the usage of the internal variable
into two variables, chunksize which is used for reading (as
before), and chunked_post which is the user-settable option,
with the values 0 and 1, where 1 is default.
Signed-off-by: Martin Storsjö <martin@martin.st>
2011-11-10 12:03:35 +03:00
|
|
|
if (!s->chunked_post) {
|
2010-06-21 21:40:53 +03:00
|
|
|
/* non-chunked data is sent without any special encoding */
|
2011-03-31 17:48:01 +03:00
|
|
|
return ffurl_write(s->hd, buf, size);
|
2010-01-12 18:36:00 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* silently ignore zero-size data since chunk encoding that would
|
|
|
|
* signal EOF */
|
|
|
|
if (size > 0) {
|
|
|
|
/* upload data using chunked encoding */
|
2010-06-21 22:02:35 +03:00
|
|
|
snprintf(temp, sizeof(temp), "%x\r\n", size);
|
2010-06-08 14:48:03 +03:00
|
|
|
|
2011-03-31 17:48:01 +03: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 18:36:00 +02:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
return size;
|
2007-02-28 05:40:23 +02:00
|
|
|
}
|
|
|
|
|
2012-05-21 12:26:40 +03:00
|
|
|
static int http_shutdown(URLContext *h, int flags)
|
2007-02-28 05:40:23 +02:00
|
|
|
{
|
2010-01-12 18:36:00 +02:00
|
|
|
int ret = 0;
|
|
|
|
char footer[] = "0\r\n\r\n";
|
2007-02-28 05:40:23 +02:00
|
|
|
HTTPContext *s = h->priv_data;
|
2010-01-12 18:36:00 +02:00
|
|
|
|
|
|
|
/* signal end of chunked encoding if used */
|
2012-05-21 12:26:40 +03:00
|
|
|
if ((flags & AVIO_FLAG_WRITE) && s->chunked_post) {
|
2011-03-31 17:48:01 +03:00
|
|
|
ret = ffurl_write(s->hd, footer, sizeof(footer) - 1);
|
2010-01-12 18:36:00 +02:00
|
|
|
ret = ret > 0 ? 0 : ret;
|
2012-05-21 12:26:40 +03:00
|
|
|
s->end_chunked_post = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int http_close(URLContext *h)
|
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
HTTPContext *s = h->priv_data;
|
|
|
|
|
2013-07-22 23:07:10 +03:00
|
|
|
#if CONFIG_ZLIB
|
|
|
|
inflateEnd(&s->inflate_stream);
|
|
|
|
av_freep(&s->inflate_buffer);
|
|
|
|
#endif
|
|
|
|
|
2012-05-21 12:26:40 +03: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 18:36:00 +02:00
|
|
|
}
|
|
|
|
|
2010-06-08 14:18:22 +03:00
|
|
|
if (s->hd)
|
2011-03-31 18:36:06 +03:00
|
|
|
ffurl_close(s->hd);
|
2011-11-07 12:06:50 +03:00
|
|
|
av_dict_free(&s->chained_options);
|
2010-01-12 18:36:00 +02:00
|
|
|
return ret;
|
2007-02-28 05:40:23 +02:00
|
|
|
}
|
|
|
|
|
2008-10-03 13:16:29 +03:00
|
|
|
static int64_t http_seek(URLContext *h, int64_t off, int whence)
|
2007-02-28 05:40:23 +02:00
|
|
|
{
|
|
|
|
HTTPContext *s = h->priv_data;
|
|
|
|
URLContext *old_hd = s->hd;
|
2008-10-03 13:16:29 +03:00
|
|
|
int64_t old_off = s->off;
|
2010-01-14 01:27:52 +02:00
|
|
|
uint8_t old_buf[BUFFER_SIZE];
|
|
|
|
int old_buf_size;
|
2011-11-07 12:06:50 +03:00
|
|
|
AVDictionary *options = NULL;
|
2007-02-28 05:40:23 +02: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-14 01:27:52 +02:00
|
|
|
old_buf_size = s->buf_end - s->buf_ptr;
|
|
|
|
memcpy(old_buf, s->buf_ptr, old_buf_size);
|
2007-02-28 05:40:23 +02: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 12:06:50 +03:00
|
|
|
av_dict_copy(&options, s->chained_options, 0);
|
|
|
|
if (http_open_cnx(h, &options) < 0) {
|
|
|
|
av_dict_free(&options);
|
2010-01-14 01:27:52 +02: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 05:40:23 +02:00
|
|
|
s->hd = old_hd;
|
|
|
|
s->off = old_off;
|
|
|
|
return -1;
|
|
|
|
}
|
2011-11-07 12:06:50 +03:00
|
|
|
av_dict_free(&options);
|
2011-03-31 18:36:06 +03:00
|
|
|
ffurl_close(old_hd);
|
2007-02-28 05:40:23 +02:00
|
|
|
return off;
|
|
|
|
}
|
|
|
|
|
2009-03-03 19:04:51 +02:00
|
|
|
static int
|
|
|
|
http_get_file_handle(URLContext *h)
|
|
|
|
{
|
|
|
|
HTTPContext *s = h->priv_data;
|
2011-03-31 18:51:24 +03:00
|
|
|
return ffurl_get_file_handle(s->hd);
|
2009-03-03 19:04:51 +02:00
|
|
|
}
|
|
|
|
|
2011-02-06 00:20:26 +02:00
|
|
|
#if CONFIG_HTTP_PROTOCOL
|
2011-01-26 00:03:28 +02:00
|
|
|
URLProtocol ff_http_protocol = {
|
2011-04-08 08:41:47 +03:00
|
|
|
.name = "http",
|
2011-11-07 12:06:50 +03:00
|
|
|
.url_open2 = http_open,
|
2011-04-08 08:41:47 +03:00
|
|
|
.url_read = http_read,
|
|
|
|
.url_write = http_write,
|
|
|
|
.url_seek = http_seek,
|
|
|
|
.url_close = http_close,
|
2009-03-03 19:04:51 +02:00
|
|
|
.url_get_file_handle = http_get_file_handle,
|
2012-05-21 12:26:40 +03:00
|
|
|
.url_shutdown = http_shutdown,
|
2011-04-08 08:41:47 +03:00
|
|
|
.priv_data_size = sizeof(HTTPContext),
|
2011-11-05 14:54:01 +03:00
|
|
|
.priv_data_class = &http_context_class,
|
2011-12-30 12:38:05 +03:00
|
|
|
.flags = URL_PROTOCOL_FLAG_NETWORK,
|
2007-02-28 05:40:23 +02:00
|
|
|
};
|
2011-02-06 00:20:26 +02:00
|
|
|
#endif
|
|
|
|
#if CONFIG_HTTPS_PROTOCOL
|
|
|
|
URLProtocol ff_https_protocol = {
|
|
|
|
.name = "https",
|
2011-11-07 12:06:50 +03: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 14:54:01 +03:00
|
|
|
.priv_data_class = &https_context_class,
|
2011-12-30 12:38:05 +03:00
|
|
|
.flags = URL_PROTOCOL_FLAG_NETWORK,
|
2011-02-06 00:20:26 +02:00
|
|
|
};
|
|
|
|
#endif
|
2011-11-10 15:53:16 +03:00
|
|
|
|
|
|
|
#if CONFIG_HTTPPROXY_PROTOCOL
|
|
|
|
static int http_proxy_close(URLContext *h)
|
|
|
|
{
|
|
|
|
HTTPContext *s = h->priv_data;
|
|
|
|
if (s->hd)
|
|
|
|
ffurl_close(s->hd);
|
|
|
|
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 17:20:56 +03:00
|
|
|
char lower_url[100];
|
2012-03-12 15:00:16 +03:00
|
|
|
int port, ret = 0, attempts = 0;
|
2011-11-10 15:53:16 +03:00
|
|
|
HTTPAuthType cur_auth_type;
|
|
|
|
char *authstr;
|
2012-05-20 17:20:56 +03:00
|
|
|
int new_loc;
|
2011-11-10 15:53:16 +03:00
|
|
|
|
|
|
|
h->is_streamed = 1;
|
|
|
|
|
|
|
|
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 17:20:56 +03: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 15:53:16 +03:00
|
|
|
|
2012-03-12 15:00:16 +03: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) {
|
2011-11-10 15:53:16 +03:00
|
|
|
ffurl_close(s->hd);
|
|
|
|
s->hd = NULL;
|
|
|
|
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 12:38:05 +03:00
|
|
|
.flags = URL_PROTOCOL_FLAG_NETWORK,
|
2011-11-10 15:53:16 +03:00
|
|
|
};
|
|
|
|
#endif
|