http.c 37.8 KB
Newer Older
1 2
/*
 * HTTP protocol for ffmpeg client
3
 * Copyright (c) 2000, 2001 Fabrice Bellard
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
 *
 * 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
 */
21 22

#include "libavutil/avstring.h"
23
#include "avformat.h"
24
#include "internal.h"
25
#include "network.h"
26
#include "http.h"
27
#include "os_support.h"
28
#include "httpauth.h"
29
#include "url.h"
30
#include "libavutil/opt.h"
31

32 33 34 35
#if CONFIG_ZLIB
#include <zlib.h>
#endif

Diego Biurrun's avatar
Diego Biurrun committed
36 37
/* XXX: POST protocol is not completely implemented because ffmpeg uses
   only a subset of it. */
38

39 40 41 42 43
/* 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
44 45 46
#define MAX_REDIRECTS 8

typedef struct {
47
    const AVClass *class;
48 49 50 51
    URLContext *hd;
    unsigned char buffer[BUFFER_SIZE], *buf_ptr, *buf_end;
    int line_count;
    int http_code;
52
    int64_t chunksize;      /**< Used if "Transfer-Encoding: chunked" otherwise -1. */
53
    char *content_type;
54
    char *user_agent;
55
    int64_t off, filesize;
56 57
    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
58
    char *location;
59
    HTTPAuthState auth_state;
60
    HTTPAuthState proxy_auth_state;
61
    char *headers;
62
    int willclose;          /**< Set if the server correctly handles Connection: close and will close the connection after feeding us the content. */
63
    int seekable;           /**< Control seekability, 0 = disable, 1 = enable, -1 = probe. */
64
    int chunked_post;
65
    int end_chunked_post;   /**< A flag which indicates if the end of chunked encoding has been sent. */
66
    int end_header;         /**< A flag which indicates we have finished to read POST reply. */
67
    int multiple_requests;  /**< A flag which indicates if we use persistent connections. */
68 69
    uint8_t *post_data;
    int post_datalen;
70
    int is_akamai;
71
    int is_mediagateway;
72
    char *mime_type;
73
    char *cookies;          ///< holds newline (\n) delimited Set-Cookie header field values (without the "Set-Cookie: " field name)
74 75 76
    int icy;
    char *icy_metadata_headers;
    char *icy_metadata_packet;
77 78 79 80 81
#if CONFIG_ZLIB
    int compressed;
    z_stream inflate_stream;
    uint8_t *inflate_buffer;
#endif
82
    AVDictionary *chained_options;
83
    int send_expect_100;
84 85
} HTTPContext;

86
#define OFFSET(x) offsetof(HTTPContext, x)
87 88
#define D AV_OPT_FLAG_DECODING_PARAM
#define E AV_OPT_FLAG_ENCODING_PARAM
89
#define DEFAULT_USER_AGENT "Lavf/" AV_STRINGIFY(LIBAVFORMAT_VERSION)
90
static const AVOption options[] = {
91
{"seekable", "control seekability of connection", OFFSET(seekable), AV_OPT_TYPE_INT, {.i64 = -1}, -1, 1, D },
92
{"chunked_post", "use chunked transfer-encoding for posts", OFFSET(chunked_post), AV_OPT_TYPE_INT, {.i64 = 1}, 0, 1, E },
93
{"headers", "set custom HTTP headers, can override built in default headers", OFFSET(headers), AV_OPT_TYPE_STRING, { 0 }, 0, 0, D|E },
94
{"content_type", "force a content type", OFFSET(content_type), AV_OPT_TYPE_STRING, { 0 }, 0, 0, D|E },
95
{"user-agent", "override User-Agent header", OFFSET(user_agent), AV_OPT_TYPE_STRING, {.str = DEFAULT_USER_AGENT}, 0, 0, D },
96
{"multiple_requests", "use persistent connections", OFFSET(multiple_requests), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, D|E },
97 98
{"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 },
99
{"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, D },
100 101 102
{"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 },
103 104 105
{"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" },
106
{"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 },
107
{"location", "The actual location of the data received", OFFSET(location), AV_OPT_TYPE_STRING, { 0 }, 0, 0, D|E },
108 109
{NULL}
};
110 111 112 113 114 115
#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,\
116
}
117

118 119 120
HTTP_CLASS(http);
HTTP_CLASS(https);

121 122 123
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);
124

125 126 127 128
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));
129 130 131
    memcpy(&((HTTPContext*)dest->priv_data)->proxy_auth_state,
           &((HTTPContext*)src->priv_data)->proxy_auth_state,
           sizeof(HTTPAuthState));
