http.c 27.3 KB
Newer Older
1
/*
2
 * HTTP protocol for avconv client
3
 * Copyright (c) 2000, 2001 Fabrice Bellard
4
 *
5
 * This file is part of Libav.
6
 *
7
 * Libav is free software; you can redistribute it and/or
8 9 10 11
 * 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.
 *
12
 * Libav is distributed in the hope that it will be useful,
13 14 15 16 17
 * 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
18
 * License along with Libav; if not, write to the Free Software
19 20
 * 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

36
/* XXX: POST protocol is not completely implemented because avconv uses
Diego Biurrun's avatar
Diego Biurrun committed
37
   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
    int64_t off, filesize;
54
    char location[MAX_URL_SIZE];
55
    HTTPAuthState auth_state;
56
    HTTPAuthState proxy_auth_state;
57
    char *headers;
58
    int willclose;          /**< Set if the server correctly handles Connection: close and will close the connection after feeding us the content. */
59
    int chunked_post;
60
    int end_chunked_post;   /**< A flag which indicates if the end of chunked encoding has been sent. */
61
    int end_header;         /**< A flag which indicates we have finished to read POST reply. */
62
    int multiple_requests;  /**< A flag which indicates if we use persistent connections. */
63 64
    uint8_t *post_data;
    int post_datalen;
65 66 67 68 69
#if CONFIG_ZLIB
    int compressed;
    z_stream inflate_stream;
    uint8_t *inflate_buffer;
#endif
70
    AVDictionary *chained_options;
71 72
} HTTPContext;

73
#define OFFSET(x) offsetof(HTTPContext, x)
74 75
#define D AV_OPT_FLAG_DECODING_PARAM
#define E AV_OPT_FLAG_ENCODING_PARAM
76
static const AVOption options[] = {
77
{"chunked_post", "use chunked transfer-encoding for posts", OFFSET(chunked_post), AV_OPT_TYPE_INT, {.i64 = 1}, 0, 1, E },
78
{"headers", "custom HTTP headers, can override built in default headers", OFFSET(headers), AV_OPT_TYPE_STRING, { 0 }, 0, 0, D|E },
79
{"multiple_requests", "use persistent connections", OFFSET(multiple_requests), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, D|E },
80
{"post_data", "custom HTTP post data", OFFSET(post_data), AV_OPT_TYPE_BINARY, .flags = D|E },
81 82
{NULL}
};
83 84 85 86 87 88
#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,\
Mans Rullgard's avatar
Mans Rullgard committed
89
}
90

91 92 93
HTTP_CLASS(http);
HTTP_CLASS(https);

94 95 96
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);
97

98 99 100 101
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));
102 103 104
    memcpy(&((HTTPContext*)dest->priv_data)->proxy_auth_state,
           &((HTTPContext*)src->priv_data)->proxy_auth_state,
           sizeof(HTTPAuthState));
105 106
}

107
/* return non zero if error */
108
static int http_open_cnx(URLContext *h, AVDictionary **options)
109
{
110
    const char *path, *proxy_path, *lower_proto = "tcp", *local_path;
111
    char hostname[1024], hoststr[1024], proto[10];
112
    char auth[1024], proxyauth[1024] = "";
113 114
    char path1[MAX_URL_SIZE];
    char buf[1024], urlbuf[MAX_URL_SIZE];
115
    int port, use_proxy, err, location_changed = 0, redirects = 0, attempts = 0;
116
    HTTPAuthType cur_auth_type, cur_proxy_auth_type;
117 118 119 120 121
    HTTPContext *s = h->priv_data;

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

127 128 129 130
    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);

131 132
    if (!strcmp(proto, "https")) {
        lower_proto = "tls";
133
        use_proxy = 0;
134 135 136 137 138 139
        if (port < 0)
            port = 443;
    }
    if (port < 0)
        port = 80;

140 141 142 143 144
    if (path1[0] == '\0')
        path = "/";
    else
        path = path1;
    local_path = path;
