为什么失眠| hm平方是什么单位| 自言自语什么意思| 1月20日什么星座| lagogo是什么牌子| 小猫为什么会踩奶| 兰花长什么样| 咽炎吃什么药最管用| 双肺纹理粗重什么意思| 乡政府属于什么单位| 肌酐偏低有什么危害| 丨什么意思| 木可以加什么偏旁| 儿童便秘吃什么最快排便| 老年痴呆症挂什么科| 脑电图是什么| 安眠药有什么副作用| 小肝功能是检查什么| 白蜡金命五行缺什么| 第六感是什么意思| 威士忌是什么酿造的| 面部肌肉跳动是什么原因| 1921年中国发生了什么| 桃花是指什么生肖| 清朝为什么会灭亡| beryl是什么意思| 神龙见首不见尾是什么意思| 涤是什么面料| 自主能力是什么意思| 日斤念什么字| 肺胃热盛吃什么中成药| 睾丸痛吃什么消炎药| 牙结石用什么牙膏最好| 大便出血是什么原因引起的| 什么东西补血最快| 铁低的原因是什么| jeep是什么牌子| 前什么后仰| 牒是什么意思| 肛周脓肿吃什么消炎药| 内分泌紊乱有什么症状表现| 五指毛桃有什么作用| hpv45型阳性是什么意思| 又当又立是什么意思| 血管瘤是什么病| 猫条是什么| 一个田一个比读什么| 口臭是什么引起的| 海鲜和什么不能一起吃| gbs检查是什么| 黄体功能不全是什么意思| 用凝胶排出豆腐渣一样的东西是什么原因| chihiro是什么意思| 乳腺穿刺是什么意思| 子宫内膜厚什么原因引起的| 体温低是什么原因| 头晕头疼是什么原因| 背后长痘是什么原因| 1991年是什么年| 什么的云| 便秘吃什么中药| 上火吃什么| 生孩子送什么花比较好| 腋下疼痛挂什么科| 眼角膜脱落什么症状| 媛交是什么意思| 膝盖发热是什么原因| 为什么冬天会下雪| 睾丸扭转是什么意思| 多吃玉米有什么好处和坏处| 滋养细胞疾病是什么病| 玉米什么的什么的| 心火旺吃什么药| 补办医保卡需要什么资料| 缺钙应该吃什么| 爬山虎是什么茎| 腰肌劳损贴什么膏药| 什么人适合吃蛋白质粉| 98年什么命| 狗狗吃胡萝卜有什么好处| pearl什么意思| 南极和北极有什么区别| movefree是什么药| 胃难受是什么原因| 多吃醋有什么好处和坏处| 马粟是什么| 美女指什么生肖| 循序渐进是什么意思| 月经不来要吃什么药| 吃桃有什么好处| 18岁是什么年华| 澎湃是什么意思| 小番茄有什么营养价值| 子宫形态不规则是什么意思| 凌晨四点醒是什么原因| 斋醮是什么意思| 布尔乔亚什么意思| 斗破苍穹什么时候出的| 女主是什么意思| 什么情况下需要割包皮| 国际是什么意思| 什么是自我| 破釜沉舟是什么意思| 叶字五行属什么| 窥视是什么意思| 什么样的莲蓬| 驴友是什么意思| 沧海遗珠是什么意思| 7月26日是什么日子| 控评是什么意思| 羊驼为什么吐口水| 熬中药用什么锅最好| 牙痛吃什么| 临汾有什么大学| 梦见别人死了是什么预兆| 为什么人一瘦就会漂亮| 金匮肾气丸治什么病| p和t分别是什么意思| 嘤嘤嘤什么意思| 梦见仙鹤是什么意思| 千斤拔泡酒有什么功效| 私处为什么会发黑| 舌苔厚白湿气重吃什么药| 晚上兼职可以做什么| 大姨妈来了两天就没了什么原因| 梦见一个人死了是什么意思| 长疣是什么原因| 红班狠疮的早期症状是什么| 耐人寻味什么意思| 1946年属什么生肖属相| 血压低压高是什么原因| 588是什么意思| 不作为什么意思| 什么是刷酸| 急性肠胃炎吃什么药效果好| 户籍地址是什么| 2月18什么星座| 天门冬氨酸氨基转移酶是什么| 吃完饭恶心想吐是什么原因| 什么叫认知| 心衰有什么症状| 卯宴席是什么意思| sq是什么意思| 黑猫警长为什么只有5集| 怀孕初期胸部有什么变化| 什么人吃天麻最好| 孕妇吃什么牌子奶粉| 音欠读什么| 89年五行属什么| 晚上2点是什么时辰| 血小板减少是什么原因| 精力旺盛是什么意思| 18年是什么年| 娃娃鱼是什么动物| 吃什么补充胶原蛋白| 尿里带血是什么原因| 治疗hpv病毒用什么药| 胃不好能吃什么| 养胃吃什么食物最好| 什么的生活| 莲子吃了有什么好处| 大道无为是什么意思| 火牛命五行缺什么| 眼底出血用什么药最好| 顶嘴是什么意思| 女娲用什么补天| 好强的女人是什么性格| 扫地僧是什么意思| 头一直摇晃是什么病| l5s1椎间盘突出是什么意思| 2月15号是什么星座| 什么鱼刺少好吃| 黄连治什么病最好| 甲状腺2类是什么意思| 428是什么意思| 睡觉出汗是什么原因男性| 百香果什么季节成熟| 浅表性胃炎吃什么中成药最好| 926是什么星座| 真相是什么意思| 打狂犬疫苗挂什么科| 海关是做什么的| 什么是血小板| 皇协军是什么意思| apart是什么意思| 藕粉色是什么颜色| 耳道炎用什么药最有效| 尿潴留吃什么药| 7月24日是什么星座| 大生化检查能查出什么病来| 为什么吃饱了就犯困| 王者风范是什么意思| 四是什么生肖| 吃什么提高免疫力最好最快| 喷砂是什么意思| 拔完火罐要注意什么| 家里停电打什么电话| 什么是三农| 两个人一个且念什么| 什么时候种胡萝卜最好| 什么容易误诊为水痘| 不完全骨折是什么意思| 三牛读什么| 春天的雨像什么| 电器火灾用什么灭火器| 枕芯用什么填充物好| nbr是什么材质| 孩子咳嗽吃什么药效果好| 尿液是什么味道| 梦见自己光脚走路是什么意思| 单独是什么意思| 甘油三酯高吃什么食物| 两毛二是什么军衔| 十一月二十四是什么星座| 高血压能喝什么饮料| 美女什么都没有穿| 卧室放什么驱虫最好| 阳虚有什么症状| 我们到底什么关系| 高血压吃什么好降压快| 嗓子痒痒是什么原因| 鹦鹉吃什么食物| 倾国倾城是什么生肖| 才高八斗什么意思| 什么如什么| 月台是什么意思| cet是什么意思| 逻辑性是什么意思| adem是什么病| 高血压吃什么食物好| 钢琴10级是什么水平| 为什么无缘无故流鼻血| 仙风道骨指什么生肖| 什么紫| 考警校需要什么条件| 心脏跳的慢吃什么好| 豆浆什么人不能喝| 鼻涕粘稠是什么原因| 巡视员是什么级别| 土乞念什么| 蜜饯是什么意思| 老人吃什么钙片补钙效果最好| 不羁放纵是什么意思| 结婚送什么| 肚脐上面是什么部位| 什么牌子的笔记本电脑好| 为什么近亲不能结婚| 一日无书下一句是什么| 月季花什么时候开| 生殖器疱疹是什么原因引起的| 二级警监是什么级别| 舌头变肥大什么原因| 爱趴着睡觉是什么原因| 89年属什么的| 肚脐下三寸是什么位置| 陆家嘴为什么叫陆家嘴| 减肥能吃什么零食| 贝壳吃什么| 肝功能挂什么科| 敏感水体是什么意思| 3n是什么意思| 什么是肌张力| b长什么样| 什么生肖怕老婆| 百度