132 133
}

134
/* return non zero if error */
135
static int http_open_cnx(URLContext *h, AVDictionary **options)
136
{
137
    const char *path, *proxy_path, *lower_proto = "tcp", *local_path;
138
    char hostname[1024], hoststr[1024], proto[10];
139
    char auth[1024], proxyauth[1024] = "";
140 141
    char path1[MAX_URL_SIZE];
    char buf[1024], urlbuf[MAX_URL_SIZE];
142
    int port, use_proxy, err, location_changed = 0, redirects = 0, attempts = 0;
143
    HTTPAuthType cur_auth_type, cur_proxy_auth_type;
144 145 146 147 148
    HTTPContext *s = h->priv_data;

    /* fill the dest addr */
 redo:
    /* needed in any case to build the host string */
149 150
    av_url_split(proto, sizeof(proto), auth, sizeof(auth),
                 hostname, sizeof(hostname), &port,
Martin Storsjö's avatar
Martin Storsjö committed
151
                 path1, sizeof(path1), s->location);
152
    ff_url_join(hoststr, sizeof(hoststr), NULL, NULL, hostname, port, NULL);
153

154 155 156 157
    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);

158 159
    if (!strcmp(proto, "https")) {
        lower_proto = "tls";
160
        use_proxy = 0;
161 162 163 164 165 166
        if (port < 0)
            port = 443;
    }
    if (port < 0)
        port = 80;

167 168 169 170 171
    if (path1[0] == '\0')
        path = "/";
    else
        path = path1;
    local_path = path;
172
    if (use_proxy) {
173 174 175 176 177 178 179
        /* 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);
180 181
    }

182
    ff_url_join(buf, sizeof(buf), lower_proto, NULL, hostname, port, NULL);
183

184
    if (!s->hd) {
185
        err = ffurl_open(&s->hd, buf, AVIO_FLAG_READ_WRITE,
186
                         &h->interrupt_callback, options);
187 188 189 190
        if (err < 0)
            goto fail;
    }

191
    cur_auth_type = s->auth_state.auth_type;
192 193
    cur_proxy_auth_type = s->auth_state.auth_type;
    if (http_connect(h, path, local_path, hoststr, auth, proxyauth, &location_changed) < 0)
194
        goto fail;
195
    attempts++;
196
    if (s->http_code == 401) {
197 198
        if ((cur_auth_type == HTTP_AUTH_NONE || s->auth_state.stale) &&
            s->auth_state.auth_type != HTTP_AUTH_NONE && attempts < 4) {
199
            ffurl_closep(&s->hd);
200 201 202 203
            goto redo;
        } else
            goto fail;
    }
204
    if (s->http_code == 407) {
205 206
        if ((cur_proxy_auth_type == HTTP_AUTH_NONE || s->proxy_auth_state.stale) &&
            s->proxy_auth_state.auth_type != HTTP_AUTH_NONE && attempts < 4) {
207
            ffurl_closep(&s->hd);
208 209 210 211
            goto redo;
        } else
            goto fail;
    }
212 213
    if ((s->http_code == 301 || s->http_code == 302 || s->http_code == 303 || s->http_code == 307)
        && location_changed == 1) {
214
        /* url moved, get next */
215
        ffurl_closep(&s->hd);
216
        if (redirects++ >= MAX_REDIRECTS)
217
            return AVERROR(EIO);
218 219 220
        /* Restart the authentication process with the new target, which
         * might use a different auth mechanism. */
        memset(&s->auth_state, 0, sizeof(s->auth_state));
221
        attempts = 0;
222 223 224 225 226
        location_changed = 0;
        goto redo;
    }
    return 0;
 fail:
227
    if (s->hd)
228
        ffurl_closep(&s->hd);
229
    return AVERROR(EIO);
230 231
}

