2 * HTTP protocol for ffmpeg client
3 * Copyright (c) 2000, 2001 Fabrice Bellard
5 * This file is part of FFmpeg.
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.
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.
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
26 #endif /* CONFIG_ZLIB */
28 #include "libavutil/avstring.h"
29 #include "libavutil/opt.h"
36 #include "os_support.h"
39 /* XXX: POST protocol is not completely implemented because ffmpeg uses
40 * only a subset of it. */
42 /* The IO buffer size is unrelated to the max URL size in itself, but needs
43 * to be large enough to fit the full request headers (including long
45 #define BUFFER_SIZE MAX_URL_SIZE
46 #define MAX_REDIRECTS 8
51 unsigned char buffer
[BUFFER_SIZE
], *buf_ptr
, *buf_end
;
54 /* Used if "Transfer-Encoding: chunked" otherwise -1. */
56 int64_t off
, end_off
, filesize
;
58 HTTPAuthState auth_state
;
59 HTTPAuthState proxy_auth_state
;
64 /* Set if the server correctly handles Connection: close and will close
65 * the connection after feeding us the content. */
67 int seekable
; /**< Control seekability, 0 = disable, 1 = enable, -1 = probe. */
69 /* A flag which indicates if the end of chunked encoding has been sent. */
71 /* A flag which indicates we have finished to read POST reply. */
73 /* A flag which indicates if we use persistent connections. */
74 int multiple_requests
;
79 char *cookies
; ///< holds newline (\n) delimited Set-Cookie header field values (without the "Set-Cookie: " field name)
81 /* how much data was read since the last ICY metadata packet */
83 /* after how many bytes of read data a new metadata packet will be found */
85 char *icy_metadata_headers
;
86 char *icy_metadata_packet
;
87 AVDictionary
*metadata
;
90 z_stream inflate_stream
;
91 uint8_t *inflate_buffer
;
92 #endif /* CONFIG_ZLIB */
93 AVDictionary
*chained_options
;
98 #define OFFSET(x) offsetof(HTTPContext, x)
99 #define D AV_OPT_FLAG_DECODING_PARAM
100 #define E AV_OPT_FLAG_ENCODING_PARAM
101 #define DEFAULT_USER_AGENT "Lavf/" AV_STRINGIFY(LIBAVFORMAT_VERSION)
103 static const AVOption options
[] = {
104 { "seekable", "control seekability of connection", OFFSET(seekable
), AV_OPT_TYPE_INT
, { .i64
= -1 }, -1, 1, D
},
105 { "chunked_post", "use chunked transfer-encoding for posts", OFFSET(chunked_post
), AV_OPT_TYPE_INT
, { .i64
= 1 }, 0, 1, E
},
106 { "headers", "set custom HTTP headers, can override built in default headers", OFFSET(headers
), AV_OPT_TYPE_STRING
, { 0 }, 0, 0, D
| E
},
107 { "content_type", "set a specific content type for the POST messages", OFFSET(content_type
), AV_OPT_TYPE_STRING
, { 0 }, 0, 0, D
| E
},
108 { "user_agent", "override User-Agent header", OFFSET(user_agent
), AV_OPT_TYPE_STRING
, { .str
= DEFAULT_USER_AGENT
}, 0, 0, D
},
109 { "user-agent", "override User-Agent header", OFFSET(user_agent
), AV_OPT_TYPE_STRING
, { .str
= DEFAULT_USER_AGENT
}, 0, 0, D
},
110 { "multiple_requests", "use persistent connections", OFFSET(multiple_requests
), AV_OPT_TYPE_INT
, { .i64
= 0 }, 0, 1, D
| E
},
111 { "post_data", "set custom HTTP post data", OFFSET(post_data
), AV_OPT_TYPE_BINARY
, .flags
= D
| E
},
112 { "mime_type", "export the MIME type", OFFSET(mime_type
), AV_OPT_TYPE_STRING
, { 0 }, 0, 0, AV_OPT_FLAG_EXPORT
| AV_OPT_FLAG_READONLY
},
113 { "cookies", "set cookies to be sent in applicable future requests, use newline delimited Set-Cookie HTTP field value syntax", OFFSET(cookies
), AV_OPT_TYPE_STRING
, { 0 }, 0, 0, D
},
114 { "icy", "request ICY metadata", OFFSET(icy
), AV_OPT_TYPE_INT
, { .i64
= 1 }, 0, 1, D
},
115 { "icy_metadata_headers", "return ICY metadata headers", OFFSET(icy_metadata_headers
), AV_OPT_TYPE_STRING
, { 0 }, 0, 0, AV_OPT_FLAG_EXPORT
},
116 { "icy_metadata_packet", "return current ICY metadata packet", OFFSET(icy_metadata_packet
), AV_OPT_TYPE_STRING
, { 0 }, 0, 0, AV_OPT_FLAG_EXPORT
},
117 { "metadata", "metadata read from the bitstream", OFFSET(metadata
), AV_OPT_TYPE_DICT
, {0}, 0, 0, AV_OPT_FLAG_EXPORT
},
118 { "auth_type", "HTTP authentication type", OFFSET(auth_state
.auth_type
), AV_OPT_TYPE_INT
, { .i64
= HTTP_AUTH_NONE
}, HTTP_AUTH_NONE
, HTTP_AUTH_BASIC
, D
| E
, "auth_type"},
119 { "none", "No auth method set, autodetect", 0, AV_OPT_TYPE_CONST
, { .i64
= HTTP_AUTH_NONE
}, 0, 0, D
| E
, "auth_type"},
120 { "basic", "HTTP basic authentication", 0, AV_OPT_TYPE_CONST
, { .i64
= HTTP_AUTH_BASIC
}, 0, 0, D
| E
, "auth_type"},
121 { "send_expect_100", "Force sending an Expect: 100-continue header for POST", OFFSET(send_expect_100
), AV_OPT_TYPE_INT
, { .i64
= 0 }, 0, 1, E
},
122 { "location", "The actual location of the data received", OFFSET(location
), AV_OPT_TYPE_STRING
, { 0 }, 0, 0, D
| E
},
123 { "offset", "initial byte offset", OFFSET(off
), AV_OPT_TYPE_INT64
, { .i64
= 0 }, 0, INT64_MAX
, D
},
124 { "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
},
125 { "method", "Override the HTTP method", OFFSET(method
), AV_OPT_TYPE_STRING
, { .str
= NULL
}, 0, 0, E
},
129 static int http_connect(URLContext
*h
, const char *path
, const char *local_path
,
130 const char *hoststr
, const char *auth
,
131 const char *proxyauth
, int *new_location
);
133 void ff_http_init_auth_state(URLContext
*dest
, const URLContext
*src
)
135 memcpy(&((HTTPContext
*)dest
->priv_data
)->auth_state
,
136 &((HTTPContext
*)src
->priv_data
)->auth_state
,
137 sizeof(HTTPAuthState
));
138 memcpy(&((HTTPContext
*)dest
->priv_data
)->proxy_auth_state
,
139 &((HTTPContext
*)src
->priv_data
)->proxy_auth_state
,
140 sizeof(HTTPAuthState
));
143 static int http_open_cnx_internal(URLContext
*h
, AVDictionary
**options
)
145 const char *path
, *proxy_path
, *lower_proto
= "tcp", *local_path
;
146 char hostname
[1024], hoststr
[1024], proto
[10];
147 char auth
[1024], proxyauth
[1024] = "";
148 char path1
[MAX_URL_SIZE
];
149 char buf
[1024], urlbuf
[MAX_URL_SIZE
];
150 int port
, use_proxy
, err
, location_changed
= 0;
151 HTTPContext
*s
= h
->priv_data
;
153 av_url_split(proto
, sizeof(proto
), auth
, sizeof(auth
),
154 hostname
, sizeof(hostname
), &port
,
155 path1
, sizeof(path1
), s
->location
);
156 ff_url_join(hoststr
, sizeof(hoststr
), NULL
, NULL
, hostname
, port
, NULL
);
158 proxy_path
= getenv("http_proxy");
159 use_proxy
= !ff_http_match_no_proxy(getenv("no_proxy"), hostname
) &&
160 proxy_path
&& av_strstart(proxy_path
, "http://", NULL
);
162 if (!strcmp(proto
, "https")) {
171 if (path1
[0] == '\0')
177 /* Reassemble the request URL without auth string - we don't
178 * want to leak the auth to the proxy. */
179 ff_url_join(urlbuf
, sizeof(urlbuf
), proto
, NULL
, hostname
, port
, "%s",
182 av_url_split(NULL
, 0, proxyauth
, sizeof(proxyauth
),
183 hostname
, sizeof(hostname
), &port
, NULL
, 0, proxy_path
);
186 ff_url_join(buf
, sizeof(buf
), lower_proto
, NULL
, hostname
, port
, NULL
);
189 err
= ffurl_open(&s
->hd
, buf
, AVIO_FLAG_READ_WRITE
,
190 &h
->interrupt_callback
, options
);
195 err
= http_connect(h
, path
, local_path
, hoststr
,
196 auth
, proxyauth
, &location_changed
);
200 return location_changed
;
203 /* return non zero if error */
204 static int http_open_cnx(URLContext
*h
, AVDictionary
**options
)
206 HTTPAuthType cur_auth_type
, cur_proxy_auth_type
;
207 HTTPContext
*s
= h
->priv_data
;
208 int location_changed
, attempts
= 0, redirects
= 0;
210 av_dict_copy(options
, s
->chained_options
, 0);
212 cur_auth_type
= s
->auth_state
.auth_type
;
213 cur_proxy_auth_type
= s
->auth_state
.auth_type
;
215 location_changed
= http_open_cnx_internal(h
, options
);
216 if (location_changed
< 0)
220 if (s
->http_code
== 401) {
221 if ((cur_auth_type
== HTTP_AUTH_NONE
|| s
->auth_state
.stale
) &&
222 s
->auth_state
.auth_type
!= HTTP_AUTH_NONE
&& attempts
< 4) {
223 ffurl_closep(&s
->hd
);
228 if (s
->http_code
== 407) {
229 if ((cur_proxy_auth_type
== HTTP_AUTH_NONE
|| s
->proxy_auth_state
.stale
) &&
230 s
->proxy_auth_state
.auth_type
!= HTTP_AUTH_NONE
&& attempts
< 4) {
231 ffurl_closep(&s
->hd
);
236 if ((s
->http_code
== 301 || s
->http_code
== 302 ||
237 s
->http_code
== 303 || s
->http_code
== 307) &&
238 location_changed
== 1) {
239 /* url moved, get next */
240 ffurl_closep(&s
->hd
);
241 if (redirects
++ >= MAX_REDIRECTS
)
243 /* Restart the authentication process with the new target, which
244 * might use a different auth mechanism. */
245 memset(&s
->auth_state
, 0, sizeof(s
->auth_state
));
247 location_changed
= 0;
254 ffurl_closep(&s
->hd
);
255 if (location_changed
< 0)
256 return location_changed
;
257 return ff_http_averror(s
->http_code
, AVERROR(EIO
));
260 int ff_http_do_new_request(URLContext
*h
, const char *uri
)
262 HTTPContext
*s
= h
->priv_data
;
263 AVDictionary
*options
= NULL
;
267 s
->icy_data_read
= 0;
268 av_free(s
->location
);
269 s
->location
= av_strdup(uri
);
271 return AVERROR(ENOMEM
);
273 ret
= http_open_cnx(h
, &options
);
274 av_dict_free(&options
);
278 int ff_http_averror(int status_code
, int default_averror
)
280 switch (status_code
) {
281 case 400: return AVERROR_HTTP_BAD_REQUEST
;
282 case 401: return AVERROR_HTTP_UNAUTHORIZED
;
283 case 403: return AVERROR_HTTP_FORBIDDEN
;
284 case 404: return AVERROR_HTTP_NOT_FOUND
;
287 if (status_code
>= 400 && status_code
<= 499)
288 return AVERROR_HTTP_OTHER_4XX
;
289 else if (status_code
>= 500)
290 return AVERROR_HTTP_SERVER_ERROR
;
292 return default_averror
;
295 static int http_open(URLContext
*h
, const char *uri
, int flags
,
296 AVDictionary
**options
)
298 HTTPContext
*s
= h
->priv_data
;
301 if( s
->seekable
== 1 )
307 s
->location
= av_strdup(uri
);
309 return AVERROR(ENOMEM
);
311 av_dict_copy(&s
->chained_options
, *options
, 0);
314 int len
= strlen(s
->headers
);
315 if (len
< 2 || strcmp("\r\n", s
->headers
+ len
- 2))
316 av_log(h
, AV_LOG_WARNING
,
317 "No trailing CRLF found in HTTP header.\n");
320 ret
= http_open_cnx(h
, options
);
322 av_dict_free(&s
->chained_options
);
326 static int http_getc(HTTPContext
*s
)
329 if (s
->buf_ptr
>= s
->buf_end
) {
330 len
= ffurl_read(s
->hd
, s
->buffer
, BUFFER_SIZE
);
333 } else if (len
== 0) {
336 s
->buf_ptr
= s
->buffer
;
337 s
->buf_end
= s
->buffer
+ len
;
340 return *s
->buf_ptr
++;
343 static int http_get_line(HTTPContext
*s
, char *line
, int line_size
)
355 if (q
> line
&& q
[-1] == '\r')
361 if ((q
- line
) < line_size
- 1)
367 static int check_http_code(URLContext
*h
, int http_code
, const char *end
)
369 HTTPContext
*s
= h
->priv_data
;
370 /* error codes are 4xx and 5xx, but regard 401 as a success, so we
371 * don't abort until all headers have been parsed. */
372 if (http_code
>= 400 && http_code
< 600 &&
373 (http_code
!= 401 || s
->auth_state
.auth_type
!= HTTP_AUTH_NONE
) &&
374 (http_code
!= 407 || s
->proxy_auth_state
.auth_type
!= HTTP_AUTH_NONE
)) {
375 end
+= strspn(end
, SPACE_CHARS
);
376 av_log(h
, AV_LOG_WARNING
, "HTTP error %d %s\n", http_code
, end
);
377 return ff_http_averror(http_code
, AVERROR(EIO
));
382 static int parse_location(HTTPContext
*s
, const char *p
)
384 char redirected_location
[MAX_URL_SIZE
], *new_loc
;
385 ff_make_absolute_url(redirected_location
, sizeof(redirected_location
),
387 new_loc
= av_strdup(redirected_location
);
389 return AVERROR(ENOMEM
);
390 av_free(s
->location
);
391 s
->location
= new_loc
;
395 /* "bytes $from-$to/$document_size" */
396 static void parse_content_range(URLContext
*h
, const char *p
)
398 HTTPContext
*s
= h
->priv_data
;
401 if (!strncmp(p
, "bytes ", 6)) {
403 s
->off
= strtoll(p
, NULL
, 10);
404 if ((slash
= strchr(p
, '/')) && strlen(slash
) > 0)
405 s
->filesize
= strtoll(slash
+ 1, NULL
, 10);
407 if (s
->seekable
== -1 && (!s
->is_akamai
|| s
->filesize
!= 2147483647))
408 h
->is_streamed
= 0; /* we _can_ in fact seek */
411 static int parse_content_encoding(URLContext
*h
, const char *p
)
413 if (!av_strncasecmp(p
, "gzip", 4) ||
414 !av_strncasecmp(p
, "deflate", 7)) {
416 HTTPContext
*s
= h
->priv_data
;
419 inflateEnd(&s
->inflate_stream
);
420 if (inflateInit2(&s
->inflate_stream
, 32 + 15) != Z_OK
) {
421 av_log(h
, AV_LOG_WARNING
, "Error during zlib initialisation: %s\n",
422 s
->inflate_stream
.msg
);
423 return AVERROR(ENOSYS
);
425 if (zlibCompileFlags() & (1 << 17)) {
426 av_log(h
, AV_LOG_WARNING
,
427 "Your zlib was compiled without gzip support.\n");
428 return AVERROR(ENOSYS
);
431 av_log(h
, AV_LOG_WARNING
,
432 "Compressed (%s) content, need zlib with gzip support\n", p
);
433 return AVERROR(ENOSYS
);
434 #endif /* CONFIG_ZLIB */
435 } else if (!av_strncasecmp(p
, "identity", 8)) {
436 // The normal, no-encoding case (although servers shouldn't include
437 // the header at all if this is the case).
439 av_log(h
, AV_LOG_WARNING
, "Unknown content coding: %s\n", p
);
444 // Concat all Icy- header lines
445 static int parse_icy(HTTPContext
*s
, const char *tag
, const char *p
)
447 int len
= 4 + strlen(p
) + strlen(tag
);
448 int is_first
= !s
->icy_metadata_headers
;
451 av_dict_set(&s
->metadata
, tag
, p
, 0);
453 if (s
->icy_metadata_headers
)
454 len
+= strlen(s
->icy_metadata_headers
);
456 if ((ret
= av_reallocp(&s
->icy_metadata_headers
, len
)) < 0)
460 *s
->icy_metadata_headers
= '\0';
462 av_strlcatf(s
->icy_metadata_headers
, len
, "%s: %s\n", tag
, p
);
467 static int process_line(URLContext
*h
, char *line
, int line_count
,
470 HTTPContext
*s
= h
->priv_data
;
475 if (line
[0] == '\0') {
481 if (line_count
== 0) {
482 while (!av_isspace(*p
) && *p
!= '\0')
484 while (av_isspace(*p
))
486 s
->http_code
= strtol(p
, &end
, 10);
488 av_log(h
, AV_LOG_DEBUG
, "http_code=%d\n", s
->http_code
);
490 if ((ret
= check_http_code(h
, s
->http_code
, end
)) < 0)
493 while (*p
!= '\0' && *p
!= ':')
501 while (av_isspace(*p
))
503 if (!av_strcasecmp(tag
, "Location")) {
504 if ((ret
= parse_location(s
, p
)) < 0)
507 } else if (!av_strcasecmp(tag
, "Content-Length") && s
->filesize
== -1) {
508 s
->filesize
= strtoll(p
, NULL
, 10);
509 } else if (!av_strcasecmp(tag
, "Content-Range")) {
510 parse_content_range(h
, p
);
511 } else if (!av_strcasecmp(tag
, "Accept-Ranges") &&
512 !strncmp(p
, "bytes", 5) &&
515 } else if (!av_strcasecmp(tag
, "Transfer-Encoding") &&
516 !av_strncasecmp(p
, "chunked", 7)) {
519 } else if (!av_strcasecmp(tag
, "WWW-Authenticate")) {
520 ff_http_auth_handle_header(&s
->auth_state
, tag
, p
);
521 } else if (!av_strcasecmp(tag
, "Authentication-Info")) {
522 ff_http_auth_handle_header(&s
->auth_state
, tag
, p
);
523 } else if (!av_strcasecmp(tag
, "Proxy-Authenticate")) {
524 ff_http_auth_handle_header(&s
->proxy_auth_state
, tag
, p
);
525 } else if (!av_strcasecmp(tag
, "Connection")) {
526 if (!strcmp(p
, "close"))
528 } else if (!av_strcasecmp(tag
, "Server")) {
529 if (!av_strcasecmp(p
, "AkamaiGHost")) {
531 } else if (!av_strncasecmp(p
, "MediaGateway", 12)) {
532 s
->is_mediagateway
= 1;
534 } else if (!av_strcasecmp(tag
, "Content-Type")) {
535 av_free(s
->mime_type
);
536 s
->mime_type
= av_strdup(p
);
537 } else if (!av_strcasecmp(tag
, "Set-Cookie")) {
539 if (!(s
->cookies
= av_strdup(p
)))
540 return AVERROR(ENOMEM
);
542 char *tmp
= s
->cookies
;
543 size_t str_size
= strlen(tmp
) + strlen(p
) + 2;
544 if (!(s
->cookies
= av_malloc(str_size
))) {
546 return AVERROR(ENOMEM
);
548 snprintf(s
->cookies
, str_size
, "%s\n%s", tmp
, p
);
551 } else if (!av_strcasecmp(tag
, "Icy-MetaInt")) {
552 s
->icy_metaint
= strtoll(p
, NULL
, 10);
553 } else if (!av_strncasecmp(tag
, "Icy-", 4)) {
554 if ((ret
= parse_icy(s
, tag
, p
)) < 0)
556 } else if (!av_strcasecmp(tag
, "Content-Encoding")) {
557 if ((ret
= parse_content_encoding(h
, p
)) < 0)
565 * Create a string containing cookie values for use as a HTTP cookie header
566 * field value for a particular path and domain from the cookie values stored in
567 * the HTTP protocol context. The cookie string is stored in *cookies.
569 * @return a negative value if an error condition occurred, 0 otherwise
571 static int get_cookies(HTTPContext
*s
, char **cookies
, const char *path
,
574 // cookie strings will look like Set-Cookie header field values. Multiple
575 // Set-Cookie fields will result in multiple values delimited by a newline
577 char *next
, *cookie
, *set_cookies
= av_strdup(s
->cookies
), *cset_cookies
= set_cookies
;
579 if (!set_cookies
) return AVERROR(EINVAL
);
582 while ((cookie
= av_strtok(set_cookies
, "\n", &next
))) {
583 int domain_offset
= 0;
584 char *param
, *next_param
, *cdomain
= NULL
, *cpath
= NULL
, *cvalue
= NULL
;
587 while ((param
= av_strtok(cookie
, "; ", &next_param
))) {
589 // first key-value pair is the actual cookie value
590 cvalue
= av_strdup(param
);
592 } else if (!av_strncasecmp("path=", param
, 5)) {
594 cpath
= av_strdup(¶m
[5]);
595 } else if (!av_strncasecmp("domain=", param
, 7)) {
596 // if the cookie specifies a sub-domain, skip the leading dot thereby
597 // supporting URLs that point to sub-domains and the master domain
598 int leading_dot
= (param
[7] == '.');
600 cdomain
= av_strdup(¶m
[7+leading_dot
]);
602 // ignore unknown attributes
606 cdomain
= av_strdup(domain
);
608 // ensure all of the necessary values are valid
609 if (!cdomain
|| !cpath
|| !cvalue
) {
610 av_log(s
, AV_LOG_WARNING
,
611 "Invalid cookie found, no value, path or domain specified\n");
615 // check if the request path matches the cookie path
616 if (av_strncasecmp(path
, cpath
, strlen(cpath
)))
619 // the domain should be at least the size of our cookie domain
620 domain_offset
= strlen(domain
) - strlen(cdomain
);
621 if (domain_offset
< 0)
624 // match the cookie domain
625 if (av_strcasecmp(&domain
[domain_offset
], cdomain
))
628 // cookie parameters match, so copy the value
630 if (!(*cookies
= av_strdup(cvalue
))) {
631 ret
= AVERROR(ENOMEM
);
635 char *tmp
= *cookies
;
636 size_t str_size
= strlen(cvalue
) + strlen(*cookies
) + 3;
637 if (!(*cookies
= av_malloc(str_size
))) {
638 ret
= AVERROR(ENOMEM
);
641 snprintf(*cookies
, str_size
, "%s; %s", tmp
, cvalue
);
650 if (*cookies
) av_freep(cookies
);
651 av_free(cset_cookies
);
656 av_free(cset_cookies
);
661 static inline int has_header(const char *str
, const char *header
)
663 /* header + 2 to skip over CRLF prefix. (make sure you have one!) */
666 return av_stristart(str
, header
+ 2, NULL
) || av_stristr(str
, header
);
669 static int http_read_header(URLContext
*h
, int *new_location
)
671 HTTPContext
*s
= h
->priv_data
;
672 char line
[MAX_URL_SIZE
];
678 if ((err
= http_get_line(s
, line
, sizeof(line
))) < 0)
681 av_log(h
, AV_LOG_DEBUG
, "header='%s'\n", line
);
683 err
= process_line(h
, line
, s
->line_count
, new_location
);
691 if (s
->seekable
== -1 && s
->is_mediagateway
&& s
->filesize
== 2000000000)
692 h
->is_streamed
= 1; /* we can in fact _not_ seek */
697 static int http_connect(URLContext
*h
, const char *path
, const char *local_path
,
698 const char *hoststr
, const char *auth
,
699 const char *proxyauth
, int *new_location
)
701 HTTPContext
*s
= h
->priv_data
;
703 char headers
[HTTP_HEADERS_SIZE
] = "";
704 char *authstr
= NULL
, *proxyauthstr
= NULL
;
705 int64_t off
= s
->off
;
708 int send_expect_100
= 0;
710 /* send http header */
711 post
= h
->flags
& AVIO_FLAG_WRITE
;
714 /* force POST method and disable chunked encoding when
715 * custom HTTP post data is set */
723 method
= post
? "POST" : "GET";
725 authstr
= ff_http_auth_create_response(&s
->auth_state
, auth
,
727 proxyauthstr
= ff_http_auth_create_response(&s
->proxy_auth_state
, proxyauth
,
729 if (post
&& !s
->post_data
) {
730 send_expect_100
= s
->send_expect_100
;
731 /* The user has supplied authentication but we don't know the auth type,
732 * send Expect: 100-continue to get the 401 response including the
733 * WWW-Authenticate header, or an 100 continue if no auth actually
736 s
->auth_state
.auth_type
== HTTP_AUTH_NONE
&&
741 /* set default headers if needed */
742 if (!has_header(s
->headers
, "\r\nUser-Agent: "))
743 len
+= av_strlcatf(headers
+ len
, sizeof(headers
) - len
,
744 "User-Agent: %s\r\n", s
->user_agent
);
745 if (!has_header(s
->headers
, "\r\nAccept: "))
746 len
+= av_strlcpy(headers
+ len
, "Accept: */*\r\n",
747 sizeof(headers
) - len
);
748 // Note: we send this on purpose even when s->off is 0 when we're probing,
749 // since it allows us to detect more reliably if a (non-conforming)
750 // server supports seeking by analysing the reply headers.
751 if (!has_header(s
->headers
, "\r\nRange: ") && !post
&& (s
->off
> 0 || s
->end_off
|| s
->seekable
== -1)) {
752 len
+= av_strlcatf(headers
+ len
, sizeof(headers
) - len
,
753 "Range: bytes=%"PRId64
"-", s
->off
);
755 len
+= av_strlcatf(headers
+ len
, sizeof(headers
) - len
,
756 "%"PRId64
, s
->end_off
- 1);
757 len
+= av_strlcpy(headers
+ len
, "\r\n",
758 sizeof(headers
) - len
);
760 if (send_expect_100
&& !has_header(s
->headers
, "\r\nExpect: "))
761 len
+= av_strlcatf(headers
+ len
, sizeof(headers
) - len
,
762 "Expect: 100-continue\r\n");
764 if (!has_header(s
->headers
, "\r\nConnection: ")) {
765 if (s
->multiple_requests
)
766 len
+= av_strlcpy(headers
+ len
, "Connection: keep-alive\r\n",
767 sizeof(headers
) - len
);
769 len
+= av_strlcpy(headers
+ len
, "Connection: close\r\n",
770 sizeof(headers
) - len
);
773 if (!has_header(s
->headers
, "\r\nHost: "))
774 len
+= av_strlcatf(headers
+ len
, sizeof(headers
) - len
,
775 "Host: %s\r\n", hoststr
);
776 if (!has_header(s
->headers
, "\r\nContent-Length: ") && s
->post_data
)
777 len
+= av_strlcatf(headers
+ len
, sizeof(headers
) - len
,
778 "Content-Length: %d\r\n", s
->post_datalen
);
780 if (!has_header(s
->headers
, "\r\nContent-Type: ") && s
->content_type
)
781 len
+= av_strlcatf(headers
+ len
, sizeof(headers
) - len
,
782 "Content-Type: %s\r\n", s
->content_type
);
783 if (!has_header(s
->headers
, "\r\nCookie: ") && s
->cookies
) {
784 char *cookies
= NULL
;
785 if (!get_cookies(s
, &cookies
, path
, hoststr
) && cookies
) {
786 len
+= av_strlcatf(headers
+ len
, sizeof(headers
) - len
,
787 "Cookie: %s\r\n", cookies
);
791 if (!has_header(s
->headers
, "\r\nIcy-MetaData: ") && s
->icy
)
792 len
+= av_strlcatf(headers
+ len
, sizeof(headers
) - len
,
793 "Icy-MetaData: %d\r\n", 1);
795 /* now add in custom headers */
797 av_strlcpy(headers
+ len
, s
->headers
, sizeof(headers
) - len
);
799 snprintf(s
->buffer
, sizeof(s
->buffer
),
808 post
&& s
->chunked_post
? "Transfer-Encoding: chunked\r\n" : "",
810 authstr
? authstr
: "",
811 proxyauthstr
? "Proxy-" : "", proxyauthstr
? proxyauthstr
: "");
813 av_log(h
, AV_LOG_DEBUG
, "request: %s\n", s
->buffer
);
815 if ((err
= ffurl_write(s
->hd
, s
->buffer
, strlen(s
->buffer
))) < 0)
819 if ((err
= ffurl_write(s
->hd
, s
->post_data
, s
->post_datalen
)) < 0)
822 /* init input buffer */
823 s
->buf_ptr
= s
->buffer
;
824 s
->buf_end
= s
->buffer
;
827 s
->icy_data_read
= 0;
830 s
->end_chunked_post
= 0;
832 if (post
&& !s
->post_data
&& !send_expect_100
) {
833 /* Pretend that it did work. We didn't read any header yet, since
834 * we've still to send the POST data, but the code calling this
835 * function will check http_code after we return. */
841 /* wait for header */
842 err
= http_read_header(h
, new_location
);
846 err
= (off
== s
->off
) ? 0 : -1;
849 av_freep(&proxyauthstr
);
853 static int http_buf_read(URLContext
*h
, uint8_t *buf
, int size
)
855 HTTPContext
*s
= h
->priv_data
;
857 /* read bytes from input buffer first */
858 len
= s
->buf_end
- s
->buf_ptr
;
862 memcpy(buf
, s
->buf_ptr
, len
);
865 if ((!s
->willclose
|| s
->chunksize
< 0) &&
866 s
->filesize
>= 0 && s
->off
>= s
->filesize
)
868 len
= ffurl_read(s
->hd
, buf
, size
);
872 if (s
->chunksize
> 0)
879 #define DECOMPRESS_BUF_SIZE (256 * 1024)
880 static int http_buf_read_compressed(URLContext
*h
, uint8_t *buf
, int size
)
882 HTTPContext
*s
= h
->priv_data
;
885 if (!s
->inflate_buffer
) {
886 s
->inflate_buffer
= av_malloc(DECOMPRESS_BUF_SIZE
);
887 if (!s
->inflate_buffer
)
888 return AVERROR(ENOMEM
);
891 if (s
->inflate_stream
.avail_in
== 0) {
892 int read
= http_buf_read(h
, s
->inflate_buffer
, DECOMPRESS_BUF_SIZE
);
895 s
->inflate_stream
.next_in
= s
->inflate_buffer
;
896 s
->inflate_stream
.avail_in
= read
;
899 s
->inflate_stream
.avail_out
= size
;
900 s
->inflate_stream
.next_out
= buf
;
902 ret
= inflate(&s
->inflate_stream
, Z_SYNC_FLUSH
);
903 if (ret
!= Z_OK
&& ret
!= Z_STREAM_END
)
904 av_log(h
, AV_LOG_WARNING
, "inflate return value: %d, %s\n",
905 ret
, s
->inflate_stream
.msg
);
907 return size
- s
->inflate_stream
.avail_out
;
909 #endif /* CONFIG_ZLIB */
911 static int http_read_stream(URLContext
*h
, uint8_t *buf
, int size
)
913 HTTPContext
*s
= h
->priv_data
;
914 int err
, new_location
;
919 if (s
->end_chunked_post
&& !s
->end_header
) {
920 err
= http_read_header(h
, &new_location
);
925 if (s
->chunksize
>= 0) {
930 if ((err
= http_get_line(s
, line
, sizeof(line
))) < 0)
932 } while (!*line
); /* skip CR LF from last chunk */
934 s
->chunksize
= strtoll(line
, NULL
, 16);
936 av_dlog(NULL
, "Chunked encoding data size: %"PRId64
"'\n",
942 size
= FFMIN(size
, s
->chunksize
);
946 return http_buf_read_compressed(h
, buf
, size
);
947 #endif /* CONFIG_ZLIB */
948 return http_buf_read(h
, buf
, size
);
951 // Like http_read_stream(), but no short reads.
952 // Assumes partial reads are an error.
953 static int http_read_stream_all(URLContext
*h
, uint8_t *buf
, int size
)
957 int len
= http_read_stream(h
, buf
+ pos
, size
- pos
);
965 static void update_metadata(HTTPContext
*s
, char *data
)
974 val
= strstr(key
, "='");
977 end
= strstr(val
, "';");
985 av_dict_set(&s
->metadata
, key
, val
, 0);
991 static int store_icy(URLContext
*h
, int size
)
993 HTTPContext
*s
= h
->priv_data
;
994 /* until next metadata packet */
995 int remaining
= s
->icy_metaint
- s
->icy_data_read
;
998 return AVERROR_INVALIDDATA
;
1001 /* The metadata packet is variable sized. It has a 1 byte header
1002 * which sets the length of the packet (divided by 16). If it's 0,
1003 * the metadata doesn't change. After the packet, icy_metaint bytes
1004 * of normal data follows. */
1006 int len
= http_read_stream_all(h
, &ch
, 1);
1010 char data
[255 * 16 + 1];
1013 ret
= http_read_stream_all(h
, data
, len
);
1017 if ((ret
= av_opt_set(s
, "icy_metadata_packet", data
, 0)) < 0)
1019 update_metadata(s
, data
);
1021 s
->icy_data_read
= 0;
1022 remaining
= s
->icy_metaint
;
1025 return FFMIN(size
, remaining
);
1028 static int http_read(URLContext
*h
, uint8_t *buf
, int size
)
1030 HTTPContext
*s
= h
->priv_data
;
1032 if (s
->icy_metaint
> 0) {
1033 size
= store_icy(h
, size
);
1038 size
= http_read_stream(h
, buf
, size
);
1040 s
->icy_data_read
+= size
;
1044 /* used only when posting data */
1045 static int http_write(URLContext
*h
, const uint8_t *buf
, int size
)
1047 char temp
[11] = ""; /* 32-bit hex + CRLF + nul */
1049 char crlf
[] = "\r\n";
1050 HTTPContext
*s
= h
->priv_data
;
1052 if (!s
->chunked_post
) {
1053 /* non-chunked data is sent without any special encoding */
1054 return ffurl_write(s
->hd
, buf
, size
);
1057 /* silently ignore zero-size data since chunk encoding that would
1060 /* upload data using chunked encoding */
1061 snprintf(temp
, sizeof(temp
), "%x\r\n", size
);
1063 if ((ret
= ffurl_write(s
->hd
, temp
, strlen(temp
))) < 0 ||
1064 (ret
= ffurl_write(s
->hd
, buf
, size
)) < 0 ||
1065 (ret
= ffurl_write(s
->hd
, crlf
, sizeof(crlf
) - 1)) < 0)
1071 static int http_shutdown(URLContext
*h
, int flags
)
1074 char footer
[] = "0\r\n\r\n";
1075 HTTPContext
*s
= h
->priv_data
;
1077 /* signal end of chunked encoding if used */
1078 if ((flags
& AVIO_FLAG_WRITE
) && s
->chunked_post
) {
1079 ret
= ffurl_write(s
->hd
, footer
, sizeof(footer
) - 1);
1080 ret
= ret
> 0 ? 0 : ret
;
1081 s
->end_chunked_post
= 1;
1087 static int http_close(URLContext
*h
)
1090 HTTPContext
*s
= h
->priv_data
;
1093 inflateEnd(&s
->inflate_stream
);
1094 av_freep(&s
->inflate_buffer
);
1095 #endif /* CONFIG_ZLIB */
1097 if (!s
->end_chunked_post
)
1098 /* Close the write direction by sending the end of chunked encoding. */
1099 ret
= http_shutdown(h
, h
->flags
);
1102 ffurl_closep(&s
->hd
);
1103 av_dict_free(&s
->chained_options
);
1107 static int64_t http_seek(URLContext
*h
, int64_t off
, int whence
)
1109 HTTPContext
*s
= h
->priv_data
;
1110 URLContext
*old_hd
= s
->hd
;
1111 int64_t old_off
= s
->off
;
1112 uint8_t old_buf
[BUFFER_SIZE
];
1113 int old_buf_size
, ret
;
1114 AVDictionary
*options
= NULL
;
1116 if (whence
== AVSEEK_SIZE
)
1118 else if ((whence
== SEEK_CUR
&& off
== 0) ||
1119 (whence
== SEEK_SET
&& off
== s
->off
))
1121 else if ((s
->filesize
== -1 && whence
== SEEK_END
) || h
->is_streamed
)
1122 return AVERROR(ENOSYS
);
1124 if (whence
== SEEK_CUR
)
1126 else if (whence
== SEEK_END
)
1128 else if (whence
!= SEEK_SET
)
1129 return AVERROR(EINVAL
);
1131 return AVERROR(EINVAL
);
1134 /* we save the old context in case the seek fails */
1135 old_buf_size
= s
->buf_end
- s
->buf_ptr
;
1136 memcpy(old_buf
, s
->buf_ptr
, old_buf_size
);
1139 /* if it fails, continue on old connection */
1140 if ((ret
= http_open_cnx(h
, &options
)) < 0) {
1141 av_dict_free(&options
);
1142 memcpy(s
->buffer
, old_buf
, old_buf_size
);
1143 s
->buf_ptr
= s
->buffer
;
1144 s
->buf_end
= s
->buffer
+ old_buf_size
;
1149 av_dict_free(&options
);
1150 ffurl_close(old_hd
);
1154 static int http_get_file_handle(URLContext
*h
)
1156 HTTPContext
*s
= h
->priv_data
;
1157 return ffurl_get_file_handle(s
->hd
);
1160 #define HTTP_CLASS(flavor) \
1161 static const AVClass flavor ## _context_class = { \
1162 .class_name = # flavor, \
1163 .item_name = av_default_item_name, \
1164 .option = options, \
1165 .version = LIBAVUTIL_VERSION_INT, \
1168 #if CONFIG_HTTP_PROTOCOL
1171 URLProtocol ff_http_protocol
= {
1173 .url_open2
= http_open
,
1174 .url_read
= http_read
,
1175 .url_write
= http_write
,
1176 .url_seek
= http_seek
,
1177 .url_close
= http_close
,
1178 .url_get_file_handle
= http_get_file_handle
,
1179 .url_shutdown
= http_shutdown
,
1180 .priv_data_size
= sizeof(HTTPContext
),
1181 .priv_data_class
= &http_context_class
,
1182 .flags
= URL_PROTOCOL_FLAG_NETWORK
,
1184 #endif /* CONFIG_HTTP_PROTOCOL */
1186 #if CONFIG_HTTPS_PROTOCOL
1189 URLProtocol ff_https_protocol
= {
1191 .url_open2
= http_open
,
1192 .url_read
= http_read
,
1193 .url_write
= http_write
,
1194 .url_seek
= http_seek
,
1195 .url_close
= http_close
,
1196 .url_get_file_handle
= http_get_file_handle
,
1197 .url_shutdown
= http_shutdown
,
1198 .priv_data_size
= sizeof(HTTPContext
),
1199 .priv_data_class
= &https_context_class
,
1200 .flags
= URL_PROTOCOL_FLAG_NETWORK
,
1202 #endif /* CONFIG_HTTPS_PROTOCOL */
1204 #if CONFIG_HTTPPROXY_PROTOCOL
1205 static int http_proxy_close(URLContext
*h
)
1207 HTTPContext
*s
= h
->priv_data
;
1209 ffurl_closep(&s
->hd
);
1213 static int http_proxy_open(URLContext
*h
, const char *uri
, int flags
)
1215 HTTPContext
*s
= h
->priv_data
;
1216 char hostname
[1024], hoststr
[1024];
1217 char auth
[1024], pathbuf
[1024], *path
;
1218 char lower_url
[100];
1219 int port
, ret
= 0, attempts
= 0;
1220 HTTPAuthType cur_auth_type
;
1224 if( s
->seekable
== 1 )
1229 av_url_split(NULL
, 0, auth
, sizeof(auth
), hostname
, sizeof(hostname
), &port
,
1230 pathbuf
, sizeof(pathbuf
), uri
);
1231 ff_url_join(hoststr
, sizeof(hoststr
), NULL
, NULL
, hostname
, port
, NULL
);
1236 ff_url_join(lower_url
, sizeof(lower_url
), "tcp", NULL
, hostname
, port
,
1239 ret
= ffurl_open(&s
->hd
, lower_url
, AVIO_FLAG_READ_WRITE
,
1240 &h
->interrupt_callback
, NULL
);
1244 authstr
= ff_http_auth_create_response(&s
->proxy_auth_state
, auth
,
1246 snprintf(s
->buffer
, sizeof(s
->buffer
),
1247 "CONNECT %s HTTP/1.1\r\n"
1249 "Connection: close\r\n"
1254 authstr
? "Proxy-" : "", authstr
? authstr
: "");
1257 if ((ret
= ffurl_write(s
->hd
, s
->buffer
, strlen(s
->buffer
))) < 0)
1260 s
->buf_ptr
= s
->buffer
;
1261 s
->buf_end
= s
->buffer
;
1264 cur_auth_type
= s
->proxy_auth_state
.auth_type
;
1266 /* Note: This uses buffering, potentially reading more than the
1267 * HTTP header. If tunneling a protocol where the server starts
1268 * the conversation, we might buffer part of that here, too.
1269 * Reading that requires using the proper ffurl_read() function
1270 * on this URLContext, not using the fd directly (as the tls
1271 * protocol does). This shouldn't be an issue for tls though,
1272 * since the client starts the conversation there, so there
1273 * is no extra data that we might buffer up here.
1275 ret
= http_read_header(h
, &new_loc
);
1280 if (s
->http_code
== 407 &&
1281 (cur_auth_type
== HTTP_AUTH_NONE
|| s
->proxy_auth_state
.stale
) &&
1282 s
->proxy_auth_state
.auth_type
!= HTTP_AUTH_NONE
&& attempts
< 2) {
1283 ffurl_closep(&s
->hd
);
1287 if (s
->http_code
< 400)
1289 ret
= ff_http_averror(s
->http_code
, AVERROR(EIO
));
1292 http_proxy_close(h
);
1296 static int http_proxy_write(URLContext
*h
, const uint8_t *buf
, int size
)
1298 HTTPContext
*s
= h
->priv_data
;
1299 return ffurl_write(s
->hd
, buf
, size
);
1302 URLProtocol ff_httpproxy_protocol
= {
1303 .name
= "httpproxy",
1304 .url_open
= http_proxy_open
,
1305 .url_read
= http_buf_read
,
1306 .url_write
= http_proxy_write
,
1307 .url_close
= http_proxy_close
,
1308 .url_get_file_handle
= http_get_file_handle
,
1309 .priv_data_size
= sizeof(HTTPContext
),
1310 .flags
= URL_PROTOCOL_FLAG_NETWORK
,
1312 #endif /* CONFIG_HTTPPROXY_PROTOCOL */