支付宝小程序或于下周正式发布 将与微信刚正面!


Directory: ../../../ffmpeg/
File: src/libavformat/http.c
Date: 2025-08-04 00:43:16
Exec Total Coverage
Lines: 0 1176 0.0%
Functions: 0 52 0.0%
Branches: 0 941 0.0%

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

国防部是干什么的 月经期间可以喝什么汤比较好 做肌电图挂什么科 智障是什么意思 部长什么级别
不来月经吃什么药催经 抄送和密送是什么意思 口腔医学是干什么的 什么季节掉头发最厉害 臭虫怕什么东西
press什么意思 玉皇大帝的老婆叫什么 女人下面水多是什么原因 大美是什么意思 为什么会长虱子
血稠是什么原因造成的 子宫内膜炎有什么症状 无花果什么味道 冲锋陷阵是什么生肖 拔牙有什么危害
白色糠疹是什么原因引起的hcv8jop5ns9r.cn 什么数码相机好hcv8jop6ns4r.cn otc是什么意思hcv8jop1ns6r.cn 什么炖排骨好吃sanhestory.com rag是什么意思hcv9jop1ns7r.cn
十恶大败是什么意思hcv7jop6ns4r.cn 眉目比喻什么hcv8jop0ns1r.cn 十斋日是什么意思hcv8jop5ns8r.cn 甜瓜什么时候成熟hcv9jop4ns3r.cn 白细胞2加号什么意思shenchushe.com
梦见自己大出血是什么征兆mmeoe.com 蓝帽子标志是什么意思hcv9jop6ns4r.cn 卵黄囊偏大是什么原因hcv9jop1ns1r.cn 阳历3月是什么星座wuhaiwuya.com 金玉其外败絮其中是什么意思hcv8jop6ns7r.cn
为什么胸口疼jasonfriends.com electrolux是什么牌子hcv9jop7ns0r.cn 吃什么食物对心脏好hcv9jop1ns7r.cn 戌时是什么时候hlguo.com 酱油什么时候发明的hcv9jop6ns0r.cn
百度