232 233 234
int ff_http_do_new_request(URLContext *h, const char *uri)
{
    HTTPContext *s = h->priv_data;
235 236
    AVDictionary *options = NULL;
    int ret;
237 238

    s->off = 0;
239
    s->icy_data_read = 0;
240 241 242 243
    av_free(s->location);
    s->location = av_strdup(uri);
    if (!s->location)
        return AVERROR(ENOMEM);
244

245 246 247 248
    av_dict_copy(&options, s->chained_options, 0);
    ret = http_open_cnx(h, &options);
    av_dict_free(&options);
    return ret;
249 250
}

251 252
static int http_open(URLContext *h, const char *uri, int flags,
                     AVDictionary **options)
253
{
254
    HTTPContext *s = h->priv_data;
255
    int ret;
256

257 258 259 260
    if( s->seekable == 1 )
        h->is_streamed = 0;
    else
        h->is_streamed = 1;
261 262

    s->filesize = -1;
263 264 265
    s->location = av_strdup(uri);
    if (!s->location)
        return AVERROR(ENOMEM);
266 267
    if (options)
        av_dict_copy(&s->chained_options, *options, 0);
268

269 270 271
    if (s->headers) {
        int len = strlen(s->headers);
        if (len < 2 || strcmp("\r\n", s->headers + len - 2))
272
            av_log(h, AV_LOG_WARNING, "No trailing CRLF found in HTTP header.\n");
273 274
    }

275 276 277 278
    ret = http_open_cnx(h, options);
    if (ret < 0)
        av_dict_free(&s->chained_options);
    return ret;
279 280 281 282 283
}
static int http_getc(HTTPContext *s)
{
    int len;
    if (s->buf_ptr >= s->buf_end) {
284
        len = ffurl_read(s->hd, s->buffer, BUFFER_SIZE);
285
        if (len < 0) {
286
            return len;
287 288 289 290 291 292 293 294 295 296
        } else if (len == 0) {
            return -1;
        } else {
            s->buf_ptr = s->buffer;
            s->buf_end = s->buffer + len;
        }
    }
    return *s->buf_ptr++;
}

297 298 299 300 301 302 303 304 305
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)
306
            return ch;
307 308 309 310 311 312 313 314 315 316 317 318 319 320
        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;
        }
    }
}

321 322 323 324
static int process_line(URLContext *h, char *line, int line_count,
                        int *new_location)
{
    HTTPContext *s = h->priv_data;
325
    char *tag, *p, *end;
326 327

    /* end of header */
328 329
    if (line[0] == '\0') {
        s->end_header = 1;
330
        return 0;
331
    }
332 333 334

    p = line;
    if (line_count == 0) {
335
        while (!av_isspace(*p) && *p != '\0')
336
            p++;
337
        while (av_isspace(*p))
338
            p++;
339
        s->http_code = strtol(p, &end, 10);
340

341
        av_log(h, AV_LOG_DEBUG, "http_code=%d\n", s->http_code);
342

343 344
        /* error codes are 4xx and 5xx, but regard 401 as a success, so we
         * don't abort until all headers have been parsed. */
345
        if (s->http_code >= 400 && s->http_code < 600 && (s->http_code != 401
346 347
            || s->auth_state.auth_type != HTTP_AUTH_NONE) &&
            (s->http_code != 407 || s->proxy_auth_state.auth_type != HTTP_AUTH_NONE)) {
348
            end += strspn(end, SPACE_CHARS);
349
            av_log(h, AV_LOG_WARNING, "HTTP error %d %s\n",
350
                   s->http_code, end);
351
            return -1;
352
        }
353 354 355 356 357 358 359 360 361
    } else {
        while (*p != '\0' && *p != ':')
            p++;
        if (*p != ':')
            return 1;

        *p = '\0';
        tag = line;
        p++;
362
        while (av_isspace(*p))
363
            p++;
364
        if (!av_strcasecmp(tag, "Location")) {
365
            char redirected_location[MAX_URL_SIZE], *new_loc;
366 367
            ff_make_absolute_url(redirected_location, sizeof(redirected_location),
                                 s->location, p);
368 369 370 371 372
            new_loc = av_strdup(redirected_location);
            if (!new_loc)
                return AVERROR(ENOMEM);
            av_free(s->location);
            s->location = new_loc;
373
            *new_location = 1;
374
        } else if (!av_strcasecmp (tag, "Content-Length") && s->filesize == -1) {
375
            s->filesize = strtoll(p, NULL, 10);
376
        } else if (!av_strcasecmp (tag, "Content-Range")) {
377 378 379 380
            /* "bytes $from-$to/$document_size" */
            const char *slash;
            if (!strncmp (p, "bytes ", 6)) {
                p += 6;
381
                s->off = strtoll(p, NULL, 10);
382
                if ((slash = strchr(p, '/')) && strlen(slash) > 0)
383
                    s->filesize = strtoll(slash+1, NULL, 10);
384
            }
385
            if (s->seekable == -1 && (!s->is_akamai || s->filesize != 2147483647))
386
                h->is_streamed = 0; /* we _can_ in fact seek */
387
        } else if (!av_strcasecmp(tag, "Accept-Ranges") && !strncmp(p, "bytes", 5) && s->seekable == -1) {
388
            h->is_streamed = 0;
389
        } else if (!av_strcasecmp (tag, "Transfer-Encoding") && !av_strncasecmp(p, "chunked", 7)) {
390 391
            s->filesize = -1;
            s->chunksize = 0;
392
        } else if (!av_strcasecmp (tag, "WWW-Authenticate")) {
393
            ff_http_auth_handle_header(&s->auth_state, tag, p);
394
        } else if (!av_strcasecmp (tag, "Authentication-Info")) {
395
            ff_http_auth_handle_header(&s->auth_state, tag, p);
396 397
        } else if (!av_strcasecmp (tag, "Proxy-Authenticate")) {
            ff_http_auth_handle_header(&s->proxy_auth_state, tag, p);
398
        } else if (!av_strcasecmp (tag, "Connection")) {
399 400
            if (!strcmp(p, "close"))
                s->willclose = 1;
401 402 403 404 405 406
        } else if (!av_strcasecmp (tag, "Server")) {
            if (!av_strcasecmp (p, "AkamaiGHost")) {
                s->is_akamai = 1;
            } else if (!av_strncasecmp (p, "MediaGateway", 12)) {
                s->is_mediagateway = 1;
            }
407
        } else if (!av_strcasecmp (tag, "Content-Type")) {
408
            av_free(s->mime_type); s->mime_type = av_strdup(p);
409 410 411 412 413 414 415 416 417 418 419 420 421 422
        } 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);
            }