145
    if (use_proxy) {
146 147 148 149 150 151 152
        /* 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);
153 154
    }

155
    ff_url_join(buf, sizeof(buf), lower_proto, NULL, hostname, port, NULL);
156

157
    if (!s->hd) {
158
        err = ffurl_open(&s->hd, buf, AVIO_FLAG_READ_WRITE,
159
                         &h->interrupt_callback, options);
160 161 162 163
        if (err < 0)
            goto fail;
    }

164
    cur_auth_type = s->auth_state.auth_type;
165 166
    cur_proxy_auth_type = s->auth_state.auth_type;
    if (http_connect(h, path, local_path, hoststr, auth, proxyauth, &location_changed) < 0)
167
        goto fail;
168
    attempts++;
169
    if (s->http_code == 401) {
170 171
        if ((cur_auth_type == HTTP_AUTH_NONE || s->auth_state.stale) &&
            s->auth_state.auth_type != HTTP_AUTH_NONE && attempts < 4) {
172 173
            ffurl_close(s->hd);
            s->hd = NULL;
174 175 176 177
            goto redo;
        } else
            goto fail;
    }
178
    if (s->http_code == 407) {
179 180
        if ((cur_proxy_auth_type == HTTP_AUTH_NONE || s->proxy_auth_state.stale) &&
            s->proxy_auth_state.auth_type != HTTP_AUTH_NONE && attempts < 4) {
181 182
            ffurl_close(s->hd);
            s->hd = NULL;
183 184 185 186
            goto redo;
        } else
            goto fail;
    }
187 188
    if ((s->http_code == 301 || s->http_code == 302 || s->http_code == 303 || s->http_code == 307)
        && location_changed == 1) {
189
        /* url moved, get next */
190 191
        ffurl_close(s->hd);
        s->hd = NULL;
192
        if (redirects++ >= MAX_REDIRECTS)
193
            return AVERROR(EIO);
194 195 196
        /* Restart the authentication process with the new target, which
         * might use a different auth mechanism. */
        memset(&s->auth_state, 0, sizeof(s->auth_state));
197
        attempts = 0;
198 199 200 201 202
        location_changed = 0;
        goto redo;
    }
    return 0;
 fail:
203 204
    if (s->hd)
        ffurl_close(s->hd);
205
    s->hd = NULL;
206
    return AVERROR(EIO);
207 208
}

209 210 211
int ff_http_do_new_request(URLContext *h, const char *uri)
{
    HTTPContext *s = h->priv_data;
212 213
    AVDictionary *options = NULL;
    int ret;
214 215 216 217

    s->off = 0;
    av_strlcpy(s->location, uri, sizeof(s->location));

218 219 220 221
    av_dict_copy(&options, s->chained_options, 0);
    ret = http_open_cnx(h, &options);
    av_dict_free(&options);
    return ret;
222 223
}

224 225
static int http_open(URLContext *h, const char *uri, int flags,
                     AVDictionary **options)
226
{
227
    HTTPContext *s = h->priv_data;
228
    int ret;
229 230 231 232

    h->is_streamed = 1;

    s->filesize = -1;
233
    av_strlcpy(s->location, uri, sizeof(s->location));
234 235
    if (options)
        av_dict_copy(&s->chained_options, *options, 0);
236

237 238 239
    if (s->headers) {
        int len = strlen(s->headers);
        if (len < 2 || strcmp("\r\n", s->headers + len - 2))
240
            av_log(h, AV_LOG_WARNING, "No trailing CRLF found in HTTP header.\n");
241 242
    }

243 244 245 246
    ret = http_open_cnx(h, options);
    if (ret < 0)
        av_dict_free(&s->chained_options);
    return ret;
247 248 249 250 251
}
static int http_getc(HTTPContext *s)
{
    int len;
    if (s->buf_ptr >= s->buf_end) {
252
        len = ffurl_read(s->hd, s->buffer, BUFFER_SIZE);
253
        if (len < 0) {
254
            return len;
255 256 257 258 259 260 261 262 263 264
        } else if (len == 0) {
            return -1;
        } else {
            s->buf_ptr = s->buffer;
            s->buf_end = s->buffer + len;
        }
    }
    return *s->buf_ptr++;
}

265 266 267 268 269 270 271 272 273
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)
274
            return ch;
275 276 277 278 279 280 281 282 283 284 285 286 287 288
        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;
        }
    }
}

