FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavformat/http.c
Date: 2024-02-29 09:57:37
Exec Total Coverage
Lines: 0 1129 0.0%
Functions: 0 51 0.0%
Branches: 0 903 0.0%

Line Branch Exec Source
1 /*
2 * HTTP protocol for ffmpeg client
3 * Copyright (c) 2000, 2001 Fabrice Bellard
4 *
5 * This file is part of FFmpeg.
6 *
7 * FFmpeg is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * FFmpeg is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with FFmpeg; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22 #include "config.h"
23 #include "config_components.h"
24
25 #include <time.h>
26 #if CONFIG_ZLIB
27 #include <zlib.h>
28 #endif /* CONFIG_ZLIB */
29
30 #include "libavutil/avassert.h"
31 #include "libavutil/avstring.h"
32 #include "libavutil/bprint.h"
33 #include "libavutil/getenv_utf8.h"
34 #include "libavutil/opt.h"
35 #include "libavutil/time.h"
36 #include "libavutil/parseutils.h"
37
38 #include "avformat.h"
39 #include "http.h"
40 #include "httpauth.h"
41 #include "internal.h"
42 #include "network.h"
43 #include "os_support.h"
44 #include "url.h"
45 #include "version.h"
46
47 /* XXX: POST protocol is not completely implemented because ffmpeg uses
48 * only a subset of it. */
49
50 /* The IO buffer size is unrelated to the max URL size in itself, but needs
51 * to be large enough to fit the full request headers (including long
52 * path names). */
53 #define BUFFER_SIZE (MAX_URL_SIZE + HTTP_HEADERS_SIZE)
54 #define MAX_REDIRECTS 8
55 #define MAX_CACHED_REDIRECTS 32
56 #define HTTP_SINGLE 1
57 #define HTTP_MUTLI 2
58 #define MAX_EXPIRY 19
59 #define WHITESPACES " \n\t\r"
60 typedef enum {
61 LOWER_PROTO,
62 READ_HEADERS,
63 WRITE_REPLY_HEADERS,
64 FINISH
65 }HandshakeState;
66
67 typedef struct HTTPContext {
68 const AVClass *class;
69 URLContext *hd;
70 unsigned char buffer[BUFFER_SIZE], *buf_ptr, *buf_end;
71 int line_count;
72 int http_code;
73 /* Used if "Transfer-Encoding: chunked" otherwise -1. */
74 uint64_t chunksize;
75 int chunkend;
76 uint64_t off, end_off, filesize;
77 char *uri;
78 char *location;
79 HTTPAuthState auth_state;
80 HTTPAuthState proxy_auth_state;
81 char *http_proxy;
82 char *headers;
83 char *mime_type;
84 char *http_version;
85 char *user_agent;
86 char *referer;
87 char *content_type;
88 /* Set if the server correctly handles Connection: close and will close
89 * the connection after feeding us the content. */
90 int willclose;
91 int seekable; /**< Control seekability, 0 = disable, 1 = enable, -1 = probe. */
92 int chunked_post;
93 /* A flag which indicates if the end of chunked encoding has been sent. */
94 int end_chunked_post;
95 /* A flag which indicates we have finished to read POST reply. */
96 int end_header;
97 /* A flag which indicates if we use persistent connections. */
98 int multiple_requests;
99 uint8_t *post_data;
100 int post_datalen;
101 int is_akamai;
102 int is_mediagateway;
103 char *cookies; ///< holds newline (\n) delimited Set-Cookie header field values (without the "Set-Cookie: " field name)
104 /* A dictionary containing cookies keyed by cookie name */
105 AVDictionary *cookie_dict;
106 int icy;
107 /* how much data was read since the last ICY metadata packet */
108 uint64_t icy_data_read;
109 /* after how many bytes of read data a new metadata packet will be found */
110 uint64_t icy_metaint;
111 char *icy_metadata_headers;
112 char *icy_metadata_packet;
113 AVDictionary *metadata;
114 #if CONFIG_ZLIB
115 int compressed;
116 z_stream inflate_stream;
117 uint8_t *inflate_buffer;
118 #endif /* CONFIG_ZLIB */
119 AVDictionary *chained_options;
120 /* -1 = try to send if applicable, 0 = always disabled, 1 = always enabled */
121 int send_expect_100;
122 char *method;
123 int reconnect;
124 int reconnect_at_eof;
125 int reconnect_on_network_error;
126 int reconnect_streamed;
127 int reconnect_delay_max;
128 char *reconnect_on_http_error;
129 int listen;
130 char *resource;
131 int reply_code;
132 int is_multi_client;
133 HandshakeState handshake_step;
134 int is_connected_server;
135 int short_seek_size;
136 int64_t expires;
137 char *new_location;
138 AVDictionary *redirect_cache;
139 uint64_t filesize_from_content_range;
140 } HTTPContext;
141
142 #define OFFSET(x) offsetof(HTTPContext, x)
143 #define D AV_OPT_FLAG_DECODING_PARAM
144 #define E AV_OPT_FLAG_ENCODING_PARAM
145 #define DEFAULT_USER_AGENT "Lavf/" AV_STRINGIFY(LIBAVFORMAT_VERSION)
146
147 static const AVOption options[] = {
148 { "seekable", "control seekability of connection", OFFSET(seekable), AV_OPT_TYPE_BOOL, { .i64 = -1 }, -1, 1, D },
149 { "chunked_post", "use chunked transfer-encoding for posts", OFFSET(chunked_post), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, E },
150 { "http_proxy", "set HTTP proxy to tunnel through", OFFSET(http_proxy), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, D | E },
151 { "headers", "set custom HTTP headers, can override built in default headers", OFFSET(headers), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, D | E },
152 { "content_type", "set a specific content type for the POST messages", OFFSET(content_type), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, D | E },
153 { "user_agent", "override User-Agent header", OFFSET(user_agent), AV_OPT_TYPE_STRING, { .str = DEFAULT_USER_AGENT }, 0, 0, D },
154 { "referer", "override referer header", OFFSET(referer), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, D },
155 { "multiple_requests", "use persistent connections", OFFSET(multiple_requests), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, D | E },
156 { "post_data", "set custom HTTP post data", OFFSET(post_data), AV_OPT_TYPE_BINARY, .flags = D | E },
157 { "mime_type", "export the MIME type", OFFSET(mime_type), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, AV_OPT_FLAG_EXPORT | AV_OPT_FLAG_READONLY },
158 { "http_version", "export the http response version", OFFSET(http_version), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, AV_OPT_FLAG_EXPORT | AV_OPT_FLAG_READONLY },
159 { "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, { .str = NULL }, 0, 0, D },
160 { "icy", "request ICY metadata", OFFSET(icy), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, D },
161 { "icy_metadata_headers", "return ICY metadata headers", OFFSET(icy_metadata_headers), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, AV_OPT_FLAG_EXPORT },
162 { "icy_metadata_packet", "return current ICY metadata packet", OFFSET(icy_metadata_packet), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, AV_OPT_FLAG_EXPORT },
163 { "metadata", "metadata read from the bitstream", OFFSET(metadata), AV_OPT_TYPE_DICT, {0}, 0, 0, AV_OPT_FLAG_EXPORT },
164 { "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, .unit = "auth_type"},
165 { "none", "No auth method set, autodetect", 0, AV_OPT_TYPE_CONST, { .i64 = HTTP_AUTH_NONE }, 0, 0, D | E, .unit = "auth_type"},
166 { "basic", "HTTP basic authentication", 0, AV_OPT_TYPE_CONST, { .i64 = HTTP_AUTH_BASIC }, 0, 0, D | E, .unit = "auth_type"},
167 { "send_expect_100", "Force sending an Expect: 100-continue header for POST", OFFSET(send_expect_100), AV_OPT_TYPE_BOOL, { .i64 = -1 }, -1, 1, E },
168 { "location", "The actual location of the data received", OFFSET(location), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, D | E },
169 { "offset", "initial byte offset", OFFSET(off), AV_OPT_TYPE_INT64, { .i64 = 0 }, 0, INT64_MAX, D },
170 { "end_offset", "try to limit the request to bytes preceding this offset", OFFSET(end_off), AV_OPT_TYPE_INT64, { .i64 = 0 }, 0, INT64_MAX, D },
171 { "method", "Override the HTTP method or set the expected HTTP method from a client", OFFSET(method), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, D | E },
172 { "reconnect", "auto reconnect after disconnect before EOF", OFFSET(reconnect), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, D },
173 { "reconnect_at_eof", "auto reconnect at EOF", OFFSET(reconnect_at_eof), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, D },
174 { "reconnect_on_network_error", "auto reconnect in case of tcp/tls error during connect", OFFSET(reconnect_on_network_error), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, D },
175 { "reconnect_on_http_error", "list of http status codes to reconnect on", OFFSET(reconnect_on_http_error), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, D },
176 { "reconnect_streamed", "auto reconnect streamed / non seekable streams", OFFSET(reconnect_streamed), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, D },
177 { "reconnect_delay_max", "max reconnect delay in seconds after which to give up", OFFSET(reconnect_delay_max), AV_OPT_TYPE_INT, { .i64 = 120 }, 0, UINT_MAX/1000/1000, D },
178 { "listen", "listen on HTTP", OFFSET(listen), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 2, D | E },
179 { "resource", "The resource requested by a client", OFFSET(resource), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, E },
180 { "reply_code", "The http status code to return to a client", OFFSET(reply_code), AV_OPT_TYPE_INT, { .i64 = 200}, INT_MIN, 599, E},
181 { "short_seek_size", "Threshold to favor readahead over seek.", OFFSET(short_seek_size), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, D },
182 { NULL }
183 };
184
185 static int http_connect(URLContext *h, const char *path, const char *local_path,
186 const char *hoststr, const char *auth,
187 const char *proxyauth);
188 static int http_read_header(URLContext *h);
189 static int http_shutdown(URLContext *h, int flags);
190
191 void ff_http_init_auth_state(URLContext *dest, const URLContext *src)
192 {
193 memcpy(&((HTTPContext *)dest->priv_data)->auth_state,
194 &((HTTPContext *)src->priv_data)->auth_state,
195 sizeof(HTTPAuthState));
196 memcpy(&((HTTPContext *)dest->priv_data)->proxy_auth_state,
197 &((HTTPContext *)src->priv_data)->proxy_auth_state,
198 sizeof(HTTPAuthState));
199 }
200
201 static int http_open_cnx_internal(URLContext *h, AVDictionary **options)
202 {
203 const char *path, *proxy_path, *lower_proto = "tcp", *local_path;
204 char *env_http_proxy, *env_no_proxy;
205 char *hashmark;
206 char hostname[1024], hoststr[1024], proto[10];
207 char auth[1024], proxyauth[1024] = "";
208 char path1[MAX_URL_SIZE], sanitized_path[MAX_URL_SIZE + 1];
209 char buf[1024], urlbuf[MAX_URL_SIZE];
210 int port, use_proxy, err = 0;
211 HTTPContext *s = h->priv_data;
212
213 av_url_split(proto, sizeof(proto), auth, sizeof(auth),
214 hostname, sizeof(hostname), &port,
215 path1, sizeof(path1), s->location);
216 ff_url_join(hoststr, sizeof(hoststr), NULL, NULL, hostname, port, NULL);
217
218 env_http_proxy = getenv_utf8("http_proxy");
219 proxy_path = s->http_proxy ? s->http_proxy : env_http_proxy;
220
221 env_no_proxy = getenv_utf8("no_proxy");
222 use_proxy = !ff_http_match_no_proxy(env_no_proxy, hostname) &&
223 proxy_path && av_strstart(proxy_path, "http://", NULL);
224 freeenv_utf8(env_no_proxy);
225
226 if (!strcmp(proto, "https")) {
227 lower_proto = "tls";
228 use_proxy = 0;
229 if (port < 0)
230 port = 443;
231 /* pass http_proxy to underlying protocol */
232 if (s->http_proxy) {
233 err = av_dict_set(options, "http_proxy", s->http_proxy, 0);
234 if (err < 0)
235 goto end;
236 }
237 }
238 if (port < 0)
239 port = 80;
240
241 hashmark = strchr(path1, '#');
242 if (hashmark)
243 *hashmark = '\0';
244
245 if (path1[0] == '\0') {
246 path = "/";
247 } else if (path1[0] == '?') {
248 snprintf(sanitized_path, sizeof(sanitized_path), "/%s", path1);
249 path = sanitized_path;
250 } else {
251 path = path1;
252 }
253 local_path = path;
254 if (use_proxy) {
255 /* Reassemble the request URL without auth string - we don't
256 * want to leak the auth to the proxy. */
257 ff_url_join(urlbuf, sizeof(urlbuf), proto, NULL, hostname, port, "%s",
258 path1);
259 path = urlbuf;
260 av_url_split(NULL, 0, proxyauth, sizeof(proxyauth),
261 hostname, sizeof(hostname), &port, NULL, 0, proxy_path);
262 }
263
264 ff_url_join(buf, sizeof(buf), lower_proto, NULL, hostname, port, NULL);
265
266 if (!s->hd) {
267 err = ffurl_open_whitelist(&s->hd, buf, AVIO_FLAG_READ_WRITE,
268 &h->interrupt_callback, options,
269 h->protocol_whitelist, h->protocol_blacklist, h);
270 }
271
272 end:
273 freeenv_utf8(env_http_proxy);
274 return err < 0 ? err : http_connect(
275 h, path, local_path, hoststr, auth, proxyauth);
276 }
277
278 static int http_should_reconnect(HTTPContext *s, int err)
279 {
280 const char *status_group;
281 char http_code[4];
282
283 switch (err) {
284 case AVERROR_HTTP_BAD_REQUEST:
285 case AVERROR_HTTP_UNAUTHORIZED:
286 case AVERROR_HTTP_FORBIDDEN:
287 case AVERROR_HTTP_NOT_FOUND:
288 case AVERROR_HTTP_OTHER_4XX:
289 status_group = "4xx";
290 break;
291
292 case AVERROR_HTTP_SERVER_ERROR:
293 status_group = "5xx";
294 break;
295
296 default:
297 return s->reconnect_on_network_error;
298 }
299
300 if (!s->reconnect_on_http_error)
301 return 0;
302
303 if (av_match_list(status_group, s->reconnect_on_http_error, ',') > 0)
304 return 1;
305
306 snprintf(http_code, sizeof(http_code), "%d", s->http_code);
307
308 return av_match_list(http_code, s->reconnect_on_http_error, ',') > 0;
309 }
310
311 static char *redirect_cache_get(HTTPContext *s)
312 {
313 AVDictionaryEntry *re;
314 int64_t expiry;
315 char *delim;
316
317 re = av_dict_get(s->redirect_cache, s->location, NULL, AV_DICT_MATCH_CASE);
318 if (!re) {
319 return NULL;
320 }
321
322 delim = strchr(re->value, ';');
323 if (!delim) {
324 return NULL;
325 }
326
327 expiry = strtoll(re->value, NULL, 10);
328 if (time(NULL) > expiry) {
329 return NULL;
330 }
331
332 return delim + 1;
333 }
334
335 static int redirect_cache_set(HTTPContext *s, const char *source, const char *dest, int64_t expiry)
336 {
337 char *value;
338 int ret;
339
340 value = av_asprintf("%"PRIi64";%s", expiry, dest);
341 if (!value) {
342 return AVERROR(ENOMEM);
343 }
344
345 ret = av_dict_set(&s->redirect_cache, source, value, AV_DICT_MATCH_CASE | AV_DICT_DONT_STRDUP_VAL);
346 if (ret < 0)
347 return ret;
348
349 return 0;
350 }
351
352 /* return non zero if error */
353 static int http_open_cnx(URLContext *h, AVDictionary **options)
354 {
355 HTTPAuthType cur_auth_type, cur_proxy_auth_type;
356 HTTPContext *s = h->priv_data;
357 int ret, attempts = 0, redirects = 0;
358 int reconnect_delay = 0;
359 uint64_t off;
360 char *cached;
361
362 redo:
363
364 cached = redirect_cache_get(s);
365 if (cached) {
366 av_free(s->location);
367 s->location = av_strdup(cached);
368 if (!s->location) {
369 ret = AVERROR(ENOMEM);
370 goto fail;
371 }
372 goto redo;
373 }
374
375 av_dict_copy(options, s->chained_options, 0);
376
377 cur_auth_type = s->auth_state.auth_type;
378 cur_proxy_auth_type = s->auth_state.auth_type;
379
380 off = s->off;
381 ret = http_open_cnx_internal(h, options);
382 if (ret < 0) {
383 if (!http_should_reconnect(s, ret) ||
384 reconnect_delay > s->reconnect_delay_max)
385 goto fail;
386
387 av_log(h, AV_LOG_WARNING, "Will reconnect at %"PRIu64" in %d second(s).\n", off, reconnect_delay);
388 ret = ff_network_sleep_interruptible(1000U * 1000 * reconnect_delay, &h->interrupt_callback);
389 if (ret != AVERROR(ETIMEDOUT))
390 goto fail;
391 reconnect_delay = 1 + 2 * reconnect_delay;
392
393 /* restore the offset (http_connect resets it) */
394 s->off = off;
395
396 ffurl_closep(&s->hd);
397 goto redo;
398 }
399
400 attempts++;
401 if (s->http_code == 401) {
402 if ((cur_auth_type == HTTP_AUTH_NONE || s->auth_state.stale) &&
403 s->auth_state.auth_type != HTTP_AUTH_NONE && attempts < 4) {
404 ffurl_closep(&s->hd);
405 goto redo;
406 } else
407 goto fail;
408 }
409 if (s->http_code == 407) {
410 if ((cur_proxy_auth_type == HTTP_AUTH_NONE || s->proxy_auth_state.stale) &&
411 s->proxy_auth_state.auth_type != HTTP_AUTH_NONE && attempts < 4) {
412 ffurl_closep(&s->hd);
413 goto redo;
414 } else
415 goto fail;
416 }
417 if ((s->http_code == 301 || s->http_code == 302 ||
418 s->http_code == 303 || s->http_code == 307 || s->http_code == 308) &&
419 s->new_location) {
420 /* url moved, get next */
421 ffurl_closep(&s->hd);
422 if (redirects++ >= MAX_REDIRECTS)
423 return AVERROR(EIO);
424
425 if (!s->expires) {
426 s->expires = (s->http_code == 301 || s->http_code == 308) ? INT64_MAX : -1;
427 }
428
429 if (s->expires > time(NULL) && av_dict_count(s->redirect_cache) < MAX_CACHED_REDIRECTS) {
430 redirect_cache_set(s, s->location, s->new_location, s->expires);
431 }
432
433 av_free(s->location);
434 s->location = s->new_location;
435 s->new_location = NULL;
436
437 /* Restart the authentication process with the new target, which
438 * might use a different auth mechanism. */
439 memset(&s->auth_state, 0, sizeof(s->auth_state));
440 attempts = 0;
441 goto redo;
442 }
443 return 0;
444
445 fail:
446 if (s->hd)
447 ffurl_closep(&s->hd);
448 if (ret < 0)
449 return ret;
450 return ff_http_averror(s->http_code, AVERROR(EIO));
451 }
452
453 int ff_http_do_new_request(URLContext *h, const char *uri) {
454 return ff_http_do_new_request2(h, uri, NULL);
455 }
456
457 int ff_http_do_new_request2(URLContext *h, const char *uri, AVDictionary **opts)
458 {
459 HTTPContext *s = h->priv_data;
460 AVDictionary *options = NULL;
461 int ret;
462 char hostname1[1024], hostname2[1024], proto1[10], proto2[10];
463 int port1, port2;
464
465 if (!h->prot ||
466 !(!strcmp(h->prot->name, "http") ||
467 !strcmp(h->prot->name, "https")))
468 return AVERROR(EINVAL);
469
470 av_url_split(proto1, sizeof(proto1), NULL, 0,
471 hostname1, sizeof(hostname1), &port1,
472 NULL, 0, s->location);
473 av_url_split(proto2, sizeof(proto2), NULL, 0,
474 hostname2, sizeof(hostname2), &port2,
475 NULL, 0, uri);
476 if (port1 != port2 || strncmp(hostname1, hostname2, sizeof(hostname2)) != 0) {
477 av_log(h, AV_LOG_ERROR, "Cannot reuse HTTP connection for different host: %s:%d != %s:%d\n",
478 hostname1, port1,
479 hostname2, port2
480 );
481 return AVERROR(EINVAL);
482 }
483
484 if (!s->end_chunked_post) {
485 ret = http_shutdown(h, h->flags);
486 if (ret < 0)
487 return ret;
488 }
489
490 if (s->willclose)
491 return AVERROR_EOF;
492
493 s->end_chunked_post = 0;
494 s->chunkend = 0;
495 s->off = 0;
496 s->icy_data_read = 0;
497
498 av_free(s->location);
499 s->location = av_strdup(uri);
500 if (!s->location)
501 return AVERROR(ENOMEM);
502
503 av_free(s->uri);
504 s->uri = av_strdup(uri);
505 if (!s->uri)
506 return AVERROR(ENOMEM);
507
508 if ((ret = av_opt_set_dict(s, opts)) < 0)
509 return ret;
510
511 av_log(s, AV_LOG_INFO, "Opening \'%s\' for %s\n", uri, h->flags & AVIO_FLAG_WRITE ? "writing" : "reading");
512 ret = http_open_cnx(h, &options);
513 av_dict_free(&options);
514 return ret;
515 }
516
517 int ff_http_averror(int status_code, int default_averror)
518 {
519 switch (status_code) {
520 case 400: return AVERROR_HTTP_BAD_REQUEST;
521 case 401: return AVERROR_HTTP_UNAUTHORIZED;
522 case 403: return AVERROR_HTTP_FORBIDDEN;
523 case 404: return AVERROR_HTTP_NOT_FOUND;
524 default: break;
525 }
526 if (status_code >= 400 && status_code <= 499)
527 return AVERROR_HTTP_OTHER_4XX;
528 else if (status_code >= 500)
529 return AVERROR_HTTP_SERVER_ERROR;
530 else
531 return default_averror;
532 }
533
534 static int http_write_reply(URLContext* h, int status_code)
535 {
536 int ret, body = 0, reply_code, message_len;
537 const char *reply_text, *content_type;
538 HTTPContext *s = h->priv_data;
539 char message[BUFFER_SIZE];
540 content_type = "text/plain";
541
542 if (status_code < 0)
543 body = 1;
544 switch (status_code) {
545 case AVERROR_HTTP_BAD_REQUEST:
546 case 400:
547 reply_code = 400;
548 reply_text = "Bad Request";
549 break;
550 case AVERROR_HTTP_FORBIDDEN:
551 case 403:
552 reply_code = 403;
553 reply_text = "Forbidden";
554 break;
555 case AVERROR_HTTP_NOT_FOUND:
556 case 404:
557 reply_code = 404;
558 reply_text = "Not Found";
559 break;
560 case 200:
561 reply_code = 200;
562 reply_text = "OK";
563 content_type = s->content_type ? s->content_type : "application/octet-stream";
564 break;
565 case AVERROR_HTTP_SERVER_ERROR:
566 case 500:
567 reply_code = 500;
568 reply_text = "Internal server error";
569 break;
570 default:
571 return AVERROR(EINVAL);
572 }
573 if (body) {
574 s->chunked_post = 0;
575 message_len = snprintf(message, sizeof(message),
576 "HTTP/1.1 %03d %s\r\n"
577 "Content-Type: %s\r\n"
578 "Content-Length: %"SIZE_SPECIFIER"\r\n"
579 "%s"
580 "\r\n"
581 "%03d %s\r\n",
582 reply_code,
583 reply_text,
584 content_type,
585 strlen(reply_text) + 6, // 3 digit status code + space + \r\n
586 s->headers ? s->headers : "",
587 reply_code,
588 reply_text);
589 } else {
590 s->chunked_post = 1;
591 message_len = snprintf(message, sizeof(message),
592 "HTTP/1.1 %03d %s\r\n"
593 "Content-Type: %s\r\n"
594 "Transfer-Encoding: chunked\r\n"
595 "%s"
596 "\r\n",
597 reply_code,
598 reply_text,
599 content_type,
600 s->headers ? s->headers : "");
601 }
602 av_log(h, AV_LOG_TRACE, "HTTP reply header: \n%s----\n", message);
603 if ((ret = ffurl_write(s->hd, message, message_len)) < 0)
604 return ret;
605 return 0;
606 }
607
608 static void handle_http_errors(URLContext *h, int error)
609 {
610 av_assert0(error < 0);
611 http_write_reply(h, error);
612 }
613
614 static int http_handshake(URLContext *c)
615 {
616 int ret, err;
617 HTTPContext *ch = c->priv_data;
618 URLContext *cl = ch->hd;
619 switch (ch->handshake_step) {
620 case LOWER_PROTO:
621 av_log(c, AV_LOG_TRACE, "Lower protocol\n");
622 if ((ret = ffurl_handshake(cl)) > 0)
623 return 2 + ret;
624 if (ret < 0)
625 return ret;
626 ch->handshake_step = READ_HEADERS;
627 ch->is_connected_server = 1;
628 return 2;
629 case READ_HEADERS:
630 av_log(c, AV_LOG_TRACE, "Read headers\n");
631 if ((err = http_read_header(c)) < 0) {
632 handle_http_errors(c, err);
633 return err;
634 }
635 ch->handshake_step = WRITE_REPLY_HEADERS;
636 return 1;
637 case WRITE_REPLY_HEADERS:
638 av_log(c, AV_LOG_TRACE, "Reply code: %d\n", ch->reply_code);
639 if ((err = http_write_reply(c, ch->reply_code)) < 0)
640 return err;
641 ch->handshake_step = FINISH;
642 return 1;
643 case FINISH:
644 return 0;
645 }
646 // this should never be reached.
647 return AVERROR(EINVAL);
648 }
649
650 static int http_listen(URLContext *h, const char *uri, int flags,
651 AVDictionary **options) {
652 HTTPContext *s = h->priv_data;
653 int ret;
654 char hostname[1024], proto[10];
655 char lower_url[100];
656 const char *lower_proto = "tcp";
657 int port;
658 av_url_split(proto, sizeof(proto), NULL, 0, hostname, sizeof(hostname), &port,
659 NULL, 0, uri);
660 if (!strcmp(proto, "https"))
661 lower_proto = "tls";
662 ff_url_join(lower_url, sizeof(lower_url), lower_proto, NULL, hostname, port,
663 NULL);
664 if ((ret = av_dict_set_int(options, "listen", s->listen, 0)) < 0)
665 goto fail;
666 if ((ret = ffurl_open_whitelist(&s->hd, lower_url, AVIO_FLAG_READ_WRITE,
667 &h->interrupt_callback, options,
668 h->protocol_whitelist, h->protocol_blacklist, h
669 )) < 0)
670 goto fail;
671 s->handshake_step = LOWER_PROTO;
672 if (s->listen == HTTP_SINGLE) { /* single client */
673 s->reply_code = 200;
674 while ((ret = http_handshake(h)) > 0);
675 }
676 fail:
677 av_dict_free(&s->chained_options);
678 av_dict_free(&s->cookie_dict);
679 return ret;
680 }
681
682 static int http_open(URLContext *h, const char *uri, int flags,
683 AVDictionary **options)
684 {
685 HTTPContext *s = h->priv_data;
686 int ret;
687
688 if( s->seekable == 1 )
689 h->is_streamed = 0;
690 else
691 h->is_streamed = 1;
692
693 s->filesize = UINT64_MAX;
694
695 s->location = av_strdup(uri);
696 if (!s->location)
697 return AVERROR(ENOMEM);
698
699 s->uri = av_strdup(uri);
700 if (!s->uri)
701 return AVERROR(ENOMEM);
702
703 if (options)
704 av_dict_copy(&s->chained_options, *options, 0);
705
706 if (s->headers) {
707 int len = strlen(s->headers);
708 if (len < 2 || strcmp("\r\n", s->headers + len - 2)) {
709 av_log(h, AV_LOG_WARNING,
710 "No trailing CRLF found in HTTP header. Adding it.\n");
711 ret = av_reallocp(&s->headers, len + 3);
712 if (ret < 0)
713 goto bail_out;
714 s->headers[len] = '\r';
715 s->headers[len + 1] = '\n';
716 s->headers[len + 2] = '\0';
717 }
718 }
719
720 if (s->listen) {
721 return http_listen(h, uri, flags, options);
722 }
723 ret = http_open_cnx(h, options);
724 bail_out:
725 if (ret < 0) {
726 av_dict_free(&s->chained_options);
727 av_dict_free(&s->cookie_dict);
728 av_dict_free(&s->redirect_cache);
729 av_freep(&s->new_location);
730 av_freep(&s->uri);
731 }
732 return ret;
733 }
734
735 static int http_accept(URLContext *s, URLContext **c)
736 {
737 int ret;
738 HTTPContext *sc = s->priv_data;
739 HTTPContext *cc;
740 URLContext *sl = sc->hd;
741 URLContext *cl = NULL;
742
743 av_assert0(sc->listen);
744 if ((ret = ffurl_alloc(c, s->filename, s->flags, &sl->interrupt_callback)) < 0)
745 goto fail;
746 cc = (*c)->priv_data;
747 if ((ret = ffurl_accept(sl, &cl)) < 0)
748 goto fail;
749 cc->hd = cl;
750 cc->is_multi_client = 1;
751 return 0;
752 fail:
753 if (c) {
754 ffurl_closep(c);
755 }
756 return ret;
757 }
758
759 static int http_getc(HTTPContext *s)
760 {
761 int len;
762 if (s->buf_ptr >= s->buf_end) {
763 len = ffurl_read(s->hd, s->buffer, BUFFER_SIZE);
764 if (len < 0) {
765 return len;
766 } else if (len == 0) {
767 return AVERROR_EOF;
768 } else {
769 s->buf_ptr = s->buffer;
770 s->buf_end = s->buffer + len;
771 }
772 }
773 return *s->buf_ptr++;
774 }
775
776 static int http_get_line(HTTPContext *s, char *line, int line_size)
777 {
778 int ch;
779 char *q;
780
781 q = line;
782 for (;;) {
783 ch = http_getc(s);
784 if (ch < 0)
785 return ch;
786 if (ch == '\n') {
787 /* process line */
788 if (q > line && q[-1] == '\r')
789 q--;
790 *q = '\0';
791
792 return 0;
793 } else {
794 if ((q - line) < line_size - 1)
795 *q++ = ch;
796 }
797 }
798 }
799
800 static int check_http_code(URLContext *h, int http_code, const char *end)
801 {
802 HTTPContext *s = h->priv_data;
803 /* error codes are 4xx and 5xx, but regard 401 as a success, so we
804 * don't abort until all headers have been parsed. */
805 if (http_code >= 400 && http_code < 600 &&
806 (http_code != 401 || s->auth_state.auth_type != HTTP_AUTH_NONE) &&
807 (http_code != 407 || s->proxy_auth_state.auth_type != HTTP_AUTH_NONE)) {
808 end += strspn(end, SPACE_CHARS);
809 av_log(h, AV_LOG_WARNING, "HTTP error %d %s\n", http_code, end);
810 return ff_http_averror(http_code, AVERROR(EIO));
811 }
812 return 0;
813 }
814
815 static int parse_location(HTTPContext *s, const char *p)
816 {
817 char redirected_location[MAX_URL_SIZE];
818 ff_make_absolute_url(redirected_location, sizeof(redirected_location),
819 s->location, p);
820 av_freep(&s->new_location);
821 s->new_location = av_strdup(redirected_location);
822 if (!s->new_location)
823 return AVERROR(ENOMEM);
824 return 0;
825 }
826
827 /* "bytes $from-$to/$document_size" */
828 static void parse_content_range(URLContext *h, const char *p)
829 {
830 HTTPContext *s = h->priv_data;
831 const char *slash;
832
833 if (!strncmp(p, "bytes ", 6)) {
834 p += 6;
835 s->off = strtoull(p, NULL, 10);
836 if ((slash = strchr(p, '/')) && strlen(slash) > 0)
837 s->filesize_from_content_range = strtoull(slash + 1, NULL, 10);
838 }
839 if (s->seekable == -1 && (!s->is_akamai || s->filesize != 2147483647))
840 h->is_streamed = 0; /* we _can_ in fact seek */
841 }
842
843 static int parse_content_encoding(URLContext *h, const char *p)
844 {
845 if (!av_strncasecmp(p, "gzip", 4) ||
846 !av_strncasecmp(p, "deflate", 7)) {
847 #if CONFIG_ZLIB
848 HTTPContext *s = h->priv_data;
849
850 s->compressed = 1;
851 inflateEnd(&s->inflate_stream);
852 if (inflateInit2(&s->inflate_stream, 32 + 15) != Z_OK) {
853 av_log(h, AV_LOG_WARNING, "Error during zlib initialisation: %s\n",
854 s->inflate_stream.msg);
855 return AVERROR(ENOSYS);
856 }
857 if (zlibCompileFlags() & (1 << 17)) {
858 av_log(h, AV_LOG_WARNING,
859 "Your zlib was compiled without gzip support.\n");
860 return AVERROR(ENOSYS);
861 }
862 #else
863 av_log(h, AV_LOG_WARNING,
864 "Compressed (%s) content, need zlib with gzip support\n", p);
865 return AVERROR(ENOSYS);
866 #endif /* CONFIG_ZLIB */
867 } else if (!av_strncasecmp(p, "identity", 8)) {
868 // The normal, no-encoding case (although servers shouldn't include
869 // the header at all if this is the case).
870 } else {
871 av_log(h, AV_LOG_WARNING, "Unknown content coding: %s\n", p);
872 }
873 return 0;
874 }
875
876 // Concat all Icy- header lines
877 static int parse_icy(HTTPContext *s, const char *tag, const char *p)
878 {
879 int len = 4 + strlen(p) + strlen(tag);
880 int is_first = !s->icy_metadata_headers;
881 int ret;
882
883 av_dict_set(&s->metadata, tag, p, 0);
884
885 if (s->icy_metadata_headers)
886 len += strlen(s->icy_metadata_headers);
887
888 if ((ret = av_reallocp(&s->icy_metadata_headers, len)) < 0)
889 return ret;
890
891 if (is_first)
892 *s->icy_metadata_headers = '\0';
893
894 av_strlcatf(s->icy_metadata_headers, len, "%s: %s\n", tag, p);
895
896 return 0;
897 }
898
899 static int parse_set_cookie_expiry_time(const char *exp_str, struct tm *buf)
900 {
901 char exp_buf[MAX_EXPIRY];
902 int i, j, exp_buf_len = MAX_EXPIRY-1;
903 char *expiry;
904
905 // strip off any punctuation or whitespace
906 for (i = 0, j = 0; exp_str[i] != '\0' && j < exp_buf_len; i++) {
907 if ((exp_str[i] >= '0' && exp_str[i] <= '9') ||
908 (exp_str[i] >= 'A' && exp_str[i] <= 'Z') ||
909 (exp_str[i] >= 'a' && exp_str[i] <= 'z')) {
910 exp_buf[j] = exp_str[i];
911 j++;
912 }
913 }
914 exp_buf[j] = '\0';
915 expiry = exp_buf;
916
917 // move the string beyond the day of week
918 while ((*expiry < '0' || *expiry > '9') && *expiry != '\0')
919 expiry++;
920
921 return av_small_strptime(expiry, "%d%b%Y%H%M%S", buf) ? 0 : AVERROR(EINVAL);
922 }
923
924 static int parse_set_cookie(const char *set_cookie, AVDictionary **dict)
925 {
926 char *param, *next_param, *cstr, *back;
927 char *saveptr = NULL;
928
929 if (!set_cookie[0])
930 return 0;
931
932 if (!(cstr = av_strdup(set_cookie)))
933 return AVERROR(EINVAL);
934
935 // strip any trailing whitespace
936 back = &cstr[strlen(cstr)-1];
937 while (strchr(WHITESPACES, *back)) {
938 *back='\0';
939 if (back == cstr)
940 break;
941 back--;
942 }
943
944 next_param = cstr;
945 while ((param = av_strtok(next_param, ";", &saveptr))) {
946 char *name, *value;
947 next_param = NULL;
948 param += strspn(param, WHITESPACES);
949 if ((name = av_strtok(param, "=", &value))) {
950 if (av_dict_set(dict, name, value, 0) < 0) {
951 av_free(cstr);
952 return -1;
953 }
954 }
955 }
956
957 av_free(cstr);
958 return 0;
959 }
960
961 static int parse_cookie(HTTPContext *s, const char *p, AVDictionary **cookies)
962 {
963 AVDictionary *new_params = NULL;
964 AVDictionaryEntry *e, *cookie_entry;
965 char *eql, *name;
966
967 // ensure the cookie is parsable
968 if (parse_set_cookie(p, &new_params))
969 return -1;
970
971 // if there is no cookie value there is nothing to parse
972 cookie_entry = av_dict_get(new_params, "", NULL, AV_DICT_IGNORE_SUFFIX);
973 if (!cookie_entry || !cookie_entry->value) {
974 av_dict_free(&new_params);
975 return -1;
976 }
977
978 // ensure the cookie is not expired or older than an existing value
979 if ((e = av_dict_get(new_params, "expires", NULL, 0)) && e->value) {
980 struct tm new_tm = {0};
981 if (!parse_set_cookie_expiry_time(e->value, &new_tm)) {
982 AVDictionaryEntry *e2;
983
984 // if the cookie has already expired ignore it
985 if (av_timegm(&new_tm) < av_gettime() / 1000000) {
986 av_dict_free(&new_params);
987 return 0;
988 }
989
990 // only replace an older cookie with the same name
991 e2 = av_dict_get(*cookies, cookie_entry->key, NULL, 0);
992 if (e2 && e2->value) {
993 AVDictionary *old_params = NULL;
994 if (!parse_set_cookie(p, &old_params)) {
995 e2 = av_dict_get(old_params, "expires", NULL, 0);
996 if (e2 && e2->value) {
997 struct tm old_tm = {0};
998 if (!parse_set_cookie_expiry_time(e->value, &old_tm)) {
999 if (av_timegm(&new_tm) < av_timegm(&old_tm)) {
1000 av_dict_free(&new_params);
1001 av_dict_free(&old_params);
1002 return -1;
1003 }
1004 }
1005 }
1006 }
1007 av_dict_free(&old_params);
1008 }
1009 }
1010 }
1011 av_dict_free(&new_params);
1012
1013 // duplicate the cookie name (dict will dupe the value)
1014 if (!(eql = strchr(p, '='))) return AVERROR(EINVAL);
1015 if (!(name = av_strndup(p, eql - p))) return AVERROR(ENOMEM);
1016
1017 // add the cookie to the dictionary
1018 av_dict_set(cookies, name, eql, AV_DICT_DONT_STRDUP_KEY);
1019
1020 return 0;
1021 }
1022
1023 static int cookie_string(AVDictionary *dict, char **cookies)
1024 {
1025 const AVDictionaryEntry *e = NULL;
1026 int len = 1;
1027
1028 // determine how much memory is needed for the cookies string
1029 while ((e = av_dict_iterate(dict, e)))
1030 len += strlen(e->key) + strlen(e->value) + 1;
1031
1032 // reallocate the cookies
1033 e = NULL;
1034 if (*cookies) av_free(*cookies);
1035 *cookies = av_malloc(len);
1036 if (!*cookies) return AVERROR(ENOMEM);
1037 *cookies[0] = '\0';
1038
1039 // write out the cookies
1040 while ((e = av_dict_iterate(dict, e)))
1041 av_strlcatf(*cookies, len, "%s%s\n", e->key, e->value);
1042
1043 return 0;
1044 }
1045
1046 static void parse_expires(HTTPContext *s, const char *p)
1047 {
1048 struct tm tm;
1049
1050 if (!parse_set_cookie_expiry_time(p, &tm)) {
1051 s->expires = av_timegm(&tm);
1052 }
1053 }
1054
1055 static void parse_cache_control(HTTPContext *s, const char *p)
1056 {
1057 char *age;
1058 int offset;
1059
1060 /* give 'Expires' higher priority over 'Cache-Control' */
1061 if (s->expires) {
1062 return;
1063 }
1064
1065 if (av_stristr(p, "no-cache") || av_stristr(p, "no-store")) {
1066 s->expires = -1;
1067 return;
1068 }
1069
1070 age = av_stristr(p, "s-maxage=");
1071 offset = 9;
1072 if (!age) {
1073 age = av_stristr(p, "max-age=");
1074 offset = 8;
1075 }
1076
1077 if (age) {
1078 s->expires = time(NULL) + atoi(p + offset);
1079 }
1080 }
1081
1082 static int process_line(URLContext *h, char *line, int line_count)
1083 {
1084 HTTPContext *s = h->priv_data;
1085 const char *auto_method = h->flags & AVIO_FLAG_READ ? "POST" : "GET";
1086 char *tag, *p, *end, *method, *resource, *version;
1087 int ret;
1088
1089 /* end of header */
1090 if (line[0] == '\0') {
1091 s->end_header = 1;
1092 return 0;
1093 }
1094
1095 p = line;
1096 if (line_count == 0) {
1097 if (s->is_connected_server) {
1098 // HTTP method
1099 method = p;
1100 while (*p && !av_isspace(*p))
1101 p++;
1102 *(p++) = '\0';
1103 av_log(h, AV_LOG_TRACE, "Received method: %s\n", method);
1104 if (s->method) {
1105 if (av_strcasecmp(s->method, method)) {
1106 av_log(h, AV_LOG_ERROR, "Received and expected HTTP method do not match. (%s expected, %s received)\n",
1107 s->method, method);
1108 return ff_http_averror(400, AVERROR(EIO));
1109 }
1110 } else {
1111 // use autodetected HTTP method to expect
1112 av_log(h, AV_LOG_TRACE, "Autodetected %s HTTP method\n", auto_method);
1113 if (av_strcasecmp(auto_method, method)) {
1114 av_log(h, AV_LOG_ERROR, "Received and autodetected HTTP method did not match "
1115 "(%s autodetected %s received)\n", auto_method, method);
1116 return ff_http_averror(400, AVERROR(EIO));
1117 }
1118 if (!(s->method = av_strdup(method)))
1119 return AVERROR(ENOMEM);
1120 }
1121
1122 // HTTP resource
1123 while (av_isspace(*p))
1124 p++;
1125 resource = p;
1126 while (*p && !av_isspace(*p))
1127 p++;
1128 *(p++) = '\0';
1129 av_log(h, AV_LOG_TRACE, "Requested resource: %s\n", resource);
1130 if (!(s->resource = av_strdup(resource)))
1131 return AVERROR(ENOMEM);
1132
1133 // HTTP version
1134 while (av_isspace(*p))
1135 p++;
1136 version = p;
1137 while (*p && !av_isspace(*p))
1138 p++;
1139 *p = '\0';
1140 if (av_strncasecmp(version, "HTTP/", 5)) {
1141 av_log(h, AV_LOG_ERROR, "Malformed HTTP version string.\n");
1142 return ff_http_averror(400, AVERROR(EIO));
1143 }
1144 av_log(h, AV_LOG_TRACE, "HTTP version string: %s\n", version);
1145 } else {
1146 if (av_strncasecmp(p, "HTTP/1.0", 8) == 0)
1147 s->willclose = 1;
1148 while (*p != '/' && *p != '\0')
1149 p++;
1150 while (*p == '/')
1151 p++;
1152 av_freep(&s->http_version);
1153 s->http_version = av_strndup(p, 3);
1154 while (!av_isspace(*p) && *p != '\0')
1155 p++;
1156 while (av_isspace(*p))
1157 p++;
1158 s->http_code = strtol(p, &end, 10);
1159
1160 av_log(h, AV_LOG_TRACE, "http_code=%d\n", s->http_code);
1161
1162 if ((ret = check_http_code(h, s->http_code, end)) < 0)
1163 return ret;
1164 }
1165 } else {
1166 while (*p != '\0' && *p != ':')
1167 p++;
1168 if (*p != ':')
1169 return 1;
1170
1171 *p = '\0';
1172 tag = line;
1173 p++;
1174 while (av_isspace(*p))
1175 p++;
1176 if (!av_strcasecmp(tag, "Location")) {
1177 if ((ret = parse_location(s, p)) < 0)
1178 return ret;
1179 } else if (!av_strcasecmp(tag, "Content-Length") &&
1180 s->filesize == UINT64_MAX) {
1181 s->filesize = strtoull(p, NULL, 10);
1182 } else if (!av_strcasecmp(tag, "Content-Range")) {
1183 parse_content_range(h, p);
1184 } else if (!av_strcasecmp(tag, "Accept-Ranges") &&
1185 !strncmp(p, "bytes", 5) &&
1186 s->seekable == -1) {
1187 h->is_streamed = 0;
1188 } else if (!av_strcasecmp(tag, "Transfer-Encoding") &&
1189 !av_strncasecmp(p, "chunked", 7)) {
1190 s->filesize = UINT64_MAX;
1191 s->chunksize = 0;
1192 } else if (!av_strcasecmp(tag, "WWW-Authenticate")) {
1193 ff_http_auth_handle_header(&s->auth_state, tag, p);
1194 } else if (!av_strcasecmp(tag, "Authentication-Info")) {
1195 ff_http_auth_handle_header(&s->auth_state, tag, p);
1196 } else if (!av_strcasecmp(tag, "Proxy-Authenticate")) {
1197 ff_http_auth_handle_header(&s->proxy_auth_state, tag, p);
1198 } else if (!av_strcasecmp(tag, "Connection")) {
1199 if (!strcmp(p, "close"))
1200 s->willclose = 1;
1201 } else if (!av_strcasecmp(tag, "Server")) {
1202 if (!av_strcasecmp(p, "AkamaiGHost")) {
1203 s->is_akamai = 1;
1204 } else if (!av_strncasecmp(p, "MediaGateway", 12)) {
1205 s->is_mediagateway = 1;
1206 }
1207 } else if (!av_strcasecmp(tag, "Content-Type")) {
1208 av_free(s->mime_type);
1209 s->mime_type = av_get_token((const char **)&p, ";");
1210 } else if (!av_strcasecmp(tag, "Set-Cookie")) {
1211 if (parse_cookie(s, p, &s->cookie_dict))
1212 av_log(h, AV_LOG_WARNING, "Unable to parse '%s'\n", p);
1213 } else if (!av_strcasecmp(tag, "Icy-MetaInt")) {
1214 s->icy_metaint = strtoull(p, NULL, 10);
1215 } else if (!av_strncasecmp(tag, "Icy-", 4)) {
1216 if ((ret = parse_icy(s, tag, p)) < 0)
1217 return ret;
1218 } else if (!av_strcasecmp(tag, "Content-Encoding")) {
1219 if ((ret = parse_content_encoding(h, p)) < 0)
1220 return ret;
1221 } else if (!av_strcasecmp(tag, "Expires")) {
1222 parse_expires(s, p);
1223 } else if (!av_strcasecmp(tag, "Cache-Control")) {
1224 parse_cache_control(s, p);
1225 }
1226 }
1227 return 1;
1228 }
1229
1230 /**
1231 * Create a string containing cookie values for use as a HTTP cookie header
1232 * field value for a particular path and domain from the cookie values stored in
1233 * the HTTP protocol context. The cookie string is stored in *cookies, and may
1234 * be NULL if there are no valid cookies.
1235 *
1236 * @return a negative value if an error condition occurred, 0 otherwise
1237 */
1238 static int get_cookies(HTTPContext *s, char **cookies, const char *path,
1239 const char *domain)
1240 {
1241 // cookie strings will look like Set-Cookie header field values. Multiple
1242 // Set-Cookie fields will result in multiple values delimited by a newline
1243 int ret = 0;
1244 char *cookie, *set_cookies, *next;
1245 char *saveptr = NULL;
1246
1247 // destroy any cookies in the dictionary.
1248 av_dict_free(&s->cookie_dict);
1249
1250 if (!s->cookies)
1251 return 0;
1252
1253 next = set_cookies = av_strdup(s->cookies);
1254 if (!next)
1255 return AVERROR(ENOMEM);
1256
1257 *cookies = NULL;
1258 while ((cookie = av_strtok(next, "\n", &saveptr)) && !ret) {
1259 AVDictionary *cookie_params = NULL;
1260 AVDictionaryEntry *cookie_entry, *e;
1261
1262 next = NULL;
1263 // store the cookie in a dict in case it is updated in the response
1264 if (parse_cookie(s, cookie, &s->cookie_dict))
1265 av_log(s, AV_LOG_WARNING, "Unable to parse '%s'\n", cookie);
1266
1267 // continue on to the next cookie if this one cannot be parsed
1268 if (parse_set_cookie(cookie, &cookie_params))
1269 goto skip_cookie;
1270
1271 // if the cookie has no value, skip it
1272 cookie_entry = av_dict_get(cookie_params, "", NULL, AV_DICT_IGNORE_SUFFIX);
1273 if (!cookie_entry || !cookie_entry->value)
1274 goto skip_cookie;
1275
1276 // if the cookie has expired, don't add it
1277 if ((e = av_dict_get(cookie_params, "expires", NULL, 0)) && e->value) {
1278 struct tm tm_buf = {0};
1279 if (!parse_set_cookie_expiry_time(e->value, &tm_buf)) {
1280 if (av_timegm(&tm_buf) < av_gettime() / 1000000)
1281 goto skip_cookie;
1282 }
1283 }
1284
1285 // if no domain in the cookie assume it appied to this request
1286 if ((e = av_dict_get(cookie_params, "domain", NULL, 0)) && e->value) {
1287 // find the offset comparison is on the min domain (b.com, not a.b.com)
1288 int domain_offset = strlen(domain) - strlen(e->value);
1289 if (domain_offset < 0)
1290 goto skip_cookie;
1291
1292 // match the cookie domain
1293 if (av_strcasecmp(&domain[domain_offset], e->value))
1294 goto skip_cookie;
1295 }
1296
1297 // if a cookie path is provided, ensure the request path is within that path
1298 e = av_dict_get(cookie_params, "path", NULL, 0);
1299 if (e && av_strncasecmp(path, e->value, strlen(e->value)))
1300 goto skip_cookie;
1301
1302 // cookie parameters match, so copy the value
1303 if (!*cookies) {
1304 *cookies = av_asprintf("%s=%s", cookie_entry->key, cookie_entry->value);
1305 } else {
1306 char *tmp = *cookies;
1307 *cookies = av_asprintf("%s; %s=%s", tmp, cookie_entry->key, cookie_entry->value);
1308 av_free(tmp);
1309 }
1310 if (!*cookies)
1311 ret = AVERROR(ENOMEM);
1312
1313 skip_cookie:
1314 av_dict_free(&cookie_params);
1315 }
1316
1317 av_free(set_cookies);
1318
1319 return ret;
1320 }
1321
1322 static inline int has_header(const char *str, const char *header)
1323 {
1324 /* header + 2 to skip over CRLF prefix. (make sure you have one!) */
1325 if (!str)
1326 return 0;
1327 return av_stristart(str, header + 2, NULL) || av_stristr(str, header);
1328 }
1329
1330 static int http_read_header(URLContext *h)
1331 {
1332 HTTPContext *s = h->priv_data;
1333 char line[MAX_URL_SIZE];
1334 int err = 0;
1335
1336 av_freep(&s->new_location);
1337 s->expires = 0;
1338 s->chunksize = UINT64_MAX;
1339 s->filesize_from_content_range = UINT64_MAX;
1340
1341 for (;;) {
1342 if ((err = http_get_line(s, line, sizeof(line))) < 0)
1343 return err;
1344
1345 av_log(h, AV_LOG_TRACE, "header='%s'\n", line);
1346
1347 err = process_line(h, line, s->line_count);
1348 if (err < 0)
1349 return err;
1350 if (err == 0)
1351 break;
1352 s->line_count++;
1353 }
1354
1355 // filesize from Content-Range can always be used, even if using chunked Transfer-Encoding
1356 if (s->filesize_from_content_range != UINT64_MAX)
1357 s->filesize = s->filesize_from_content_range;
1358
1359 if (s->seekable == -1 && s->is_mediagateway && s->filesize == 2000000000)
1360 h->is_streamed = 1; /* we can in fact _not_ seek */
1361
1362 // add any new cookies into the existing cookie string
1363 cookie_string(s->cookie_dict, &s->cookies);
1364 av_dict_free(&s->cookie_dict);
1365
1366 return err;
1367 }
1368
1369 /**
1370 * Escape unsafe characters in path in order to pass them safely to the HTTP
1371 * request. Insipred by the algorithm in GNU wget:
1372 * - escape "%" characters not followed by two hex digits
1373 * - escape all "unsafe" characters except which are also "reserved"
1374 * - pass through everything else
1375 */
1376 static void bprint_escaped_path(AVBPrint *bp, const char *path)
1377 {
1378 #define NEEDS_ESCAPE(ch) \
1379 ((ch) <= ' ' || (ch) >= '\x7f' || \
1380 (ch) == '"' || (ch) == '%' || (ch) == '<' || (ch) == '>' || (ch) == '\\' || \
1381 (ch) == '^' || (ch) == '`' || (ch) == '{' || (ch) == '}' || (ch) == '|')
1382 while (*path) {
1383 char buf[1024];
1384 char *q = buf;
1385 while (*path && q - buf < sizeof(buf) - 4) {
1386 if (path[0] == '%' && av_isxdigit(path[1]) && av_isxdigit(path[2])) {
1387 *q++ = *path++;
1388 *q++ = *path++;
1389 *q++ = *path++;
1390 } else if (NEEDS_ESCAPE(*path)) {
1391 q += snprintf(q, 4, "%%%02X", (uint8_t)*path++);
1392 } else {
1393 *q++ = *path++;
1394 }
1395 }
1396 av_bprint_append_data(bp, buf, q - buf);
1397 }
1398 }
1399
1400 static int http_connect(URLContext *h, const char *path, const char *local_path,
1401 const char *hoststr, const char *auth,
1402 const char *proxyauth)
1403 {
1404 HTTPContext *s = h->priv_data;
1405 int post, err;
1406 AVBPrint request;
1407 char *authstr = NULL, *proxyauthstr = NULL;
1408 uint64_t off = s->off;
1409 const char *method;
1410 int send_expect_100 = 0;
1411
1412 av_bprint_init_for_buffer(&request, s->buffer, sizeof(s->buffer));
1413
1414 /* send http header */
1415 post = h->flags & AVIO_FLAG_WRITE;
1416
1417 if (s->post_data) {
1418 /* force POST method and disable chunked encoding when
1419 * custom HTTP post data is set */
1420 post = 1;
1421 s->chunked_post = 0;
1422 }
1423
1424 if (s->method)
1425 method = s->method;
1426 else
1427 method = post ? "POST" : "GET";
1428
1429 authstr = ff_http_auth_create_response(&s->auth_state, auth,
1430 local_path, method);
1431 proxyauthstr = ff_http_auth_create_response(&s->proxy_auth_state, proxyauth,
1432 local_path, method);
1433
1434 if (post && !s->post_data) {
1435 if (s->send_expect_100 != -1) {
1436 send_expect_100 = s->send_expect_100;
1437 } else {
1438 send_expect_100 = 0;
1439 /* The user has supplied authentication but we don't know the auth type,
1440 * send Expect: 100-continue to get the 401 response including the
1441 * WWW-Authenticate header, or an 100 continue if no auth actually
1442 * is needed. */
1443 if (auth && *auth &&
1444 s->auth_state.auth_type == HTTP_AUTH_NONE &&
1445 s->http_code != 401)
1446 send_expect_100 = 1;
1447 }
1448 }
1449
1450 av_bprintf(&request, "%s ", method);
1451 bprint_escaped_path(&request, path);
1452 av_bprintf(&request, " HTTP/1.1\r\n");
1453
1454 if (post && s->chunked_post)
1455 av_bprintf(&request, "Transfer-Encoding: chunked\r\n");
1456 /* set default headers if needed */
1457 if (!has_header(s->headers, "\r\nUser-Agent: "))
1458 av_bprintf(&request, "User-Agent: %s\r\n", s->user_agent);
1459 if (s->referer) {
1460 /* set default headers if needed */
1461 if (!has_header(s->headers, "\r\nReferer: "))
1462 av_bprintf(&request, "Referer: %s\r\n", s->referer);
1463 }
1464 if (!has_header(s->headers, "\r\nAccept: "))
1465 av_bprintf(&request, "Accept: */*\r\n");
1466 // Note: we send the Range header on purpose, even when we're probing,
1467 // since it allows us to detect more reliably if a (non-conforming)
1468 // server supports seeking by analysing the reply headers.
1469 if (!has_header(s->headers, "\r\nRange: ") && !post && (s->off > 0 || s->end_off || s->seekable != 0)) {
1470 av_bprintf(&request, "Range: bytes=%"PRIu64"-", s->off);
1471 if (s->end_off)
1472 av_bprintf(&request, "%"PRId64, s->end_off - 1);
1473 av_bprintf(&request, "\r\n");
1474 }
1475 if (send_expect_100 && !has_header(s->headers, "\r\nExpect: "))
1476 av_bprintf(&request, "Expect: 100-continue\r\n");
1477
1478 if (!has_header(s->headers, "\r\nConnection: "))
1479 av_bprintf(&request, "Connection: %s\r\n", s->multiple_requests ? "keep-alive" : "close");
1480
1481 if (!has_header(s->headers, "\r\nHost: "))
1482 av_bprintf(&request, "Host: %s\r\n", hoststr);
1483 if (!has_header(s->headers, "\r\nContent-Length: ") && s->post_data)
1484 av_bprintf(&request, "Content-Length: %d\r\n", s->post_datalen);
1485
1486 if (!has_header(s->headers, "\r\nContent-Type: ") && s->content_type)
1487 av_bprintf(&request, "Content-Type: %s\r\n", s->content_type);
1488 if (!has_header(s->headers, "\r\nCookie: ") && s->cookies) {
1489 char *cookies = NULL;
1490 if (!get_cookies(s, &cookies, path, hoststr) && cookies) {
1491 av_bprintf(&request, "Cookie: %s\r\n", cookies);
1492 av_free(cookies);
1493 }
1494 }
1495 if (!has_header(s->headers, "\r\nIcy-MetaData: ") && s->icy)
1496 av_bprintf(&request, "Icy-MetaData: 1\r\n");
1497
1498 /* now add in custom headers */
1499 if (s->headers)
1500 av_bprintf(&request, "%s", s->headers);
1501
1502 if (authstr)
1503 av_bprintf(&request, "%s", authstr);
1504 if (proxyauthstr)
1505 av_bprintf(&request, "Proxy-%s", proxyauthstr);
1506 av_bprintf(&request, "\r\n");
1507
1508 av_log(h, AV_LOG_DEBUG, "request: %s\n", request.str);
1509
1510 if (!av_bprint_is_complete(&request)) {
1511 av_log(h, AV_LOG_ERROR, "overlong headers\n");
1512 err = AVERROR(EINVAL);
1513 goto done;
1514 }
1515
1516 if ((err = ffurl_write(s->hd, request.str, request.len)) < 0)
1517 goto done;
1518
1519 if (s->post_data)
1520 if ((err = ffurl_write(s->hd, s->post_data, s->post_datalen)) < 0)
1521 goto done;
1522
1523 /* init input buffer */
1524 s->buf_ptr = s->buffer;
1525 s->buf_end = s->buffer;
1526 s->line_count = 0;
1527 s->off = 0;
1528 s->icy_data_read = 0;
1529 s->filesize = UINT64_MAX;
1530 s->willclose = 0;
1531 s->end_chunked_post = 0;
1532 s->end_header = 0;
1533 #if CONFIG_ZLIB
1534 s->compressed = 0;
1535 #endif
1536 if (post && !s->post_data && !send_expect_100) {
1537 /* Pretend that it did work. We didn't read any header yet, since
1538 * we've still to send the POST data, but the code calling this
1539 * function will check http_code after we return. */
1540 s->http_code = 200;
1541 err = 0;
1542 goto done;
1543 }
1544
1545 /* wait for header */
1546 err = http_read_header(h);
1547 if (err < 0)
1548 goto done;
1549
1550 if (s->new_location)
1551 s->off = off;
1552
1553 err = (off == s->off) ? 0 : -1;
1554 done:
1555 av_freep(&authstr);
1556 av_freep(&proxyauthstr);
1557 return err;
1558 }
1559
1560 static int http_buf_read(URLContext *h, uint8_t *buf, int size)
1561 {
1562 HTTPContext *s = h->priv_data;
1563 int len;
1564
1565 if (s->chunksize != UINT64_MAX) {
1566 if (s->chunkend) {
1567 return AVERROR_EOF;
1568 }
1569 if (!s->chunksize) {
1570 char line[32];
1571 int err;
1572
1573 do {
1574 if ((err = http_get_line(s, line, sizeof(line))) < 0)
1575 return err;
1576 } while (!*line); /* skip CR LF from last chunk */
1577
1578 s->chunksize = strtoull(line, NULL, 16);
1579
1580 av_log(h, AV_LOG_TRACE,
1581 "Chunked encoding data size: %"PRIu64"\n",
1582 s->chunksize);
1583
1584 if (!s->chunksize && s->multiple_requests) {
1585 http_get_line(s, line, sizeof(line)); // read empty chunk
1586 s->chunkend = 1;
1587 return 0;
1588 }
1589 else if (!s->chunksize) {
1590 av_log(h, AV_LOG_DEBUG, "Last chunk received, closing conn\n");
1591 ffurl_closep(&s->hd);
1592 return 0;
1593 }
1594 else if (s->chunksize == UINT64_MAX) {
1595 av_log(h, AV_LOG_ERROR, "Invalid chunk size %"PRIu64"\n",
1596 s->chunksize);
1597 return AVERROR(EINVAL);
1598 }
1599 }
1600 size = FFMIN(size, s->chunksize);
1601 }
1602
1603 /* read bytes from input buffer first */
1604 len = s->buf_end - s->buf_ptr;
1605 if (len > 0) {
1606 if (len > size)
1607 len = size;
1608 memcpy(buf, s->buf_ptr, len);
1609 s->buf_ptr += len;
1610 } else {
1611 uint64_t target_end = s->end_off ? s->end_off : s->filesize;
1612 if ((!s->willclose || s->chunksize == UINT64_MAX) && s->off >= target_end)
1613 return AVERROR_EOF;
1614 len = ffurl_read(s->hd, buf, size);
1615 if ((!len || len == AVERROR_EOF) &&
1616 (!s->willclose || s->chunksize == UINT64_MAX) && s->off < target_end) {
1617 av_log(h, AV_LOG_ERROR,
1618 "Stream ends prematurely at %"PRIu64", should be %"PRIu64"\n",
1619 s->off, target_end
1620 );
1621 return AVERROR(EIO);
1622 }
1623 }
1624 if (len > 0) {
1625 s->off += len;
1626 if (s->chunksize > 0 && s->chunksize != UINT64_MAX) {
1627 av_assert0(s->chunksize >= len);
1628 s->chunksize -= len;
1629 }
1630 }
1631 return len;
1632 }
1633
1634 #if CONFIG_ZLIB
1635 #define DECOMPRESS_BUF_SIZE (256 * 1024)
1636 static int http_buf_read_compressed(URLContext *h, uint8_t *buf, int size)
1637 {
1638 HTTPContext *s = h->priv_data;
1639 int ret;
1640
1641 if (!s->inflate_buffer) {
1642 s->inflate_buffer = av_malloc(DECOMPRESS_BUF_SIZE);
1643 if (!s->inflate_buffer)
1644 return AVERROR(ENOMEM);
1645 }
1646
1647 if (s->inflate_stream.avail_in == 0) {
1648 int read = http_buf_read(h, s->inflate_buffer, DECOMPRESS_BUF_SIZE);
1649 if (read <= 0)
1650 return read;
1651 s->inflate_stream.next_in = s->inflate_buffer;
1652 s->inflate_stream.avail_in = read;
1653 }
1654
1655 s->inflate_stream.avail_out = size;
1656 s->inflate_stream.next_out = buf;
1657
1658 ret = inflate(&s->inflate_stream, Z_SYNC_FLUSH);
1659 if (ret != Z_OK && ret != Z_STREAM_END)
1660 av_log(h, AV_LOG_WARNING, "inflate return value: %d, %s\n",
1661 ret, s->inflate_stream.msg);
1662
1663 return size - s->inflate_stream.avail_out;
1664 }
1665 #endif /* CONFIG_ZLIB */
1666
1667 static int64_t http_seek_internal(URLContext *h, int64_t off, int whence, int force_reconnect);
1668
1669 static int http_read_stream(URLContext *h, uint8_t *buf, int size)
1670 {
1671 HTTPContext *s = h->priv_data;
1672 int err, read_ret;
1673 int64_t seek_ret;
1674 int reconnect_delay = 0;
1675
1676 if (!s->hd)
1677 return AVERROR_EOF;
1678
1679 if (s->end_chunked_post && !s->end_header) {
1680 err = http_read_header(h);
1681 if (err < 0)
1682 return err;
1683 }
1684
1685 #if CONFIG_ZLIB
1686 if (s->compressed)
1687 return http_buf_read_compressed(h, buf, size);
1688 #endif /* CONFIG_ZLIB */
1689 read_ret = http_buf_read(h, buf, size);
1690 while (read_ret < 0) {
1691 uint64_t target = h->is_streamed ? 0 : s->off;
1692
1693 if (read_ret == AVERROR_EXIT)
1694 break;
1695
1696 if (h->is_streamed && !s->reconnect_streamed)
1697 break;
1698
1699 if (!(s->reconnect && s->filesize > 0 && s->off < s->filesize) &&
1700 !(s->reconnect_at_eof && read_ret == AVERROR_EOF))
1701 break;
1702
1703 if (reconnect_delay > s->reconnect_delay_max)
1704 return AVERROR(EIO);
1705
1706 av_log(h, AV_LOG_WARNING, "Will reconnect at %"PRIu64" in %d second(s), error=%s.\n", s->off, reconnect_delay, av_err2str(read_ret));
1707 err = ff_network_sleep_interruptible(1000U*1000*reconnect_delay, &h->interrupt_callback);
1708 if (err != AVERROR(ETIMEDOUT))
1709 return err;
1710 reconnect_delay = 1 + 2*reconnect_delay;
1711 seek_ret = http_seek_internal(h, target, SEEK_SET, 1);
1712 if (seek_ret >= 0 && seek_ret != target) {
1713 av_log(h, AV_LOG_ERROR, "Failed to reconnect at %"PRIu64".\n", target);
1714 return read_ret;
1715 }
1716
1717 read_ret = http_buf_read(h, buf, size);
1718 }
1719
1720 return read_ret;
1721 }
1722
1723 // Like http_read_stream(), but no short reads.
1724 // Assumes partial reads are an error.
1725 static int http_read_stream_all(URLContext *h, uint8_t *buf, int size)
1726 {
1727 int pos = 0;
1728 while (pos < size) {
1729 int len = http_read_stream(h, buf + pos, size - pos);
1730 if (len < 0)
1731 return len;
1732 pos += len;
1733 }
1734 return pos;
1735 }
1736
1737 static void update_metadata(URLContext *h, char *data)
1738 {
1739 char *key;
1740 char *val;
1741 char *end;
1742 char *next = data;
1743 HTTPContext *s = h->priv_data;
1744
1745 while (*next) {
1746 key = next;
1747 val = strstr(key, "='");
1748 if (!val)
1749 break;
1750 end = strstr(val, "';");
1751 if (!end)
1752 break;
1753
1754 *val = '\0';
1755 *end = '\0';
1756 val += 2;
1757
1758 av_dict_set(&s->metadata, key, val, 0);
1759 av_log(h, AV_LOG_VERBOSE, "Metadata update for %s: %s\n", key, val);
1760
1761 next = end + 2;
1762 }
1763 }
1764
1765 static int store_icy(URLContext *h, int size)
1766 {
1767 HTTPContext *s = h->priv_data;
1768 /* until next metadata packet */
1769 uint64_t remaining;
1770
1771 if (s->icy_metaint < s->icy_data_read)
1772 return AVERROR_INVALIDDATA;
1773 remaining = s->icy_metaint - s->icy_data_read;
1774
1775 if (!remaining) {
1776 /* The metadata packet is variable sized. It has a 1 byte header
1777 * which sets the length of the packet (divided by 16). If it's 0,
1778 * the metadata doesn't change. After the packet, icy_metaint bytes
1779 * of normal data follows. */
1780 uint8_t ch;
1781 int len = http_read_stream_all(h, &ch, 1);
1782 if (len < 0)
1783 return len;
1784 if (ch > 0) {
1785 char data[255 * 16 + 1];
1786 int ret;
1787 len = ch * 16;
1788 ret = http_read_stream_all(h, data, len);
1789 if (ret < 0)
1790 return ret;
1791 data[len + 1] = 0;
1792 if ((ret = av_opt_set(s, "icy_metadata_packet", data, 0)) < 0)
1793 return ret;
1794 update_metadata(h, data);
1795 }
1796 s->icy_data_read = 0;
1797 remaining = s->icy_metaint;
1798 }
1799
1800 return FFMIN(size, remaining);
1801 }
1802
1803 static int http_read(URLContext *h, uint8_t *buf, int size)
1804 {
1805 HTTPContext *s = h->priv_data;
1806
1807 if (s->icy_metaint > 0) {
1808 size = store_icy(h, size);
1809 if (size < 0)
1810 return size;
1811 }
1812
1813 size = http_read_stream(h, buf, size);
1814 if (size > 0)
1815 s->icy_data_read += size;
1816 return size;
1817 }
1818
1819 /* used only when posting data */
1820 static int http_write(URLContext *h, const uint8_t *buf, int size)
1821 {
1822 char temp[11] = ""; /* 32-bit hex + CRLF + nul */
1823 int ret;
1824 char crlf[] = "\r\n";
1825 HTTPContext *s = h->priv_data;
1826
1827 if (!s->chunked_post) {
1828 /* non-chunked data is sent without any special encoding */
1829 return ffurl_write(s->hd, buf, size);
1830 }
1831
1832 /* silently ignore zero-size data since chunk encoding that would
1833 * signal EOF */
1834 if (size > 0) {
1835 /* upload data using chunked encoding */
1836 snprintf(temp, sizeof(temp), "%x\r\n", size);
1837
1838 if ((ret = ffurl_write(s->hd, temp, strlen(temp))) < 0 ||
1839 (ret = ffurl_write(s->hd, buf, size)) < 0 ||
1840 (ret = ffurl_write(s->hd, crlf, sizeof(crlf) - 1)) < 0)
1841 return ret;
1842 }
1843 return size;
1844 }
1845
1846 static int http_shutdown(URLContext *h, int flags)
1847 {
1848 int ret = 0;
1849 char footer[] = "0\r\n\r\n";
1850 HTTPContext *s = h->priv_data;
1851
1852 /* signal end of chunked encoding if used */
1853 if (((flags & AVIO_FLAG_WRITE) && s->chunked_post) ||
1854 ((flags & AVIO_FLAG_READ) && s->chunked_post && s->listen)) {
1855 ret = ffurl_write(s->hd, footer, sizeof(footer) - 1);
1856 ret = ret > 0 ? 0 : ret;
1857 /* flush the receive buffer when it is write only mode */
1858 if (!(flags & AVIO_FLAG_READ)) {
1859 char buf[1024];
1860 int read_ret;
1861 s->hd->flags |= AVIO_FLAG_NONBLOCK;
1862 read_ret = ffurl_read(s->hd, buf, sizeof(buf));
1863 s->hd->flags &= ~AVIO_FLAG_NONBLOCK;
1864 if (read_ret < 0 && read_ret != AVERROR(EAGAIN)) {
1865 av_log(h, AV_LOG_ERROR, "URL read error: %s\n", av_err2str(read_ret));
1866 ret = read_ret;
1867 }
1868 }
1869 s->end_chunked_post = 1;
1870 }
1871
1872 return ret;
1873 }
1874
1875 static int http_close(URLContext *h)
1876 {
1877 int ret = 0;
1878 HTTPContext *s = h->priv_data;
1879
1880 #if CONFIG_ZLIB
1881 inflateEnd(&s->inflate_stream);
1882 av_freep(&s->inflate_buffer);
1883 #endif /* CONFIG_ZLIB */
1884
1885 if (s->hd && !s->end_chunked_post)
1886 /* Close the write direction by sending the end of chunked encoding. */
1887 ret = http_shutdown(h, h->flags);
1888
1889 if (s->hd)
1890 ffurl_closep(&s->hd);
1891 av_dict_free(&s->chained_options);
1892 av_dict_free(&s->cookie_dict);
1893 av_dict_free(&s->redirect_cache);
1894 av_freep(&s->new_location);
1895 av_freep(&s->uri);
1896 return ret;
1897 }
1898
1899 static int64_t http_seek_internal(URLContext *h, int64_t off, int whence, int force_reconnect)
1900 {
1901 HTTPContext *s = h->priv_data;
1902 URLContext *old_hd = s->hd;
1903 uint64_t old_off = s->off;
1904 uint8_t old_buf[BUFFER_SIZE];
1905 int old_buf_size, ret;
1906 AVDictionary *options = NULL;
1907
1908 if (whence == AVSEEK_SIZE)
1909 return s->filesize;
1910 else if (!force_reconnect &&
1911 ((whence == SEEK_CUR && off == 0) ||
1912 (whence == SEEK_SET && off == s->off)))
1913 return s->off;
1914 else if ((s->filesize == UINT64_MAX && whence == SEEK_END))
1915 return AVERROR(ENOSYS);
1916
1917 if (whence == SEEK_CUR)
1918 off += s->off;
1919 else if (whence == SEEK_END)
1920 off += s->filesize;
1921 else if (whence != SEEK_SET)
1922 return AVERROR(EINVAL);
1923 if (off < 0)
1924 return AVERROR(EINVAL);
1925 s->off = off;
1926
1927 if (s->off && h->is_streamed)
1928 return AVERROR(ENOSYS);
1929
1930 /* do not try to make a new connection if seeking past the end of the file */
1931 if (s->end_off || s->filesize != UINT64_MAX) {
1932 uint64_t end_pos = s->end_off ? s->end_off : s->filesize;
1933 if (s->off >= end_pos)
1934 return s->off;
1935 }
1936
1937 /* if the location changed (redirect), revert to the original uri */
1938 if (strcmp(s->uri, s->location)) {
1939 char *new_uri;
1940 new_uri = av_strdup(s->uri);
1941 if (!new_uri)
1942 return AVERROR(ENOMEM);
1943 av_free(s->location);
1944 s->location = new_uri;
1945 }
1946
1947 /* we save the old context in case the seek fails */
1948 old_buf_size = s->buf_end - s->buf_ptr;
1949 memcpy(old_buf, s->buf_ptr, old_buf_size);
1950 s->hd = NULL;
1951
1952 /* if it fails, continue on old connection */
1953 if ((ret = http_open_cnx(h, &options)) < 0) {
1954 av_dict_free(&options);
1955 memcpy(s->buffer, old_buf, old_buf_size);
1956 s->buf_ptr = s->buffer;
1957 s->buf_end = s->buffer + old_buf_size;
1958 s->hd = old_hd;
1959 s->off = old_off;
1960 return ret;
1961 }
1962 av_dict_free(&options);
1963 ffurl_close(old_hd);
1964 return off;
1965 }
1966
1967 static int64_t http_seek(URLContext *h, int64_t off, int whence)
1968 {
1969 return http_seek_internal(h, off, whence, 0);
1970 }
1971
1972 static int http_get_file_handle(URLContext *h)
1973 {
1974 HTTPContext *s = h->priv_data;
1975 return ffurl_get_file_handle(s->hd);
1976 }
1977
1978 static int http_get_short_seek(URLContext *h)
1979 {
1980 HTTPContext *s = h->priv_data;
1981 if (s->short_seek_size >= 1)
1982 return s->short_seek_size;
1983 return ffurl_get_short_seek(s->hd);
1984 }
1985
1986 #define HTTP_CLASS(flavor) \
1987 static const AVClass flavor ## _context_class = { \
1988 .class_name = # flavor, \
1989 .item_name = av_default_item_name, \
1990 .option = options, \
1991 .version = LIBAVUTIL_VERSION_INT, \
1992 }
1993
1994 #if CONFIG_HTTP_PROTOCOL
1995 HTTP_CLASS(http);
1996
1997 const URLProtocol ff_http_protocol = {
1998 .name = "http",
1999 .url_open2 = http_open,
2000 .url_accept = http_accept,
2001 .url_handshake = http_handshake,
2002 .url_read = http_read,
2003 .url_write = http_write,
2004 .url_seek = http_seek,
2005 .url_close = http_close,
2006 .url_get_file_handle = http_get_file_handle,
2007 .url_get_short_seek = http_get_short_seek,
2008 .url_shutdown = http_shutdown,
2009 .priv_data_size = sizeof(HTTPContext),
2010 .priv_data_class = &http_context_class,
2011 .flags = URL_PROTOCOL_FLAG_NETWORK,
2012 .default_whitelist = "http,https,tls,rtp,tcp,udp,crypto,httpproxy,data"
2013 };
2014 #endif /* CONFIG_HTTP_PROTOCOL */
2015
2016 #if CONFIG_HTTPS_PROTOCOL
2017 HTTP_CLASS(https);
2018
2019 const URLProtocol ff_https_protocol = {
2020 .name = "https",
2021 .url_open2 = http_open,
2022 .url_read = http_read,
2023 .url_write = http_write,
2024 .url_seek = http_seek,
2025 .url_close = http_close,
2026 .url_get_file_handle = http_get_file_handle,
2027 .url_get_short_seek = http_get_short_seek,
2028 .url_shutdown = http_shutdown,
2029 .priv_data_size = sizeof(HTTPContext),
2030 .priv_data_class = &https_context_class,
2031 .flags = URL_PROTOCOL_FLAG_NETWORK,
2032 .default_whitelist = "http,https,tls,rtp,tcp,udp,crypto,httpproxy"
2033 };
2034 #endif /* CONFIG_HTTPS_PROTOCOL */
2035
2036 #if CONFIG_HTTPPROXY_PROTOCOL
2037 static int http_proxy_close(URLContext *h)
2038 {
2039 HTTPContext *s = h->priv_data;
2040 if (s->hd)
2041 ffurl_closep(&s->hd);
2042 return 0;
2043 }
2044
2045 static int http_proxy_open(URLContext *h, const char *uri, int flags)
2046 {
2047 HTTPContext *s = h->priv_data;
2048 char hostname[1024], hoststr[1024];
2049 char auth[1024], pathbuf[1024], *path;
2050 char lower_url[100];
2051 int port, ret = 0, attempts = 0;
2052 HTTPAuthType cur_auth_type;
2053 char *authstr;
2054
2055 if( s->seekable == 1 )
2056 h->is_streamed = 0;
2057 else
2058 h->is_streamed = 1;
2059
2060 av_url_split(NULL, 0, auth, sizeof(auth), hostname, sizeof(hostname), &port,
2061 pathbuf, sizeof(pathbuf), uri);
2062 ff_url_join(hoststr, sizeof(hoststr), NULL, NULL, hostname, port, NULL);
2063 path = pathbuf;
2064 if (*path == '/')
2065 path++;
2066
2067 ff_url_join(lower_url, sizeof(lower_url), "tcp", NULL, hostname, port,
2068 NULL);
2069 redo:
2070 ret = ffurl_open_whitelist(&s->hd, lower_url, AVIO_FLAG_READ_WRITE,
2071 &h->interrupt_callback, NULL,
2072 h->protocol_whitelist, h->protocol_blacklist, h);
2073 if (ret < 0)
2074 return ret;
2075
2076 authstr = ff_http_auth_create_response(&s->proxy_auth_state, auth,
2077 path, "CONNECT");
2078 snprintf(s->buffer, sizeof(s->buffer),
2079 "CONNECT %s HTTP/1.1\r\n"
2080 "Host: %s\r\n"
2081 "Connection: close\r\n"
2082 "%s%s"
2083 "\r\n",
2084 path,
2085 hoststr,
2086 authstr ? "Proxy-" : "", authstr ? authstr : "");
2087 av_freep(&authstr);
2088
2089 if ((ret = ffurl_write(s->hd, s->buffer, strlen(s->buffer))) < 0)
2090 goto fail;
2091
2092 s->buf_ptr = s->buffer;
2093 s->buf_end = s->buffer;
2094 s->line_count = 0;
2095 s->filesize = UINT64_MAX;
2096 cur_auth_type = s->proxy_auth_state.auth_type;
2097
2098 /* Note: This uses buffering, potentially reading more than the
2099 * HTTP header. If tunneling a protocol where the server starts
2100 * the conversation, we might buffer part of that here, too.
2101 * Reading that requires using the proper ffurl_read() function
2102 * on this URLContext, not using the fd directly (as the tls
2103 * protocol does). This shouldn't be an issue for tls though,
2104 * since the client starts the conversation there, so there
2105 * is no extra data that we might buffer up here.
2106 */
2107 ret = http_read_header(h);
2108 if (ret < 0)
2109 goto fail;
2110
2111 attempts++;
2112 if (s->http_code == 407 &&
2113 (cur_auth_type == HTTP_AUTH_NONE || s->proxy_auth_state.stale) &&
2114 s->proxy_auth_state.auth_type != HTTP_AUTH_NONE && attempts < 2) {
2115 ffurl_closep(&s->hd);
2116 goto redo;
2117 }
2118
2119 if (s->http_code < 400)
2120 return 0;
2121 ret = ff_http_averror(s->http_code, AVERROR(EIO));
2122
2123 fail:
2124 http_proxy_close(h);
2125 return ret;
2126 }
2127
2128 static int http_proxy_write(URLContext *h, const uint8_t *buf, int size)
2129 {
2130 HTTPContext *s = h->priv_data;
2131 return ffurl_write(s->hd, buf, size);
2132 }
2133
2134 const URLProtocol ff_httpproxy_protocol = {
2135 .name = "httpproxy",
2136 .url_open = http_proxy_open,
2137 .url_read = http_buf_read,
2138 .url_write = http_proxy_write,
2139 .url_close = http_proxy_close,
2140 .url_get_file_handle = http_get_file_handle,
2141 .priv_data_size = sizeof(HTTPContext),
2142 .flags = URL_PROTOCOL_FLAG_NETWORK,
2143 };
2144 #endif /* CONFIG_HTTPPROXY_PROTOCOL */
2145