423 424 425 426 427 428 429 430 431 432
        } 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;
433 434 435 436 437
        } 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);
438 439
                if (inflateInit2(&s->inflate_stream, 32 + 15) != Z_OK) {
                    av_log(h, AV_LOG_WARNING, "Error during zlib initialisation: %s\n",
440
                           s->inflate_stream.msg);
441 442 443 444 445 446
                    return AVERROR(ENOSYS);
                }
                if (zlibCompileFlags() & (1 << 17)) {
                    av_log(h, AV_LOG_WARNING, "Your zlib was compiled without gzip support.\n");
                    return AVERROR(ENOSYS);
                }
447
#else
448 449
                av_log(h, AV_LOG_WARNING, "Compressed (%s) content, need zlib with gzip support\n", p);
                return AVERROR(ENOSYS);
450
#endif
451 452 453 454 455
            } 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);
456
            }
457 458 459 460 461
        }
    }
    return 1;
}

462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487
/**
 * 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)) {
488
                av_free(cpath);
489 490
                cpath = av_strdup(&param[5]);
            } else if (!av_strncasecmp("domain=", param, 7)) {
491
                av_free(cdomain);
492 493 494 495 496 497 498
                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 {
499
                av_free(cvalue);
500 501 502
                cvalue = av_strdup(param);
            }
        }
503 504
        if (!cdomain)
            cdomain = av_strdup(domain);
505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558

        // 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;
}

559 560 561
static inline int has_header(const char *str, const char *header)
{
    /* header + 2 to skip over CRLF prefix. (make sure you have one!) */
562 563
    if (!str)
        return 0;
564 565 566
    return av_stristart(str, header + 2, NULL) || av_stristr(str, header);
}