289 290 291 292
static int process_line(URLContext *h, char *line, int line_count,
                        int *new_location)
{
    HTTPContext *s = h->priv_data;
293
    char *tag, *p, *end;
294 295

    /* end of header */
296 297
    if (line[0] == '\0') {
        s->end_header = 1;
298
        return 0;
299
    }
300 301 302

    p = line;
    if (line_count == 0) {
303
        while (!av_isspace(*p) && *p != '\0')
304
            p++;
305
        while (av_isspace(*p))
306
            p++;
307
        s->http_code = strtol(p, &end, 10);
308

Luca Barbato's avatar
Luca Barbato committed
309
        av_dlog(NULL, "http_code=%d\n", s->http_code);
310

311 312
        /* error codes are 4xx and 5xx, but regard 401 as a success, so we
         * don't abort until all headers have been parsed. */
313
        if (s->http_code >= 400 && s->http_code < 600 && (s->http_code != 401
314 315
            || s->auth_state.auth_type != HTTP_AUTH_NONE) &&
            (s->http_code != 407 || s->proxy_auth_state.auth_type != HTTP_AUTH_NONE)) {
316
            end += strspn(end, SPACE_CHARS);
317
            av_log(h, AV_LOG_WARNING, "HTTP error %d %s\n",
318
                   s->http_code, end);
Alex Beregszaszi's avatar
Alex Beregszaszi committed
319
            return -1;
320
        }
321 322 323 324 325 326 327 328 329
    } else {
        while (*p != '\0' && *p != ':')
            p++;
        if (*p != ':')
            return 1;

        *p = '\0';
        tag = line;
        p++;
330
        while (av_isspace(*p))
331
            p++;
332
        if (!av_strcasecmp(tag, "Location")) {
333
            av_strlcpy(s->location, p, sizeof(s->location));
334
            *new_location = 1;
335
        } else if (!av_strcasecmp (tag, "Content-Length") && s->filesize == -1) {
336
            s->filesize = strtoll(p, NULL, 10);
337
        } else if (!av_strcasecmp (tag, "Content-Range")) {
338 339 340 341
            /* "bytes $from-$to/$document_size" */
            const char *slash;
            if (!strncmp (p, "bytes ", 6)) {
                p += 6;
342
                s->off = strtoll(p, NULL, 10);
343
                if ((slash = strchr(p, '/')) && strlen(slash) > 0)
344
                    s->filesize = strtoll(slash+1, NULL, 10);
345 346
            }
            h->is_streamed = 0; /* we _can_ in fact seek */
347
        } else if (!av_strcasecmp(tag, "Accept-Ranges") && !strncmp(p, "bytes", 5)) {
348
            h->is_streamed = 0;
349
        } else if (!av_strcasecmp (tag, "Transfer-Encoding") && !av_strncasecmp(p, "chunked", 7)) {
350 351
            s->filesize = -1;
            s->chunksize = 0;
352
        } else if (!av_strcasecmp (tag, "WWW-Authenticate")) {
353
            ff_http_auth_handle_header(&s->auth_state, tag, p);
354
        } else if (!av_strcasecmp (tag, "Authentication-Info")) {
355
            ff_http_auth_handle_header(&s->auth_state, tag, p);
356 357
        } else if (!av_strcasecmp (tag, "Proxy-Authenticate")) {
            ff_http_auth_handle_header(&s->proxy_auth_state, tag, p);
358
        } else if (!av_strcasecmp (tag, "Connection")) {
359 360
            if (!strcmp(p, "close"))
                s->willclose = 1;
361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385
        } 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);
            }
386 387 388 389 390
        }
    }
    return 1;
}

391 392 393
static inline int has_header(const char *str, const char *header)
{
    /* header + 2 to skip over CRLF prefix. (make sure you have one!) */
394 395
    if (!str)
        return 0;
396 397 398
    return av_stristart(str, header + 2, NULL) || av_stristr(str, header);
}

