X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=libavformat%2Fhttp.c;h=10fcc50d126cab1772904ffc5670314ca99563ae;hb=f542dedf72091af8e6f32a12bd64289c58857c21;hp=bcb1227ddbdff143f4342e50bbc67152f9412c6e;hpb=1876e7c0c2ec0d4a9c2daa45e0ab311b1c2ac413;p=ffmpeg diff --git a/libavformat/http.c b/libavformat/http.c index bcb1227ddbd..10fcc50d126 100644 --- a/libavformat/http.c +++ b/libavformat/http.c @@ -21,7 +21,6 @@ #include "libavutil/avstring.h" #include "avformat.h" -#include #include "internal.h" #include "network.h" #include "http.h" @@ -30,11 +29,18 @@ #include "url.h" #include "libavutil/opt.h" +#if CONFIG_ZLIB +#include +#endif + /* XXX: POST protocol is not completely implemented because avconv uses only a subset of it. */ -/* used for protocol handling */ -#define BUFFER_SIZE 1024 +/* 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 #define MAX_REDIRECTS 8 typedef struct { @@ -56,15 +62,20 @@ typedef struct { int multiple_requests; /**< A flag which indicates if we use persistent connections. */ uint8_t *post_data; int post_datalen; +#if CONFIG_ZLIB + int compressed; + z_stream inflate_stream; + uint8_t *inflate_buffer; +#endif } HTTPContext; #define OFFSET(x) offsetof(HTTPContext, x) #define D AV_OPT_FLAG_DECODING_PARAM #define E AV_OPT_FLAG_ENCODING_PARAM static const AVOption options[] = { -{"chunked_post", "use chunked transfer-encoding for posts", OFFSET(chunked_post), AV_OPT_TYPE_INT, {.dbl = 1}, 0, 1, E }, +{"chunked_post", "use chunked transfer-encoding for posts", OFFSET(chunked_post), AV_OPT_TYPE_INT, {.i64 = 1}, 0, 1, E }, {"headers", "custom HTTP headers, can override built in default headers", OFFSET(headers), AV_OPT_TYPE_STRING, { 0 }, 0, 0, D|E }, -{"multiple_requests", "use persistent connections", OFFSET(multiple_requests), AV_OPT_TYPE_INT, {.dbl = 0}, 0, 1, D|E }, +{"multiple_requests", "use persistent connections", OFFSET(multiple_requests), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, D|E }, {"post_data", "custom HTTP post data", OFFSET(post_data), AV_OPT_TYPE_BINARY, .flags = D|E }, {NULL} }; @@ -98,16 +109,11 @@ static int http_open_cnx(URLContext *h) const char *path, *proxy_path, *lower_proto = "tcp", *local_path; char hostname[1024], hoststr[1024], proto[10]; char auth[1024], proxyauth[1024] = ""; - char path1[1024]; - char buf[1024], urlbuf[1024]; + char path1[MAX_URL_SIZE]; + char buf[1024], urlbuf[MAX_URL_SIZE]; int port, use_proxy, err, location_changed = 0, redirects = 0, attempts = 0; HTTPAuthType cur_auth_type, cur_proxy_auth_type; HTTPContext *s = h->priv_data; - URLContext *hd = NULL; - - proxy_path = getenv("http_proxy"); - use_proxy = (proxy_path != NULL) && !getenv("no_proxy") && - av_strstart(proxy_path, "http://", NULL); /* fill the dest addr */ redo: @@ -117,6 +123,10 @@ static int http_open_cnx(URLContext *h) path1, sizeof(path1), s->location); ff_url_join(hoststr, sizeof(hoststr), NULL, NULL, hostname, port, NULL); + 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); + if (!strcmp(proto, "https")) { lower_proto = "tls"; use_proxy = 0; @@ -144,12 +154,10 @@ static int http_open_cnx(URLContext *h) ff_url_join(buf, sizeof(buf), lower_proto, NULL, hostname, port, NULL); if (!s->hd) { - err = ffurl_open(&hd, buf, AVIO_FLAG_READ_WRITE, + err = ffurl_open(&s->hd, buf, AVIO_FLAG_READ_WRITE, &h->interrupt_callback, NULL); if (err < 0) goto fail; - - s->hd = hd; } cur_auth_type = s->auth_state.auth_type; @@ -160,7 +168,8 @@ static int http_open_cnx(URLContext *h) if (s->http_code == 401) { if ((cur_auth_type == HTTP_AUTH_NONE || s->auth_state.stale) && s->auth_state.auth_type != HTTP_AUTH_NONE && attempts < 4) { - ffurl_close(hd); + ffurl_close(s->hd); + s->hd = NULL; goto redo; } else goto fail; @@ -168,7 +177,8 @@ static int http_open_cnx(URLContext *h) if (s->http_code == 407) { if ((cur_proxy_auth_type == HTTP_AUTH_NONE || s->proxy_auth_state.stale) && s->proxy_auth_state.auth_type != HTTP_AUTH_NONE && attempts < 4) { - ffurl_close(hd); + ffurl_close(s->hd); + s->hd = NULL; goto redo; } else goto fail; @@ -176,7 +186,8 @@ static int http_open_cnx(URLContext *h) if ((s->http_code == 301 || s->http_code == 302 || s->http_code == 303 || s->http_code == 307) && location_changed == 1) { /* url moved, get next */ - ffurl_close(hd); + ffurl_close(s->hd); + s->hd = NULL; if (redirects++ >= MAX_REDIRECTS) return AVERROR(EIO); /* Restart the authentication process with the new target, which @@ -188,8 +199,8 @@ static int http_open_cnx(URLContext *h) } return 0; fail: - if (hd) - ffurl_close(hd); + if (s->hd) + ffurl_close(s->hd); s->hd = NULL; return AVERROR(EIO); } @@ -227,7 +238,7 @@ static int http_getc(HTTPContext *s) if (s->buf_ptr >= s->buf_end) { len = ffurl_read(s->hd, s->buffer, BUFFER_SIZE); if (len < 0) { - return AVERROR(EIO); + return len; } else if (len == 0) { return -1; } else { @@ -247,7 +258,7 @@ static int http_get_line(HTTPContext *s, char *line, int line_size) for(;;) { ch = http_getc(s); if (ch < 0) - return AVERROR(EIO); + return ch; if (ch == '\n') { /* process line */ if (q > line && q[-1] == '\r') @@ -276,9 +287,9 @@ static int process_line(URLContext *h, char *line, int line_count, p = line; if (line_count == 0) { - while (!isspace(*p) && *p != '\0') + while (!av_isspace(*p) && *p != '\0') p++; - while (isspace(*p)) + while (av_isspace(*p)) p++; s->http_code = strtol(p, &end, 10); @@ -303,21 +314,21 @@ static int process_line(URLContext *h, char *line, int line_count, *p = '\0'; tag = line; p++; - while (isspace(*p)) + while (av_isspace(*p)) p++; if (!av_strcasecmp(tag, "Location")) { - strcpy(s->location, p); + av_strlcpy(s->location, p, sizeof(s->location)); *new_location = 1; } else if (!av_strcasecmp (tag, "Content-Length") && s->filesize == -1) { - s->filesize = atoll(p); + s->filesize = strtoll(p, NULL, 10); } else if (!av_strcasecmp (tag, "Content-Range")) { /* "bytes $from-$to/$document_size" */ const char *slash; if (!strncmp (p, "bytes ", 6)) { p += 6; - s->off = atoll(p); + s->off = strtoll(p, NULL, 10); if ((slash = strchr(p, '/')) && strlen(slash) > 0) - s->filesize = atoll(slash+1); + s->filesize = strtoll(slash+1, NULL, 10); } h->is_streamed = 0; /* we _can_ in fact seek */ } else if (!av_strcasecmp(tag, "Accept-Ranges") && !strncmp(p, "bytes", 5)) { @@ -334,6 +345,31 @@ static int process_line(URLContext *h, char *line, int line_count, } else if (!av_strcasecmp (tag, "Connection")) { if (!strcmp(p, "close")) s->willclose = 1; + } 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); + } } } return 1; @@ -350,12 +386,14 @@ static inline int has_header(const char *str, const char *header) static int http_read_header(URLContext *h, int *new_location) { HTTPContext *s = h->priv_data; - char line[1024]; + char line[MAX_URL_SIZE]; int err = 0; + s->chunksize = -1; + for (;;) { - if (http_get_line(s, line, sizeof(line)) < 0) - return AVERROR(EIO); + if ((err = http_get_line(s, line, sizeof(line))) < 0) + return err; av_dlog(NULL, "header='%s'\n", line); @@ -447,8 +485,8 @@ static int http_connect(URLContext *h, const char *path, const char *local_path, av_freep(&authstr); av_freep(&proxyauthstr); - if (ffurl_write(s->hd, s->buffer, strlen(s->buffer)) < 0) - return AVERROR(EIO); + if ((err = ffurl_write(s->hd, s->buffer, strlen(s->buffer))) < 0) + return err; if (s->post_data) if ((err = ffurl_write(s->hd, s->post_data, s->post_datalen)) < 0) @@ -470,7 +508,6 @@ static int http_connect(URLContext *h, const char *path, const char *local_path, s->http_code = 200; return 0; } - s->chunksize = -1; /* wait for header */ err = http_read_header(h, new_location); @@ -505,19 +542,50 @@ static int http_buf_read(URLContext *h, uint8_t *buf, int size) return len; } +#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 + static int http_read(URLContext *h, uint8_t *buf, int size) { HTTPContext *s = h->priv_data; int err, new_location; - if (s->end_chunked_post) { - if (!s->end_header) { - err = http_read_header(h, &new_location); - if (err < 0) - return err; - } + if (!s->hd) + return AVERROR_EOF; - return http_buf_read(h, buf, size); + if (s->end_chunked_post && !s->end_header) { + err = http_read_header(h, &new_location); + if (err < 0) + return err; } if (s->chunksize >= 0) { @@ -526,8 +594,8 @@ static int http_read(URLContext *h, uint8_t *buf, int size) for(;;) { do { - if (http_get_line(s, line, sizeof(line)) < 0) - return AVERROR(EIO); + if ((err = http_get_line(s, line, sizeof(line))) < 0) + return err; } while (!*line); /* skip CR LF from last chunk */ s->chunksize = strtoll(line, NULL, 16); @@ -541,6 +609,10 @@ static int http_read(URLContext *h, uint8_t *buf, int size) } size = FFMIN(size, s->chunksize); } +#if CONFIG_ZLIB + if (s->compressed) + return http_buf_read_compressed(h, buf, size); +#endif return http_buf_read(h, buf, size); } @@ -592,6 +664,11 @@ static int http_close(URLContext *h) int ret = 0; HTTPContext *s = h->priv_data; +#if CONFIG_ZLIB + inflateEnd(&s->inflate_stream); + av_freep(&s->inflate_buffer); +#endif + if (!s->end_chunked_post) { /* Close the write direction by sending the end of chunked encoding. */ ret = http_shutdown(h, h->flags); @@ -669,6 +746,7 @@ URLProtocol ff_https_protocol = { .url_seek = http_seek, .url_close = http_close, .url_get_file_handle = http_get_file_handle, + .url_shutdown = http_shutdown, .priv_data_size = sizeof(HTTPContext), .priv_data_class = &https_context_class, .flags = URL_PROTOCOL_FLAG_NETWORK,