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