399 400 401
static int http_read_header(URLContext *h, int *new_location)
{
    HTTPContext *s = h->priv_data;
402
    char line[MAX_URL_SIZE];
403 404
    int err = 0;

405 406
    s->chunksize = -1;

407
    for (;;) {
408 409
        if ((err = http_get_line(s, line, sizeof(line))) < 0)
            return err;
410 411 412 413 414 415 416 417 418 419 420 421 422 423

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

424 425 426
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)
427 428
{
    HTTPContext *s = h->priv_data;
429
    int post, err;
430
    char headers[1024] = "";
431
    char *authstr = NULL, *proxyauthstr = NULL;
432
    int64_t off = s->off;
433
    int len = 0;
434
    const char *method;
435 436 437


    /* send http header */
438
    post = h->flags & AVIO_FLAG_WRITE;
439 440 441 442 443 444 445 446

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

447 448 449 450 451
    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);
452 453 454 455 456 457 458 459

    /* 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);
460
    if (!has_header(s->headers, "\r\nRange: ") && !post)
461
        len += av_strlcatf(headers + len, sizeof(headers) - len,
462
                           "Range: bytes=%"PRId64"-\r\n", s->off);
463 464 465 466 467 468 469 470 471 472 473

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

474 475 476
    if (!has_header(s->headers, "\r\nHost: "))
        len += av_strlcatf(headers + len, sizeof(headers) - len,
                           "Host: %s\r\n", hoststr);
477 478 479
    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);
480 481

    /* now add in custom headers */
482 483
    if (s->headers)
        av_strlcpy(headers + len, s->headers, sizeof(headers) - len);
484

485 486
    snprintf(s->buffer, sizeof(s->buffer),
             "%s %s HTTP/1.1\r\n"
487
             "%s"
488
             "%s"
489
             "%s"
490
             "%s%s"
491
             "\r\n",
492
             method,
493
             path,
494
             post && s->chunked_post ? "Transfer-Encoding: chunked\r\n" : "",
495
             headers,
496 497
             authstr ? authstr : "",
             proxyauthstr ? "Proxy-" : "", proxyauthstr ? proxyauthstr : "");
498

499
    av_freep(&authstr);
500
    av_freep(&proxyauthstr);
501 502
    if ((err = ffurl_write(s->hd, s->buffer, strlen(s->buffer))) < 0)
        return err;
503

504 505 506 507
    if (s->post_data)
        if ((err = ffurl_write(s->hd, s->post_data, s->post_datalen)) < 0)
            return err;

508 509 510 511 512
    /* init input buffer */
    s->buf_ptr = s->buffer;
    s->buf_end = s->buffer;
    s->line_count = 0;
    s->off = 0;
513
    s->filesize = -1;
514
    s->willclose = 0;
515
    s->end_chunked_post = 0;
516
    s->end_header = 0;
517
    if (post && !s->post_data) {
518 519 520 521
        /* 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;
522 523 524 525
        return 0;
    }

    /* wait for header */
526 527 528
    err = http_read_header(h, new_location);
    if (err < 0)
        return err;
529 530 531 532 533

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


534
static int http_buf_read(URLContext *h, uint8_t *buf, int size)
535 536 537
{
    HTTPContext *s = h->priv_data;
    int len;
538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557
    /* 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;
}

558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589
#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

590 591 592
static int http_read(URLContext *h, uint8_t *buf, int size)
{
    HTTPContext *s = h->priv_data;
593 594
    int err, new_location;

595 596 597
    if (!s->hd)
        return AVERROR_EOF;

598 599 600 601
    if (s->end_chunked_post && !s->end_header) {
        err = http_read_header(h, &new_location);
        if (err < 0)
            return err;
602
    }
603

604 605 606 607 608 609
    if (s->chunksize >= 0) {
        if (!s->chunksize) {
            char line[32];

            for(;;) {
                do {
610 611
                    if ((err = http_get_line(s, line, sizeof(line))) < 0)
                        return err;
612 613 614 615
                } while (!*line);    /* skip CR LF from last chunk */

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

Luca Barbato's avatar
Luca Barbato committed
616
                av_dlog(NULL, "Chunked encoding data size: %"PRId64"'\n", s->chunksize);
617 618 619 620 621 622 623 624

                if (!s->chunksize)
                    return 0;
                break;
            }
        }
        size = FFMIN(size, s->chunksize);
    }
625 626 627 628
#if CONFIG_ZLIB
    if (s->compressed)
        return http_buf_read_compressed(h, buf, size);
#endif
629
    return http_buf_read(h, buf, size);
630 631 632
}