567 568 569
static int http_read_header(URLContext *h, int *new_location)
{
    HTTPContext *s = h->priv_data;
570
    char line[MAX_URL_SIZE];
571 572
    int err = 0;

573 574
    s->chunksize = -1;

575
    for (;;) {
576 577
        if ((err = http_get_line(s, line, sizeof(line))) < 0)
            return err;
578

579
        av_log(h, AV_LOG_DEBUG, "header='%s'\n", line);
580 581 582 583 584 585 586 587 588

        err = process_line(h, line, s->line_count, new_location);
        if (err < 0)
            return err;
        if (err == 0)
            break;
        s->line_count++;
    }

589 590 591
    if (s->seekable == -1 && s->is_mediagateway && s->filesize == 2000000000)
        h->is_streamed = 1; /* we can in fact _not_ seek */

592 593 594
    return err;
}

595 596 597
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)
598 599
{
    HTTPContext *s = h->priv_data;
600
    int post, err;
601
    char headers[4096] = "";
602
    char *authstr = NULL, *proxyauthstr = NULL;
603
    int64_t off = s->off;
604
    int len = 0;
605
    const char *method;
606
    int send_expect_100 = 0;
607 608 609


    /* send http header */
610
    post = h->flags & AVIO_FLAG_WRITE;
611 612 613 614 615 616 617 618

    if (s->post_data) {
        /* force POST method and disable chunked encoding when
         * custom HTTP post data is set */
        post = 1;
        s->chunked_post = 0;
    }

619 620 621 622 623
    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);
624 625 626 627 628 629
    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. */
630 631
        if (auth && *auth &&
            s->auth_state.auth_type == HTTP_AUTH_NONE &&
632 633 634
            s->http_code != 401)
            send_expect_100 = 1;
    }
635 636 637

    /* set default headers if needed */
    if (!has_header(s->headers, "\r\nUser-Agent: "))
638
        len += av_strlcatf(headers + len, sizeof(headers) - len,
639
                           "User-Agent: %s\r\n", s->user_agent);
640 641 642
    if (!has_header(s->headers, "\r\nAccept: "))
        len += av_strlcpy(headers + len, "Accept: */*\r\n",
                          sizeof(headers) - len);
643
    // Note: we send this on purpose even when s->off is 0 when we're probing,
644 645
    // since it allows us to detect more reliably if a (non-conforming)
    // server supports seeking by analysing the reply headers.
646
    if (!has_header(s->headers, "\r\nRange: ") && !post && (s->off > 0 || s->seekable == -1))
647
        len += av_strlcatf(headers + len, sizeof(headers) - len,
648
                           "Range: bytes=%"PRId64"-\r\n", s->off);
649 650 651
    if (send_expect_100 && !has_header(s->headers, "\r\nExpect: "))
        len += av_strlcatf(headers + len, sizeof(headers) - len,
                           "Expect: 100-continue\r\n");
652 653 654 655 656 657 658 659 660 661 662

    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);
        }
    }

663 664 665
    if (!has_header(s->headers, "\r\nHost: "))
        len += av_strlcatf(headers + len, sizeof(headers) - len,
                           "Host: %s\r\n", hoststr);
666 667 668
    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);
669 670 671
    if (!has_header(s->headers, "\r\nContent-Type: ") && s->content_type)
        len += av_strlcatf(headers + len, sizeof(headers) - len,
                           "Content-Type: %s\r\n", s->content_type);
672 673 674 675 676 677 678 679
    if (!has_header(s->headers, "\r\nCookie: ") && 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);
        }
    }
680 681 682 683
    if (!has_header(s->headers, "\r\nIcy-MetaData: ") && s->icy) {
        len += av_strlcatf(headers + len, sizeof(headers) - len,
                           "Icy-MetaData: %d\r\n", 1);
    }
684 685

    /* now add in custom headers */
686 687
    if (s->headers)
        av_strlcpy(headers + len, s->headers, sizeof(headers) - len);
688

689 690
    snprintf(s->buffer, sizeof(s->buffer),
             "%s %s HTTP/1.1\r\n"
691
             "%s"
692
             "%s"
693
             "%s"
694
             "%s%s"
695
             "\r\n",
696
             method,
697
             path,
698
             post && s->chunked_post ? "Transfer-Encoding: chunked\r\n" : "",
699
             headers,
700 701
             authstr ? authstr : "",
             proxyauthstr ? "Proxy-" : "", proxyauthstr ? proxyauthstr : "");
702

703
    av_freep(&authstr);
704
    av_freep(&proxyauthstr);
705 706 707

    av_log(h, AV_LOG_DEBUG, "request: %s\n", s->buffer);