/* used only when posting data */
633
static int http_write(URLContext *h, const uint8_t *buf, int size)
634
{
635
    char temp[11] = "";  /* 32-bit hex + CRLF + nul */
636 637
    int ret;
    char crlf[] = "\r\n";
638
    HTTPContext *s = h->priv_data;
639

640
    if (!s->chunked_post) {
Martin Storsjö's avatar
Martin Storsjö committed
641
        /* non-chunked data is sent without any special encoding */
642
        return ffurl_write(s->hd, buf, size);
643 644 645 646 647 648
    }

    /* 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
649
        snprintf(temp, sizeof(temp), "%x\r\n", size);
650

651 652 653
        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)
654 655 656
            return ret;
    }
    return size;
657 658
}

659
static int http_shutdown(URLContext *h, int flags)
660
{
661 662
    int ret = 0;
    char footer[] = "0\r\n\r\n";
663
    HTTPContext *s = h->priv_data;
664 665

    /* signal end of chunked encoding if used */
666
    if ((flags & AVIO_FLAG_WRITE) && s->chunked_post) {
667
        ret = ffurl_write(s->hd, footer, sizeof(footer) - 1);
668
        ret = ret > 0 ? 0 : ret;
669 670 671 672 673 674 675 676 677 678 679
        s->end_chunked_post = 1;
    }

    return ret;
}

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

680 681 682 683 684
#if CONFIG_ZLIB
    inflateEnd(&s->inflate_stream);
    av_freep(&s->inflate_buffer);
#endif

685 686 687
    if (!s->end_chunked_post) {
        /* Close the write direction by sending the end of chunked encoding. */
        ret = http_shutdown(h, h->flags);
688 689
    }

690
    if (s->hd)
691
        ffurl_close(s->hd);
692
    av_dict_free(&s->chained_options);
693
    return ret;
694 695
}

696
static int64_t http_seek(URLContext *h, int64_t off, int whence)
697 698 699
{
    HTTPContext *s = h->priv_data;
    URLContext *old_hd = s->hd;
700
    int64_t old_off = s->off;
701 702
    uint8_t old_buf[BUFFER_SIZE];
    int old_buf_size;
703
    AVDictionary *options = NULL;
704 705 706 707 708 709 710

    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 */
711 712
    old_buf_size = s->buf_end - s->buf_ptr;
    memcpy(old_buf, s->buf_ptr, old_buf_size);
713 714 715 716 717 718 719 720
    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 */
721 722 723
    av_dict_copy(&options, s->chained_options, 0);
    if (http_open_cnx(h, &options) < 0) {
        av_dict_free(&options);
724 725 726
        memcpy(s->buffer, old_buf, old_buf_size);
        s->buf_ptr = s->buffer;
        s->buf_end = s->buffer + old_buf_size;
727 728 729 730
        s->hd = old_hd;
        s->off = old_off;
        return -1;
    }
731
    av_dict_free(&options);
732
    ffurl_close(old_hd);
733 734 735
    return off;
}

736 737 738 739
static int
http_get_file_handle(URLContext *h)
{
    HTTPContext *s = h->priv_data;
740
    return ffurl_get_file_handle(s->hd);
741 742
}

743
#if CONFIG_HTTP_PROTOCOL
744
URLProtocol ff_http_protocol = {
745
    .name                = "http",
746
    .url_open2           = http_open,
747 748 749 750
    .url_read            = http_read,
    .url_write           = http_write,
    .url_seek            = http_seek,
    .url_close           = http_close,
751
    .url_get_file_handle = http_get_file_handle,
752
    .url_shutdown        = http_shutdown,
753
    .priv_data_size      = sizeof(HTTPContext),
754
    .priv_data_class     = &http_context_class,
755
    .flags               = URL_PROTOCOL_FLAG_NETWORK,
756
};
757 758 759 760
#endif
#if CONFIG_HTTPS_PROTOCOL
URLProtocol ff_https_protocol = {
    .name                = "https",
761
    .url_open2           = http_open,
762 763 764 765 766
    .url_read            = http_read,
    .url_write           = http_write,
    .url_seek            = http_seek,
    .url_close           = http_close,
    .url_get_file_handle = http_get_file_handle,
767
    .url_shutdown        = http_shutdown,
768
    .priv_data_size      = sizeof(HTTPContext),
769
    .priv_data_class     = &https_context_class,
770
    .flags               = URL_PROTOCOL_FLAG_NETWORK,
771 772
};
#endif
773 774 775 776 777 778 779 780 781 782 783 784 785 786 787

#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;
788
    char lower_url[100];
789
    int port, ret = 0, attempts = 0;
790 791
    HTTPAuthType cur_auth_type;
    char *authstr;
792
    int new_loc;
793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832

    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;

833 834 835 836 837 838 839 840 841 842 843 844
    /* 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;
845

846 847 848 849
    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) {
850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877
        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),
878
    .flags               = URL_PROTOCOL_FLAG_NETWORK,
879 880
};
#endif