708 709
    if ((err = ffurl_write(s->hd, s->buffer, strlen(s->buffer))) < 0)
        return err;
710

711 712 713
    if (s->post_data)
        if ((err = ffurl_write(s->hd, s->post_data, s->post_datalen)) < 0)
            return err;
714 715 716 717 718 719

    /* init input buffer */
    s->buf_ptr = s->buffer;
    s->buf_end = s->buffer;
    s->line_count = 0;
    s->off = 0;
720
    s->icy_data_read = 0;
721
    s->filesize = -1;
722
    s->willclose = 0;
723
    s->end_chunked_post = 0;
724
    s->end_header = 0;
725
    if (post && !s->post_data && !send_expect_100) {
726 727 728 729
        /* 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;
730 731 732 733
        return 0;
    }

    /* wait for header */
734 735 736
    err = http_read_header(h, new_location);
    if (err < 0)
        return err;
737 738 739 740 741

    return (off == s->off) ? 0 : -1;
}


742
static int http_buf_read(URLContext *h, uint8_t *buf, int size)
743 744 745
{
    HTTPContext *s = h->priv_data;
    int len;
746 747 748 749 750 751 752 753 754 755 756 757 758 759
    /* 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;
760
        s->icy_data_read += len;
761 762 763 764 765 766
        if (s->chunksize > 0)
            s->chunksize -= len;
    }
    return len;
}

767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792
#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)
793
        av_log(h, AV_LOG_WARNING, "inflate return value: %d, %s\n", ret, s->inflate_stream.msg);
794 795 796 797 798

    return size - s->inflate_stream.avail_out;
}
#endif

799 800 801
static int http_read(URLContext *h, uint8_t *buf, int size)
{
    HTTPContext *s = h->priv_data;
802 803
    int err, new_location;

804 805
    if (!s->hd)
        return AVERROR_EOF;
806

807 808 809 810
    if (s->end_chunked_post && !s->end_header) {
        err = http_read_header(h, &new_location);
        if (err < 0)
            return err;
811
    }
812

813 814 815 816 817 818
    if (s->chunksize >= 0) {
        if (!s->chunksize) {
            char line[32];

            for(;;) {
                do {
819 820
                    if ((err = http_get_line(s, line, sizeof(line))) < 0)
                        return err;
821 822 823 824
                } while (!*line);    /* skip CR LF from last chunk */

                s->chunksize = strtoll(line, NULL, 16);

825
                av_dlog(NULL, "Chunked encoding data size: %"PRId64"'\n", s->chunksize);
826 827 828 829 830 831 832 833

                if (!s->chunksize)
                    return 0;
                break;
            }
        }
        size = FFMIN(size, s->chunksize);
    }
834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859
    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);
    }
860 861 862 863
#if CONFIG_ZLIB
    if (s->compressed)
        return http_buf_read_compressed(h, buf, size);
#endif
864
    return http_buf_read(h, buf, size);
865 866 867
}

/* used only when posting data */
868
static int http_write(URLContext *h, const uint8_t *buf, int size)
869
{
870
    char temp[11] = "";  /* 32-bit hex + CRLF + nul */
871 872
    int ret;
    char crlf[] = "\r\n";
873
    HTTPContext *s = h->priv_data;
874

875
    if (!s->chunked_post) {
876
        /* non-chunked data is sent without any special encoding */
877
        return ffurl_write(s->hd, buf, size);
878 879 880 881 882 883
    }

    /* silently ignore zero-size data since chunk encoding that would
     * signal EOF */
    if (size > 0) {
        /* upload data using chunked encoding */
Martin Storsjö's avatar
Martin Storsjö committed
884
        snprintf(temp, sizeof(temp), "%x\r\n", size);
885

886 887 888
        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)
889 890 891
            return ret;
    }
    return size;
892 893
}

894
static int http_shutdown(URLContext *h, int flags)
895
{
896 897
    int ret = 0;
    char footer[] = "0\r\n\r\n";
898
    HTTPContext *s = h->priv_data;
899 900

    /* signal end of chunked encoding if used */
901
    if ((flags & AVIO_FLAG_WRITE) && s->chunked_post) {
902
        ret = ffurl_write(s->hd, footer, sizeof(footer) - 1);
903
        ret = ret > 0 ? 0 : ret;
904 905 906 907 908 909 910 911 912 913 914
        s->end_chunked_post = 1;
    }

    return ret;
}

static int http_close(URLContext *h)
{
    int ret = 0;
    HTTPContext *s = h->priv_data;

915 916 917 918 919
#if CONFIG_ZLIB
    inflateEnd(&s->inflate_stream);
    av_freep(&s->inflate_buffer);
#endif

920 921 922
    if (!s->end_chunked_post) {
        /* Close the write direction by sending the end of chunked encoding. */
        ret = http_shutdown(h, h->flags);
923 924
    }

925
    if (s->hd)
926
        ffurl_closep(&s->hd);
927
    av_dict_free(&s->chained_options);
928
    return ret;
929 930
}

931
static int64_t http_seek(URLContext *h, int64_t off, int whence)
932 933 934
{
    HTTPContext *s = h->priv_data;
    URLContext *old_hd = s->hd;
935
    int64_t old_off = s->off;
936 937
    uint8_t old_buf[BUFFER_SIZE];
    int old_buf_size;
938
    AVDictionary *options = NULL;
939 940 941 942 943 944 945

    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 */
946 947
    old_buf_size = s->buf_end - s->buf_ptr;
    memcpy(old_buf, s->buf_ptr, old_buf_size);
948 949 950 951 952 953 954 955
    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 */
956 957 958
    av_dict_copy(&options, s->chained_options, 0);
    if (http_open_cnx(h, &options) < 0) {
        av_dict_free(&options);
959 960 961
        memcpy(s->buffer, old_buf, old_buf_size);
        s->buf_ptr = s->buffer;
        s->buf_end = s->buffer + old_buf_size;
962 963 964 965
        s->hd = old_hd;
        s->off = old_off;
        return -1;
    }
966
    av_dict_free(&options);
967
    ffurl_close(old_hd);
968 969 970
    return off;
}

971 972 973 974
static int
http_get_file_handle(URLContext *h)
{
    HTTPContext *s = h->priv_data;
975
    return ffurl_get_file_handle(s->hd);
976 977
}

978
#if CONFIG_HTTP_PROTOCOL
979
URLProtocol ff_http_protocol = {
980
    .name                = "http",
981
    .url_open2           = http_open,
982 983 984 985
    .url_read            = http_read,
    .url_write           = http_write,
    .url_seek            = http_seek,
    .url_close           = http_close,
986
    .url_get_file_handle = http_get_file_handle,
987
    .url_shutdown        = http_shutdown,
988
    .priv_data_size      = sizeof(HTTPContext),
989
    .priv_data_class     = &http_context_class,
990
    .flags               = URL_PROTOCOL_FLAG_NETWORK,
991
};
992 993 994 995
#endif
#if CONFIG_HTTPS_PROTOCOL
URLProtocol ff_https_protocol = {
    .name                = "https",
996
    .url_open2           = http_open,
997 998 999 1000 1001
    .url_read            = http_read,
    .url_write           = http_write,
    .url_seek            = http_seek,
    .url_close           = http_close,
    .url_get_file_handle = http_get_file_handle,
1002
    .url_shutdown        = http_shutdown,
1003
    .priv_data_size      = sizeof(HTTPContext),
1004
    .priv_data_class     = &https_context_class,
1005
    .flags               = URL_PROTOCOL_FLAG_NETWORK,
1006 1007
};
#endif
1008 1009 1010 1011 1012 1013

#if CONFIG_HTTPPROXY_PROTOCOL
static int http_proxy_close(URLContext *h)
{
    HTTPContext *s = h->priv_data;
    if (s->hd)
1014
        ffurl_closep(&s->hd);
1015 1016 1017 1018 1019 1020 1021 1022
    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;
1023
    char lower_url[100];
1024
    int port, ret = 0, attempts = 0;
1025 1026
    HTTPAuthType cur_auth_type;
    char *authstr;
1027
    int new_loc;
1028

1029 1030 1031 1032
    if( s->seekable == 1 )
        h->is_streamed = 0;
    else
        h->is_streamed = 1;
1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070

    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;

1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082
    /* 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;
1083

1084 1085 1086 1087
    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) {
1088
        ffurl_closep(&s->hd);
1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114
        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),
1115
    .flags               = URL_PROTOCOL_FLAG_NETWORK,
1116 1117
};
#endif