/src/gpac/src/utils/downloader.c
Line | Count | Source |
1 | | /* |
2 | | * GPAC Multimedia Framework |
3 | | * |
4 | | * Authors: Jean Le Feuvre |
5 | | * Copyright (c) Telecom ParisTech 2005-2025 |
6 | | * All rights reserved |
7 | | * |
8 | | * This file is part of GPAC / downloader sub-project |
9 | | * |
10 | | * GPAC is free software; you can redistribute it and/or modify |
11 | | * it under the terms of the GNU Lesser General Public License as published by |
12 | | * the Free Software Foundation; either version 2, or (at your option) |
13 | | * any later version. |
14 | | * |
15 | | * GPAC is distributed in the hope that it will be useful, |
16 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
17 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
18 | | * GNU Lesser General Public License for more details. |
19 | | * |
20 | | * You should have received a copy of the GNU Lesser General Public |
21 | | * License along with this library; see the file COPYING. If not, write to |
22 | | * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. |
23 | | * |
24 | | */ |
25 | | |
26 | | #include "downloader.h" |
27 | | |
28 | | #ifndef GPAC_DISABLE_NETWORK |
29 | | |
30 | | static void gf_dm_connect(GF_DownloadSession *sess); |
31 | | |
32 | | void dm_sess_sk_del(GF_DownloadSession *sess) |
33 | 0 | { |
34 | | #ifdef GPAC_HAS_CURL |
35 | | if (sess->curl_hnd) { |
36 | | curl_destroy(sess); |
37 | | return; |
38 | | } |
39 | | #endif |
40 | 0 | if (sess->sock) { |
41 | 0 | GF_Socket *sock = sess->sock; |
42 | 0 | sess->sock = NULL; |
43 | 0 | GF_LOG(GF_LOG_DEBUG, GF_LOG_HTTP, ("[Downloader] closing socket\n")); |
44 | 0 | if (sess->sock_group) gf_sk_group_unregister(sess->sock_group, sock); |
45 | 0 | gf_sk_del(sock); |
46 | | #ifdef GPAC_HAS_HTTP2 |
47 | | sess->h2_upgrade_state = 0; |
48 | | #endif |
49 | 0 | } |
50 | 0 | } |
51 | | |
52 | | static void sess_connection_closed(GF_DownloadSession *sess) |
53 | 0 | { |
54 | | #ifdef GPAC_HTTPMUX |
55 | | if (sess->hmux_sess) { |
56 | | sess->hmux_sess->do_shutdown = GF_TRUE; |
57 | | sess->hmux_switch_sess = GF_TRUE; |
58 | | } |
59 | | #endif |
60 | 0 | } |
61 | | |
62 | | GF_Err dm_sess_write(GF_DownloadSession *session, const u8 *buffer, u32 size) |
63 | 0 | { |
64 | 0 | GF_Err e; |
65 | 0 | u32 written=0; |
66 | 0 | GF_DownloadSession *par_sess = session; |
67 | | #ifdef GPAC_HTTPMUX |
68 | | //if h2 we aggregate pending frames on the session currently holding the HTTP2 session |
69 | | if (session->hmux_sess) par_sess = session->hmux_sess->net_sess; |
70 | | #endif |
71 | | |
72 | | //we are writing and we already have pending data, append and try to flush after |
73 | 0 | if (par_sess->async_buf_size && (buffer != par_sess->async_buf)) { |
74 | 0 | e = GF_IP_NETWORK_EMPTY; |
75 | 0 | } else |
76 | 0 | #ifdef GPAC_HAS_SSL |
77 | 0 | if (session->ssl) { |
78 | 0 | e = gf_ssl_write(session, buffer, size, &written); |
79 | 0 | if (e==GF_IP_NETWORK_FAILURE) |
80 | 0 | e = GF_IP_CONNECTION_CLOSED; |
81 | 0 | } else |
82 | 0 | #endif |
83 | 0 | { |
84 | 0 | e = gf_sk_send_ex(session->sock, buffer, size, &written); |
85 | 0 | } |
86 | | |
87 | |
|
88 | 0 | if (!(session->flags & GF_NETIO_SESSION_NO_BLOCK) || (e!=GF_IP_NETWORK_EMPTY)) { |
89 | 0 | par_sess->async_buf_size = 0; |
90 | 0 | return e; |
91 | 0 | } |
92 | | |
93 | | //we are blocking, store content |
94 | 0 | u32 remain = size - written; |
95 | 0 | if (buffer == par_sess->async_buf) { |
96 | 0 | if (written) { |
97 | 0 | if (par_sess->async_buf_size >= written) { |
98 | 0 | memmove(par_sess->async_buf, par_sess->async_buf + written, remain); |
99 | 0 | par_sess->async_buf_size -= written; |
100 | 0 | } else { |
101 | 0 | gf_assert(0); |
102 | 0 | par_sess->async_buf_size = 0; |
103 | 0 | } |
104 | 0 | } |
105 | 0 | } else { |
106 | 0 | if (par_sess->async_buf_alloc < par_sess->async_buf_size + remain) { |
107 | 0 | par_sess->async_buf_alloc = par_sess->async_buf_size + remain; |
108 | 0 | par_sess->async_buf = gf_realloc(par_sess->async_buf, par_sess->async_buf_alloc); |
109 | 0 | if (!par_sess->async_buf) return GF_OUT_OF_MEM; |
110 | 0 | } |
111 | 0 | memcpy(par_sess->async_buf+par_sess->async_buf_size, buffer + written, remain); |
112 | 0 | par_sess->async_buf_size += remain; |
113 | 0 | } |
114 | 0 | return GF_OK; |
115 | 0 | } |
116 | | |
117 | | |
118 | | static Bool gf_dm_is_local(GF_DownloadManager *dm, const char *url) |
119 | 0 | { |
120 | 0 | if (!strnicmp(url, "file://", 7)) return GF_TRUE; |
121 | 0 | if (!strstr(url, "://")) return GF_TRUE; |
122 | 0 | return GF_FALSE; |
123 | 0 | } |
124 | | |
125 | | Bool gf_dm_can_handle_url(const char *url) |
126 | 0 | { |
127 | 0 | if (!strnicmp(url, "http://", 7)) return GF_TRUE; |
128 | 0 | #ifdef GPAC_HAS_SSL |
129 | 0 | if (!strnicmp(url, "https://", 8)) return GF_TRUE; |
130 | 0 | #endif |
131 | | |
132 | | #ifdef GPAC_HAS_CURL |
133 | | if (curl_can_handle_url(url)) return GF_TRUE; |
134 | | #endif |
135 | 0 | return GF_FALSE; |
136 | 0 | } |
137 | | |
138 | | |
139 | | void gf_dm_sess_set_header_ex(GF_DownloadSession *sess, const char *name, const char *value, Bool allow_overwrite) |
140 | 0 | { |
141 | 0 | GF_HTTPHeader *hdr; |
142 | 0 | if (!sess) return; |
143 | | |
144 | | #ifdef GPAC_HTTPMUX |
145 | | if (sess->hmux_sess |
146 | | #ifdef GPAC_HAS_HTTP2 |
147 | | || sess->h2_upgrade_settings |
148 | | #endif |
149 | | ) { |
150 | | if (!stricmp(name, "Transfer-Encoding")) |
151 | | return; |
152 | | if (!stricmp(name, "Connection")) return; |
153 | | if (!stricmp(name, "Keep-Alive")) return; |
154 | | } |
155 | | #endif |
156 | | //check existing headers |
157 | 0 | u32 i, count = gf_list_count(sess->headers); |
158 | 0 | for (i=0; i<count; i++) { |
159 | 0 | hdr = gf_list_get(sess->headers, i); |
160 | 0 | if (stricmp(hdr->name, name)) continue; |
161 | 0 | if (!allow_overwrite) return; |
162 | | |
163 | 0 | gf_free(hdr->value); |
164 | 0 | if (value) { |
165 | 0 | hdr->value = gf_strdup(value); |
166 | 0 | return; |
167 | 0 | } |
168 | 0 | gf_list_rem(sess->headers, i); |
169 | 0 | gf_free(hdr->name); |
170 | 0 | gf_free(hdr); |
171 | 0 | return; |
172 | 0 | } |
173 | | |
174 | 0 | GF_SAFEALLOC(hdr, GF_HTTPHeader) |
175 | 0 | if (hdr) { |
176 | 0 | hdr->name = gf_strdup(name); |
177 | 0 | hdr->value = gf_strdup(value); |
178 | 0 | gf_list_add(sess->headers, hdr); |
179 | 0 | } |
180 | 0 | } |
181 | | |
182 | | void gf_dm_sess_set_header(GF_DownloadSession *sess, const char *name, const char *value) |
183 | 0 | { |
184 | 0 | gf_dm_sess_set_header_ex(sess, name, value, GF_TRUE); |
185 | 0 | } |
186 | | |
187 | | GF_Err gf_dm_sess_send_reply(GF_DownloadSession *sess, u32 reply_code, const char *response_body, u32 body_len, Bool no_body) |
188 | 0 | { |
189 | 0 | u32 i, count; |
190 | 0 | GF_Err e; |
191 | 0 | char szFmt[50]; |
192 | 0 | char *rsp_buf = NULL; |
193 | 0 | if (!sess || !sess->server_mode) return GF_BAD_PARAM; |
194 | | |
195 | 0 | count = gf_list_count(sess->headers); |
196 | |
|
197 | | #ifdef GPAC_HAS_HTTP2 |
198 | | e = http2_check_upgrade(sess); |
199 | | if (e) return e; |
200 | | #endif |
201 | |
|
202 | | #ifdef GPAC_HTTPMUX |
203 | | if (sess->hmux_sess) { |
204 | | return sess->hmux_sess->send_reply(sess, reply_code, response_body, body_len, no_body); |
205 | | } |
206 | | #endif |
207 | | |
208 | |
|
209 | 0 | sprintf(szFmt, "HTTP/1.1 %d ", reply_code); |
210 | 0 | gf_dynstrcat(&rsp_buf, szFmt, NULL); |
211 | 0 | switch (reply_code) { |
212 | 0 | case 400: gf_dynstrcat(&rsp_buf, "Bad Request", NULL); break; |
213 | 0 | case 401: gf_dynstrcat(&rsp_buf, "Unauthorized", NULL); break; |
214 | 0 | case 403: gf_dynstrcat(&rsp_buf, "Forbidden", NULL); break; |
215 | 0 | case 405: gf_dynstrcat(&rsp_buf, "Not Allowed", NULL); break; |
216 | 0 | case 416: gf_dynstrcat(&rsp_buf, "Requested Range Not Satisfiable", NULL); break; |
217 | 0 | case 411: gf_dynstrcat(&rsp_buf, "Length Required", NULL); break; |
218 | 0 | case 404: gf_dynstrcat(&rsp_buf, "Not Found", NULL); break; |
219 | 0 | case 501: gf_dynstrcat(&rsp_buf, "Not Implemented", NULL); break; |
220 | 0 | case 500: gf_dynstrcat(&rsp_buf, "Internal Server Error", NULL); break; |
221 | 0 | case 304: gf_dynstrcat(&rsp_buf, "Not Modified", NULL); break; |
222 | 0 | case 204: gf_dynstrcat(&rsp_buf, "No Content", NULL); break; |
223 | 0 | case 206: gf_dynstrcat(&rsp_buf, "Partial Content", NULL); break; |
224 | 0 | case 200: gf_dynstrcat(&rsp_buf, "OK", NULL); break; |
225 | 0 | case 201: gf_dynstrcat(&rsp_buf, "Created", NULL); break; |
226 | 0 | case 101: gf_dynstrcat(&rsp_buf, "Switching Protocols", NULL); break; |
227 | 0 | default: |
228 | 0 | gf_dynstrcat(&rsp_buf, "ERROR", NULL); break; |
229 | 0 | } |
230 | 0 | gf_dynstrcat(&rsp_buf, "\r\n", NULL); |
231 | 0 | if (!rsp_buf) return GF_OUT_OF_MEM; |
232 | | |
233 | 0 | for (i=0; i<count; i++) { |
234 | 0 | GF_HTTPHeader *hdr = gf_list_get(sess->headers, i); |
235 | 0 | gf_dynstrcat(&rsp_buf, hdr->name, NULL); |
236 | 0 | gf_dynstrcat(&rsp_buf, ": ", NULL); |
237 | 0 | gf_dynstrcat(&rsp_buf, hdr->value, NULL); |
238 | 0 | gf_dynstrcat(&rsp_buf, "\r\n", NULL); |
239 | 0 | } |
240 | 0 | gf_dynstrcat(&rsp_buf, "\r\n", NULL); |
241 | 0 | if (!rsp_buf) return GF_OUT_OF_MEM; |
242 | | |
243 | 0 | GF_LOG(GF_LOG_INFO, GF_LOG_HTTP, ("[%s] send reply for %s:\n%s\n", sess->log_name, sess->orig_url, rsp_buf)); |
244 | |
|
245 | 0 | count = (u32) strlen(rsp_buf); |
246 | 0 | if (response_body) { |
247 | 0 | rsp_buf = gf_realloc(rsp_buf, count+body_len); |
248 | 0 | if (!rsp_buf) return GF_OUT_OF_MEM; |
249 | 0 | memcpy(rsp_buf+count, response_body, body_len); |
250 | 0 | count+=body_len; |
251 | 0 | } |
252 | 0 | e = dm_sess_write(sess, rsp_buf, count); |
253 | 0 | gf_free(rsp_buf); |
254 | 0 | return e; |
255 | 0 | } |
256 | | |
257 | | void gf_dm_sess_clear_headers(GF_DownloadSession *sess) |
258 | 0 | { |
259 | 0 | while (gf_list_count(sess->headers)) { |
260 | 0 | GF_HTTPHeader *hdr = (GF_HTTPHeader*)gf_list_last(sess->headers); |
261 | 0 | gf_list_rem_last(sess->headers); |
262 | 0 | gf_free(hdr->name); |
263 | 0 | gf_free(hdr->value); |
264 | 0 | gf_free(hdr); |
265 | 0 | } |
266 | 0 | if (sess->mime_type) { |
267 | 0 | gf_free(sess->mime_type); |
268 | 0 | sess->mime_type = NULL; |
269 | 0 | } |
270 | 0 | } |
271 | | |
272 | | |
273 | | void gf_dm_disconnect(GF_DownloadSession *sess, HTTPCloseType close_type) |
274 | 0 | { |
275 | 0 | gf_assert( sess ); |
276 | 0 | if (sess->connection_close) close_type = HTTP_RESET_CONN; |
277 | 0 | sess->connection_close = GF_FALSE; |
278 | 0 | sess->remaining_data_size = 0; |
279 | 0 | if (sess->async_req_reply) gf_free(sess->async_req_reply); |
280 | 0 | sess->async_req_reply = NULL; |
281 | 0 | sess->async_req_reply_size = 0; |
282 | 0 | sess->async_buf_size = 0; |
283 | 0 | if (sess->cached_file) { |
284 | 0 | gf_fclose(sess->cached_file); |
285 | 0 | sess->cached_file = NULL; |
286 | 0 | } |
287 | |
|
288 | 0 | if ((sess->status >= GF_NETIO_DISCONNECTED) |
289 | | #ifdef GPAC_HAS_CURL |
290 | | && !sess->curl_hnd |
291 | | #endif |
292 | 0 | ) { |
293 | 0 | if (close_type && sess->use_cache_file && sess->cache_entry) { |
294 | 0 | gf_cache_close_write_cache(sess->cache_entry, sess, GF_FALSE); |
295 | 0 | } |
296 | 0 | return; |
297 | 0 | } |
298 | 0 | GF_LOG(GF_LOG_DEBUG, GF_LOG_CORE, ("[Downloader] gf_dm_disconnect(%p)\n", sess )); |
299 | |
|
300 | 0 | gf_mx_p(sess->mx); |
301 | |
|
302 | 0 | if (!sess->server_mode) { |
303 | 0 | Bool do_close = (close_type || !(sess->flags & GF_NETIO_SESSION_PERSISTENT)) ? GF_TRUE : GF_FALSE; |
304 | | #ifdef GPAC_HTTPMUX |
305 | | if (sess->hmux_sess) { |
306 | | do_close = (close_type==HTTP_RESET_CONN) ? GF_TRUE : GF_FALSE; |
307 | | } |
308 | | //if H2 stream is still valid, issue a reset stream |
309 | | if (sess->hmux_sess && (sess->hmux_stream_id>=0) && (sess->put_state!=2)) |
310 | | sess->hmux_sess->stream_reset(sess, GF_FALSE); |
311 | | #endif |
312 | |
|
313 | | #ifdef GPAC_HAS_CURL |
314 | | //always remove curl session, let multi-connection manager handle disconnect/reconnect |
315 | | if (sess->curl_hnd) { |
316 | | sess->local_buf_len = 0; |
317 | | dm_sess_sk_del(sess); |
318 | | } else |
319 | | #endif |
320 | |
|
321 | 0 | if (do_close) { |
322 | | #ifdef GPAC_HTTPMUX |
323 | | if (sess->hmux_sess) { |
324 | | sess->hmux_sess->do_shutdown = GF_TRUE; |
325 | | hmux_detach_session(sess->hmux_sess, sess); |
326 | | } |
327 | | #endif |
328 | |
|
329 | 0 | #ifdef GPAC_HAS_SSL |
330 | 0 | if (sess->ssl) { |
331 | 0 | SSL_shutdown(sess->ssl); |
332 | 0 | SSL_free(sess->ssl); |
333 | 0 | sess->ssl = NULL; |
334 | 0 | } |
335 | 0 | #endif |
336 | 0 | dm_sess_sk_del(sess); |
337 | 0 | } |
338 | |
|
339 | 0 | if (close_type && sess->use_cache_file && sess->cache_entry) { |
340 | 0 | gf_cache_close_write_cache(sess->cache_entry, sess, GF_FALSE); |
341 | 0 | } |
342 | 0 | } |
343 | | |
344 | |
|
345 | 0 | sess->status = GF_NETIO_DISCONNECTED; |
346 | 0 | if (sess->num_retry) sess->num_retry--; |
347 | |
|
348 | 0 | gf_mx_v(sess->mx); |
349 | 0 | } |
350 | | |
351 | | GF_EXPORT |
352 | | void gf_dm_sess_del(GF_DownloadSession *sess) |
353 | 0 | { |
354 | 0 | if (!sess) |
355 | 0 | return; |
356 | | |
357 | 0 | GF_LOG(GF_LOG_DEBUG, GF_LOG_HTTP, ("[Downloader] Destroy session URL %s\n", sess->orig_url)); |
358 | | /*self-destruction, let the download manager destroy us*/ |
359 | 0 | if (sess->th || sess->ftask) { |
360 | 0 | sess->destroy = GF_TRUE; |
361 | 0 | if (sess->ftask->in_task) |
362 | 0 | return; |
363 | 0 | } |
364 | 0 | gf_dm_disconnect(sess, HTTP_CLOSE); |
365 | 0 | gf_dm_sess_clear_headers(sess); |
366 | | |
367 | | /*if threaded wait for thread exit*/ |
368 | 0 | if (sess->th) { |
369 | 0 | while (!(sess->flags & GF_DOWNLOAD_SESSION_THREAD_DEAD)) |
370 | 0 | gf_sleep(1); |
371 | 0 | gf_th_stop(sess->th); |
372 | 0 | gf_th_del(sess->th); |
373 | 0 | sess->th = NULL; |
374 | 0 | } |
375 | |
|
376 | 0 | if (sess->dm) { |
377 | 0 | gf_mx_p(sess->dm->cache_mx); |
378 | 0 | gf_list_del_item(sess->dm->all_sessions, sess); |
379 | 0 | gf_mx_v(sess->dm->cache_mx); |
380 | 0 | } |
381 | |
|
382 | 0 | gf_cache_remove_entry_from_session(sess); |
383 | 0 | if (sess->orig_url) gf_free(sess->orig_url); |
384 | 0 | if (sess->orig_url_before_redirect) gf_free(sess->orig_url_before_redirect); |
385 | 0 | if (sess->server_name) gf_free(sess->server_name); |
386 | 0 | sess->server_name = NULL; |
387 | 0 | if (sess->remote_path) gf_free(sess->remote_path); |
388 | | /* Credentials are stored into the sess->dm */ |
389 | 0 | if (sess->creds) sess->creds = NULL; |
390 | 0 | if (sess->init_data) gf_free(sess->init_data); |
391 | 0 | if (sess->remaining_data) gf_free(sess->remaining_data); |
392 | 0 | if (sess->async_req_reply) gf_free(sess->async_req_reply); |
393 | |
|
394 | 0 | sess->orig_url = sess->server_name = sess->remote_path; |
395 | 0 | sess->creds = NULL; |
396 | |
|
397 | | #ifdef GPAC_HTTPMUX |
398 | | if (sess->hmux_sess) { |
399 | | sess->hmux_sess->setup_session(sess, GF_TRUE); |
400 | | gf_mx_p(sess->mx); |
401 | | hmux_detach_session(sess->hmux_sess, sess); |
402 | | gf_mx_v(sess->mx); |
403 | | } |
404 | | #endif |
405 | |
|
406 | | #ifdef GPAC_HAS_HTTP2 |
407 | | if (sess->h2_upgrade_settings) |
408 | | gf_free(sess->h2_upgrade_settings); |
409 | | #endif |
410 | |
|
411 | | #if defined(GPAC_HTTPMUX) || defined(GPAC_HAS_CURL) |
412 | | if (sess->local_buf) |
413 | | gf_free(sess->local_buf); |
414 | | #endif |
415 | | |
416 | | //free this once we have reassigned the session |
417 | 0 | if (sess->async_buf) gf_free(sess->async_buf); |
418 | |
|
419 | 0 | #ifdef GPAC_HAS_SSL |
420 | | //in server mode SSL context is managed by caller |
421 | 0 | if (sess->ssl) { |
422 | 0 | GF_LOG(GF_LOG_DEBUG, GF_LOG_HTTP, ("[Downloader] shut down SSL context\n")); |
423 | 0 | SSL_shutdown(sess->ssl); |
424 | 0 | SSL_free(sess->ssl); |
425 | 0 | sess->ssl = NULL; |
426 | 0 | } |
427 | 0 | #endif |
428 | 0 | dm_sess_sk_del(sess); |
429 | |
|
430 | 0 | gf_list_del(sess->headers); |
431 | |
|
432 | | #ifdef GPAC_HAS_CURL |
433 | | gf_assert(!sess->curl_hnd); |
434 | | if (sess->curl_hdrs) curl_slist_free_all(sess->curl_hdrs); |
435 | | #endif |
436 | |
|
437 | 0 | #ifndef GPAC_DISABLE_LOG |
438 | 0 | if (sess->log_name) gf_free(sess->log_name); |
439 | 0 | #endif |
440 | 0 | assert(!sess->ftask || !sess->ftask->in_task || !sess->mx); |
441 | 0 | gf_mx_del(sess->mx); |
442 | |
|
443 | 0 | if (sess->http_buf) gf_free(sess->http_buf); |
444 | |
|
445 | 0 | if (sess->ftask) { |
446 | 0 | sess->ftask->sess = NULL; |
447 | 0 | sess->ftask = NULL; |
448 | 0 | } |
449 | |
|
450 | 0 | gf_free(sess); |
451 | 0 | } |
452 | | |
453 | | void gf_dm_sess_notify_state(GF_DownloadSession *sess, GF_NetIOStatus dnload_status, GF_Err error) |
454 | 0 | { |
455 | 0 | if (!sess->user_proc) return; |
456 | 0 | GF_NETIO_Parameter par; |
457 | 0 | sess->in_callback = GF_TRUE; |
458 | 0 | memset(&par, 0, sizeof(GF_NETIO_Parameter)); |
459 | 0 | par.msg_type = dnload_status; |
460 | 0 | par.error = error; |
461 | 0 | par.sess = sess; |
462 | 0 | par.reply = 200; |
463 | 0 | sess->user_proc(sess->usr_cbk, &par); |
464 | 0 | sess->in_callback = GF_FALSE; |
465 | 0 | } |
466 | | |
467 | | static void gf_dm_sess_user_io(GF_DownloadSession *sess, GF_NETIO_Parameter *par) |
468 | 0 | { |
469 | 0 | if (sess->user_proc) { |
470 | 0 | sess->in_callback = GF_TRUE; |
471 | 0 | par->sess = sess; |
472 | 0 | sess->user_proc(sess->usr_cbk, par); |
473 | 0 | sess->in_callback = GF_FALSE; |
474 | 0 | } |
475 | 0 | } |
476 | | |
477 | | |
478 | | GF_EXPORT |
479 | | GF_Err gf_dm_sess_last_error(GF_DownloadSession *sess) |
480 | 0 | { |
481 | 0 | if (!sess) |
482 | 0 | return GF_BAD_PARAM; |
483 | 0 | return sess->last_error; |
484 | 0 | } |
485 | | |
486 | | GF_EXPORT |
487 | | void gf_dm_url_info_init(GF_URL_Info * info) |
488 | 0 | { |
489 | 0 | memset(info, 0, sizeof(GF_URL_Info)); |
490 | 0 | } |
491 | | |
492 | | GF_EXPORT |
493 | 0 | void gf_dm_url_info_del(GF_URL_Info * info) { |
494 | 0 | if (!info) |
495 | 0 | return; |
496 | 0 | if (info->protocol) |
497 | 0 | gf_free(info->protocol); |
498 | 0 | if (info->canonicalRepresentation) |
499 | 0 | gf_free(info->canonicalRepresentation); |
500 | 0 | if (info->password) |
501 | 0 | gf_free(info->password); |
502 | 0 | if (info->userName) |
503 | 0 | gf_free(info->userName); |
504 | 0 | if (info->remotePath) |
505 | 0 | gf_free(info->remotePath); |
506 | 0 | if (info->server_name) |
507 | 0 | gf_free(info->server_name); |
508 | 0 | gf_dm_url_info_init(info); |
509 | 0 | } |
510 | | |
511 | | /** |
512 | | \param url The url to parse for protocol |
513 | | \param info The info to fill |
514 | | \return Returns the offset in url of the protocol found -1 if not found |
515 | | */ |
516 | | static s32 gf_dm_parse_protocol(const char * url, GF_URL_Info * info) |
517 | 0 | { |
518 | 0 | gf_assert(info); |
519 | 0 | gf_assert(url); |
520 | 0 | if (!strnicmp(url, "http://", 7)) { |
521 | 0 | info->port = 80; |
522 | 0 | info->protocol = gf_strdup("http"); |
523 | 0 | return 7; |
524 | 0 | } |
525 | 0 | else if (!strnicmp(url, "https://", 8)) { |
526 | 0 | info->port = 443; |
527 | | #ifndef GPAC_HAS_SSL |
528 | | return -1; |
529 | | #endif |
530 | 0 | info->protocol = gf_strdup("https"); |
531 | 0 | return 8; |
532 | 0 | } |
533 | 0 | char *sep = strstr(url, "://"); |
534 | 0 | if (sep) { |
535 | 0 | sep[0] = 0; |
536 | 0 | info->protocol = gf_strdup(url); |
537 | 0 | sep[0] = ':'; |
538 | 0 | return 3 + (u32) (sep-url); |
539 | 0 | } |
540 | 0 | return -1; |
541 | 0 | } |
542 | | |
543 | | GF_EXPORT |
544 | 0 | GF_Err gf_dm_get_url_info(const char * url, GF_URL_Info * info, const char * baseURL) { |
545 | 0 | char *tmp, *tmp_url, *current_pos, *urlConcatenateWithBaseURL, *ipv6; |
546 | 0 | char * copyOfUrl; |
547 | 0 | s32 proto_offset; |
548 | 0 | u32 default_port; |
549 | 0 | gf_dm_url_info_del(info); |
550 | 0 | urlConcatenateWithBaseURL = NULL; |
551 | 0 | proto_offset = gf_dm_parse_protocol(url, info); |
552 | 0 | default_port = info->port; |
553 | |
|
554 | 0 | if (proto_offset > 0) { |
555 | 0 | url += proto_offset; |
556 | 0 | } else { |
557 | | /*relative URL*/ |
558 | 0 | if (!strstr(url, "://")) { |
559 | 0 | u32 i; |
560 | 0 | gf_dm_url_info_del(info); |
561 | 0 | gf_dm_url_info_init(info); |
562 | 0 | if (baseURL) { |
563 | 0 | urlConcatenateWithBaseURL = gf_url_concatenate(baseURL, url); |
564 | | /*relative file path*/ |
565 | 0 | if (!strstr(baseURL, "://")) { |
566 | 0 | info->protocol = gf_strdup("file"); |
567 | 0 | info->canonicalRepresentation = urlConcatenateWithBaseURL; |
568 | 0 | return GF_OK; |
569 | 0 | } |
570 | 0 | proto_offset = gf_dm_parse_protocol(urlConcatenateWithBaseURL, info); |
571 | 0 | } else { |
572 | 0 | proto_offset = -1; |
573 | 0 | } |
574 | | |
575 | 0 | if (proto_offset < 0) { |
576 | 0 | tmp = urlConcatenateWithBaseURL; |
577 | 0 | gf_assert( ! info->remotePath ); |
578 | 0 | info->remotePath = gf_url_percent_encode(tmp); |
579 | 0 | gf_free( urlConcatenateWithBaseURL ); |
580 | 0 | urlConcatenateWithBaseURL = NULL; |
581 | |
|
582 | 0 | if (!info->remotePath) { |
583 | 0 | GF_LOG(GF_LOG_WARNING, GF_LOG_HTTP, ("[Network] No supported protocol for url %s\n", url)); |
584 | 0 | gf_dm_url_info_del(info); |
585 | 0 | return GF_BAD_PARAM; |
586 | 0 | } |
587 | 0 | for (i=0; i<strlen(info->remotePath); i++) |
588 | 0 | if (info->remotePath[i]=='\\') info->remotePath[i]='/'; |
589 | |
|
590 | 0 | info->canonicalRepresentation = NULL; |
591 | 0 | gf_dynstrcat(&info->canonicalRepresentation, info->protocol, NULL); |
592 | 0 | gf_dynstrcat(&info->canonicalRepresentation, "://", NULL); |
593 | 0 | gf_dynstrcat(&info->canonicalRepresentation, info->remotePath, NULL); |
594 | |
|
595 | 0 | return GF_OK; |
596 | 0 | } else { |
597 | | /* We continue the parsing as usual */ |
598 | 0 | url = urlConcatenateWithBaseURL + proto_offset; |
599 | 0 | } |
600 | 0 | } else { |
601 | 0 | GF_LOG(GF_LOG_WARNING, GF_LOG_HTTP, ("[Network] No supported protocol for url %s\n", url)); |
602 | 0 | gf_dm_url_info_del(info); |
603 | 0 | return GF_BAD_PARAM; |
604 | 0 | } |
605 | 0 | } |
606 | 0 | gf_assert( proto_offset >= 0 ); |
607 | 0 | tmp = strchr(url, '/'); |
608 | | //patch for some broken url http://servername?cgi |
609 | 0 | if (!tmp) |
610 | 0 | tmp = strchr(url, '?'); |
611 | 0 | gf_assert( !info->remotePath ); |
612 | 0 | info->remotePath = gf_url_percent_encode(tmp ? tmp : "/"); |
613 | 0 | if (info->remotePath[0]=='?') { |
614 | 0 | char *rpath = NULL; |
615 | 0 | gf_dynstrcat(&rpath, "/", NULL); |
616 | 0 | gf_dynstrcat(&rpath, info->remotePath, NULL); |
617 | 0 | gf_free(info->remotePath); |
618 | 0 | info->remotePath = rpath; |
619 | 0 | } |
620 | |
|
621 | 0 | if (tmp) { |
622 | 0 | tmp[0] = 0; |
623 | 0 | copyOfUrl = gf_strdup(url); |
624 | 0 | tmp[0] = '/'; |
625 | 0 | } else { |
626 | 0 | copyOfUrl = gf_strdup(url); |
627 | 0 | } |
628 | 0 | tmp_url = copyOfUrl; |
629 | 0 | current_pos = tmp_url; |
630 | 0 | tmp = strrchr(tmp_url, '@'); |
631 | 0 | if (tmp) { |
632 | 0 | current_pos = tmp + 1; |
633 | 0 | gf_assert( ! info->server_name ); |
634 | 0 | info->server_name = gf_strdup(current_pos); |
635 | 0 | tmp[0] = 0; |
636 | 0 | tmp = strchr(tmp_url, ':'); |
637 | |
|
638 | 0 | if (tmp) { |
639 | 0 | tmp[0] = 0; |
640 | 0 | info->password = gf_strdup(tmp+1); |
641 | 0 | } |
642 | 0 | info->userName = gf_strdup(tmp_url); |
643 | 0 | } else { |
644 | 0 | gf_assert( ! info->server_name ); |
645 | 0 | info->server_name = gf_strdup(tmp_url); |
646 | 0 | } |
647 | | |
648 | | //scan for port number after IPv6 address ']' end char |
649 | 0 | ipv6 = strrchr(current_pos, ']'); |
650 | 0 | tmp = strrchr(ipv6 ? ipv6 : current_pos, ':'); |
651 | |
|
652 | 0 | if (tmp) { |
653 | 0 | info->port = atoi(tmp+1); |
654 | 0 | tmp[0] = 0; |
655 | 0 | if (info->server_name) { |
656 | 0 | gf_free(info->server_name); |
657 | 0 | } |
658 | 0 | info->server_name = gf_strdup(current_pos); |
659 | 0 | } |
660 | | |
661 | | /* We dont't want orig_url to contain user/passwords for security reasons or mismatch in cache hit */ |
662 | 0 | info->canonicalRepresentation = NULL; |
663 | 0 | gf_dynstrcat(&info->canonicalRepresentation, info->protocol, NULL); |
664 | 0 | gf_dynstrcat(&info->canonicalRepresentation, "://", NULL); |
665 | 0 | gf_dynstrcat(&info->canonicalRepresentation, info->server_name, NULL); |
666 | 0 | if (info->port && (info->port!=default_port)) { |
667 | 0 | char port[8]; |
668 | 0 | snprintf(port, sizeof(port)-1, ":%d", info->port); |
669 | 0 | gf_dynstrcat(&info->canonicalRepresentation, port, NULL); |
670 | 0 | } |
671 | 0 | gf_dynstrcat(&info->canonicalRepresentation, info->remotePath, NULL); |
672 | |
|
673 | 0 | gf_free(copyOfUrl); |
674 | 0 | if (urlConcatenateWithBaseURL) |
675 | 0 | gf_free(urlConcatenateWithBaseURL); |
676 | 0 | return GF_OK; |
677 | 0 | } |
678 | | |
679 | | GF_EXPORT |
680 | | GF_Err gf_dm_sess_setup_from_url(GF_DownloadSession *sess, const char *url, Bool allow_direct_reuse) |
681 | 0 | { |
682 | 0 | Bool socket_changed = GF_FALSE; |
683 | 0 | GF_URL_Info info; |
684 | 0 | char *sep_frag=NULL; |
685 | 0 | if (!url) |
686 | 0 | return GF_BAD_PARAM; |
687 | | |
688 | 0 | gf_dm_sess_clear_headers(sess); |
689 | 0 | sess->allow_direct_reuse = allow_direct_reuse; |
690 | 0 | gf_dm_url_info_init(&info); |
691 | |
|
692 | 0 | if (!sess->sock) |
693 | 0 | socket_changed = GF_TRUE; |
694 | 0 | else if (sess->status>GF_NETIO_DISCONNECTED) |
695 | 0 | socket_changed = GF_TRUE; |
696 | | |
697 | 0 | else if (sess->connection_timeout_ms) { |
698 | 0 | u32 diff = (u32) ( gf_sys_clock_high_res() - sess->last_fetch_time) / 1000; |
699 | 0 | if (diff >= sess->connection_timeout_ms) { |
700 | 0 | GF_LOG(GF_LOG_INFO, GF_LOG_HTTP, ("[%s] Timeout reached (%d ms since last vs %d ms timeout), reconnecting\n", sess->log_name, diff, sess->connection_timeout_ms)); |
701 | 0 | socket_changed = GF_TRUE; |
702 | 0 | } |
703 | 0 | } |
704 | |
|
705 | 0 | gf_fatal_assert(sess->status != GF_NETIO_WAIT_FOR_REPLY); |
706 | 0 | gf_fatal_assert(sess->status != GF_NETIO_DATA_EXCHANGE); |
707 | | //always detach task if any |
708 | 0 | if (sess->ftask) { |
709 | 0 | sess->ftask->sess = NULL; |
710 | 0 | sess->ftask = NULL; |
711 | 0 | } |
712 | | |
713 | | //strip fragment |
714 | 0 | sep_frag = strchr(url, '#'); |
715 | 0 | if (sep_frag) sep_frag[0]=0; |
716 | 0 | sess->last_error = gf_dm_get_url_info(url, &info, sess->orig_url); |
717 | 0 | if (sess->last_error) { |
718 | 0 | if (sep_frag) sep_frag[0]='#'; |
719 | 0 | return sess->last_error; |
720 | 0 | } |
721 | | |
722 | 0 | if (!strstr(url, "://")) { |
723 | 0 | char *sep; |
724 | 0 | gf_dm_url_info_del(&info); |
725 | 0 | gf_dm_url_info_init(&info); |
726 | 0 | info.port = sess->port; |
727 | 0 | info.server_name = sess->server_name ? gf_strdup(sess->server_name) : NULL; |
728 | 0 | info.remotePath = gf_strdup(url); |
729 | 0 | sep = strstr(sess->orig_url_before_redirect, "://"); |
730 | 0 | gf_assert(sep); |
731 | 0 | sep[0] = 0; |
732 | 0 | info.protocol = gf_strdup(sess->orig_url_before_redirect); |
733 | 0 | sep[0] = ':'; |
734 | 0 | } |
735 | |
|
736 | 0 | if (sess->port != info.port) { |
737 | 0 | socket_changed = GF_TRUE; |
738 | 0 | sess->port = info.port; |
739 | 0 | } |
740 | | #ifdef GPAC_HTTPMUX |
741 | | //safety in case we had a previous error but underlying stream_id was not reset |
742 | | sess->hmux_stream_id = -1; |
743 | | #endif |
744 | |
|
745 | 0 | if (sess->cached_file) { |
746 | 0 | socket_changed = GF_TRUE; |
747 | 0 | gf_fclose(sess->cached_file); |
748 | 0 | sess->cached_file = NULL; |
749 | 0 | if (sess->cache_entry) { |
750 | 0 | gf_cache_remove_entry_from_session(sess); |
751 | 0 | sess->cache_entry = NULL; |
752 | 0 | } |
753 | 0 | } |
754 | 0 | if (sess->log_name) { |
755 | 0 | gf_free(sess->log_name); |
756 | 0 | sess->log_name = NULL; |
757 | 0 | } |
758 | 0 | if (!strcmp("http", info.protocol) || !strcmp("https", info.protocol)) { |
759 | 0 | sess->log_name = gf_strdup("HTTP"); |
760 | 0 | if (sess->do_requests != http_do_requests) { |
761 | 0 | sess->do_requests = http_do_requests; |
762 | 0 | socket_changed = GF_TRUE; |
763 | 0 | } |
764 | 0 | Bool use_ssl = !strcmp("https", info.protocol) ? GF_TRUE : GF_FALSE; |
765 | | //if proxy, check scheme and port |
766 | 0 | const char *proxy = (sess->flags & GF_NETIO_SESSION_NO_PROXY) ? NULL : gf_opts_get_key("core", "proxy"); |
767 | 0 | if (proxy) { |
768 | 0 | if (!strnicmp(proxy, "http://", 7)) use_ssl = GF_FALSE; |
769 | 0 | else if (!strnicmp(proxy, "https://", 8)) use_ssl = GF_TRUE; |
770 | 0 | else if (strstr(proxy, ":80")) use_ssl = GF_FALSE; |
771 | 0 | else if (strstr(proxy, ":443")) use_ssl = GF_TRUE; |
772 | 0 | } |
773 | |
|
774 | 0 | if (use_ssl) { |
775 | 0 | if (!(sess->flags & GF_DOWNLOAD_SESSION_USE_SSL)) { |
776 | 0 | sess->flags |= GF_DOWNLOAD_SESSION_USE_SSL; |
777 | 0 | socket_changed = GF_TRUE; |
778 | 0 | } |
779 | 0 | } else if (sess->flags & GF_DOWNLOAD_SESSION_USE_SSL) { |
780 | 0 | sess->flags &= ~GF_DOWNLOAD_SESSION_USE_SSL; |
781 | 0 | socket_changed = GF_TRUE; |
782 | 0 | } |
783 | 0 | } else { |
784 | 0 | sess->do_requests = NULL; |
785 | |
|
786 | | #ifdef GPAC_HAS_CURL |
787 | | if (!sess->dm->curl_multi) { |
788 | | sess->dm->curl_multi = curl_multi_init(); |
789 | | } |
790 | | #endif |
791 | |
|
792 | 0 | } |
793 | |
|
794 | 0 | if (sess->server_name && info.server_name && !strcmp(sess->server_name, info.server_name)) { |
795 | 0 | } else { |
796 | 0 | socket_changed = GF_TRUE; |
797 | 0 | if (sess->server_name) gf_free(sess->server_name); |
798 | 0 | sess->server_name = info.server_name ? gf_strdup(info.server_name) : NULL; |
799 | 0 | } |
800 | |
|
801 | 0 | if (info.canonicalRepresentation) { |
802 | 0 | if (sess->orig_url) gf_free(sess->orig_url); |
803 | 0 | sess->orig_url = gf_strdup(info.canonicalRepresentation); |
804 | 0 | } else { |
805 | 0 | if (sess->orig_url) gf_free(sess->orig_url); |
806 | 0 | sess->orig_url = gf_strdup(info.protocol); |
807 | 0 | gf_dynstrcat(&sess->orig_url, info.server_name, "://"); |
808 | 0 | if (info.port) { |
809 | 0 | char szTmp[10]; |
810 | 0 | sprintf(szTmp, ":%u", info.port); |
811 | 0 | gf_dynstrcat(&sess->orig_url, szTmp, NULL); |
812 | 0 | } |
813 | 0 | gf_dynstrcat(&sess->orig_url, info.remotePath, NULL); |
814 | 0 | } |
815 | |
|
816 | 0 | if (!sess->orig_url_before_redirect) |
817 | 0 | sess->orig_url_before_redirect = gf_strdup(url); |
818 | |
|
819 | 0 | if (sess->remote_path) gf_free(sess->remote_path); |
820 | 0 | sess->remote_path = gf_strdup(info.remotePath); |
821 | |
|
822 | 0 | if (sess->status==GF_NETIO_STATE_ERROR) |
823 | 0 | socket_changed = GF_TRUE; |
824 | |
|
825 | 0 | if (!socket_changed && info.userName && !strcmp(info.userName, sess->creds->username)) { |
826 | 0 | } else { |
827 | 0 | sess->creds = NULL; |
828 | 0 | if (info.userName) { |
829 | 0 | if (! sess->dm) { |
830 | 0 | GF_LOG(GF_LOG_ERROR, GF_LOG_HTTP, ("[%s] Did not found any download manager, credentials not supported\n", sess->log_name)); |
831 | 0 | } else |
832 | 0 | sess->creds = gf_user_credentials_register(sess->dm, !strcmp("https", info.protocol), sess->server_name, info.userName, info.password, info.userName && info.password); |
833 | 0 | } |
834 | 0 | } |
835 | 0 | gf_dm_url_info_del(&info); |
836 | 0 | if (sep_frag) sep_frag[0]='#'; |
837 | |
|
838 | | #ifdef GPAC_HTTPMUX |
839 | | if (sess->hmux_sess) { |
840 | | if (sess->hmux_sess->do_shutdown) |
841 | | socket_changed = GF_TRUE; |
842 | | sess->hmux_buf.size = 0; |
843 | | sess->hmux_buf.offset = 0; |
844 | | } |
845 | | #endif |
846 | | |
847 | |
|
848 | | #ifdef GPAC_HAS_CURL |
849 | | if (sess->dm->curl_multi && (!sess->do_requests || gf_opts_get_bool("core", "curl")) |
850 | | //if the caller wants a GF_Socket interface, we cannot use curl |
851 | | && !(sess->flags & GF_NETIO_SESSION_SHARE_SOCKET) |
852 | | ) { |
853 | | GF_Err e = curl_setup_session(sess); |
854 | | if (e) return e; |
855 | | } else |
856 | | #endif |
857 | |
|
858 | 0 | if (sess->sock && !socket_changed) { |
859 | 0 | if (!sess->do_requests) return GF_NOT_SUPPORTED; |
860 | 0 | sess->status = GF_NETIO_CONNECTED; |
861 | 0 | sess->last_error = GF_OK; |
862 | | //reset number of retry and start time |
863 | 0 | sess->num_retry = SESSION_RETRY_COUNT; |
864 | 0 | sess->start_time = 0; |
865 | 0 | sess->needs_cache_reconfig = 1; |
866 | 0 | } else { |
867 | |
|
868 | 0 | if (!sess->do_requests) return GF_NOT_SUPPORTED; |
869 | | #ifdef GPAC_HTTPMUX |
870 | | if (sess->hmux_sess) { |
871 | | gf_mx_p(sess->mx); |
872 | | hmux_detach_session(sess->hmux_sess, sess); |
873 | | gf_mx_v(sess->mx); |
874 | | } |
875 | | #endif |
876 | | |
877 | 0 | dm_sess_sk_del(sess); |
878 | |
|
879 | 0 | sess->status = GF_NETIO_SETUP; |
880 | 0 | sess->last_error = GF_OK; |
881 | | //reset number of retry and start time |
882 | 0 | sess->num_retry = SESSION_RETRY_COUNT; |
883 | 0 | sess->start_time = 0; |
884 | 0 | #ifdef GPAC_HAS_SSL |
885 | 0 | if (sess->ssl) { |
886 | 0 | SSL_shutdown(sess->ssl); |
887 | 0 | SSL_free(sess->ssl); |
888 | 0 | sess->ssl = NULL; |
889 | 0 | } |
890 | 0 | #endif |
891 | 0 | } |
892 | 0 | sess->total_size = 0; |
893 | 0 | sess->bytes_done = 0; |
894 | 0 | sess->full_resource_size = 0; |
895 | | //could be not-0 after a byte-range request using chunk transfer |
896 | 0 | sess->remaining_data_size = 0; |
897 | |
|
898 | 0 | sess->local_cache_only = GF_FALSE; |
899 | 0 | if (sess->dm && sess->dm->local_cache_url_provider_cbk) { |
900 | 0 | Bool res = sess->dm->local_cache_url_provider_cbk(sess->dm->lc_udta, (char *)url, GF_FALSE); |
901 | 0 | if (res == GF_TRUE) { |
902 | 0 | sess->local_cache_only = GF_TRUE; |
903 | 0 | gf_free(sess->orig_url); |
904 | 0 | sess->orig_url = gf_strdup(url); |
905 | 0 | SET_LAST_ERR(sess->last_error) |
906 | 0 | sess->use_cache_file = GF_TRUE; |
907 | 0 | gf_dm_configure_cache(sess); |
908 | 0 | sess->bytes_done = 0; |
909 | 0 | if (sess->cache_entry && gf_cache_is_deleted(sess->cache_entry)) { |
910 | 0 | sess->status = GF_NETIO_DATA_TRANSFERED; |
911 | 0 | SET_LAST_ERR(GF_URL_REMOVED) |
912 | | //return GF_OK; |
913 | 0 | } else if (! gf_cache_is_done(sess->cache_entry)) { |
914 | 0 | sess->total_size = 0; |
915 | 0 | sess->status = GF_NETIO_DATA_EXCHANGE; |
916 | 0 | } else { |
917 | 0 | sess->total_size = gf_cache_get_content_length(sess->cache_entry); |
918 | 0 | sess->bytes_done = sess->total_size; |
919 | 0 | sess->status = GF_NETIO_DATA_TRANSFERED; |
920 | 0 | if (!sess->cache_entry) { |
921 | 0 | SET_LAST_ERR(GF_URL_ERROR) |
922 | 0 | } else { |
923 | 0 | SET_LAST_ERR(GF_OK) |
924 | 0 | } |
925 | 0 | } |
926 | |
|
927 | 0 | sess->total_time_since_req = gf_cache_get_downtime(sess->cache_entry); |
928 | 0 | if (sess->total_time_since_req) |
929 | 0 | sess->bytes_per_sec = (u32) ((1000 * (u64) sess->bytes_done) / sess->total_time_since_req); |
930 | 0 | else |
931 | 0 | sess->bytes_per_sec = 0; |
932 | 0 | gf_dm_sess_reload_cached_headers(sess); |
933 | 0 | } |
934 | 0 | } |
935 | 0 | if (sess->last_error) |
936 | 0 | return sess->last_error; |
937 | 0 | return gf_dm_sess_set_range(sess, 0, 0, GF_TRUE); |
938 | 0 | } |
939 | | |
940 | | |
941 | | Bool gf_dm_session_do_task(GF_DownloadSession *sess) |
942 | 0 | { |
943 | 0 | Bool do_run = GF_TRUE; |
944 | |
|
945 | 0 | if (sess->destroy) { |
946 | 0 | do_run = GF_FALSE; |
947 | 0 | } else { |
948 | 0 | Bool unlock = sess->mx ? GF_TRUE : GF_FALSE; |
949 | 0 | gf_mx_p(sess->mx); |
950 | 0 | if (sess->status >= GF_NETIO_DATA_TRANSFERED) { |
951 | 0 | do_run = GF_FALSE; |
952 | 0 | } else { |
953 | 0 | if (sess->status < GF_NETIO_CONNECTED) { |
954 | 0 | gf_dm_connect(sess); |
955 | 0 | } else { |
956 | 0 | sess->do_requests(sess); |
957 | 0 | } |
958 | 0 | } |
959 | 0 | if (unlock) |
960 | 0 | gf_mx_v(sess->mx); |
961 | 0 | } |
962 | 0 | if (do_run) return GF_TRUE; |
963 | | |
964 | | /*destroy all session but keep connection active*/ |
965 | 0 | gf_dm_disconnect(sess, HTTP_NO_CLOSE); |
966 | 0 | if (sess->last_error==GF_EOS) sess->last_error = GF_OK; |
967 | 0 | else if (sess->last_error==GF_IP_NETWORK_EMPTY) sess->last_error = GF_OK; |
968 | 0 | else if (sess->last_error) { |
969 | 0 | sess->status = GF_NETIO_STATE_ERROR; |
970 | 0 | } |
971 | 0 | return GF_FALSE; |
972 | 0 | } |
973 | | |
974 | | Bool gf_dm_session_task(GF_FilterSession *fsess, void *callback, u32 *reschedule_ms) |
975 | 0 | { |
976 | 0 | GF_SessTask *task = callback; |
977 | 0 | GF_DownloadSession *sess = task->sess; |
978 | 0 | if (!sess || sess->destroy) { |
979 | 0 | gf_free(task); |
980 | 0 | if (sess) sess->ftask = NULL; |
981 | 0 | return GF_FALSE; |
982 | 0 | } |
983 | 0 | task->in_task = GF_TRUE; |
984 | 0 | Bool ret = gf_dm_session_do_task(sess); |
985 | 0 | task->in_task = GF_FALSE; |
986 | 0 | if (ret) { |
987 | 0 | if (sess->rate_regulated) { |
988 | 0 | *reschedule_ms = (sess->last_cap_rate_bytes_per_sec > sess->max_data_rate) ? 1000 : 100; |
989 | 0 | } else { |
990 | 0 | *reschedule_ms = 1; |
991 | 0 | }; |
992 | 0 | return GF_TRUE; |
993 | 0 | } |
994 | 0 | gf_assert(sess->ftask); |
995 | 0 | gf_free(sess->ftask); |
996 | 0 | sess->ftask = NULL; |
997 | 0 | if (sess->destroy) { |
998 | 0 | gf_dm_sess_del(sess); |
999 | 0 | } |
1000 | 0 | return GF_FALSE; |
1001 | 0 | } |
1002 | | |
1003 | | #ifndef GPAC_DISABLE_THREADS |
1004 | | static u32 gf_dm_session_thread(void *par) |
1005 | 0 | { |
1006 | 0 | GF_DownloadSession *sess = (GF_DownloadSession *)par; |
1007 | 0 | if (!sess) return 0; |
1008 | | |
1009 | 0 | GF_LOG(GF_LOG_DEBUG, GF_LOG_CORE, ("[Downloader] Entering thread ID %d\n", gf_th_id() )); |
1010 | 0 | sess->flags &= ~GF_DOWNLOAD_SESSION_THREAD_DEAD; |
1011 | 0 | while (!sess->destroy) { |
1012 | 0 | Bool ret = gf_dm_session_do_task(sess); |
1013 | 0 | if (!ret) break; |
1014 | 0 | gf_sleep(sess->rate_regulated ? 100 : 0); |
1015 | 0 | } |
1016 | 0 | sess->flags |= GF_DOWNLOAD_SESSION_THREAD_DEAD; |
1017 | 0 | if (sess->destroy) |
1018 | 0 | gf_dm_sess_del(sess); |
1019 | 0 | return 1; |
1020 | 0 | } |
1021 | | #endif |
1022 | | |
1023 | | GF_DownloadSession *gf_dm_sess_new_internal(GF_DownloadManager * dm, const char *url, u32 dl_flags, |
1024 | | gf_dm_user_io user_io, |
1025 | | void *usr_cbk, |
1026 | | GF_Socket *server, |
1027 | | Bool force_server, |
1028 | | GF_Err *e) |
1029 | 0 | { |
1030 | 0 | GF_DownloadSession *sess; |
1031 | |
|
1032 | 0 | GF_SAFEALLOC(sess, GF_DownloadSession); |
1033 | 0 | if (!sess) { |
1034 | 0 | return NULL; |
1035 | 0 | } |
1036 | | #ifdef GPAC_HTTPMUX |
1037 | | sess->hmux_stream_id = -1; |
1038 | | #endif |
1039 | 0 | sess->headers = gf_list_new(); |
1040 | 0 | sess->flags = dl_flags; |
1041 | 0 | if (sess->flags & GF_NETIO_SESSION_NOTIFY_DATA) |
1042 | 0 | sess->force_data_write_callback = GF_TRUE; |
1043 | 0 | sess->user_proc = user_io; |
1044 | 0 | sess->usr_cbk = usr_cbk; |
1045 | 0 | sess->creds = NULL; |
1046 | 0 | sess->log_name = gf_strdup("HTTP"); |
1047 | 0 | sess->http_buf_size = dm ? dm->read_buf_size : GF_DOWNLOAD_BUFFER_SIZE; |
1048 | 0 | sess->http_buf = gf_malloc(sess->http_buf_size + 1); |
1049 | |
|
1050 | 0 | sess->conn_timeout = gf_opts_get_int("core", "tcp-timeout"); |
1051 | 0 | if (!sess->conn_timeout) sess->conn_timeout = 5000; |
1052 | |
|
1053 | 0 | sess->request_timeout = gf_opts_get_int("core", "req-timeout"); |
1054 | |
|
1055 | 0 | sess->chunk_wnd_dur = gf_opts_get_int("core", "cte-rate-wnd") * 1000; |
1056 | 0 | if (!sess->chunk_wnd_dur) sess->chunk_wnd_dur = 20000; |
1057 | |
|
1058 | 0 | sess->dm = dm; |
1059 | 0 | if (server || force_server) { |
1060 | 0 | sess->sock = server; |
1061 | 0 | sess->flags = GF_NETIO_SESSION_NOT_THREADED; |
1062 | 0 | sess->status = GF_NETIO_CONNECTED; |
1063 | 0 | sess->server_mode = GF_TRUE; |
1064 | 0 | sess->do_requests = http_do_requests; |
1065 | 0 | if (e) *e = GF_OK; |
1066 | 0 | if (dl_flags & GF_NETIO_SESSION_NO_BLOCK) { |
1067 | 0 | sess->flags |= GF_NETIO_SESSION_NO_BLOCK; |
1068 | 0 | if (server) |
1069 | 0 | gf_sk_set_block_mode(server, GF_TRUE); |
1070 | 0 | } |
1071 | 0 | return sess; |
1072 | 0 | } |
1073 | | |
1074 | 0 | if (!sess->conn_timeout) sess->server_only_understand_get = GF_TRUE; |
1075 | 0 | if (dm) |
1076 | 0 | sess->disable_cache = dm->disable_cache; |
1077 | |
|
1078 | 0 | if (! (dl_flags & GF_NETIO_SESSION_NOT_THREADED) |
1079 | | #ifdef GPAC_HAS_CURL |
1080 | | && !sess->curl_hnd |
1081 | | #endif |
1082 | 0 | ) { |
1083 | 0 | sess->mx = gf_mx_new(url); |
1084 | 0 | if (!sess->mx) { |
1085 | 0 | gf_free(sess); |
1086 | 0 | return NULL; |
1087 | 0 | } |
1088 | 0 | } |
1089 | | |
1090 | 0 | if ((dm->h3_mode == H3_MODE_FIRST) || (dm->h3_mode == H3_MODE_ONLY)) |
1091 | 0 | sess->flags |= GF_NETIO_SESSION_USE_QUIC; |
1092 | 0 | else if (dm->h3_mode == H3_MODE_AUTO) |
1093 | 0 | sess->flags |= GF_NETIO_SESSION_RETRY_QUIC; |
1094 | |
|
1095 | 0 | *e = gf_dm_sess_setup_from_url(sess, url, GF_FALSE); |
1096 | 0 | if (*e) { |
1097 | 0 | GF_LOG(GF_LOG_INFO, GF_LOG_HTTP, ("[Downloader] failed to create session for %s: %s\n", url, gf_error_to_string(*e))); |
1098 | 0 | gf_dm_sess_del(sess); |
1099 | 0 | return NULL; |
1100 | 0 | } |
1101 | 0 | gf_assert( sess ); |
1102 | 0 | sess->num_retry = SESSION_RETRY_COUNT; |
1103 | | /*threaded session must be started with gf_dm_sess_process*/ |
1104 | 0 | return sess; |
1105 | 0 | } |
1106 | | |
1107 | | GF_EXPORT |
1108 | | GF_DownloadSession *gf_dm_sess_new_server(GF_DownloadManager *dm, GF_Socket *server, |
1109 | | void *ssl_sock_ctx, |
1110 | | gf_dm_user_io user_io, |
1111 | | void *usr_cbk, |
1112 | | Bool async, |
1113 | | GF_Err *e) |
1114 | 0 | { |
1115 | 0 | GF_DownloadSession *sess = gf_dm_sess_new_internal(dm, NULL, async ? GF_NETIO_SESSION_NO_BLOCK : 0, user_io, usr_cbk, server, GF_TRUE, e); |
1116 | 0 | if (!sess) return NULL; |
1117 | | |
1118 | 0 | #ifdef GPAC_HAS_SSL |
1119 | 0 | if (ssl_sock_ctx) { |
1120 | 0 | sess->ssl = ssl_sock_ctx; |
1121 | 0 | gf_dm_sess_server_setup_ssl(sess); |
1122 | 0 | } |
1123 | 0 | #endif |
1124 | 0 | return sess; |
1125 | 0 | } |
1126 | | |
1127 | | |
1128 | | u32 gf_dm_sess_subsession_count(GF_DownloadSession *sess) |
1129 | 0 | { |
1130 | | #ifdef GPAC_HTTPMUX |
1131 | | if (sess->hmux_sess) |
1132 | | return gf_list_count(sess->hmux_sess->sessions); |
1133 | | #endif |
1134 | 0 | return 1; |
1135 | 0 | } |
1136 | | |
1137 | | |
1138 | | void gf_dm_sess_server_reset(GF_DownloadSession *sess) |
1139 | 0 | { |
1140 | 0 | if (!sess->server_mode) return; |
1141 | | //DO NOT clear headers if H2: the underlying h2 session could have been ended (stream_id=0) and then reassigned |
1142 | | //when processsing another session, so the headers would be the ones of the new stream_id |
1143 | | //for H2, the reset is done when reassigning sessions |
1144 | | #ifdef GPAC_HTTPMUX |
1145 | | if (!sess->hmux_sess) |
1146 | | #endif |
1147 | 0 | gf_dm_sess_clear_headers(sess); |
1148 | |
|
1149 | 0 | sess->total_size = sess->bytes_done = 0; |
1150 | 0 | sess->async_buf_size = 0; |
1151 | 0 | sess->chunk_bytes = 0; |
1152 | 0 | sess->chunk_header_bytes = 0; |
1153 | 0 | sess->chunked = GF_FALSE; |
1154 | 0 | sess->status = GF_NETIO_CONNECTED; |
1155 | 0 | } |
1156 | | |
1157 | | |
1158 | | GF_EXPORT |
1159 | | GF_DownloadSession *gf_dm_sess_new_simple(GF_DownloadManager * dm, const char *url, u32 dl_flags, |
1160 | | gf_dm_user_io user_io, |
1161 | | void *usr_cbk, |
1162 | | GF_Err *e) |
1163 | 0 | { |
1164 | 0 | return gf_dm_sess_new_internal(dm, url, dl_flags, user_io, usr_cbk, NULL, GF_FALSE, e); |
1165 | 0 | } |
1166 | | GF_EXPORT |
1167 | | GF_DownloadSession *gf_dm_sess_new(GF_DownloadManager *dm, const char *url, u32 dl_flags, |
1168 | | gf_dm_user_io user_io, |
1169 | | void *usr_cbk, |
1170 | | GF_Err *e) |
1171 | 0 | { |
1172 | 0 | GF_DownloadSession *sess; |
1173 | 0 | *e = GF_OK; |
1174 | 0 | if (gf_dm_is_local(dm, url)) { |
1175 | 0 | *e = GF_NOT_SUPPORTED; |
1176 | 0 | return NULL; |
1177 | 0 | } |
1178 | | |
1179 | 0 | if (!gf_dm_can_handle_url(url)) { |
1180 | 0 | *e = GF_NOT_SUPPORTED; |
1181 | 0 | return NULL; |
1182 | 0 | } |
1183 | 0 | sess = gf_dm_sess_new_simple(dm, url, dl_flags, user_io, usr_cbk, e); |
1184 | 0 | if (sess && dm) { |
1185 | 0 | sess->dm = dm; |
1186 | 0 | gf_mx_p(dm->cache_mx); |
1187 | 0 | gf_list_add(dm->all_sessions, sess); |
1188 | 0 | gf_mx_v(dm->cache_mx); |
1189 | 0 | } |
1190 | 0 | return sess; |
1191 | 0 | } |
1192 | | |
1193 | | |
1194 | | GF_Err gf_dm_read_data(GF_DownloadSession *sess, char *data, u32 data_size, u32 *out_read) |
1195 | 0 | { |
1196 | 0 | GF_Err e; |
1197 | |
|
1198 | 0 | if (sess->cached_file) { |
1199 | 0 | *out_read = (u32) gf_fread(data, data_size, sess->cached_file); |
1200 | 0 | if (! *out_read && gf_cache_is_done(sess->cache_entry)) { |
1201 | 0 | sess->total_size = gf_cache_get_content_length(sess->cache_entry); |
1202 | 0 | if (sess->total_size != sess->bytes_done) { |
1203 | 0 | sess->status = GF_NETIO_STATE_ERROR; |
1204 | 0 | SET_LAST_ERR(GF_CORRUPTED_DATA); |
1205 | 0 | GF_LOG(GF_LOG_ERROR, GF_LOG_HTTP, ("[%s] Broken cache for %s: %u bytes in cache but %u advertized\n", sess->log_name, sess->orig_url, sess->bytes_done, sess->total_size)); |
1206 | 0 | return GF_CORRUPTED_DATA; |
1207 | 0 | } |
1208 | 0 | } |
1209 | 0 | return GF_OK; |
1210 | 0 | } |
1211 | | |
1212 | | #ifdef GPAC_HAS_CURL |
1213 | | if (sess->curl_hnd) { |
1214 | | return curl_read_data(sess, data, data_size, out_read); |
1215 | | } |
1216 | | #endif |
1217 | | |
1218 | 0 | if (sess->dm && sess->dm->simulate_no_connection) { |
1219 | 0 | if (sess->sock) { |
1220 | 0 | sess->status = GF_NETIO_DISCONNECTED; |
1221 | 0 | } |
1222 | 0 | return GF_IP_NETWORK_FAILURE; |
1223 | 0 | } |
1224 | | |
1225 | 0 | if (!sess) |
1226 | 0 | return GF_BAD_PARAM; |
1227 | | |
1228 | 0 | if (sess->server_mode && (sess->flags & GF_NETIO_SESSION_USE_QUIC)) |
1229 | 0 | return GF_IP_NETWORK_EMPTY; |
1230 | | |
1231 | 0 | gf_mx_p(sess->mx); |
1232 | 0 | if (!sess->sock) { |
1233 | 0 | sess->status = GF_NETIO_DISCONNECTED; |
1234 | 0 | gf_mx_v(sess->mx); |
1235 | 0 | return GF_IP_CONNECTION_CLOSED; |
1236 | 0 | } |
1237 | | |
1238 | 0 | *out_read = 0; |
1239 | |
|
1240 | 0 | #ifdef GPAC_HAS_SSL |
1241 | 0 | if (sess->ssl && !(sess->flags & GF_NETIO_SESSION_USE_QUIC)) { |
1242 | 0 | e = gf_ssl_read_data(sess, data, data_size, out_read); |
1243 | 0 | if (e==GF_NOT_READY) { |
1244 | 0 | gf_mx_v(sess->mx); |
1245 | 0 | return GF_IP_NETWORK_EMPTY; |
1246 | 0 | } |
1247 | 0 | } else |
1248 | 0 | #endif |
1249 | 0 | e = gf_sk_receive(sess->sock, data, data_size, out_read); |
1250 | | |
1251 | | #ifdef GPAC_HTTPMUX |
1252 | | if (sess->hmux_sess) { |
1253 | | GF_Err hme = sess->hmux_sess->data_received(sess, data, *out_read); |
1254 | | if (hme) { |
1255 | | gf_mx_v(sess->mx); |
1256 | | *out_read = 0; |
1257 | | return hme; |
1258 | | } |
1259 | | } |
1260 | | #endif //GPAC_HTTPMUX |
1261 | | |
1262 | 0 | if (*out_read) |
1263 | 0 | sess->last_fetch_time = gf_sys_clock_high_res(); |
1264 | |
|
1265 | 0 | gf_mx_v(sess->mx); |
1266 | 0 | return e; |
1267 | 0 | } |
1268 | | |
1269 | | static void gf_dm_connect(GF_DownloadSession *sess) |
1270 | 0 | { |
1271 | 0 | GF_Err e; |
1272 | 0 | Bool register_sock; |
1273 | 0 | Bool allow_offline = sess->dm ? sess->dm->allow_offline_cache : GF_FALSE; |
1274 | 0 | u16 proxy_port = 0; |
1275 | 0 | char szProxy[GF_MAX_PATH]; |
1276 | 0 | const char *proxy; |
1277 | | |
1278 | | //offline cache enabled, setup cache before connection |
1279 | 0 | if (!sess->connect_pending && allow_offline) { |
1280 | 0 | gf_dm_configure_cache(sess); |
1281 | 0 | if (sess->cached_file) return; |
1282 | 0 | } |
1283 | | |
1284 | | #ifdef GPAC_HAS_CURL |
1285 | | if (sess->curl_hnd) { |
1286 | | curl_connect(sess); |
1287 | | return; |
1288 | | } |
1289 | | #endif |
1290 | | |
1291 | | #ifdef GPAC_HTTPMUX |
1292 | | if (sess->hmux_switch_sess) { |
1293 | | sess->hmux_switch_sess = 0; |
1294 | | gf_mx_p(sess->mx); |
1295 | | hmux_detach_session(sess->hmux_sess, sess); |
1296 | | gf_mx_v(sess->mx); |
1297 | | |
1298 | | if (sess->num_retry) { |
1299 | | SET_LAST_ERR(GF_OK) |
1300 | | sess->num_retry--; |
1301 | | GF_LOG(GF_LOG_INFO, GF_LOG_HTTP, ("[%s] stream_id "LLD" (%s) refused by server, retrying and marking session as no longer available\n", sess->log_name, sess->hmux_stream_id, sess->remote_path ? sess->remote_path : sess->orig_url)); |
1302 | | |
1303 | | sess->hmux_stream_id = -1; |
1304 | | } else { |
1305 | | GF_LOG(GF_LOG_ERROR, GF_LOG_HTTP, ("%s] stream_id "LLD" (%s) refused by server after all retries, marking session as no longer available\n", sess->log_name, sess->hmux_stream_id, sess->remote_path ? sess->remote_path : sess->orig_url)); |
1306 | | sess->status = GF_NETIO_STATE_ERROR; |
1307 | | SET_LAST_ERR(GF_REMOTE_SERVICE_ERROR) |
1308 | | sess->hmux_stream_id = -1; |
1309 | | return; |
1310 | | } |
1311 | | } |
1312 | | if (sess->hmux_sess && sess->hmux_sess->connected) { |
1313 | | sess->connect_time = 0; |
1314 | | sess->status = GF_NETIO_CONNECTED; |
1315 | | sess->last_error = GF_OK; |
1316 | | gf_dm_sess_notify_state(sess, GF_NETIO_CONNECTED, GF_OK); |
1317 | | if (!allow_offline) |
1318 | | gf_dm_configure_cache(sess); |
1319 | | return; |
1320 | | } |
1321 | | Bool attach_to_parent_sess = GF_FALSE; |
1322 | | if (sess->dm && !sess->sock) { |
1323 | | #ifdef GPAC_HAS_HTTP2 |
1324 | | if (!sess->dm->disable_http2 && (sess->h2_upgrade_state<3)) attach_to_parent_sess = GF_TRUE; |
1325 | | #endif |
1326 | | } |
1327 | | |
1328 | | if (attach_to_parent_sess) { |
1329 | | gf_mx_p(sess->dm->cache_mx); |
1330 | | u32 i, count = gf_list_count(sess->dm->all_sessions); |
1331 | | for (i=0; i<count; i++) { |
1332 | | GF_DownloadSession *a_sess = gf_list_get(sess->dm->all_sessions, i); |
1333 | | if (strcmp(a_sess->server_name, sess->server_name) || (a_sess->port != sess->port)) continue; |
1334 | | if (!a_sess->hmux_sess) { |
1335 | | #ifdef GPAC_HAS_HTTP2 |
1336 | | //we already ahd a connection to this server with H2 failure, do not try h2 |
1337 | | if (a_sess->h2_upgrade_state==4) { |
1338 | | sess->h2_upgrade_state=4; |
1339 | | break; |
1340 | | } |
1341 | | #endif |
1342 | | //we have the same server name |
1343 | | //trick in non-block mode, we want to wait for upgrade to be completed |
1344 | | //before creating a new socket |
1345 | | if ((a_sess != sess) |
1346 | | && a_sess->sock |
1347 | | && (a_sess->status <= GF_NETIO_WAIT_FOR_REPLY) |
1348 | | && (sess->flags & GF_NETIO_SESSION_NO_BLOCK) |
1349 | | #ifdef GPAC_HAS_HTTP2 |
1350 | | && (a_sess->h2_upgrade_state<2) |
1351 | | && !sess->dm->disable_http2 |
1352 | | && !gf_opts_get_bool("core", "no-h2c") |
1353 | | #endif |
1354 | | //TODO: H3 test |
1355 | | ) { |
1356 | | sess->connect_pending = 1; |
1357 | | SET_LAST_ERR(GF_IP_NETWORK_EMPTY) |
1358 | | gf_mx_v(sess->dm->cache_mx); |
1359 | | return; |
1360 | | } |
1361 | | continue; |
1362 | | } |
1363 | | if (a_sess->hmux_sess->do_shutdown) continue; |
1364 | | |
1365 | | GF_LOG(GF_LOG_DEBUG, GF_LOG_HTTP, ("[%s] associating session %s to existing %s session: %s:%u\n", sess->log_name, sess->remote_path ? sess->remote_path : sess->orig_url, a_sess->log_name, a_sess->server_name, a_sess->port)); |
1366 | | |
1367 | | u32 nb_locks=0; |
1368 | | if (sess->mx) { |
1369 | | nb_locks = gf_mx_get_num_locks(sess->mx); |
1370 | | if (nb_locks) gf_mx_v(sess->mx); |
1371 | | gf_mx_del(sess->mx); |
1372 | | } |
1373 | | sess->hmux_sess = a_sess->hmux_sess; |
1374 | | sess->mx = a_sess->hmux_sess->mx; |
1375 | | if (nb_locks) |
1376 | | gf_mx_p(sess->mx); |
1377 | | |
1378 | | sess->sock = a_sess->sock; |
1379 | | #ifdef GPAC_HAS_SSL |
1380 | | sess->ssl = a_sess->ssl; |
1381 | | #endif |
1382 | | sess->proxy_enabled = a_sess->proxy_enabled; |
1383 | | sess->hmux_sess->setup_session(sess, GF_FALSE); |
1384 | | |
1385 | | gf_list_add(sess->hmux_sess->sessions, sess); |
1386 | | |
1387 | | //reconfigure cache |
1388 | | if (sess->allow_direct_reuse) { |
1389 | | gf_dm_configure_cache(sess); |
1390 | | if (sess->cached_file) { |
1391 | | gf_mx_v(sess->dm->cache_mx); |
1392 | | return; |
1393 | | } |
1394 | | } |
1395 | | |
1396 | | sess->connect_time = 0; |
1397 | | if (sess->hmux_sess->connected) { |
1398 | | sess->status = GF_NETIO_CONNECTED; |
1399 | | gf_dm_sess_notify_state(sess, GF_NETIO_CONNECTED, GF_OK); |
1400 | | gf_dm_configure_cache(sess); |
1401 | | } |
1402 | | gf_mx_v(sess->dm->cache_mx); |
1403 | | return; |
1404 | | } // end all_sessions loop |
1405 | | gf_mx_v(sess->dm->cache_mx); |
1406 | | } |
1407 | | #endif |
1408 | | |
1409 | 0 | resetup_socket: |
1410 | |
|
1411 | 0 | register_sock = GF_FALSE; |
1412 | 0 | if (!sess->sock) { |
1413 | 0 | u32 sock_type = GF_SOCK_TYPE_TCP; |
1414 | 0 | if (sess->flags & GF_NETIO_SESSION_USE_QUIC) |
1415 | 0 | sock_type = GF_SOCK_TYPE_UDP; |
1416 | |
|
1417 | 0 | sess->sock = gf_sk_new_ex(sock_type, sess->netcap_id); |
1418 | |
|
1419 | 0 | if (sess->sock && (sess->flags & GF_NETIO_SESSION_NO_BLOCK)) |
1420 | 0 | gf_sk_set_block_mode(sess->sock, GF_TRUE); |
1421 | |
|
1422 | 0 | if (sess->sock_group) register_sock = GF_TRUE; |
1423 | 0 | } |
1424 | | |
1425 | | /*connect*/ |
1426 | 0 | sess->status = GF_NETIO_SETUP; |
1427 | 0 | gf_dm_sess_notify_state(sess, sess->status, GF_OK); |
1428 | | |
1429 | | /*PROXY setup*/ |
1430 | 0 | if (sess->proxy_enabled!=2) { |
1431 | 0 | proxy = (sess->flags & GF_NETIO_SESSION_NO_PROXY) ? NULL : gf_opts_get_key("core", "proxy"); |
1432 | 0 | if (proxy) { |
1433 | 0 | u32 i; |
1434 | 0 | proxy_port = 80; |
1435 | 0 | char *sep = strstr(proxy, "://"); |
1436 | 0 | strcpy(szProxy, sep ? sep+3 : proxy); |
1437 | 0 | sep = strchr(szProxy, ':'); |
1438 | 0 | if (sep) { |
1439 | 0 | proxy_port = atoi(sep+1); |
1440 | 0 | sep[0]=0; |
1441 | 0 | } |
1442 | 0 | proxy = szProxy; |
1443 | 0 | Bool use_proxy=GF_TRUE; |
1444 | 0 | for (i=0; i<gf_list_count(sess->dm->skip_proxy_servers); i++) { |
1445 | 0 | char *skip = (char*)gf_list_get(sess->dm->skip_proxy_servers, i); |
1446 | 0 | if (!strcmp(skip, sess->server_name)) { |
1447 | 0 | use_proxy=GF_FALSE; |
1448 | 0 | break; |
1449 | 0 | } |
1450 | 0 | } |
1451 | 0 | if (!use_proxy) { |
1452 | 0 | proxy = NULL; |
1453 | 0 | } else { |
1454 | 0 | sess->proxy_enabled = 1; |
1455 | 0 | } |
1456 | 0 | } else { |
1457 | 0 | proxy = NULL; |
1458 | 0 | sess->proxy_enabled = 0; |
1459 | 0 | } |
1460 | 0 | } else { |
1461 | 0 | proxy = NULL; |
1462 | 0 | } |
1463 | | |
1464 | |
|
1465 | 0 | if (!proxy) { |
1466 | 0 | proxy = sess->server_name; |
1467 | 0 | proxy_port = sess->port; |
1468 | 0 | } |
1469 | 0 | if (!sess->connect_pending) { |
1470 | 0 | GF_LOG(GF_LOG_INFO, GF_LOG_HTTP, ("[%s] Connecting to %s:%d for URL %s\n", sess->log_name, proxy, proxy_port, sess->remote_path ? sess->remote_path : "undefined")); |
1471 | 0 | } |
1472 | |
|
1473 | 0 | if ((sess->status == GF_NETIO_SETUP) && (sess->connect_pending<2)) { |
1474 | 0 | u64 now; |
1475 | 0 | if (sess->dm && sess->dm->simulate_no_connection) { |
1476 | 0 | sess->status = GF_NETIO_STATE_ERROR; |
1477 | 0 | SET_LAST_ERR(GF_IP_NETWORK_FAILURE) |
1478 | 0 | gf_dm_sess_notify_state(sess, sess->status, sess->last_error); |
1479 | 0 | return; |
1480 | 0 | } |
1481 | | |
1482 | 0 | sess->async_buf_size = 0; |
1483 | 0 | now = gf_sys_clock_high_res(); |
1484 | 0 | if ((sess->flags & GF_NETIO_SESSION_NO_BLOCK) && !sess->start_time) |
1485 | 0 | sess->start_time = now; |
1486 | |
|
1487 | | #ifdef GPAC_HAS_NGTCP2 |
1488 | | if (sess->flags & GF_NETIO_SESSION_USE_QUIC) { |
1489 | | e = http3_connect(sess, (char *) proxy, proxy_port); |
1490 | | if (sess->num_retry && (e==GF_IP_CONNECTION_FAILURE) ) { |
1491 | | register_sock = GF_FALSE; |
1492 | | e = GF_IP_NETWORK_EMPTY; |
1493 | | } |
1494 | | } else |
1495 | | #endif |
1496 | 0 | e = gf_sk_connect(sess->sock, (char *) proxy, proxy_port, NULL); |
1497 | | |
1498 | | /*retry*/ |
1499 | 0 | if (e == GF_IP_NETWORK_EMPTY) { |
1500 | 0 | if (sess->num_retry) { |
1501 | 0 | if (register_sock) gf_sk_group_register(sess->sock_group, sess->sock); |
1502 | 0 | sess->status = GF_NETIO_SETUP; |
1503 | | //reset pending flag |
1504 | 0 | sess->connect_pending = 0; |
1505 | 0 | if (sess->flags & GF_NETIO_SESSION_NO_BLOCK) { |
1506 | | //either timeout or set pending flag |
1507 | 0 | if ((now - sess->start_time) / 1000 > sess->conn_timeout) { |
1508 | 0 | sess->num_retry = 0; |
1509 | 0 | } else { |
1510 | 0 | sess->connect_pending = 1; |
1511 | 0 | } |
1512 | 0 | } else { |
1513 | 0 | sess->num_retry--; |
1514 | 0 | sess->connect_pending = 1; |
1515 | 0 | } |
1516 | 0 | if (sess->connect_pending) { |
1517 | 0 | SET_LAST_ERR(GF_IP_NETWORK_EMPTY) |
1518 | 0 | return; |
1519 | 0 | } |
1520 | 0 | } |
1521 | | |
1522 | 0 | e = GF_IP_CONNECTION_FAILURE; |
1523 | 0 | } |
1524 | | |
1525 | 0 | sess->connect_pending = 0; |
1526 | 0 | SET_LAST_ERR(GF_OK) |
1527 | | |
1528 | | /*failed*/ |
1529 | 0 | if (e) { |
1530 | 0 | if ((sess->flags & GF_NETIO_SESSION_RETRY_QUIC) && !(sess->flags & GF_NETIO_SESSION_USE_QUIC)) { |
1531 | 0 | sess->status = GF_NETIO_SETUP; |
1532 | 0 | sess->connect_pending = 0; |
1533 | 0 | sess->start_time = 0; |
1534 | 0 | gf_sk_group_unregister(sess->sock_group, sess->sock); |
1535 | 0 | gf_sk_del(sess->sock); |
1536 | 0 | sess->sock = NULL; |
1537 | 0 | GF_LOG(GF_LOG_WARNING, GF_LOG_HTTP, ("[%s] Failed to connect through TCP (%s), retrying with QUIC\n", sess->log_name, gf_error_to_string(e))); |
1538 | 0 | sess->flags |= GF_NETIO_SESSION_USE_QUIC; |
1539 | 0 | sess->flags &= ~GF_NETIO_SESSION_RETRY_QUIC; |
1540 | 0 | goto resetup_socket; |
1541 | 0 | } |
1542 | 0 | if ((sess->flags & GF_NETIO_SESSION_USE_QUIC) |
1543 | 0 | && !(sess->flags & GF_NETIO_SESSION_RETRY_QUIC) |
1544 | 0 | && (sess->dm->h3_mode!=H3_MODE_ONLY) |
1545 | 0 | ) { |
1546 | 0 | sess->flags &= ~GF_NETIO_SESSION_USE_QUIC; |
1547 | 0 | sess->status = GF_NETIO_SETUP; |
1548 | 0 | sess->connect_pending = 0; |
1549 | 0 | sess->start_time = 0; |
1550 | 0 | gf_sk_group_unregister(sess->sock_group, sess->sock); |
1551 | 0 | gf_sk_del(sess->sock); |
1552 | 0 | sess->sock = NULL; |
1553 | | #ifdef GPAC_HTTPMUX |
1554 | | if (sess->hmux_sess) { |
1555 | | sess->hmux_sess->destroy(sess->hmux_sess); |
1556 | | gf_list_del(sess->hmux_sess->sessions); |
1557 | | gf_free(sess->hmux_sess); |
1558 | | sess->hmux_sess = NULL; |
1559 | | } |
1560 | | #endif |
1561 | 0 | GF_LOG(GF_LOG_WARNING, GF_LOG_HTTP, ("[%s] Failed to connect through QUIC (%s), retrying with TCP\n", sess->log_name, gf_error_to_string(e))); |
1562 | 0 | goto resetup_socket; |
1563 | 0 | } |
1564 | 0 | sess->status = GF_NETIO_STATE_ERROR; |
1565 | 0 | SET_LAST_ERR(e) |
1566 | 0 | gf_dm_sess_notify_state(sess, sess->status, e); |
1567 | 0 | return; |
1568 | 0 | } |
1569 | 0 | if (register_sock) gf_sk_group_register(sess->sock_group, sess->sock); |
1570 | 0 | if (sess->allow_direct_reuse) { |
1571 | 0 | gf_dm_configure_cache(sess); |
1572 | 0 | if (sess->cached_file) return; |
1573 | 0 | } |
1574 | | |
1575 | 0 | sess->connect_time = (u32) (gf_sys_clock_high_res() - now); |
1576 | 0 | GF_LOG(GF_LOG_INFO, GF_LOG_HTTP, ("[%s] Connected to %s:%d\n", sess->log_name, proxy, proxy_port)); |
1577 | 0 | gf_dm_sess_notify_state(sess, GF_NETIO_CONNECTED, GF_OK); |
1578 | 0 | } |
1579 | | |
1580 | 0 | if (sess->status == GF_NETIO_SETUP) |
1581 | 0 | sess->status = GF_NETIO_CONNECTED; |
1582 | |
|
1583 | 0 | #ifdef GPAC_HAS_SSL |
1584 | 0 | if ((!sess->ssl || (sess->connect_pending==2)) |
1585 | 0 | && (sess->flags & (GF_DOWNLOAD_SESSION_USE_SSL|GF_DOWNLOAD_SESSION_SSL_FORCED)) |
1586 | 0 | ) { |
1587 | 0 | SSLConnectStatus ret = gf_ssl_try_connect(sess, proxy); |
1588 | 0 | if (ret != SSL_CONNECT_OK) { |
1589 | 0 | if (ret == SSL_CONNECT_RETRY) |
1590 | 0 | gf_dm_connect(sess); |
1591 | |
|
1592 | 0 | return; |
1593 | 0 | } |
1594 | 0 | } else |
1595 | 0 | #endif |
1596 | 0 | { |
1597 | 0 | GF_LOG(GF_LOG_DEBUG, GF_LOG_HTTP, ("[%s] connected\n", sess->log_name)); |
1598 | 0 | } |
1599 | | |
1600 | 0 | if (!allow_offline) |
1601 | 0 | gf_dm_configure_cache(sess); |
1602 | |
|
1603 | 0 | } |
1604 | | |
1605 | | DownloadedCacheEntry gf_dm_refresh_cache_entry(GF_DownloadSession *sess) |
1606 | 0 | { |
1607 | 0 | Bool go; |
1608 | 0 | u32 timer = 0; |
1609 | 0 | u32 flags; |
1610 | 0 | if (!sess) return NULL; |
1611 | 0 | flags = sess->flags; |
1612 | 0 | sess->flags |= GF_NETIO_SESSION_NOT_CACHED; |
1613 | 0 | go = GF_TRUE; |
1614 | 0 | while (go) { |
1615 | 0 | switch (sess->status) { |
1616 | | /*setup download*/ |
1617 | 0 | case GF_NETIO_SETUP: |
1618 | 0 | gf_dm_connect(sess); |
1619 | 0 | break; |
1620 | 0 | case GF_NETIO_WAIT_FOR_REPLY: |
1621 | 0 | if (timer == 0) |
1622 | 0 | timer = gf_sys_clock(); |
1623 | 0 | { |
1624 | 0 | u32 timer2 = gf_sys_clock(); |
1625 | 0 | if (timer2 - timer > 5000) { |
1626 | 0 | GF_Err e; |
1627 | | /* Since HEAD is not understood by this server, we use a GET instead */ |
1628 | 0 | sess->http_read_type = GET; |
1629 | 0 | sess->flags |= GF_NETIO_SESSION_NOT_CACHED; |
1630 | 0 | gf_dm_disconnect(sess, HTTP_NO_CLOSE); |
1631 | 0 | sess->status = GF_NETIO_SETUP; |
1632 | 0 | sess->server_only_understand_get = GF_TRUE; |
1633 | 0 | GF_LOG(GF_LOG_INFO, GF_LOG_HTTP, ("gf_dm_refresh_cache_entry() : Timeout with HEAD, try with GET\n")); |
1634 | 0 | e = gf_dm_sess_setup_from_url(sess, sess->orig_url, GF_FALSE); |
1635 | 0 | if (e) { |
1636 | 0 | GF_LOG(GF_LOG_WARNING, GF_LOG_HTTP, ("gf_dm_refresh_cache_entry() : Error with GET %d\n", e)); |
1637 | 0 | sess->status = GF_NETIO_STATE_ERROR; |
1638 | 0 | SET_LAST_ERR(e) |
1639 | 0 | gf_dm_sess_notify_state(sess, sess->status, e); |
1640 | 0 | } else { |
1641 | 0 | timer = 0; |
1642 | 0 | continue; |
1643 | 0 | } |
1644 | 0 | } |
1645 | 0 | } |
1646 | 0 | case GF_NETIO_CONNECTED: |
1647 | 0 | sess->do_requests(sess); |
1648 | 0 | break; |
1649 | 0 | case GF_NETIO_DATA_EXCHANGE: |
1650 | 0 | case GF_NETIO_DISCONNECTED: |
1651 | 0 | case GF_NETIO_STATE_ERROR: |
1652 | 0 | case GF_NETIO_DATA_TRANSFERED: |
1653 | 0 | go = GF_FALSE; |
1654 | 0 | break; |
1655 | 0 | default: |
1656 | 0 | break; |
1657 | 0 | } |
1658 | 0 | } |
1659 | 0 | sess->flags = flags; |
1660 | 0 | if (sess->status==GF_NETIO_STATE_ERROR) return NULL; |
1661 | 0 | return sess->cache_entry; |
1662 | 0 | } |
1663 | | |
1664 | | GF_EXPORT |
1665 | | const char *gf_dm_sess_mime_type(GF_DownloadSession *sess) |
1666 | 0 | { |
1667 | 0 | DownloadedCacheEntry entry; |
1668 | 0 | if (sess->cache_entry) { |
1669 | 0 | const char * oldMimeIfAny = gf_cache_get_mime_type(sess->cache_entry); |
1670 | 0 | if (oldMimeIfAny) |
1671 | 0 | return oldMimeIfAny; |
1672 | 0 | } |
1673 | 0 | entry = gf_dm_refresh_cache_entry (sess); |
1674 | 0 | if (!entry) |
1675 | 0 | return sess->mime_type; |
1676 | 0 | gf_assert( entry == sess->cache_entry && entry); |
1677 | 0 | return gf_cache_get_mime_type( sess->cache_entry ); |
1678 | 0 | } |
1679 | | |
1680 | | GF_EXPORT |
1681 | | GF_Err gf_dm_sess_set_range(GF_DownloadSession *sess, u64 start_range, u64 end_range, Bool discontinue_cache) |
1682 | 0 | { |
1683 | 0 | if (!sess) |
1684 | 0 | return GF_BAD_PARAM; |
1685 | 0 | if (sess->cache_entry) { |
1686 | 0 | if (!discontinue_cache) { |
1687 | 0 | if (gf_cache_get_end_range(sess->cache_entry) + 1 != start_range) |
1688 | 0 | discontinue_cache = GF_TRUE; |
1689 | 0 | } |
1690 | 0 | if (sess->sock) { |
1691 | 0 | switch (sess->status) { |
1692 | 0 | case GF_NETIO_CONNECTED: |
1693 | 0 | case GF_NETIO_DISCONNECTED: |
1694 | 0 | case GF_NETIO_DATA_TRANSFERED: |
1695 | 0 | break; |
1696 | 0 | default: |
1697 | 0 | return GF_BAD_PARAM; |
1698 | 0 | } |
1699 | 0 | } |
1700 | 0 | if (!sess->local_cache_only) { |
1701 | 0 | sess->status = sess->sock ? GF_NETIO_CONNECTED : GF_NETIO_SETUP; |
1702 | 0 | sess->num_retry = SESSION_RETRY_COUNT; |
1703 | |
|
1704 | 0 | if (!discontinue_cache) { |
1705 | 0 | gf_cache_set_end_range(sess->cache_entry, end_range); |
1706 | | /*remember this in case we get disconnected*/ |
1707 | 0 | sess->is_range_continuation = GF_TRUE; |
1708 | 0 | } else { |
1709 | 0 | sess->needs_cache_reconfig = 1; |
1710 | 0 | sess->reused_cache_entry = GF_FALSE; |
1711 | 0 | } |
1712 | 0 | } |
1713 | 0 | } else { |
1714 | 0 | if (sess->status == GF_NETIO_DISCONNECTED) |
1715 | 0 | sess->status = GF_NETIO_SETUP; |
1716 | 0 | else if ((sess->status != GF_NETIO_SETUP) && (sess->status != GF_NETIO_CONNECTED)) |
1717 | 0 | return GF_BAD_PARAM; |
1718 | 0 | } |
1719 | 0 | sess->range_start = start_range; |
1720 | 0 | sess->range_end = end_range; |
1721 | 0 | sess->needs_range = (start_range || end_range) ? GF_TRUE : GF_FALSE; |
1722 | 0 | return GF_OK; |
1723 | 0 | } |
1724 | | |
1725 | | #ifdef GPAC_HTTPMUX |
1726 | | static void gf_dm_sess_flush_input(GF_DownloadSession *sess) |
1727 | | { |
1728 | | u32 res; |
1729 | | sess->http_buf[0] = 0; |
1730 | | GF_Err e = gf_dm_read_data(sess, sess->http_buf, sess->http_buf_size, &res); |
1731 | | switch (e) { |
1732 | | case GF_IP_NETWORK_EMPTY: |
1733 | | case GF_OK: |
1734 | | return; |
1735 | | default: |
1736 | | sess->status = GF_NETIO_STATE_ERROR; |
1737 | | SET_LAST_ERR(e) |
1738 | | return; |
1739 | | } |
1740 | | } |
1741 | | #endif |
1742 | | |
1743 | | GF_EXPORT |
1744 | | GF_Err gf_dm_sess_process(GF_DownloadSession *sess) |
1745 | 0 | { |
1746 | 0 | Bool go; |
1747 | |
|
1748 | 0 | #ifndef GPAC_DISABLE_THREADS |
1749 | | /*if session is threaded, start thread*/ |
1750 | 0 | if (! (sess->flags & GF_NETIO_SESSION_NOT_THREADED)) { |
1751 | 0 | if (sess->dm->filter_session && !gf_opts_get_bool("core", "dm-threads")) { |
1752 | 0 | if (sess->ftask) |
1753 | 0 | return GF_OK; |
1754 | 0 | GF_SAFEALLOC(sess->ftask, GF_SessTask); |
1755 | 0 | if (!sess->ftask) return GF_OUT_OF_MEM; |
1756 | 0 | sess->ftask->sess = sess; |
1757 | 0 | gf_fs_post_user_task(sess->dm->filter_session, gf_dm_session_task, sess->ftask, "download"); |
1758 | 0 | return GF_OK; |
1759 | 0 | } |
1760 | 0 | if (sess->th) { |
1761 | 0 | GF_LOG(GF_LOG_WARNING, GF_LOG_HTTP, ("[%s] Session already started - ignoring start\n", sess->log_name)); |
1762 | 0 | return GF_OK; |
1763 | 0 | } |
1764 | 0 | sess->th = gf_th_new( gf_file_basename(sess->orig_url) ); |
1765 | 0 | if (!sess->th) return GF_OUT_OF_MEM; |
1766 | 0 | gf_th_run(sess->th, gf_dm_session_thread, sess); |
1767 | 0 | return GF_OK; |
1768 | 0 | } |
1769 | 0 | #endif //GPAC_DISABLE_THREADS |
1770 | | |
1771 | 0 | if (sess->put_state==2) { |
1772 | 0 | if (sess->status==GF_NETIO_DATA_TRANSFERED) |
1773 | 0 | sess->status = GF_NETIO_WAIT_FOR_REPLY; |
1774 | 0 | } |
1775 | | |
1776 | | /*otherwise do a synchronous download*/ |
1777 | 0 | go = GF_TRUE; |
1778 | 0 | while (go) { |
1779 | 0 | switch (sess->status) { |
1780 | | /*setup download*/ |
1781 | 0 | case GF_NETIO_SETUP: |
1782 | 0 | gf_dm_connect(sess); |
1783 | 0 | if (sess->connect_pending) { |
1784 | 0 | go = GF_FALSE; |
1785 | 0 | if (!sess->last_error) { |
1786 | | //in case someone forgot to set this - if no error we must be in network empty state while connection is pending |
1787 | 0 | sess->last_error = GF_IP_NETWORK_EMPTY; |
1788 | 0 | } |
1789 | 0 | } |
1790 | 0 | break; |
1791 | 0 | case GF_NETIO_WAIT_FOR_REPLY: |
1792 | 0 | case GF_NETIO_CONNECTED: |
1793 | 0 | if (!sess->last_error) |
1794 | 0 | sess->last_error = GF_IP_NETWORK_EMPTY; |
1795 | |
|
1796 | 0 | sess->do_requests(sess); |
1797 | 0 | if (sess->server_mode) { |
1798 | 0 | if (sess->status == GF_NETIO_STATE_ERROR) { |
1799 | 0 | sess->status = GF_NETIO_DISCONNECTED; |
1800 | 0 | SET_LAST_ERR(GF_IP_CONNECTION_CLOSED) |
1801 | 0 | sess_connection_closed(sess); |
1802 | 0 | go = GF_FALSE; |
1803 | 0 | } else if (sess->last_error==GF_IP_NETWORK_EMPTY) { |
1804 | 0 | go = GF_FALSE; |
1805 | 0 | } else if (sess->status==GF_NETIO_DISCONNECTED) { |
1806 | 0 | go = GF_FALSE; |
1807 | 0 | } |
1808 | 0 | } else if (sess->flags & GF_NETIO_SESSION_NO_BLOCK) { |
1809 | 0 | if (sess->last_error==GF_IP_NETWORK_EMPTY) |
1810 | 0 | go = GF_FALSE; |
1811 | 0 | } |
1812 | 0 | break; |
1813 | 0 | case GF_NETIO_DATA_EXCHANGE: |
1814 | 0 | if (sess->put_state==2) { |
1815 | 0 | sess->status = GF_NETIO_DATA_TRANSFERED; |
1816 | 0 | SET_LAST_ERR(GF_OK) |
1817 | 0 | go = GF_FALSE; |
1818 | 0 | break; |
1819 | 0 | } |
1820 | 0 | sess->do_requests(sess); |
1821 | 0 | break; |
1822 | 0 | case GF_NETIO_DATA_TRANSFERED: |
1823 | | #ifdef GPAC_HTTPMUX |
1824 | | if (sess->hmux_sess && sess->server_mode) { |
1825 | | gf_dm_sess_flush_input(sess); |
1826 | | sess->hmux_sess->write(sess); |
1827 | | } |
1828 | | //in put and waiting for EOS flush |
1829 | | if ((sess->put_state==1) && sess->hmux_is_eos) return GF_IP_NETWORK_EMPTY; |
1830 | | #endif |
1831 | 0 | go = GF_FALSE; |
1832 | 0 | break; |
1833 | 0 | case GF_NETIO_DISCONNECTED: |
1834 | 0 | case GF_NETIO_STATE_ERROR: |
1835 | 0 | go = GF_FALSE; |
1836 | 0 | break; |
1837 | | |
1838 | 0 | case GF_NETIO_GET_METHOD: |
1839 | 0 | case GF_NETIO_GET_HEADER: |
1840 | 0 | case GF_NETIO_GET_CONTENT: |
1841 | 0 | case GF_NETIO_PARSE_REPLY: |
1842 | 0 | break; |
1843 | | |
1844 | 0 | default: |
1845 | 0 | GF_LOG(GF_LOG_ERROR, GF_LOG_HTTP, ("[Downloader] Session in unknown state !! - aborting\n")); |
1846 | 0 | go = GF_FALSE; |
1847 | 0 | break; |
1848 | 0 | } |
1849 | 0 | } |
1850 | 0 | return sess->last_error; |
1851 | 0 | } |
1852 | | |
1853 | | GF_EXPORT |
1854 | | GF_Err gf_dm_sess_process_headers(GF_DownloadSession *sess) |
1855 | 0 | { |
1856 | 0 | Bool go; |
1857 | 0 | if (!(sess->flags & GF_NETIO_SESSION_NOT_THREADED)) { |
1858 | 0 | return GF_OK; |
1859 | 0 | } |
1860 | | |
1861 | 0 | go = GF_TRUE; |
1862 | 0 | while (go) { |
1863 | 0 | switch (sess->status) { |
1864 | | /*setup download*/ |
1865 | 0 | case GF_NETIO_SETUP: |
1866 | 0 | gf_dm_connect(sess); |
1867 | 0 | break; |
1868 | 0 | case GF_NETIO_WAIT_FOR_REPLY: |
1869 | 0 | case GF_NETIO_CONNECTED: |
1870 | 0 | sess->do_requests(sess); |
1871 | |
|
1872 | 0 | if (sess->reused_cache_entry && sess->cache_entry && gf_cache_are_headers_processed(sess->cache_entry) ) { |
1873 | 0 | sess->status = GF_NETIO_DATA_EXCHANGE; |
1874 | 0 | } |
1875 | 0 | break; |
1876 | 0 | case GF_NETIO_DATA_EXCHANGE: |
1877 | 0 | case GF_NETIO_DATA_TRANSFERED: |
1878 | 0 | case GF_NETIO_DISCONNECTED: |
1879 | 0 | case GF_NETIO_STATE_ERROR: |
1880 | 0 | go = GF_FALSE; |
1881 | 0 | break; |
1882 | 0 | default: |
1883 | 0 | break; |
1884 | 0 | } |
1885 | 0 | } |
1886 | 0 | return sess->last_error; |
1887 | 0 | } |
1888 | | |
1889 | | GF_EXPORT |
1890 | | GF_DownloadManager *gf_dm_new(GF_FilterSession *fsess) |
1891 | 0 | { |
1892 | 0 | const char *opt; |
1893 | 0 | const char * default_cache_dir; |
1894 | 0 | GF_DownloadManager *dm; |
1895 | 0 | GF_SAFEALLOC(dm, GF_DownloadManager); |
1896 | 0 | if (!dm) { |
1897 | 0 | GF_LOG(GF_LOG_ERROR, GF_LOG_HTTP, ("[Downloader] Failed to allocate downloader\n")); |
1898 | 0 | return NULL; |
1899 | 0 | } |
1900 | 0 | dm->all_sessions = gf_list_new(); |
1901 | 0 | dm->cache_entries = gf_list_new(); |
1902 | 0 | dm->credentials = gf_list_new(); |
1903 | 0 | dm->skip_proxy_servers = gf_list_new(); |
1904 | 0 | dm->partial_downloads = gf_list_new(); |
1905 | 0 | dm->cache_mx = gf_mx_new("download_manager_cache_mx"); |
1906 | 0 | dm->filter_session = fsess; |
1907 | 0 | default_cache_dir = NULL; |
1908 | 0 | gf_mx_p( dm->cache_mx ); |
1909 | |
|
1910 | | #ifdef GPAC_HAS_CURL |
1911 | | if (gf_opts_get_bool("core", "curl")) { |
1912 | | dm->curl_multi = curl_multi_init(); |
1913 | | curl_multi_setopt(dm->curl_multi, CURLMOPT_PIPELINING, CURLPIPE_MULTIPLEX); |
1914 | | dm->curl_mx = gf_mx_new("curl"); |
1915 | | curl_global_init(CURL_GLOBAL_ALL); |
1916 | | } |
1917 | | #endif |
1918 | |
|
1919 | 0 | dm->disable_http2 = gf_opts_get_bool("core", "no-h2"); |
1920 | | #ifdef GPAC_HAS_NGTCP2 |
1921 | | opt = gf_opts_get_key("core", "h3"); |
1922 | | if (!opt || !strcmp(opt, "auto")) dm->h3_mode = H3_MODE_AUTO; |
1923 | | else if (!strcmp(opt, "first")) dm->h3_mode = H3_MODE_FIRST; |
1924 | | else if (!strcmp(opt, "only")) dm->h3_mode = H3_MODE_ONLY; |
1925 | | else dm->h3_mode = H3_MODE_NO; |
1926 | | #else |
1927 | 0 | dm->h3_mode = H3_MODE_NO; |
1928 | 0 | #endif |
1929 | |
|
1930 | 0 | opt = gf_opts_get_key("core", "cache"); |
1931 | |
|
1932 | 0 | retry_cache: |
1933 | 0 | if (!opt) { |
1934 | 0 | default_cache_dir = gf_get_default_cache_directory(); |
1935 | 0 | opt = default_cache_dir; |
1936 | 0 | } |
1937 | 0 | if (opt[strlen(opt)-1] != GF_PATH_SEPARATOR) { |
1938 | 0 | dm->cache_directory = (char *) gf_malloc(sizeof(char)* (strlen(opt)+2)); |
1939 | 0 | sprintf(dm->cache_directory, "%s%c", opt, GF_PATH_SEPARATOR); |
1940 | 0 | } else { |
1941 | 0 | dm->cache_directory = gf_strdup(opt); |
1942 | 0 | } |
1943 | | |
1944 | | //check cache exists |
1945 | 0 | if (!default_cache_dir) { |
1946 | 0 | FILE *test; |
1947 | 0 | char szTemp[GF_MAX_PATH]; |
1948 | 0 | strcpy(szTemp, dm->cache_directory); |
1949 | 0 | strcat(szTemp, "gpaccache.test"); |
1950 | 0 | test = gf_fopen(szTemp, "wb"); |
1951 | 0 | if (!test) { |
1952 | 0 | gf_mkdir(dm->cache_directory); |
1953 | 0 | test = gf_fopen(szTemp, "wb"); |
1954 | 0 | if (!test) { |
1955 | 0 | GF_LOG(GF_LOG_WARNING, GF_LOG_HTTP, ("[Cache] Cannot write to %s directory, using system temp cache\n", dm->cache_directory )); |
1956 | 0 | gf_free(dm->cache_directory); |
1957 | 0 | dm->cache_directory = NULL; |
1958 | 0 | opt = NULL; |
1959 | 0 | goto retry_cache; |
1960 | 0 | } |
1961 | 0 | } |
1962 | 0 | if (test) { |
1963 | 0 | gf_fclose(test); |
1964 | 0 | gf_file_delete(szTemp); |
1965 | 0 | } |
1966 | 0 | } |
1967 | | |
1968 | | /*use it in in BYTES per second*/ |
1969 | 0 | dm->limit_data_rate = gf_opts_get_int("core", "maxrate") / 8; |
1970 | |
|
1971 | 0 | dm->read_buf_size = GF_DOWNLOAD_BUFFER_SIZE; |
1972 | | //when rate is limited, use smaller smaller read size |
1973 | 0 | if (dm->limit_data_rate) { |
1974 | 0 | dm->read_buf_size = GF_DOWNLOAD_BUFFER_SIZE_LIMIT_RATE; |
1975 | 0 | } |
1976 | |
|
1977 | 0 | dm->disable_cache = gf_opts_get_bool("core", "no-cache"); |
1978 | |
|
1979 | 0 | dm->allow_offline_cache = gf_opts_get_bool("core", "offline-cache"); |
1980 | |
|
1981 | 0 | dm->allow_broken_certificate = GF_FALSE; |
1982 | 0 | Bool do_clean = GF_TRUE; |
1983 | 0 | if ( gf_opts_get_bool("core", "clean-cache")) { |
1984 | 0 | dm->max_cache_size = 0; |
1985 | 0 | dm->cur_cache_size = gf_cache_cleanup(dm->cache_directory, dm->max_cache_size); |
1986 | 0 | do_clean = GF_FALSE; |
1987 | 0 | } |
1988 | 0 | dm->cache_clean_ms = 1000*gf_opts_get_int("core", "cache-check"); |
1989 | 0 | dm->max_cache_size = gf_opts_get_int("core", "cache-size"); |
1990 | 0 | dm->allow_broken_certificate = gf_opts_get_bool("core", "broken-cert"); |
1991 | |
|
1992 | 0 | gf_mx_v( dm->cache_mx ); |
1993 | |
|
1994 | 0 | #ifdef GPAC_HAS_SSL |
1995 | 0 | dm->ssl_ctx = NULL; |
1996 | 0 | #endif |
1997 | |
|
1998 | 0 | if (dm->max_cache_size && do_clean) |
1999 | 0 | dm->cur_cache_size = gf_cache_cleanup(dm->cache_directory, dm->max_cache_size); |
2000 | 0 | return dm; |
2001 | 0 | } |
2002 | | |
2003 | | GF_EXPORT |
2004 | | void gf_dm_set_auth_callback(GF_DownloadManager *dm, gf_dm_get_usr_pass get_user_password, void *usr_cbk) |
2005 | 0 | { |
2006 | 0 | if (dm) { |
2007 | 0 | dm->get_user_password = get_user_password; |
2008 | 0 | dm->usr_cbk = usr_cbk; |
2009 | 0 | } |
2010 | 0 | } |
2011 | | |
2012 | | GF_EXPORT |
2013 | | void gf_dm_del(GF_DownloadManager *dm) |
2014 | 0 | { |
2015 | 0 | if (!dm) |
2016 | 0 | return; |
2017 | 0 | gf_assert( dm->all_sessions); |
2018 | 0 | gf_assert( dm->cache_mx ); |
2019 | 0 | gf_mx_p( dm->cache_mx ); |
2020 | |
|
2021 | 0 | while (gf_list_count(dm->partial_downloads)) { |
2022 | 0 | GF_PartialDownload * entry = (GF_PartialDownload*)gf_list_get( dm->partial_downloads, 0); |
2023 | 0 | gf_list_rem( dm->partial_downloads, 0); |
2024 | 0 | gf_assert( entry->filename ); |
2025 | 0 | gf_file_delete( entry->filename ); |
2026 | 0 | gf_free(entry->filename ); |
2027 | 0 | entry->filename = NULL; |
2028 | 0 | entry->url = NULL; |
2029 | 0 | gf_free( entry ); |
2030 | 0 | } |
2031 | | |
2032 | | /*destroy all pending sessions*/ |
2033 | 0 | while (gf_list_count(dm->all_sessions)) { |
2034 | 0 | GF_DownloadSession *sess = (GF_DownloadSession *) gf_list_get(dm->all_sessions, 0); |
2035 | 0 | gf_dm_sess_del(sess); |
2036 | 0 | } |
2037 | 0 | gf_list_del(dm->all_sessions); |
2038 | 0 | dm->all_sessions = NULL; |
2039 | 0 | gf_assert( dm->skip_proxy_servers ); |
2040 | 0 | while (gf_list_count(dm->skip_proxy_servers)) { |
2041 | 0 | char *serv = (char*)gf_list_get(dm->skip_proxy_servers, 0); |
2042 | 0 | gf_list_rem(dm->skip_proxy_servers, 0); |
2043 | 0 | gf_free(serv); |
2044 | 0 | } |
2045 | 0 | gf_list_del(dm->skip_proxy_servers); |
2046 | 0 | dm->skip_proxy_servers = NULL; |
2047 | 0 | gf_assert( dm->credentials); |
2048 | 0 | while (gf_list_count(dm->credentials)) { |
2049 | 0 | GF_UserCredentials * cred = (GF_UserCredentials*)gf_list_get( dm->credentials, 0); |
2050 | 0 | gf_list_rem( dm->credentials, 0); |
2051 | 0 | gf_free( cred ); |
2052 | 0 | } |
2053 | 0 | gf_list_del( dm->credentials); |
2054 | 0 | dm->credentials = NULL; |
2055 | 0 | gf_assert( dm->cache_entries ); |
2056 | | |
2057 | | /* Deletes DownloadedCacheEntry and associated files if required */ |
2058 | 0 | while (gf_list_count(dm->cache_entries)) { |
2059 | 0 | const DownloadedCacheEntry entry = (const DownloadedCacheEntry)gf_list_pop_front( dm->cache_entries ); |
2060 | 0 | if (!dm->max_cache_size) |
2061 | 0 | gf_cache_entry_set_delete_files_when_deleted(entry); |
2062 | 0 | gf_cache_delete_entry(entry); |
2063 | 0 | } |
2064 | 0 | gf_list_del( dm->cache_entries ); |
2065 | 0 | dm->cache_entries = NULL; |
2066 | |
|
2067 | 0 | gf_list_del( dm->partial_downloads ); |
2068 | 0 | dm->partial_downloads = NULL; |
2069 | 0 | if (dm->cache_directory) |
2070 | 0 | gf_free(dm->cache_directory); |
2071 | 0 | dm->cache_directory = NULL; |
2072 | |
|
2073 | | #ifdef GPAC_HAS_CURL |
2074 | | if (dm->curl_multi) { |
2075 | | curl_multi_cleanup(dm->curl_multi); |
2076 | | } |
2077 | | gf_mx_del(dm->curl_mx); |
2078 | | #endif |
2079 | |
|
2080 | 0 | #ifdef GPAC_HAS_SSL |
2081 | 0 | if (dm->ssl_ctx) SSL_CTX_free(dm->ssl_ctx); |
2082 | 0 | #endif |
2083 | | /* Stored elsewhere, no need to free */ |
2084 | 0 | gf_mx_v( dm->cache_mx ); |
2085 | 0 | gf_mx_del( dm->cache_mx); |
2086 | 0 | dm->cache_mx = NULL; |
2087 | 0 | gf_free(dm); |
2088 | 0 | } |
2089 | | |
2090 | | /*! |
2091 | | * Skip ICY metadata from SHOUTCAST or ICECAST streams. |
2092 | | * Data will be skipped and parsed and sent as a GF_NETIO_Parameter to the user_io, |
2093 | | * so modules interrested by those streams may use the data |
2094 | | \param sess The GF_DownloadSession |
2095 | | \param data last data received |
2096 | | \param nbBytes The number of bytes contained into data |
2097 | | */ |
2098 | | static void gf_icy_skip_data(GF_DownloadSession * sess, const char * data, u32 nbBytes) |
2099 | 0 | { |
2100 | 0 | u32 icy_metaint; |
2101 | 0 | if (!sess || !data ) return; |
2102 | | |
2103 | 0 | icy_metaint = sess->icy_metaint; |
2104 | 0 | gf_assert( icy_metaint > 0 ); |
2105 | 0 | while (nbBytes) { |
2106 | 0 | if (sess->icy_bytes == icy_metaint) { |
2107 | 0 | sess->icy_count = 1 + 16* (u8) data[0]; |
2108 | | /*skip icy metadata*/ |
2109 | 0 | if (sess->icy_count > nbBytes) { |
2110 | 0 | sess->icy_count -= nbBytes; |
2111 | 0 | nbBytes = 0; |
2112 | 0 | } else { |
2113 | 0 | if (sess->icy_count > 1) { |
2114 | 0 | GF_NETIO_Parameter par; |
2115 | 0 | char szData[4096]; |
2116 | 0 | memset(szData, 0, 4096); |
2117 | 0 | memcpy(szData, data+1, sess->icy_count-1); |
2118 | 0 | szData[sess->icy_count] = 0; |
2119 | |
|
2120 | 0 | par.error = GF_OK; |
2121 | 0 | par.msg_type = GF_NETIO_ICY_META; |
2122 | 0 | par.name = "icy-meta"; |
2123 | 0 | par.value = szData; |
2124 | 0 | par.sess = sess; |
2125 | 0 | GF_LOG(GF_LOG_INFO, GF_LOG_HTTP, ("[ICY] Found metainfo in stream=%s, (every %d bytes)\n", szData, icy_metaint)); |
2126 | 0 | gf_dm_sess_user_io(sess, &par); |
2127 | 0 | } else { |
2128 | 0 | GF_LOG(GF_LOG_DEBUG, GF_LOG_HTTP, ("[ICY] Empty metainfo in stream, (every %d bytes)\n", icy_metaint)); |
2129 | 0 | } |
2130 | 0 | nbBytes -= sess->icy_count; |
2131 | 0 | data += sess->icy_count; |
2132 | 0 | sess->icy_count = 0; |
2133 | 0 | sess->icy_bytes = 0; |
2134 | 0 | } |
2135 | 0 | } else { |
2136 | 0 | GF_NETIO_Parameter par; |
2137 | 0 | u32 left = icy_metaint - sess->icy_bytes; |
2138 | 0 | if (left > nbBytes) { |
2139 | 0 | left = nbBytes; |
2140 | 0 | sess->icy_bytes += left; |
2141 | 0 | nbBytes = 0; |
2142 | 0 | } else { |
2143 | 0 | sess->icy_bytes = icy_metaint; |
2144 | 0 | nbBytes -= left; |
2145 | 0 | } |
2146 | |
|
2147 | 0 | par.msg_type = GF_NETIO_DATA_EXCHANGE; |
2148 | 0 | par.data = data; |
2149 | 0 | par.size = left; |
2150 | 0 | gf_dm_sess_user_io(sess, &par); |
2151 | |
|
2152 | 0 | data += left; |
2153 | 0 | } |
2154 | 0 | } |
2155 | 0 | } |
2156 | | |
2157 | | |
2158 | | static char *gf_dm_get_chunk_data(GF_DownloadSession *sess, Bool first_chunk_in_payload, char *body_start, u32 *payload_size, u32 *header_size) |
2159 | 0 | { |
2160 | 0 | u32 size; |
2161 | 0 | s32 res; |
2162 | 0 | char *te_header, *sep; |
2163 | |
|
2164 | 0 | if (!sess || !body_start) return NULL; |
2165 | 0 | if (!sess->chunked) return body_start; |
2166 | | |
2167 | 0 | if (sess->nb_left_in_chunk) { |
2168 | 0 | if (sess->nb_left_in_chunk > *payload_size) { |
2169 | 0 | sess->nb_left_in_chunk -= (*payload_size); |
2170 | 0 | GF_LOG(GF_LOG_DEBUG, GF_LOG_HTTP, ("[%s] Chunk encoding: still %d bytes to get\n", sess->log_name, sess->nb_left_in_chunk)); |
2171 | 0 | } else { |
2172 | 0 | *payload_size = sess->nb_left_in_chunk; |
2173 | 0 | sess->nb_left_in_chunk = 0; |
2174 | 0 | GF_LOG(GF_LOG_DEBUG, GF_LOG_HTTP, ("[%s] Chunk encoding: last bytes in chunk received\n", sess->log_name)); |
2175 | 0 | } |
2176 | 0 | *header_size = 0; |
2177 | 0 | return body_start; |
2178 | 0 | } |
2179 | | |
2180 | 0 | if (*payload_size == 2) { |
2181 | 0 | *header_size = 0; |
2182 | 0 | } |
2183 | 0 | *header_size = 0; |
2184 | | /*skip remaining CRLF from previous chunk if any*/ |
2185 | 0 | if (*payload_size >= 2) { |
2186 | 0 | if ((body_start[0]=='\r') && (body_start[1]=='\n')) { |
2187 | 0 | body_start += 2; |
2188 | 0 | *header_size = 2; |
2189 | 0 | } |
2190 | 0 | if (*payload_size <= 4) { |
2191 | 0 | *header_size = 0; |
2192 | 0 | return NULL; |
2193 | 0 | } |
2194 | 0 | te_header = strstr((char *) body_start, "\r\n"); |
2195 | 0 | } else { |
2196 | | //not enough bytes to read CRLF, don't bother parsing |
2197 | 0 | te_header = NULL; |
2198 | 0 | } |
2199 | | |
2200 | | //cannot parse now, copy over the bytes |
2201 | 0 | if (!te_header) { |
2202 | 0 | *header_size = 0; |
2203 | 0 | GF_LOG(GF_LOG_DEBUG, GF_LOG_HTTP, ("[%s] Chunk encoding: current buffer does not contain enough bytes (%d) to read the size\n", sess->log_name, *payload_size)); |
2204 | 0 | return NULL; |
2205 | 0 | } |
2206 | | |
2207 | 0 | te_header[0] = 0; |
2208 | 0 | *header_size += (u32) (strlen(body_start)) + 2; |
2209 | |
|
2210 | 0 | sep = strchr(body_start, ';'); |
2211 | 0 | if (sep) sep[0] = 0; |
2212 | 0 | res = sscanf(body_start, "%x", &size); |
2213 | 0 | if (res<0) { |
2214 | 0 | te_header[0] = '\r'; |
2215 | 0 | if (sep) sep[0] = ';'; |
2216 | 0 | *header_size = 0; |
2217 | 0 | *payload_size = 0; |
2218 | 0 | GF_LOG(GF_LOG_ERROR, GF_LOG_HTTP, ("[%s] Chunk encoding: fail to read chunk size from buffer %s, aborting\n", sess->log_name, body_start)); |
2219 | 0 | return NULL; |
2220 | 0 | } |
2221 | 0 | if (sep) sep[0] = ';'; |
2222 | 0 | *payload_size = size; |
2223 | |
|
2224 | 0 | GF_LOG(GF_LOG_DEBUG, GF_LOG_HTTP, ("[%s] Chunk Start: Header \"%s\" - header size %d - payload size %d (bytes done %d) at UTC "LLD"\n", sess->log_name, body_start, 2+strlen(body_start), size, sess->bytes_done, gf_net_get_utc())); |
2225 | |
|
2226 | 0 | te_header[0] = '\r'; |
2227 | 0 | if (!size) |
2228 | 0 | sess->last_chunk_found = GF_TRUE; |
2229 | |
|
2230 | 0 | sess->current_chunk_size = size; |
2231 | 0 | sess->current_chunk_start = gf_sys_clock_high_res(); |
2232 | 0 | return te_header+2; |
2233 | 0 | } |
2234 | | |
2235 | | |
2236 | | static void dm_sess_update_download_rate(GF_DownloadSession * sess) |
2237 | 0 | { |
2238 | 0 | if (!sess->bytes_done) { |
2239 | 0 | sess->bytes_per_sec = 0; |
2240 | 0 | return; |
2241 | 0 | } |
2242 | | |
2243 | | //session is chunked and we have reached our first full window |
2244 | 0 | if (sess->chunked && sess->cumulated_chunk_rate) { |
2245 | | /*use our cumulated weighted rate in bytes per seconds, and divide by total size*/ |
2246 | 0 | sess->bytes_per_sec = (u32) (sess->cumulated_chunk_rate / (sess->bytes_done + sess->cumulated_chunk_header_bytes) ); |
2247 | |
|
2248 | 0 | #ifndef GPAC_DISABLE_LOG |
2249 | 0 | if (gf_log_tool_level_on(GF_LOG_HTTP, GF_LOG_DEBUG)) { |
2250 | 0 | u64 runtime = (gf_sys_clock_high_res() - sess->request_start_time); |
2251 | 0 | if (!runtime) runtime=1; |
2252 | 0 | u32 kbps = (u32) ((1000000 * (u64) (sess->bytes_done + sess->cumulated_chunk_header_bytes)) / runtime) / 125; |
2253 | |
|
2254 | 0 | GF_LOG(GF_LOG_DEBUG, GF_LOG_HTTP, ("[%s] bandwidth estimation: download time "LLD" us - bytes %u - chunk rate %u kbps (overall rate rate %u kbps)\n", sess->log_name, runtime, sess->bytes_done, sess->bytes_per_sec / 125, kbps)); |
2255 | 0 | } |
2256 | 0 | #endif |
2257 | 0 | } else { |
2258 | | /*compute bps starting from request send time*/ |
2259 | 0 | u64 runtime = (gf_sys_clock_high_res() - sess->request_start_time); |
2260 | 0 | if (!runtime) runtime=1; |
2261 | |
|
2262 | 0 | sess->bytes_per_sec = (u32) ((1000000 * (u64) sess->bytes_done) / runtime); |
2263 | 0 | GF_LOG(GF_LOG_DEBUG, GF_LOG_HTTP, ("[%s] bandwidth estimation: download time "LLD" us - bytes %u - rate %u kbps\n", sess->log_name, runtime, sess->bytes_done, sess->bytes_per_sec / 125)); |
2264 | 0 | } |
2265 | 0 | } |
2266 | | |
2267 | | |
2268 | | void gf_dm_data_received(GF_DownloadSession *sess, u8 *payload, u32 payload_size, Bool store_in_init, u32 *rewrite_size, u8 *original_payload) |
2269 | 0 | { |
2270 | 0 | u32 nbBytes, remaining, hdr_size; |
2271 | 0 | u8* data = NULL; |
2272 | 0 | Bool first_chunk_in_payload = GF_TRUE; |
2273 | 0 | Bool flush_chunk = GF_FALSE; |
2274 | 0 | GF_NETIO_Parameter par; |
2275 | |
|
2276 | 0 | nbBytes = payload_size; |
2277 | 0 | hdr_size = 0; |
2278 | 0 | remaining = 0; |
2279 | 0 | if (!payload) |
2280 | 0 | return; //nothing to do |
2281 | 0 | if (sess->chunked) { |
2282 | 0 | data = (u8 *) gf_dm_get_chunk_data(sess, first_chunk_in_payload, (char *) payload, &nbBytes, &hdr_size); |
2283 | 0 | if (!hdr_size && !data && nbBytes) { |
2284 | | /* keep the data and wait for the rest */ |
2285 | 0 | sess->remaining_data_size = nbBytes; |
2286 | 0 | sess->remaining_data = (char *)gf_realloc(sess->remaining_data, nbBytes * sizeof(char)); |
2287 | 0 | memcpy(sess->remaining_data, payload, nbBytes); |
2288 | 0 | payload_size = 0; |
2289 | 0 | payload = NULL; |
2290 | 0 | } else if (hdr_size + nbBytes > payload_size) { |
2291 | | /* chunk header is processed but we will need several TCP frames to get the entire chunk*/ |
2292 | 0 | remaining = nbBytes + hdr_size - payload_size; |
2293 | 0 | gf_assert(payload_size >= hdr_size); |
2294 | 0 | nbBytes = payload_size - hdr_size; |
2295 | 0 | payload_size = 0; |
2296 | 0 | payload = NULL; |
2297 | 0 | sess->chunk_header_bytes += hdr_size; |
2298 | 0 | } else { |
2299 | 0 | payload_size -= hdr_size + nbBytes; |
2300 | 0 | payload += hdr_size + nbBytes; |
2301 | 0 | flush_chunk = GF_TRUE; |
2302 | 0 | sess->chunk_header_bytes += hdr_size; |
2303 | 0 | } |
2304 | | |
2305 | | /*chunk transfer is done*/ |
2306 | 0 | if (sess->last_chunk_found) { |
2307 | 0 | sess->total_size = sess->bytes_done; |
2308 | 0 | } |
2309 | 0 | } else { |
2310 | 0 | data = payload; |
2311 | 0 | remaining = payload_size = 0; |
2312 | 0 | } |
2313 | |
|
2314 | 0 | if (data && nbBytes && store_in_init) { |
2315 | 0 | sess->init_data = (char *) gf_realloc(sess->init_data , sizeof(char) * (sess->init_data_size + nbBytes) ); |
2316 | 0 | memcpy(sess->init_data+sess->init_data_size, data, nbBytes); |
2317 | 0 | sess->init_data_size += nbBytes; |
2318 | 0 | } |
2319 | | |
2320 | | //we have some new bytes received |
2321 | 0 | if (nbBytes && !sess->remaining_data_size) { |
2322 | 0 | sess->bytes_done += nbBytes; |
2323 | 0 | dm_sess_update_download_rate(sess); |
2324 | |
|
2325 | 0 | GF_LOG(GF_LOG_DEBUG, GF_LOG_HTTP, ("[%s] url %s received %d new bytes (%d kbps)\n", sess->log_name, sess->orig_url, nbBytes, 8*sess->bytes_per_sec/1000)); |
2326 | 0 | if (sess->total_size && (sess->bytes_done > sess->total_size)) { |
2327 | 0 | GF_LOG(GF_LOG_WARNING, GF_LOG_HTTP, ("[%s] url %s received more bytes than planned!! Got %d bytes vs %d content length\n", sess->log_name, sess->orig_url, sess->bytes_done , sess->total_size )); |
2328 | 0 | sess->bytes_done = sess->total_size; |
2329 | 0 | } |
2330 | |
|
2331 | 0 | if (sess->icy_metaint > 0) |
2332 | 0 | gf_icy_skip_data(sess, (char *) data, nbBytes); |
2333 | 0 | else { |
2334 | 0 | if (sess->use_cache_file && !sess->cached_file) |
2335 | 0 | gf_cache_write_to_cache( sess->cache_entry, sess, (char *) data, nbBytes, sess->dm ? sess->dm->cache_mx : NULL); |
2336 | |
|
2337 | 0 | par.msg_type = GF_NETIO_DATA_EXCHANGE; |
2338 | 0 | par.error = GF_OK; |
2339 | 0 | par.data = (char *) data; |
2340 | 0 | par.size = nbBytes; |
2341 | 0 | par.reply = flush_chunk; |
2342 | 0 | gf_dm_sess_user_io(sess, &par); |
2343 | 0 | } |
2344 | 0 | } |
2345 | | //and we're done |
2346 | 0 | if (sess->total_size && (sess->bytes_done == sess->total_size)) { |
2347 | 0 | u64 run_time; |
2348 | |
|
2349 | | #ifdef GPAC_HTTPMUX |
2350 | | if (sess->hmux_sess && !sess->server_mode) |
2351 | | sess->hmux_stream_id = -1; |
2352 | | #endif |
2353 | |
|
2354 | 0 | if (sess->use_cache_file) { |
2355 | | //for chunk transfer or H2/H3 |
2356 | 0 | gf_cache_set_content_length(sess->cache_entry, sess->total_size); |
2357 | 0 | gf_cache_close_write_cache(sess->cache_entry, sess, GF_TRUE); |
2358 | 0 | GF_LOG(GF_LOG_DEBUG, GF_LOG_CACHE, |
2359 | 0 | ("[CACHE] url %s saved as %s\n", gf_cache_get_url(sess->cache_entry), gf_cache_get_cache_filename(sess->cache_entry))); |
2360 | |
|
2361 | 0 | if (sess->dm && sess->dm->cache_clean_ms) { |
2362 | 0 | sess->dm->cur_cache_size += sess->total_size; |
2363 | 0 | if (sess->dm->cur_cache_size > sess->dm->max_cache_size) { |
2364 | 0 | if (!sess->dm->next_cache_clean) sess->dm->next_cache_clean = gf_sys_clock()+sess->dm->cache_clean_ms; |
2365 | 0 | else if (gf_sys_clock()>sess->dm->next_cache_clean) { |
2366 | 0 | sess->dm->cur_cache_size = gf_cache_cleanup(sess->dm->cache_directory, sess->dm->max_cache_size); |
2367 | 0 | sess->dm->next_cache_clean = 0; |
2368 | 0 | } |
2369 | 0 | } |
2370 | 0 | } |
2371 | 0 | } |
2372 | |
|
2373 | 0 | gf_dm_disconnect(sess, HTTP_NO_CLOSE); |
2374 | 0 | par.msg_type = GF_NETIO_DATA_TRANSFERED; |
2375 | 0 | par.error = GF_OK; |
2376 | |
|
2377 | 0 | gf_dm_sess_user_io(sess, &par); |
2378 | 0 | sess->total_time_since_req = (u32) (gf_sys_clock_high_res() - sess->request_start_time); |
2379 | 0 | run_time = gf_sys_clock_high_res() - sess->start_time; |
2380 | |
|
2381 | 0 | GF_LOG(GF_LOG_INFO, GF_LOG_HTTP, ("[%s] %s (%d bytes) downloaded in "LLU" us (%d kbps) (%d us since request - got response in %d us)\n", sess->log_name, gf_file_basename(gf_cache_get_url(sess->cache_entry)), sess->bytes_done, |
2382 | 0 | run_time, 8*sess->bytes_per_sec/1000, sess->total_time_since_req, sess->reply_time)); |
2383 | |
|
2384 | 0 | if (sess->chunked && (payload_size==2)) |
2385 | 0 | payload_size=0; |
2386 | 0 | sess->last_error = GF_OK; |
2387 | 0 | } |
2388 | |
|
2389 | 0 | if (rewrite_size && sess->chunked && data) { |
2390 | 0 | if (original_payload) { |
2391 | | //use memmove since regions overlap |
2392 | 0 | memmove(original_payload + *rewrite_size, data, nbBytes); |
2393 | 0 | } |
2394 | 0 | *rewrite_size += nbBytes; |
2395 | 0 | } |
2396 | |
|
2397 | 0 | if (!sess->nb_left_in_chunk && remaining) { |
2398 | 0 | sess->nb_left_in_chunk = remaining; |
2399 | 0 | } else if (payload_size) { |
2400 | 0 | gf_dm_data_received(sess, payload, payload_size, store_in_init, rewrite_size, original_payload); |
2401 | 0 | } |
2402 | 0 | } |
2403 | | |
2404 | | static Bool dm_exceeds_cap_rate(GF_DownloadManager * dm, GF_DownloadSession *for_sess) |
2405 | 0 | { |
2406 | 0 | u32 cumul_rate = 0; |
2407 | 0 | u32 i, count; |
2408 | |
|
2409 | 0 | gf_mx_p(dm->cache_mx); |
2410 | 0 | u64 now = gf_sys_clock_high_res(); |
2411 | 0 | count = gf_list_count(dm->all_sessions); |
2412 | | //check if this fits with all other sessions |
2413 | 0 | for (i=0; i<count; i++) { |
2414 | 0 | GF_DownloadSession * sess = (GF_DownloadSession*)gf_list_get(dm->all_sessions, i); |
2415 | 0 | if (for_sess && (sess != for_sess)) continue; |
2416 | | |
2417 | | //session not running done |
2418 | 0 | if (sess->status != GF_NETIO_DATA_EXCHANGE) continue; |
2419 | | |
2420 | | //compute average rate on a window of 200 ms |
2421 | | //we cannot just use sess->bytes_per_sec because the rate limit might be changed dynamically |
2422 | | //so we need a recent history, not the session history |
2423 | | //note that we don't try to use the estimated bps of chunk transfer when capping |
2424 | 0 | if (!sess->last_cap_rate_time) { |
2425 | 0 | u64 runtime; |
2426 | 0 | sess->last_cap_rate_time = sess->request_start_time; |
2427 | 0 | sess->last_cap_rate_bytes = sess->bytes_done; |
2428 | | |
2429 | | /*compute bps starting from request send time, do not call update_download_rate as we don't want the chunk transfer rate*/ |
2430 | 0 | runtime = (gf_sys_clock_high_res() - sess->request_start_time); |
2431 | 0 | if (!runtime) runtime=1; |
2432 | 0 | sess->last_cap_rate_bytes_per_sec = (u32) ((1000000 * (u64) sess->bytes_done) / runtime); |
2433 | 0 | } else if (now > sess->last_cap_rate_time) { |
2434 | 0 | u64 time = now - sess->last_cap_rate_time; |
2435 | 0 | u64 bytes = sess->bytes_done - sess->last_cap_rate_bytes; |
2436 | 0 | sess->last_cap_rate_bytes_per_sec = (u32) ((1000000 * (u64) bytes) / time); |
2437 | 0 | if (time > 200000) { |
2438 | | //this is an approximation we don't know precisely when these were received |
2439 | | //and we don't really care since next rate estimation will be really high anyway and will exceed cap |
2440 | 0 | sess->last_cap_rate_bytes = sess->bytes_done; |
2441 | 0 | sess->last_cap_rate_time = now; |
2442 | 0 | } |
2443 | 0 | } else { |
2444 | 0 | gf_mx_v(dm->cache_mx); |
2445 | 0 | return GF_TRUE; |
2446 | 0 | } |
2447 | 0 | cumul_rate += sess->last_cap_rate_bytes_per_sec; |
2448 | 0 | if (for_sess) break; |
2449 | 0 | } |
2450 | 0 | gf_mx_v(dm->cache_mx); |
2451 | 0 | if (for_sess) { |
2452 | 0 | if (cumul_rate >= for_sess->max_data_rate) |
2453 | 0 | return GF_TRUE; |
2454 | 0 | } else if ( cumul_rate >= dm->limit_data_rate) { |
2455 | 0 | return GF_TRUE; |
2456 | 0 | } |
2457 | | |
2458 | 0 | return GF_FALSE; |
2459 | 0 | } |
2460 | | |
2461 | | static void gf_dm_sess_estimate_chunk_rate(GF_DownloadSession *sess, u32 nb_bytes) |
2462 | 0 | { |
2463 | 0 | u64 now = gf_sys_clock_high_res(); |
2464 | 0 | sess->chunk_bytes += nb_bytes; |
2465 | 0 | if ((now > sess->last_chunk_start_time + sess->chunk_wnd_dur) || (sess->total_size==sess->bytes_done) ) { |
2466 | 0 | if (sess->chunk_bytes) { |
2467 | 0 | u32 tot_bytes = sess->chunk_bytes + sess->chunk_header_bytes; |
2468 | | //compute rate in bytes per seconds |
2469 | 0 | Double rate = 1000000.0 * tot_bytes; |
2470 | 0 | rate /= (now - sess->last_chunk_start_time); |
2471 | | |
2472 | | //cumulated rate is the weighted sum of our probe rates, the weight being the number of bytes |
2473 | | //when comuting the bitrate, we will divide by the total size |
2474 | 0 | sess->cumulated_chunk_rate += rate * tot_bytes; |
2475 | |
|
2476 | 0 | sess->chunk_bytes = 0; |
2477 | 0 | sess->cumulated_chunk_header_bytes += sess->chunk_header_bytes; |
2478 | 0 | sess->chunk_header_bytes = 0; |
2479 | | |
2480 | | //we are done, update rate |
2481 | 0 | if (sess->total_size==sess->bytes_done) |
2482 | 0 | dm_sess_update_download_rate(sess); |
2483 | 0 | } |
2484 | 0 | sess->last_chunk_start_time = now; |
2485 | 0 | } |
2486 | 0 | } |
2487 | | |
2488 | | GF_EXPORT |
2489 | | GF_Err gf_dm_sess_fetch_data(GF_DownloadSession *sess, char *buffer, u32 buffer_size, u32 *read_size) |
2490 | 0 | { |
2491 | 0 | u32 size; |
2492 | 0 | GF_Err e; |
2493 | |
|
2494 | 0 | if (!buffer || !buffer_size) { |
2495 | 0 | if (sess->put_state) { |
2496 | 0 | sess->put_state = 2; |
2497 | 0 | sess->status = GF_NETIO_WAIT_FOR_REPLY; |
2498 | 0 | return GF_OK; |
2499 | 0 | } |
2500 | 0 | return GF_BAD_PARAM; |
2501 | 0 | } |
2502 | 0 | if (sess->th) |
2503 | 0 | return GF_BAD_PARAM; |
2504 | 0 | if (sess->status == GF_NETIO_DISCONNECTED) { |
2505 | 0 | if (!sess->init_data_size) |
2506 | 0 | return GF_EOS; |
2507 | 0 | } |
2508 | 0 | else if (sess->status == GF_NETIO_STATE_ERROR) { |
2509 | 0 | return sess->last_error; |
2510 | 0 | } |
2511 | 0 | else if (sess->status > GF_NETIO_DATA_TRANSFERED) |
2512 | 0 | return GF_BAD_PARAM; |
2513 | | |
2514 | 0 | *read_size = 0; |
2515 | 0 | if (sess->status == GF_NETIO_DATA_TRANSFERED) { |
2516 | 0 | if (!sess->server_mode) |
2517 | 0 | return GF_EOS; |
2518 | 0 | if (!sess->init_data_size && sess->total_size && (sess->total_size==sess->bytes_done)) |
2519 | 0 | return GF_EOS; |
2520 | 0 | sess->status = GF_NETIO_DATA_EXCHANGE; |
2521 | 0 | } |
2522 | | |
2523 | 0 | if (sess->status == GF_NETIO_SETUP) { |
2524 | 0 | gf_dm_connect(sess); |
2525 | 0 | if (sess->last_error) |
2526 | 0 | return sess->last_error; |
2527 | 0 | e = GF_IP_NETWORK_EMPTY; |
2528 | 0 | } else if (sess->status < GF_NETIO_DATA_EXCHANGE) { |
2529 | 0 | sess->do_requests(sess); |
2530 | 0 | if (!sess->server_mode |
2531 | 0 | && (sess->status > GF_NETIO_WAIT_FOR_REPLY) |
2532 | 0 | && sess->needs_range |
2533 | 0 | && (sess->rsp_code==200) |
2534 | 0 | ) { |
2535 | | //reset for next call to process if user wants so |
2536 | 0 | sess->needs_range = GF_FALSE; |
2537 | 0 | return GF_IO_BYTE_RANGE_NOT_SUPPORTED; |
2538 | 0 | } |
2539 | 0 | e = sess->last_error ? sess->last_error : GF_IP_NETWORK_EMPTY; |
2540 | 0 | } |
2541 | | /*we're running but we had data previously*/ |
2542 | 0 | else if (sess->init_data) { |
2543 | 0 | e = GF_OK; |
2544 | 0 | if (sess->init_data_size<=buffer_size) { |
2545 | 0 | memcpy(buffer, sess->init_data, sizeof(char)*sess->init_data_size); |
2546 | 0 | *read_size = sess->init_data_size; |
2547 | 0 | gf_free(sess->init_data); |
2548 | 0 | sess->init_data = NULL; |
2549 | 0 | if (sess->init_data_size==sess->total_size) |
2550 | 0 | e = GF_EOS; |
2551 | 0 | sess->init_data_size = 0; |
2552 | 0 | } else { |
2553 | 0 | memcpy(buffer, sess->init_data, sizeof(char)*buffer_size); |
2554 | 0 | *read_size = buffer_size; |
2555 | 0 | sess->init_data_size -= buffer_size; |
2556 | 0 | memmove(sess->init_data, sess->init_data+buffer_size, sizeof(char)*sess->init_data_size); |
2557 | 0 | e = GF_OK; |
2558 | 0 | } |
2559 | 0 | } else if (sess->local_cache_only) { |
2560 | 0 | Bool was_modified; |
2561 | 0 | u32 to_copy, full_cache_size, max_valid_size=0, cache_done; |
2562 | 0 | const u8 *ptr; |
2563 | 0 | e = GF_OK; |
2564 | 0 | gf_assert(sess->cache_entry); |
2565 | | //always refresh total size |
2566 | 0 | sess->total_size = gf_cache_get_content_length(sess->cache_entry); |
2567 | |
|
2568 | 0 | ptr = gf_cache_get_content(sess->cache_entry, &full_cache_size, &max_valid_size, &was_modified); |
2569 | |
|
2570 | 0 | cache_done = gf_cache_is_done(sess->cache_entry); |
2571 | | //something went wrong, we cannot have less valid bytes than what we had at previous call(s) |
2572 | 0 | if (max_valid_size < sess->bytes_done) |
2573 | 0 | cache_done = 2; |
2574 | |
|
2575 | 0 | if ((sess->bytes_done >= full_cache_size)|| (cache_done==2)) { |
2576 | 0 | *read_size = 0; |
2577 | 0 | gf_cache_release_content(sess->cache_entry); |
2578 | 0 | if (cache_done==2) { |
2579 | 0 | sess->status = GF_NETIO_STATE_ERROR; |
2580 | 0 | SET_LAST_ERR(GF_IP_NETWORK_FAILURE) |
2581 | 0 | return GF_IP_NETWORK_FAILURE; |
2582 | 0 | } |
2583 | 0 | else if (cache_done) { |
2584 | 0 | sess->status = GF_NETIO_DATA_TRANSFERED; |
2585 | 0 | SET_LAST_ERR( GF_OK) |
2586 | 0 | return GF_EOS; |
2587 | 0 | } |
2588 | 0 | return was_modified ? GF_OK : GF_IP_NETWORK_EMPTY; |
2589 | 0 | } |
2590 | 0 | if (!ptr) return GF_OUT_OF_MEM; |
2591 | | |
2592 | | //only copy valid bytes for http |
2593 | 0 | to_copy = max_valid_size - sess->bytes_done; |
2594 | 0 | if (to_copy > buffer_size) to_copy = buffer_size; |
2595 | |
|
2596 | 0 | memcpy(buffer, ptr + sess->bytes_done, to_copy); |
2597 | 0 | sess->bytes_done += to_copy; |
2598 | 0 | *read_size = to_copy; |
2599 | 0 | if ((cache_done==1) && (sess->bytes_done == sess->total_size) ) { |
2600 | 0 | sess->status = GF_NETIO_DATA_TRANSFERED; |
2601 | 0 | SET_LAST_ERR( (cache_done==2) ? GF_IP_NETWORK_FAILURE : GF_OK) |
2602 | 0 | } else { |
2603 | 0 | sess->total_size = 0; |
2604 | 0 | } |
2605 | 0 | gf_cache_release_content(sess->cache_entry); |
2606 | 0 | } else { |
2607 | |
|
2608 | 0 | if (sess->dm && (sess->dm->limit_data_rate || sess->max_data_rate)) { |
2609 | 0 | if (dm_exceeds_cap_rate(sess->dm, sess->max_data_rate ? sess : NULL)) |
2610 | 0 | return GF_IP_NETWORK_EMPTY; |
2611 | | |
2612 | 0 | if (buffer_size > sess->dm->read_buf_size) |
2613 | 0 | buffer_size = sess->dm->read_buf_size; |
2614 | 0 | } |
2615 | | |
2616 | 0 | e = GF_OK; |
2617 | 0 | *read_size = 0; |
2618 | 0 | u32 nb_read = 0; |
2619 | | //perform a loop, mostly for chunk-tranfer mode where a server may push a lot of small TCP frames, |
2620 | | //we want to flush everything as fast as possible |
2621 | 0 | while (1) { |
2622 | 0 | u32 single_read = 0; |
2623 | |
|
2624 | 0 | if (sess->remaining_data && sess->remaining_data_size) { |
2625 | 0 | if (nb_read + sess->remaining_data_size >= buffer_size) { |
2626 | 0 | if (!nb_read) { |
2627 | 0 | GF_LOG(GF_LOG_ERROR, GF_LOG_HTTP, ("[%s] No HTTP chunk header found for %d bytes, assuming broken chunk transfer and aborting\n", sess->log_name, sess->remaining_data_size)); |
2628 | 0 | return GF_NON_COMPLIANT_BITSTREAM; |
2629 | 0 | } |
2630 | 0 | break; |
2631 | 0 | } |
2632 | 0 | memcpy(buffer + nb_read, sess->remaining_data, sess->remaining_data_size); |
2633 | 0 | } else if (nb_read >= buffer_size) { |
2634 | 0 | break; |
2635 | 0 | } |
2636 | 0 | e = gf_dm_read_data(sess, buffer + nb_read + sess->remaining_data_size, buffer_size - sess->remaining_data_size - nb_read, &single_read); |
2637 | 0 | if (e<0) { |
2638 | 0 | gf_assert(single_read==0); |
2639 | 0 | break; |
2640 | 0 | } |
2641 | | |
2642 | 0 | size = sess->remaining_data_size + single_read; |
2643 | 0 | sess->remaining_data_size = 0; |
2644 | 0 | single_read = 0; |
2645 | |
|
2646 | | #ifdef GPAC_HTTPMUX |
2647 | | //do NOT call data received if hmux, done once input is empty |
2648 | | if (!sess->hmux_sess || sess->cached_file) |
2649 | | #endif |
2650 | 0 | gf_dm_data_received(sess, (u8 *) buffer + nb_read, size, GF_FALSE, &single_read, buffer + nb_read); |
2651 | | |
2652 | |
|
2653 | 0 | if (!sess->chunked) |
2654 | 0 | single_read = size; |
2655 | |
|
2656 | 0 | nb_read += single_read; |
2657 | 0 | } |
2658 | | |
2659 | | #ifdef GPAC_HTTPMUX |
2660 | | //process any received data |
2661 | | if (sess->hmux_sess) { |
2662 | | nb_read = 0; |
2663 | | hmux_fetch_data(sess, buffer, buffer_size, &nb_read); |
2664 | | sess->hmux_sess->write(sess); |
2665 | | |
2666 | | //stream is over and all data flushed, move to GF_NETIO_DATA_TRANSFERED in client mode |
2667 | | if ((sess->hmux_stream_id<0) && sess->hmux_data_done && !sess->hmux_buf.size && !sess->server_mode) { |
2668 | | sess->status = GF_NETIO_DATA_TRANSFERED; |
2669 | | SET_LAST_ERR(GF_OK) |
2670 | | } |
2671 | | } |
2672 | | #endif |
2673 | | |
2674 | 0 | *read_size = nb_read; |
2675 | | //we had data but last call to gf_dm_read_data may have returned network empty |
2676 | 0 | if (nb_read && (e<0)) |
2677 | 0 | e = GF_OK; |
2678 | | |
2679 | | |
2680 | | //estimate rate for chunk-transfer - we only do that for fetch_data |
2681 | 0 | if (sess->chunked |
2682 | | #ifdef GPAC_HTTPMUX |
2683 | | || sess->hmux_sess |
2684 | | #endif |
2685 | 0 | ) |
2686 | 0 | gf_dm_sess_estimate_chunk_rate(sess, nb_read); |
2687 | |
|
2688 | 0 | if (! (*read_size) && (e==GF_IP_NETWORK_EMPTY)) { |
2689 | | #ifdef GPAC_HTTPMUX |
2690 | | if (sess->hmux_sess && !sess->total_size |
2691 | | //for client, wait for close - for server move to data_transfered as soon as we're done pushing data |
2692 | | && (((sess->hmux_stream_id<0) && sess->bytes_done) || (sess->hmux_data_done && sess->server_mode)) |
2693 | | ) { |
2694 | | sess->status = GF_NETIO_DATA_TRANSFERED; |
2695 | | SET_LAST_ERR(GF_OK) |
2696 | | return GF_EOS; |
2697 | | } |
2698 | | #endif |
2699 | |
|
2700 | | #ifdef GPAC_HAS_CURL |
2701 | | if (sess->curl_hnd) { |
2702 | | if (sess->status == GF_NETIO_WAIT_FOR_REPLY) |
2703 | | return GF_IP_NETWORK_EMPTY; |
2704 | | } else |
2705 | | #endif |
2706 | 0 | if (sess->sock) |
2707 | 0 | e = gf_sk_probe(sess->sock); |
2708 | |
|
2709 | 0 | if ((e==GF_IP_CONNECTION_CLOSED) |
2710 | 0 | || (sess->request_timeout && (gf_sys_clock_high_res() - sess->last_fetch_time > 1000 * sess->request_timeout)) |
2711 | 0 | ) { |
2712 | 0 | if (e==GF_IP_CONNECTION_CLOSED) { |
2713 | 0 | SET_LAST_ERR(GF_IP_CONNECTION_CLOSED) |
2714 | 0 | sess_connection_closed(sess); |
2715 | 0 | } else { |
2716 | 0 | GF_LOG(GF_LOG_WARNING, GF_LOG_HTTP, |
2717 | 0 | ("[%s] Session timeout for %s after %u ms, aborting\n", sess->log_name, sess->orig_url, sess->request_timeout)); |
2718 | |
|
2719 | 0 | SET_LAST_ERR(GF_IP_NETWORK_FAILURE) |
2720 | 0 | } |
2721 | 0 | sess->status = GF_NETIO_STATE_ERROR; |
2722 | 0 | return GF_IP_NETWORK_EMPTY; |
2723 | 0 | } |
2724 | 0 | } |
2725 | 0 | } |
2726 | | |
2727 | | #ifdef GPAC_HTTPMUX |
2728 | | if (sess->hmux_sess && sess->hmux_buf.size) { |
2729 | | return e; |
2730 | | } |
2731 | | #endif |
2732 | | |
2733 | 0 | if (sess->server_mode && (sess->status == GF_NETIO_DATA_EXCHANGE)) { |
2734 | 0 | sess->status = GF_NETIO_DATA_TRANSFERED; |
2735 | 0 | SET_LAST_ERR(GF_OK) |
2736 | 0 | } |
2737 | |
|
2738 | 0 | return e; |
2739 | 0 | } |
2740 | | |
2741 | | GF_EXPORT |
2742 | | GF_Err gf_dm_sess_get_stats(GF_DownloadSession * sess, const char **server, const char **path, u64 *total_size, u64 *bytes_done, u32 *bytes_per_sec, GF_NetIOStatus *net_status) |
2743 | 0 | { |
2744 | 0 | if (!sess) |
2745 | 0 | return GF_BAD_PARAM; |
2746 | 0 | if (server) *server = sess->server_name; |
2747 | 0 | if (path) *path = sess->remote_path; |
2748 | 0 | if (total_size) { |
2749 | 0 | if (sess->total_size==SIZE_IN_STREAM) *total_size = 0; |
2750 | 0 | else *total_size = sess->total_size; |
2751 | 0 | } |
2752 | 0 | if (bytes_done) *bytes_done = sess->bytes_done; |
2753 | 0 | if (bytes_per_sec) { |
2754 | 0 | if (sess->dm && (sess->dm->limit_data_rate || sess->max_data_rate) && sess->last_cap_rate_bytes_per_sec) { |
2755 | 0 | *bytes_per_sec = sess->last_cap_rate_bytes_per_sec; |
2756 | 0 | } else { |
2757 | 0 | *bytes_per_sec = sess->bytes_per_sec; |
2758 | 0 | } |
2759 | 0 | } |
2760 | |
|
2761 | 0 | if (net_status) *net_status = sess->status; |
2762 | |
|
2763 | 0 | if (sess->status == GF_NETIO_DISCONNECTED) { |
2764 | 0 | if (sess->last_error) return sess->last_error; |
2765 | 0 | return GF_EOS; |
2766 | 0 | } |
2767 | 0 | else if (sess->status == GF_NETIO_STATE_ERROR) |
2768 | 0 | return sess->last_error ? sess->last_error : GF_SERVICE_ERROR; |
2769 | 0 | return GF_OK; |
2770 | 0 | } |
2771 | | |
2772 | | GF_EXPORT |
2773 | | u64 gf_dm_sess_get_utc_start(GF_DownloadSession * sess) |
2774 | 0 | { |
2775 | 0 | if (!sess) return 0; |
2776 | 0 | return sess->start_time_utc; |
2777 | 0 | } |
2778 | | |
2779 | | GF_EXPORT |
2780 | | const char *gf_dm_sess_get_cache_name(GF_DownloadSession * sess) |
2781 | 0 | { |
2782 | 0 | if (!sess) return NULL; |
2783 | 0 | if (! sess->cache_entry || sess->needs_cache_reconfig) return NULL; |
2784 | 0 | if (!sess->use_cache_file) return NULL; |
2785 | 0 | return gf_cache_get_cache_filename(sess->cache_entry); |
2786 | 0 | } |
2787 | | |
2788 | | GF_EXPORT |
2789 | | void gf_dm_sess_abort(GF_DownloadSession * sess) |
2790 | 0 | { |
2791 | 0 | if (sess) { |
2792 | 0 | gf_mx_p(sess->mx); |
2793 | |
|
2794 | | #ifdef GPAC_HTTPMUX |
2795 | | if (sess->hmux_sess && (sess->status==GF_NETIO_DATA_EXCHANGE)) { |
2796 | | sess->hmux_sess->stream_reset(sess, GF_TRUE); |
2797 | | sess->hmux_sess->write(sess); |
2798 | | } |
2799 | | #endif |
2800 | 0 | gf_dm_disconnect(sess, HTTP_CLOSE); |
2801 | | //not an error, move to DISCONNECTED state |
2802 | 0 | sess->status = GF_NETIO_DISCONNECTED; |
2803 | 0 | SET_LAST_ERR(GF_IP_CONNECTION_CLOSED) |
2804 | 0 | gf_mx_v(sess->mx); |
2805 | 0 | } |
2806 | 0 | } |
2807 | | |
2808 | | /*! |
2809 | | * Sends the HTTP headers |
2810 | | \param sess The GF_DownloadSession |
2811 | | \return GF_OK if everything went fine, the error otherwise |
2812 | | */ |
2813 | 0 | static GF_Err http_send_headers(GF_DownloadSession *sess) { |
2814 | 0 | GF_Err e; |
2815 | 0 | GF_NETIO_Parameter par; |
2816 | 0 | Bool no_cache = GF_FALSE; |
2817 | 0 | char range_buf[1024]; |
2818 | 0 | char pass_buf[1124]; |
2819 | 0 | char req_name[20]; |
2820 | 0 | const char *user_agent; |
2821 | 0 | const char *url; |
2822 | 0 | const char *user_profile; |
2823 | 0 | const char *param_string; |
2824 | 0 | Bool inject_icy = GF_FALSE; |
2825 | 0 | u32 i, count; |
2826 | 0 | GF_HTTPHeader *hdr; |
2827 | 0 | Bool has_accept, has_connection, has_range, has_agent, has_language, send_profile, has_mime, has_chunk_transfer; |
2828 | 0 | assert (sess->status == GF_NETIO_CONNECTED); |
2829 | |
|
2830 | 0 | char * sHTTP = sess->http_buf; |
2831 | |
|
2832 | 0 | gf_assert(sess->remaining_data_size == 0); |
2833 | |
|
2834 | 0 | if (sess->needs_cache_reconfig) { |
2835 | 0 | gf_dm_sess_clear_headers(sess); |
2836 | 0 | gf_dm_configure_cache(sess); |
2837 | 0 | sess->needs_cache_reconfig = 0; |
2838 | 0 | } |
2839 | 0 | if (sess->cached_file) { |
2840 | 0 | sess->last_fetch_time = sess->request_start_time = gf_sys_clock_high_res(); |
2841 | 0 | sess->req_hdr_size = 0; |
2842 | 0 | sess->status = GF_NETIO_WAIT_FOR_REPLY; |
2843 | 0 | gf_dm_sess_notify_state(sess, GF_NETIO_WAIT_FOR_REPLY, GF_OK); |
2844 | 0 | return GF_OK; |
2845 | 0 | } |
2846 | 0 | gf_dm_sess_clear_headers(sess); |
2847 | | |
2848 | | //in case we got disconnected, reconnect |
2849 | | #ifdef GPAC_HAS_CURL |
2850 | | if (sess->curl_hnd) |
2851 | | e = GF_OK; |
2852 | | else |
2853 | | #endif |
2854 | 0 | e = gf_sk_probe(sess->sock); |
2855 | |
|
2856 | 0 | if (e && (e!=GF_IP_NETWORK_EMPTY)) { |
2857 | 0 | sess_connection_closed(sess); |
2858 | 0 | if ((e==GF_IP_CONNECTION_CLOSED) && sess->num_retry) { |
2859 | 0 | sess->num_retry--; |
2860 | 0 | sess->status = GF_NETIO_SETUP; |
2861 | 0 | return GF_OK; |
2862 | 0 | } |
2863 | 0 | sess->status = GF_NETIO_STATE_ERROR; |
2864 | 0 | SET_LAST_ERR(e) |
2865 | 0 | gf_dm_sess_notify_state(sess, GF_NETIO_STATE_ERROR, e); |
2866 | 0 | return e; |
2867 | 0 | } |
2868 | | |
2869 | | /*setup authentication*/ |
2870 | 0 | strcpy(pass_buf, ""); |
2871 | 0 | sess->creds = gf_user_credentials_find_for_site( sess->dm, sess->server_name, NULL); |
2872 | 0 | if (sess->creds && sess->creds->valid) { |
2873 | | #ifdef GPAC_HAS_CURL |
2874 | | //let curl handle authentication methods |
2875 | | if (sess->curl_hnd) { |
2876 | | char szUsrPass[101]; |
2877 | | u32 len = gf_base64_decode(sess->creds->digest, (u32) strlen(sess->creds->digest), szUsrPass, 100); |
2878 | | szUsrPass[len]=0; |
2879 | | curl_easy_setopt(sess->curl_hnd, CURLOPT_USERPWD, szUsrPass); |
2880 | | } else |
2881 | | #endif |
2882 | 0 | sprintf(pass_buf, "Basic %s", sess->creds->digest); |
2883 | 0 | } |
2884 | |
|
2885 | 0 | user_agent = gf_opts_get_key("core", "ua"); |
2886 | 0 | if (!user_agent) user_agent = GF_DOWNLOAD_AGENT_NAME; |
2887 | |
|
2888 | 0 | sess->put_state = 0; |
2889 | |
|
2890 | 0 | par.error = GF_OK; |
2891 | 0 | par.msg_type = GF_NETIO_GET_METHOD; |
2892 | 0 | par.name = NULL; |
2893 | 0 | gf_dm_sess_user_io(sess, &par); |
2894 | 0 | if (!par.name || sess->server_only_understand_get) { |
2895 | 0 | par.name = "GET"; |
2896 | 0 | } |
2897 | |
|
2898 | 0 | strncpy(req_name, par.name, 19); |
2899 | 0 | req_name[19] = 0; |
2900 | |
|
2901 | 0 | if (!strcmp(req_name, "GET")) { |
2902 | 0 | sess->http_read_type = GET; |
2903 | | #ifdef GPAC_HTTPMUX |
2904 | | if (!sess->hmux_sess) |
2905 | | #endif |
2906 | 0 | inject_icy = GF_TRUE; |
2907 | 0 | } else if (!strcmp(req_name, "HEAD")) sess->http_read_type = HEAD; |
2908 | 0 | else sess->http_read_type = OTHER; |
2909 | |
|
2910 | 0 | if (!strcmp(req_name, "PUT") || !strcmp(req_name, "POST")) |
2911 | 0 | sess->put_state = 1; |
2912 | | |
2913 | | //url is the remote path event if proxy (Host header will point to desired host) |
2914 | | //note that url is not used for CURL, already setup together with proxy |
2915 | 0 | url = sess->remote_path; |
2916 | | |
2917 | | /*get all headers*/ |
2918 | 0 | gf_dm_sess_clear_headers(sess); |
2919 | | |
2920 | |
|
2921 | | #ifdef GPAC_HTTPMUX |
2922 | | if (!sess->hmux_sess) |
2923 | | #endif |
2924 | 0 | { |
2925 | | //always put port number when proxy is enabled |
2926 | 0 | if (sess->proxy_enabled || ((sess->port!=80) && (sess->port!=443))) { |
2927 | 0 | sprintf(sHTTP, "%s:%u", sess->server_name, sess->port); |
2928 | 0 | PUSH_HDR("Host", sHTTP); |
2929 | 0 | } else { |
2930 | 0 | PUSH_HDR("Host", sess->server_name); |
2931 | 0 | } |
2932 | 0 | } |
2933 | |
|
2934 | 0 | has_agent = has_accept = has_connection = has_range = has_language = has_mime = has_chunk_transfer = GF_FALSE; |
2935 | 0 | while (1) { |
2936 | 0 | par.msg_type = GF_NETIO_GET_HEADER; |
2937 | 0 | par.value = NULL; |
2938 | 0 | gf_dm_sess_user_io(sess, &par); |
2939 | 0 | if (!par.value) break; |
2940 | | //if name is not set, skip this header |
2941 | 0 | if (!par.name) continue; |
2942 | | |
2943 | 0 | if (!stricmp(par.name, "Connection")) { |
2944 | 0 | if (!stricmp(par.value, "close")) |
2945 | 0 | has_connection = GF_TRUE; |
2946 | 0 | else |
2947 | 0 | continue; |
2948 | 0 | } |
2949 | 0 | else if (!stricmp(par.name, "Transfer-Encoding")) { |
2950 | 0 | if (!stricmp(par.value, "chunked")) |
2951 | 0 | has_chunk_transfer = GF_TRUE; |
2952 | 0 | continue; |
2953 | 0 | } |
2954 | | |
2955 | 0 | gf_dm_sess_set_header_ex(sess, par.name, par.value, GF_TRUE); |
2956 | |
|
2957 | 0 | if (!stricmp(par.name, "Accept")) has_accept = GF_TRUE; |
2958 | 0 | else if (!stricmp(par.name, "Range")) has_range = GF_TRUE; |
2959 | 0 | else if (!stricmp(par.name, "User-Agent")) has_agent = GF_TRUE; |
2960 | 0 | else if (!stricmp(par.name, "Accept-Language")) has_language = GF_TRUE; |
2961 | 0 | else if (!stricmp(par.name, "Content-Type")) has_mime = GF_TRUE; |
2962 | |
|
2963 | 0 | if (!par.msg_type) break; |
2964 | 0 | } |
2965 | 0 | if (!has_agent) PUSH_HDR("User-Agent", user_agent) |
2966 | | |
2967 | | /*no mime and POST/PUT, default to octet stream*/ |
2968 | 0 | if (!has_mime && (sess->http_read_type==OTHER)) PUSH_HDR("Content-Type", "application/octet-stream") |
2969 | |
|
2970 | 0 | if (!has_accept && (sess->http_read_type!=OTHER) ) PUSH_HDR("Accept", "*/*") |
2971 | |
|
2972 | | #ifdef GPAC_HTTPMUX |
2973 | | if (sess->hmux_sess) |
2974 | | has_connection = GF_TRUE; |
2975 | | #endif |
2976 | | |
2977 | |
|
2978 | | #ifdef GPAC_HAS_HTTP2 |
2979 | | //inject upgrade for h2 |
2980 | | if (!has_connection && !sess->hmux_sess |
2981 | | #ifdef GPAC_HAS_CURL |
2982 | | && !sess->curl_hnd |
2983 | | #endif |
2984 | | #ifdef GPAC_HAS_SSL |
2985 | | && !sess->ssl |
2986 | | #endif |
2987 | | && !sess->dm->disable_http2 |
2988 | | && !sess->h2_upgrade_state |
2989 | | && !gf_opts_get_bool("core", "no-h2c") |
2990 | | ) { |
2991 | | http2_set_upgrade_headers(sess); |
2992 | | inject_icy = GF_FALSE; |
2993 | | } else |
2994 | | #endif //GPAC_HAS_HTTP2 |
2995 | |
|
2996 | 0 | if (sess->proxy_enabled==1) { |
2997 | | #ifdef GPAC_HTTPMUX |
2998 | | if (!sess->hmux_sess) |
2999 | | #endif |
3000 | 0 | PUSH_HDR("Proxy-Connection", "Keep-alive") |
3001 | 0 | } |
3002 | 0 | else if (!has_connection) { |
3003 | 0 | PUSH_HDR("Connection", "Keep-Alive"); |
3004 | 0 | } |
3005 | | |
3006 | |
|
3007 | 0 | if (has_chunk_transfer |
3008 | | #ifdef GPAC_HTTPMUX |
3009 | | && !sess->hmux_sess |
3010 | | #endif |
3011 | 0 | ) { |
3012 | 0 | PUSH_HDR("Transfer-Encoding", "chunked"); |
3013 | 0 | sess->chunked = GF_TRUE; |
3014 | 0 | } |
3015 | |
|
3016 | 0 | if (!has_range && sess->needs_range) { |
3017 | 0 | if (!sess->range_end) |
3018 | 0 | sprintf(range_buf, "bytes="LLD"-", sess->range_start); |
3019 | | //if end is set to -1 use open end |
3020 | 0 | else if (sess->range_end==(u64)-1) |
3021 | 0 | sprintf(range_buf, "bytes="LLD"-", sess->range_start); |
3022 | 0 | else |
3023 | 0 | sprintf(range_buf, "bytes="LLD"-"LLD"", sess->range_start, sess->range_end); |
3024 | 0 | PUSH_HDR("Range", range_buf) |
3025 | 0 | no_cache = GF_TRUE; |
3026 | 0 | } |
3027 | 0 | if (!has_language) { |
3028 | 0 | const char *opt = gf_opts_get_key("core", "lang"); |
3029 | 0 | if (opt) PUSH_HDR("Accept-Language", opt) |
3030 | 0 | } |
3031 | | |
3032 | |
|
3033 | 0 | if (strlen(pass_buf)) { |
3034 | 0 | PUSH_HDR("Authorization", pass_buf) |
3035 | 0 | } |
3036 | |
|
3037 | 0 | par.msg_type = GF_NETIO_GET_CONTENT; |
3038 | 0 | par.data = NULL; |
3039 | 0 | par.size = 0; |
3040 | | |
3041 | | /*check if we have personalization info*/ |
3042 | 0 | send_profile = GF_FALSE; |
3043 | 0 | user_profile = gf_opts_get_key("core", "user-profileid"); |
3044 | |
|
3045 | 0 | if (user_profile) { |
3046 | 0 | PUSH_HDR("X-UserProfileID", user_profile); |
3047 | 0 | } else if ((sess->http_read_type == GET) || (sess->http_read_type == HEAD) ) { |
3048 | 0 | user_profile = gf_opts_get_key("core", "user-profile"); |
3049 | 0 | if (user_profile && gf_file_exists(user_profile)) { |
3050 | 0 | FILE *profile = gf_fopen(user_profile, "rb"); |
3051 | 0 | if (profile) { |
3052 | 0 | par.size = (u32) gf_fsize(profile); |
3053 | 0 | gf_fclose(profile); |
3054 | 0 | sprintf(range_buf, "%d", par.size); |
3055 | 0 | PUSH_HDR("Content-Length", range_buf); |
3056 | 0 | PUSH_HDR("Content-Type", "text/xml"); |
3057 | 0 | send_profile = GF_TRUE; |
3058 | 0 | } |
3059 | 0 | } |
3060 | 0 | } |
3061 | | |
3062 | |
|
3063 | 0 | if (!send_profile) { |
3064 | 0 | gf_dm_sess_user_io(sess, &par); |
3065 | 0 | if (par.data && par.size) { |
3066 | 0 | sprintf(range_buf, "%d", par.size); |
3067 | 0 | PUSH_HDR("Content-Length", range_buf); |
3068 | 0 | } else { |
3069 | 0 | par.data = NULL; |
3070 | 0 | par.size = 0; |
3071 | 0 | } |
3072 | 0 | } |
3073 | |
|
3074 | 0 | if (inject_icy) { |
3075 | | /* This will force the server to respond with Icy-Metaint */ |
3076 | 0 | PUSH_HDR("Icy-Metadata", "1"); |
3077 | 0 | } |
3078 | |
|
3079 | 0 | if (sess->http_read_type!=OTHER) { |
3080 | 0 | const char *etag=NULL, *last_modif=NULL; |
3081 | | |
3082 | | /*cached headers are not appended in POST*/ |
3083 | 0 | if (!no_cache && !sess->disable_cache && (GF_OK < gf_cache_get_http_headers( sess->cache_entry, &etag, &last_modif)) ) { |
3084 | 0 | GF_LOG(GF_LOG_WARNING, GF_LOG_HTTP, ("Cache Entry : %p, FAILED to append cache directives.", sess->cache_entry)); |
3085 | 0 | } |
3086 | |
|
3087 | 0 | if (etag) PUSH_HDR("If-None-Match", etag) |
3088 | 0 | if (last_modif) PUSH_HDR("If-Modified-Since", last_modif) |
3089 | 0 | } |
3090 | | |
3091 | | |
3092 | | //done gathering headers |
3093 | |
|
3094 | 0 | param_string = gf_opts_get_key("core", "query-string"); |
3095 | |
|
3096 | | #ifdef GPAC_HTTPMUX |
3097 | | if (sess->hmux_sess) { |
3098 | | Bool has_body = GF_FALSE; |
3099 | | |
3100 | | gf_mx_p(sess->mx); |
3101 | | |
3102 | | sess->hmux_is_eos = 0; |
3103 | | sess->hmux_send_data = NULL; |
3104 | | if (par.data && par.size) { |
3105 | | has_body = GF_TRUE; |
3106 | | sess->hmux_send_data = (u8 *) par.data; |
3107 | | sess->hmux_send_data_len = par.size; |
3108 | | sess->hmux_is_eos = 1; |
3109 | | } else if (sess->put_state==1) { |
3110 | | has_body = GF_TRUE; |
3111 | | } |
3112 | | e = sess->hmux_sess->submit_request(sess, req_name, url, param_string, has_body); |
3113 | | GF_LOG(GF_LOG_INFO, GF_LOG_HTTP, ("[HTTP] Sending request %s %s:%u%s\n", req_name, sess->server_name, sess->port, url)); |
3114 | | |
3115 | | sess->last_fetch_time = sess->request_start_time = gf_sys_clock_high_res(); |
3116 | | |
3117 | | if (!e || (e==GF_IP_NETWORK_EMPTY)) { |
3118 | | e = sess->hmux_sess->send_pending_data(sess); |
3119 | | if (e==GF_IP_NETWORK_EMPTY) e = GF_OK; |
3120 | | } |
3121 | | |
3122 | | gf_mx_v(sess->mx); |
3123 | | goto req_sent; |
3124 | | } |
3125 | | #endif // GPAC_HTTPMUX |
3126 | |
|
3127 | | #ifdef GPAC_HAS_CURL |
3128 | | if (sess->curl_hnd) { |
3129 | | if (!sess->curl_not_http) |
3130 | | curl_easy_setopt(sess->curl_hnd, CURLOPT_CUSTOMREQUEST, req_name); |
3131 | | } else |
3132 | | #endif |
3133 | 0 | { |
3134 | |
|
3135 | 0 | if (param_string) { |
3136 | 0 | if (strchr(sess->remote_path, '?')) { |
3137 | 0 | sprintf(sHTTP, "%s %s&%s HTTP/1.1\r\n", req_name, url, param_string); |
3138 | 0 | } else { |
3139 | 0 | sprintf(sHTTP, "%s %s?%s HTTP/1.1\r\n", req_name, url, param_string); |
3140 | 0 | } |
3141 | 0 | } else { |
3142 | 0 | sprintf(sHTTP, "%s %s HTTP/1.1\r\n", req_name, url); |
3143 | 0 | } |
3144 | 0 | } |
3145 | | |
3146 | | //serialize headers |
3147 | 0 | count = gf_list_count(sess->headers); |
3148 | 0 | for (i=0; i<count; i++) { |
3149 | 0 | hdr = gf_list_get(sess->headers, i); |
3150 | | #ifdef GPAC_HAS_CURL |
3151 | | if (sess->curl_hnd) { |
3152 | | if (sess->curl_not_http) continue; |
3153 | | char szHDR[1000]; |
3154 | | sprintf(szHDR, "%s: %s", hdr->name, hdr->value); |
3155 | | sess->curl_hdrs = curl_slist_append(sess->curl_hdrs, szHDR); |
3156 | | continue; |
3157 | | } |
3158 | | #endif |
3159 | 0 | strcat(sHTTP, hdr->name); |
3160 | 0 | strcat(sHTTP, ": "); |
3161 | 0 | strcat(sHTTP, hdr->value); |
3162 | 0 | strcat(sHTTP, "\r\n"); |
3163 | 0 | } |
3164 | | #ifdef GPAC_HAS_CURL |
3165 | | if (sess->curl_hnd) { |
3166 | | if (!sess->curl_not_http) { |
3167 | | curl_easy_setopt(sess->curl_hnd, CURLOPT_HTTPHEADER, sess->curl_hdrs); |
3168 | | } |
3169 | | } |
3170 | | #endif |
3171 | |
|
3172 | 0 | strcat(sHTTP, "\r\n"); |
3173 | |
|
3174 | | #ifdef GPAC_HAS_CURL |
3175 | | if (sess->curl_hnd) { |
3176 | | gf_assert(!sess->curl_hnd_registered); |
3177 | | } else |
3178 | | #endif |
3179 | 0 | if (send_profile || par.data) { |
3180 | 0 | u32 len = (u32) strlen(sHTTP); |
3181 | 0 | char *tmp_buf = (char*)gf_malloc(sizeof(char)*(len+par.size+1)); |
3182 | 0 | strcpy(tmp_buf, sHTTP); |
3183 | 0 | if (par.data) { |
3184 | 0 | memcpy(tmp_buf+len, par.data, par.size); |
3185 | 0 | tmp_buf[len+par.size] = 0; |
3186 | |
|
3187 | 0 | sess->put_state = 2; |
3188 | 0 | } else { |
3189 | 0 | FILE *profile; |
3190 | 0 | user_profile = gf_opts_get_key("core", "user-profile"); |
3191 | 0 | assert (user_profile); |
3192 | 0 | profile = gf_fopen(user_profile, "rt"); |
3193 | 0 | if (profile) { |
3194 | 0 | s32 read = (s32) gf_fread(tmp_buf+len, par.size, profile); |
3195 | 0 | if ((read<0) || (read< (s32) par.size)) { |
3196 | 0 | GF_LOG(GF_LOG_WARNING, GF_LOG_HTTP, |
3197 | 0 | ("[%s] Error while loading UserProfile, size=%d, should be %d\n", sess->log_name, read, par.size)); |
3198 | 0 | for (; read < (s32) par.size; read++) { |
3199 | 0 | tmp_buf[len + read] = 0; |
3200 | 0 | } |
3201 | 0 | } |
3202 | 0 | gf_fclose(profile); |
3203 | 0 | } else { |
3204 | 0 | GF_LOG(GF_LOG_WARNING, GF_LOG_HTTP, ("[%s] Error while loading Profile file %s\n", sess->log_name, user_profile)); |
3205 | 0 | } |
3206 | 0 | } |
3207 | |
|
3208 | 0 | sess->last_fetch_time = sess->request_start_time = gf_sys_clock_high_res(); |
3209 | 0 | sess->req_hdr_size = len+par.size; |
3210 | |
|
3211 | 0 | e = dm_sess_write(sess, tmp_buf, len+par.size); |
3212 | |
|
3213 | 0 | GF_LOG(GF_LOG_INFO, GF_LOG_HTTP, ("[%s] Sending request to %s %s\n\n", sess->log_name, sess->server_name, tmp_buf)); |
3214 | 0 | gf_free(tmp_buf); |
3215 | 0 | } else { |
3216 | 0 | u32 len = (u32) strlen(sHTTP); |
3217 | |
|
3218 | 0 | sess->last_fetch_time = sess->request_start_time = gf_sys_clock_high_res(); |
3219 | 0 | sess->req_hdr_size = len; |
3220 | |
|
3221 | 0 | e = dm_sess_write(sess, sHTTP, len); |
3222 | |
|
3223 | 0 | #ifndef GPAC_DISABLE_LOG |
3224 | 0 | if (e) { |
3225 | 0 | GF_LOG(GF_LOG_ERROR, GF_LOG_HTTP, ("[%s] Error sending request %s\n", sess->log_name, gf_error_to_string(e) )); |
3226 | 0 | } else { |
3227 | 0 | GF_LOG(GF_LOG_INFO, GF_LOG_HTTP, ("[%s] Sending request to %s %s\n\n", sess->log_name, sess->server_name, sHTTP)); |
3228 | 0 | } |
3229 | 0 | #endif |
3230 | 0 | } |
3231 | | |
3232 | |
|
3233 | | #ifdef GPAC_HTTPMUX |
3234 | | req_sent: |
3235 | | #endif |
3236 | 0 | gf_dm_sess_clear_headers(sess); |
3237 | |
|
3238 | 0 | if (e) { |
3239 | 0 | SET_LAST_ERR(e) |
3240 | 0 | sess->status = GF_NETIO_STATE_ERROR; |
3241 | 0 | gf_dm_sess_notify_state(sess, GF_NETIO_STATE_ERROR, e); |
3242 | 0 | return e; |
3243 | 0 | } |
3244 | | |
3245 | 0 | gf_dm_sess_notify_state(sess, GF_NETIO_WAIT_FOR_REPLY, GF_OK); |
3246 | 0 | if (sess->put_state==1) { |
3247 | 0 | sess->status = GF_NETIO_DATA_TRANSFERED; |
3248 | 0 | } else { |
3249 | 0 | sess->status = GF_NETIO_WAIT_FOR_REPLY; |
3250 | 0 | } |
3251 | 0 | SET_LAST_ERR(GF_OK) |
3252 | |
|
3253 | | #ifdef GPAC_HAS_CURL |
3254 | | if (sess->curl_hnd) { |
3255 | | sess->last_fetch_time = sess->request_start_time = gf_sys_clock_high_res(); |
3256 | | curl_flush(sess); |
3257 | | } |
3258 | | #endif |
3259 | 0 | return GF_OK; |
3260 | 0 | } |
3261 | | |
3262 | | |
3263 | | /*! |
3264 | | * Parse the remaining part of body |
3265 | | \param sess The session |
3266 | | \return The error code if any |
3267 | | */ |
3268 | | static GF_Err http_parse_remaining_body(GF_DownloadSession * sess) |
3269 | 0 | { |
3270 | 0 | GF_Err e; |
3271 | |
|
3272 | 0 | while (1) { |
3273 | 0 | u32 prev_remaining_data_size, size=0, rewrite_size=0; |
3274 | 0 | if (sess->status>=GF_NETIO_DISCONNECTED) |
3275 | 0 | return GF_REMOTE_SERVICE_ERROR; |
3276 | | |
3277 | 0 | if (sess->dm && sess->bytes_per_sec && (sess->max_data_rate || sess->dm->limit_data_rate)) { |
3278 | 0 | sess->rate_regulated = GF_FALSE; |
3279 | 0 | if (dm_exceeds_cap_rate(sess->dm, sess->max_data_rate ? sess : NULL)) { |
3280 | 0 | sess->rate_regulated = GF_TRUE; |
3281 | 0 | return GF_OK; |
3282 | 0 | } |
3283 | 0 | } |
3284 | | |
3285 | | //the data remaining from the last buffer (i.e size for chunk that couldn't be read because the buffer does not contain enough bytes) |
3286 | 0 | if (sess->remaining_data && sess->remaining_data_size) { |
3287 | 0 | if (sess->remaining_data_size >= sess->http_buf_size) { |
3288 | 0 | GF_LOG(GF_LOG_ERROR, GF_LOG_HTTP, ("[%s] No HTTP chunk header found for %d bytes, assuming broken chunk transfer and aborting\n", sess->log_name, sess->remaining_data_size)); |
3289 | 0 | return GF_NON_COMPLIANT_BITSTREAM; |
3290 | 0 | } |
3291 | 0 | memcpy(sess->http_buf, sess->remaining_data, sess->remaining_data_size); |
3292 | 0 | } |
3293 | 0 | e = gf_dm_read_data(sess, sess->http_buf + sess->remaining_data_size, sess->http_buf_size - sess->remaining_data_size, &size); |
3294 | 0 | if ((e != GF_IP_CONNECTION_CLOSED) && (!size || e == GF_IP_NETWORK_EMPTY)) { |
3295 | 0 | if (!sess->total_size && !sess->chunked && (gf_sys_clock_high_res() - sess->start_time > 5000000)) { |
3296 | 0 | sess->total_size = sess->bytes_done; |
3297 | 0 | gf_dm_sess_notify_state(sess, GF_NETIO_DATA_TRANSFERED, GF_OK); |
3298 | 0 | gf_assert(sess->server_name); |
3299 | 0 | GF_LOG(GF_LOG_ERROR, GF_LOG_HTTP, ("[%s] Disconnected from %s: %s\n", sess->log_name, sess->server_name, gf_error_to_string(e))); |
3300 | 0 | gf_dm_disconnect(sess, HTTP_NO_CLOSE); |
3301 | 0 | } |
3302 | 0 | return GF_OK; |
3303 | 0 | } |
3304 | | |
3305 | 0 | if (e) { |
3306 | 0 | if (sess->sock && (e == GF_IP_CONNECTION_CLOSED)) { |
3307 | 0 | u32 len = gf_cache_get_content_length(sess->cache_entry); |
3308 | 0 | if (size > 0) { |
3309 | | #ifdef GPAC_HTTPMUX |
3310 | | if (sess->hmux_sess) { |
3311 | | hmux_flush_internal_data(sess, GF_FALSE); |
3312 | | } else |
3313 | | #endif |
3314 | 0 | gf_dm_data_received(sess, (u8 *) sess->http_buf, size, GF_FALSE, NULL, NULL); |
3315 | 0 | } |
3316 | |
|
3317 | 0 | if ( ( (len == 0) && sess->use_cache_file) || sess->bytes_done) { |
3318 | 0 | sess->total_size = sess->bytes_done; |
3319 | | // HTTP 1.1 without content length... |
3320 | 0 | gf_dm_sess_notify_state(sess, GF_NETIO_DATA_TRANSFERED, GF_OK); |
3321 | 0 | gf_assert(sess->server_name); |
3322 | 0 | GF_LOG(GF_LOG_ERROR, GF_LOG_HTTP, ("[%s] Disconnected from %s: %s\n", sess->log_name, sess->server_name, gf_error_to_string(e))); |
3323 | 0 | if (sess->use_cache_file) |
3324 | 0 | gf_cache_set_content_length(sess->cache_entry, sess->bytes_done); |
3325 | 0 | e = GF_OK; |
3326 | 0 | } |
3327 | 0 | } |
3328 | 0 | gf_dm_disconnect(sess, HTTP_CLOSE); |
3329 | 0 | SET_LAST_ERR(e) |
3330 | 0 | gf_dm_sess_notify_state(sess, sess->status, e); |
3331 | 0 | return e; |
3332 | 0 | } |
3333 | | |
3334 | 0 | prev_remaining_data_size = sess->remaining_data_size; |
3335 | 0 | sess->remaining_data_size = 0; |
3336 | |
|
3337 | 0 | sess->http_buf[size + prev_remaining_data_size] = 0; |
3338 | |
|
3339 | | #ifdef GPAC_HTTPMUX |
3340 | | if (sess->hmux_sess) { |
3341 | | hmux_flush_internal_data(sess, GF_FALSE); |
3342 | | if (sess->hmux_stream_id<0) { |
3343 | | sess->status = GF_NETIO_DATA_TRANSFERED; |
3344 | | SET_LAST_ERR(GF_OK) |
3345 | | } |
3346 | | } else |
3347 | | #endif |
3348 | 0 | gf_dm_data_received(sess, (u8 *) sess->http_buf, size + prev_remaining_data_size, GF_FALSE, &rewrite_size, NULL); |
3349 | |
|
3350 | 0 | if (sess->chunked) |
3351 | 0 | gf_dm_sess_estimate_chunk_rate(sess, rewrite_size); |
3352 | | |
3353 | | |
3354 | | /*socket empty*/ |
3355 | 0 | if (size < sess->http_buf_size) { |
3356 | 0 | return GF_OK; |
3357 | 0 | } |
3358 | 0 | } |
3359 | 0 | return GF_OK; |
3360 | 0 | } |
3361 | | |
3362 | | static void notify_error_body(GF_DownloadSession *sess, char * sHTTP, s32 bytesRead, s32 BodyStart) |
3363 | 0 | { |
3364 | 0 | GF_NETIO_Parameter par; |
3365 | |
|
3366 | 0 | if (sHTTP) { |
3367 | 0 | sHTTP[bytesRead]=0; |
3368 | 0 | par.error = GF_BAD_PARAM; |
3369 | 0 | par.reply = sess->rsp_code; |
3370 | 0 | par.data = sHTTP + BodyStart; |
3371 | 0 | par.size = (u32) strlen(par.data); |
3372 | 0 | par.msg_type = GF_NETIO_DATA_EXCHANGE; |
3373 | 0 | gf_dm_sess_user_io(sess, &par); |
3374 | 0 | } |
3375 | 0 | } |
3376 | | |
3377 | | static u32 http_parse_method(const char *comp) |
3378 | 0 | { |
3379 | 0 | if (!strcmp(comp, "GET")) return GF_HTTP_GET; |
3380 | 0 | else if (!strcmp(comp, "HEAD")) return GF_HTTP_HEAD; |
3381 | 0 | else if (!strcmp(comp, "OPTIONS")) return GF_HTTP_OPTIONS; |
3382 | 0 | else if (!strcmp(comp, "PUT")) return GF_HTTP_PUT; |
3383 | 0 | else if (!strcmp(comp, "POST")) return GF_HTTP_POST; |
3384 | 0 | else if (!strcmp(comp, "DELETE")) return GF_HTTP_DELETE; |
3385 | 0 | else if (!strcmp(comp, "CONNECT")) return GF_HTTP_CONNECT; |
3386 | 0 | else if (!strcmp(comp, "TRACE")) return GF_HTTP_TRACE; |
3387 | 0 | else return 0; |
3388 | 0 | } |
3389 | | |
3390 | | /*! |
3391 | | * Waits for the response HEADERS, parse the information... and so on |
3392 | | \param sess The session |
3393 | | */ |
3394 | | static GF_Err wait_for_header_and_parse(GF_DownloadSession *sess) |
3395 | 0 | { |
3396 | 0 | GF_NETIO_Parameter par; |
3397 | 0 | s32 bytesRead=0, BodyStart=0; |
3398 | 0 | u32 res, i, buf_size = sess->http_buf_size; |
3399 | 0 | s32 LinePos, Pos; |
3400 | 0 | u32 method=0; |
3401 | 0 | u32 ContentLength=0, first_byte, last_byte, total_size, range, no_range; |
3402 | 0 | Bool connection_closed = GF_FALSE; |
3403 | 0 | Bool has_content_length = GF_FALSE; |
3404 | 0 | Bool connection_keep_alive = GF_FALSE; |
3405 | 0 | u32 connection_timeout=0; |
3406 | 0 | char buf[1025]; |
3407 | 0 | char comp[400]; |
3408 | 0 | GF_Err e; |
3409 | 0 | char * new_location; |
3410 | 0 | const char * mime_type; |
3411 | | #ifdef GPAC_HAS_HTTP2 |
3412 | | Bool upgrade_to_http2 = GF_FALSE; |
3413 | | #endif |
3414 | |
|
3415 | 0 | char *sHTTP = sess->http_buf; |
3416 | 0 | sHTTP[0] = 0; |
3417 | |
|
3418 | 0 | if (sess->creds && sess->creds->req_state) { |
3419 | 0 | if (sess->creds->req_state==GF_CREDS_STATE_PENDING) |
3420 | 0 | return GF_OK; |
3421 | 0 | sess->creds->req_state = GF_CREDS_STATE_NONE; |
3422 | 0 | if (!sess->creds->valid) { |
3423 | 0 | gf_dm_disconnect(sess, HTTP_CLOSE); |
3424 | 0 | sess->status = GF_NETIO_STATE_ERROR; |
3425 | 0 | par.error = GF_AUTHENTICATION_FAILURE; |
3426 | 0 | par.msg_type = GF_NETIO_DISCONNECTED; |
3427 | 0 | gf_dm_sess_user_io(sess, &par); |
3428 | 0 | e = GF_AUTHENTICATION_FAILURE; |
3429 | 0 | SET_LAST_ERR(e) |
3430 | 0 | goto exit; |
3431 | 0 | } |
3432 | | |
3433 | 0 | sess->status = GF_NETIO_SETUP; |
3434 | 0 | e = gf_dm_sess_setup_from_url(sess, sess->orig_url, GF_FALSE); |
3435 | 0 | if (e) { |
3436 | 0 | sess->status = GF_NETIO_STATE_ERROR; |
3437 | 0 | SET_LAST_ERR(e) |
3438 | 0 | gf_dm_sess_notify_state(sess, sess->status, e); |
3439 | 0 | } |
3440 | 0 | return e; |
3441 | 0 | } |
3442 | | |
3443 | 0 | if (sess->server_mode) { |
3444 | 0 | gf_fatal_assert( sess->status == GF_NETIO_CONNECTED ); |
3445 | 0 | } else { |
3446 | 0 | gf_fatal_assert( sess->status == GF_NETIO_WAIT_FOR_REPLY ); |
3447 | 0 | if (!(sess->flags & GF_NETIO_SESSION_NOT_CACHED)) { |
3448 | 0 | sess->use_cache_file = sess->dm->disable_cache ? GF_FALSE : GF_TRUE; |
3449 | 0 | } |
3450 | 0 | } |
3451 | 0 | bytesRead = res = 0; |
3452 | 0 | new_location = NULL; |
3453 | |
|
3454 | 0 | if (sess->cached_file) { |
3455 | 0 | sess->reply_time = (u32) (gf_sys_clock_high_res() - sess->request_start_time); |
3456 | 0 | sess->rsp_hdr_size = 0; |
3457 | 0 | sess->total_size = gf_cache_get_content_length(sess->cache_entry); |
3458 | 0 | sess->bytes_done = 0; |
3459 | |
|
3460 | 0 | memset(&par, 0, sizeof(GF_NETIO_Parameter)); |
3461 | 0 | par.msg_type = GF_NETIO_DATA_EXCHANGE; |
3462 | 0 | par.error = GF_OK; |
3463 | 0 | gf_dm_sess_user_io(sess, &par); |
3464 | 0 | sess->status = GF_NETIO_DATA_EXCHANGE; |
3465 | | // gf_dm_disconnect(sess, HTTP_NO_CLOSE); |
3466 | 0 | return GF_OK; |
3467 | 0 | } |
3468 | | |
3469 | | #ifdef GPAC_HAS_CURL |
3470 | | if (sess->curl_hnd) { |
3471 | | e = curl_process_reply(sess, &ContentLength); |
3472 | | if (e) return e; |
3473 | | goto process_reply; |
3474 | | } |
3475 | | #endif |
3476 | | |
3477 | | //always set start time to the time at last attempt reply parsing |
3478 | 0 | sess->start_time = gf_sys_clock_high_res(); |
3479 | 0 | sess->start_time_utc = gf_net_get_utc(); |
3480 | 0 | sess->chunked = GF_FALSE; |
3481 | 0 | sess->last_chunk_found = GF_FALSE; |
3482 | |
|
3483 | 0 | sess->last_chunk_start_time = sess->request_start_time; |
3484 | 0 | sess->chunk_bytes = 0; |
3485 | 0 | sess->cumulated_chunk_rate = 0; |
3486 | |
|
3487 | 0 | while (1) { |
3488 | 0 | Bool probe = (!bytesRead || (sess->flags & GF_NETIO_SESSION_NO_BLOCK) ) ? GF_TRUE : GF_FALSE; |
3489 | | #ifdef GPAC_HAS_NGTCP2 |
3490 | | if (sess->server_mode && sess->hmux_sess && (sess->hmux_sess->net_sess->flags & GF_NETIO_SESSION_USE_QUIC)) { |
3491 | | GF_Err h3_check_sess(GF_DownloadSession *sess); |
3492 | | probe = GF_FALSE; |
3493 | | e = h3_check_sess(sess); |
3494 | | } else |
3495 | | #endif |
3496 | 0 | e = gf_dm_read_data(sess, sHTTP + bytesRead, buf_size - bytesRead, &res); |
3497 | |
|
3498 | | #ifdef GPAC_HTTPMUX |
3499 | | /* break as soon as we have a header frame*/ |
3500 | | if (sess->hmux_headers_seen) { |
3501 | | sess->hmux_headers_seen = 0; |
3502 | | res = 0; |
3503 | | bytesRead = 0; |
3504 | | BodyStart = 0; |
3505 | | e = GF_OK; |
3506 | | SET_LAST_ERR(GF_OK) |
3507 | | break; |
3508 | | } |
3509 | | #endif |
3510 | | //should not happen, but do it for safety |
3511 | 0 | if (!res && !bytesRead && !e) e = GF_IP_NETWORK_EMPTY; |
3512 | |
|
3513 | 0 | switch (e) { |
3514 | 0 | case GF_IP_NETWORK_EMPTY: |
3515 | 0 | if (probe) { |
3516 | 0 | e = gf_sk_probe(sess->sock); |
3517 | |
|
3518 | 0 | if (e==GF_IP_CONNECTION_CLOSED) { |
3519 | 0 | SET_LAST_ERR(GF_IP_CONNECTION_CLOSED) |
3520 | 0 | sess_connection_closed(sess); |
3521 | 0 | sess->status = GF_NETIO_STATE_ERROR; |
3522 | 0 | return GF_IP_NETWORK_EMPTY; |
3523 | 0 | } |
3524 | 0 | if (!sess->server_mode |
3525 | 0 | && sess->request_timeout |
3526 | 0 | && (gf_sys_clock_high_res() - sess->request_start_time > 1000 * sess->request_timeout) |
3527 | 0 | ) { |
3528 | 0 | GF_LOG(GF_LOG_WARNING, GF_LOG_HTTP, |
3529 | 0 | ("[%s] Session timeout for %s after %u ms, aborting\n", sess->log_name, sess->orig_url, sess->request_timeout)); |
3530 | 0 | SET_LAST_ERR(GF_IP_NETWORK_FAILURE) |
3531 | 0 | sess->status = GF_NETIO_STATE_ERROR; |
3532 | 0 | return GF_IP_NETWORK_FAILURE; |
3533 | 0 | } |
3534 | 0 | if (sess->status != GF_NETIO_STATE_ERROR) { |
3535 | 0 | if (sess->server_mode |
3536 | 0 | || (sess->flags & GF_NETIO_SESSION_NO_BLOCK) |
3537 | 0 | ) { |
3538 | 0 | SET_LAST_ERR(GF_IP_NETWORK_EMPTY) |
3539 | 0 | } |
3540 | 0 | } |
3541 | 0 | return GF_OK; |
3542 | 0 | } |
3543 | 0 | if (sess->status==GF_NETIO_STATE_ERROR) |
3544 | 0 | return sess->last_error; |
3545 | 0 | if (!res && sess->status<=GF_NETIO_CONNECTED) |
3546 | 0 | return GF_OK; |
3547 | | |
3548 | | #ifdef GPAC_HTTPMUX |
3549 | | //we may have received bytes (bytesRead>0) yet none for this session, return GF_IP_NETWORK_EMPTY if empty |
3550 | | if (sess->hmux_sess) |
3551 | | return GF_IP_NETWORK_EMPTY; |
3552 | | #endif |
3553 | | |
3554 | 0 | continue; |
3555 | | /*socket has been closed while configuring, retry (not sure if the server got the GET)*/ |
3556 | 0 | case GF_IP_CONNECTION_CLOSED: |
3557 | 0 | if (sess->http_read_type == HEAD) { |
3558 | | /* Some servers such as shoutcast directly close connection if HEAD or an unknown method is issued */ |
3559 | 0 | sess->server_only_understand_get = GF_TRUE; |
3560 | 0 | } |
3561 | 0 | if (sess->server_mode) { |
3562 | 0 | SET_LAST_ERR(GF_IP_CONNECTION_CLOSED) |
3563 | 0 | sess_connection_closed(sess); |
3564 | 0 | sess->status = GF_NETIO_DISCONNECTED; |
3565 | 0 | GF_LOG(GF_LOG_INFO, GF_LOG_HTTP, ("[%s] Connection closed by client\n", sess->log_name, sess->remote_path)); |
3566 | 0 | return GF_IP_CONNECTION_CLOSED; |
3567 | 0 | } |
3568 | 0 | gf_dm_disconnect(sess, HTTP_RESET_CONN); |
3569 | |
|
3570 | 0 | if (sess->num_retry) { |
3571 | 0 | #ifdef GPAC_HAS_SSL |
3572 | 0 | if ((sess->num_retry < SESSION_RETRY_SSL) |
3573 | 0 | && !(sess->flags & GF_DOWNLOAD_SESSION_USE_SSL) |
3574 | 0 | && !gf_opts_get_bool("core", "no-tls-rcfg") |
3575 | 0 | ) { |
3576 | 0 | GF_LOG(GF_LOG_INFO, GF_LOG_HTTP, ("[%s] Connection closed by server when processing %s - retrying using SSL\n", sess->log_name, sess->remote_path)); |
3577 | 0 | sess->flags |= GF_DOWNLOAD_SESSION_SSL_FORCED; |
3578 | 0 | } else |
3579 | 0 | #endif |
3580 | 0 | { |
3581 | 0 | GF_LOG(GF_LOG_INFO, GF_LOG_HTTP, ("[%s] Connection closed by server when processing %s - retrying\n", sess->log_name, sess->remote_path)); |
3582 | 0 | } |
3583 | 0 | sess->status = GF_NETIO_SETUP; |
3584 | 0 | } else { |
3585 | 0 | GF_LOG(GF_LOG_INFO, GF_LOG_HTTP, ("[%s] Connection closed by server when processing %s - aborting\n", sess->log_name, sess->remote_path)); |
3586 | 0 | SET_LAST_ERR(e) |
3587 | 0 | sess->status = GF_NETIO_STATE_ERROR; |
3588 | 0 | } |
3589 | 0 | return e; |
3590 | 0 | case GF_OK: |
3591 | 0 | SET_LAST_ERR(GF_OK) |
3592 | 0 | if (!res) |
3593 | 0 | return GF_OK; |
3594 | 0 | break; |
3595 | 0 | default: |
3596 | 0 | goto exit; |
3597 | 0 | } |
3598 | 0 | bytesRead += res; |
3599 | |
|
3600 | | #ifdef GPAC_HTTPMUX |
3601 | | //in case we got a refused stream |
3602 | | if (sess->status==GF_NETIO_SETUP) { |
3603 | | return GF_OK; |
3604 | | } |
3605 | | if (sess->hmux_sess) |
3606 | | continue; |
3607 | | #endif |
3608 | | |
3609 | | //HTTP1.1 only |
3610 | |
|
3611 | 0 | char *hdr_buf = sHTTP; |
3612 | 0 | u32 hdr_buf_len = bytesRead; |
3613 | 0 | if (sess->flags & GF_NETIO_SESSION_NO_BLOCK) { |
3614 | 0 | sess->async_req_reply = gf_realloc(sess->async_req_reply, sess->async_req_reply_size+res+1); |
3615 | 0 | if (!sess->async_req_reply) { |
3616 | 0 | sess->status = GF_NETIO_STATE_ERROR; |
3617 | 0 | SET_LAST_ERR(GF_OUT_OF_MEM) |
3618 | 0 | return sess->last_error; |
3619 | 0 | } |
3620 | 0 | memcpy(sess->async_req_reply + sess->async_req_reply_size, sHTTP, res); |
3621 | 0 | sess->async_req_reply_size += res; |
3622 | 0 | bytesRead = 0; |
3623 | 0 | sHTTP[0] = 0; |
3624 | 0 | hdr_buf = sess->async_req_reply; |
3625 | 0 | hdr_buf_len = sess->async_req_reply_size; |
3626 | 0 | } |
3627 | | |
3628 | | //weird bug on some servers sending twice the last chunk |
3629 | 0 | if (!strncmp(hdr_buf, "0\r\n\r\n", 5) ) { |
3630 | 0 | if (bytesRead) { |
3631 | 0 | bytesRead -= res; |
3632 | 0 | } else { |
3633 | 0 | sess->async_req_reply_size -= res; |
3634 | 0 | } |
3635 | 0 | GF_LOG(GF_LOG_WARNING, GF_LOG_HTTP, ("[%s] End of chunk found while waiting server response when processing %s - retrying\n", sess->log_name, sess->remote_path)); |
3636 | 0 | continue; |
3637 | 0 | } |
3638 | | |
3639 | | /*locate body start*/ |
3640 | 0 | BodyStart = gf_token_find(hdr_buf, 0, hdr_buf_len, "\r\n\r\n"); |
3641 | 0 | if (BodyStart > 0) { |
3642 | 0 | BodyStart += 4; |
3643 | 0 | break; |
3644 | 0 | } |
3645 | 0 | BodyStart = gf_token_find(hdr_buf, 0, hdr_buf_len, "\n\n"); |
3646 | 0 | if (BodyStart > 0) { |
3647 | 0 | BodyStart += 2; |
3648 | 0 | break; |
3649 | 0 | } |
3650 | 0 | } |
3651 | | |
3652 | 0 | no_range = range = ContentLength = first_byte = last_byte = total_size = sess->rsp_code = 0; |
3653 | |
|
3654 | 0 | if (sess->flags & GF_NETIO_SESSION_NO_BLOCK) { |
3655 | 0 | sHTTP = sess->async_req_reply; |
3656 | 0 | buf_size = bytesRead = sess->async_req_reply_size; |
3657 | 0 | sess->async_req_reply_size = 0; |
3658 | 0 | } |
3659 | |
|
3660 | | #ifdef GPAC_HTTPMUX |
3661 | | if (!sess->hmux_sess) { |
3662 | | #endif |
3663 | 0 | if (bytesRead < 0) { |
3664 | 0 | e = GF_REMOTE_SERVICE_ERROR; |
3665 | 0 | goto exit; |
3666 | 0 | } |
3667 | | |
3668 | 0 | if (!BodyStart) |
3669 | 0 | BodyStart = bytesRead; |
3670 | |
|
3671 | 0 | if (BodyStart) sHTTP[BodyStart-1] = 0; |
3672 | 0 | GF_LOG(GF_LOG_INFO, GF_LOG_HTTP, ("[%s] %s\n\n", sess->log_name, sHTTP)); |
3673 | |
|
3674 | 0 | sess->reply_time = (u32) (gf_sys_clock_high_res() - sess->request_start_time); |
3675 | 0 | sess->rsp_hdr_size = BodyStart; |
3676 | |
|
3677 | 0 | LinePos = gf_token_get_line(sHTTP, 0, bytesRead, buf, 1024); |
3678 | 0 | Pos = gf_token_get(buf, 0, " \t\r\n", comp, 400); |
3679 | | |
3680 | | //TODO for HTTP2 |
3681 | 0 | if (sess->server_mode) { |
3682 | 0 | method = http_parse_method(comp); |
3683 | |
|
3684 | 0 | Pos = gf_token_get(buf, Pos, " \t\r\n", comp, 400); |
3685 | 0 | if (sess->orig_url) gf_free(sess->orig_url); |
3686 | 0 | sess->orig_url = gf_strdup(comp); |
3687 | 0 | /*Pos = */gf_token_get(buf, Pos, " \t\r\n", comp, 400); |
3688 | 0 | if ((strncmp("HTTP", comp, 4) != 0)) { |
3689 | 0 | e = GF_REMOTE_SERVICE_ERROR; |
3690 | 0 | goto exit; |
3691 | 0 | } |
3692 | | //flush potential body except for PUT/POST |
3693 | 0 | if ((method==GF_HTTP_PUT) || (method==GF_HTTP_POST)) |
3694 | 0 | sess->rsp_code = 200; |
3695 | 0 | else |
3696 | 0 | sess->rsp_code = 300; |
3697 | 0 | } else { |
3698 | |
|
3699 | 0 | if (!strncmp("ICY", comp, 3)) { |
3700 | 0 | sess->use_cache_file = GF_FALSE; |
3701 | | /*be prepared not to receive any mime type from ShoutCast servers*/ |
3702 | 0 | if (!gf_cache_get_mime_type(sess->cache_entry)) |
3703 | 0 | gf_cache_set_mime_type(sess->cache_entry, "audio/mpeg"); |
3704 | 0 | } else if ((strncmp("HTTP", comp, 4) != 0)) { |
3705 | 0 | e = GF_REMOTE_SERVICE_ERROR; |
3706 | 0 | goto exit; |
3707 | 0 | } |
3708 | 0 | Pos = gf_token_get(buf, Pos, " ", comp, 400); |
3709 | 0 | if (Pos <= 0) { |
3710 | 0 | e = GF_REMOTE_SERVICE_ERROR; |
3711 | 0 | goto exit; |
3712 | 0 | } |
3713 | 0 | sess->rsp_code = (u32) atoi(comp); |
3714 | 0 | /*Pos = */gf_token_get(buf, Pos, " \r\n", comp, 400); |
3715 | |
|
3716 | 0 | } |
3717 | | |
3718 | | /* parse headers*/ |
3719 | 0 | while (1) { |
3720 | 0 | GF_HTTPHeader *hdrp; |
3721 | 0 | char *sep, *hdr_sep, *hdr, *hdr_val; |
3722 | 0 | if ( (s32) LinePos + 4 > BodyStart) break; |
3723 | 0 | LinePos = gf_token_get_line(sHTTP, LinePos , bytesRead, buf, 1024); |
3724 | 0 | if (LinePos < 0) break; |
3725 | | |
3726 | 0 | hdr_sep = NULL; |
3727 | 0 | hdr_val = NULL; |
3728 | 0 | hdr = buf; |
3729 | 0 | sep = strchr(buf, ':'); |
3730 | 0 | if (sep) { |
3731 | 0 | sep[0]=0; |
3732 | 0 | hdr_val = sep+1; |
3733 | 0 | while (hdr_val[0]==' ') hdr_val++; |
3734 | 0 | hdr_sep = strrchr(hdr_val, '\r'); |
3735 | 0 | if (hdr_sep) hdr_sep[0] = 0; |
3736 | 0 | } |
3737 | |
|
3738 | 0 | GF_SAFEALLOC(hdrp, GF_HTTPHeader); |
3739 | 0 | if (hdrp) { |
3740 | 0 | hdrp->name = gf_strdup(hdr); |
3741 | 0 | if (hdr_val) |
3742 | 0 | hdrp->value = gf_strdup(hdr_val); |
3743 | 0 | gf_list_add(sess->headers, hdrp); |
3744 | 0 | } |
3745 | |
|
3746 | 0 | if (sep) sep[0]=':'; |
3747 | 0 | if (hdr_sep) hdr_sep[0] = '\r'; |
3748 | |
|
3749 | 0 | if (sess->server_mode) { |
3750 | 0 | if (!stricmp(hdrp->name, "Transfer-Encoding") && !stricmp(hdrp->value, "chunked")) |
3751 | 0 | sess->chunked = GF_TRUE; |
3752 | 0 | } |
3753 | 0 | } |
3754 | |
|
3755 | | #ifdef GPAC_HTTPMUX |
3756 | | } |
3757 | | #endif |
3758 | | |
3759 | |
|
3760 | | #ifdef GPAC_HAS_CURL |
3761 | | process_reply: |
3762 | | #endif |
3763 | |
|
3764 | 0 | if (!sess->server_mode) { |
3765 | 0 | Bool cache_no_store = GF_FALSE; |
3766 | 0 | Bool cache_must_revalidate = GF_FALSE; |
3767 | 0 | u32 delta_age = 0; |
3768 | 0 | u32 max_age = 0; |
3769 | | |
3770 | | //default pre-processing of headers - needs cleanup, not all of these have to be parsed before checking reply code |
3771 | 0 | for (i=0; i<gf_list_count(sess->headers); i++) { |
3772 | 0 | char *val; |
3773 | 0 | GF_HTTPHeader *hdr = (GF_HTTPHeader*)gf_list_get(sess->headers, i); |
3774 | |
|
3775 | | #ifdef GPAC_HTTPMUX |
3776 | | if (!stricmp(hdr->name, ":status") ) { |
3777 | | sess->rsp_code = (u32) atoi(hdr->value); |
3778 | | } else |
3779 | | #endif |
3780 | 0 | if (!stricmp(hdr->name, "Content-Length") ) { |
3781 | 0 | ContentLength = (u32) atoi(hdr->value); |
3782 | 0 | has_content_length=GF_TRUE; |
3783 | |
|
3784 | 0 | if ((sess->rsp_code<300) && sess->cache_entry) |
3785 | 0 | gf_cache_set_content_length(sess->cache_entry, ContentLength); |
3786 | |
|
3787 | 0 | } |
3788 | 0 | else if (!stricmp(hdr->name, "Content-Type")) { |
3789 | 0 | char *mime = gf_strdup(hdr->value); |
3790 | 0 | while (1) { |
3791 | 0 | u32 len = (u32) strlen(mime); |
3792 | 0 | char c = len ? mime[len-1] : 0; |
3793 | 0 | if ((c=='\r') || (c=='\n')) { |
3794 | 0 | mime[len-1] = 0; |
3795 | 0 | } else { |
3796 | 0 | break; |
3797 | 0 | } |
3798 | 0 | } |
3799 | 0 | val = strchr(mime, ';'); |
3800 | 0 | if (val) val[0] = 0; |
3801 | |
|
3802 | 0 | strlwr(mime); |
3803 | 0 | if (sess->rsp_code<300) { |
3804 | 0 | if (sess->cache_entry) { |
3805 | 0 | gf_cache_set_mime_type(sess->cache_entry, mime); |
3806 | 0 | } else { |
3807 | 0 | sess->mime_type = mime; |
3808 | 0 | mime = NULL; |
3809 | 0 | } |
3810 | 0 | } |
3811 | 0 | if (mime) gf_free(mime); |
3812 | 0 | } |
3813 | 0 | else if (!stricmp(hdr->name, "Content-Range")) { |
3814 | 0 | if (!strnicmp(hdr->value, "bytes", 5)) { |
3815 | 0 | val = hdr->value + 5; |
3816 | 0 | while (strchr(":= ", val[0])) |
3817 | 0 | val++; |
3818 | |
|
3819 | 0 | if (val[0] == '*') { |
3820 | 0 | sscanf(val, "*/%u", &total_size); |
3821 | 0 | sess->full_resource_size = total_size; |
3822 | 0 | sess->rsp_code = 416; |
3823 | 0 | } else if (strstr(val, "/*")) { |
3824 | 0 | sscanf(val, "%u-%u/*", &first_byte, &last_byte); |
3825 | 0 | sess->full_resource_size = 0; |
3826 | 0 | } else { |
3827 | 0 | sscanf(val, "%u-%u/%u", &first_byte, &last_byte, &total_size); |
3828 | 0 | sess->full_resource_size = total_size; |
3829 | 0 | } |
3830 | 0 | } |
3831 | 0 | } |
3832 | 0 | else if (!stricmp(hdr->name, "Accept-Ranges")) { |
3833 | 0 | if (strstr(hdr->value, "none")) no_range = 1; |
3834 | 0 | } |
3835 | 0 | else if (!stricmp(hdr->name, "Location")) |
3836 | 0 | new_location = gf_strdup(hdr->value); |
3837 | 0 | else if (!strnicmp(hdr->name, "ice", 3) || !strnicmp(hdr->name, "icy", 3) ) { |
3838 | | /* For HTTP icy servers, we disable cache */ |
3839 | 0 | if (sess->icy_metaint == 0) |
3840 | 0 | sess->icy_metaint = -1; |
3841 | 0 | sess->use_cache_file = GF_FALSE; |
3842 | 0 | if (!stricmp(hdr->name, "icy-metaint")) { |
3843 | 0 | sess->icy_metaint = atoi(hdr->value); |
3844 | 0 | } |
3845 | 0 | } |
3846 | 0 | else if (!stricmp(hdr->name, "Age")) { |
3847 | 0 | sscanf(hdr->value, "%u", &delta_age); |
3848 | 0 | } |
3849 | 0 | else if (!stricmp(hdr->name, "Cache-Control")) { |
3850 | 0 | char *hval = hdr->value; |
3851 | 0 | while (hval[0]) { |
3852 | 0 | char *hsep = strchr(hval, ','); |
3853 | 0 | if (hsep) hsep[0] = 0; |
3854 | 0 | while (hval[0]==' ') hval++; |
3855 | 0 | char *vsep = strchr(hval, '='); |
3856 | 0 | if (vsep) vsep[0] = 0; |
3857 | 0 | if (!strcmp(hval, "no-store")) cache_no_store = GF_TRUE; |
3858 | 0 | else if (!strcmp(hval, "no-cache")) cache_no_store = GF_TRUE; |
3859 | 0 | else if (!strcmp(hval, "private")) { |
3860 | | //we need a way to differentiate proxy modes and client modes |
3861 | 0 | } |
3862 | 0 | else if (!strcmp(hval, "public")) {} |
3863 | 0 | else if (!strcmp(hval, "max-age") && vsep && !max_age) { |
3864 | 0 | sscanf(vsep+1, "%u", &max_age); |
3865 | 0 | } |
3866 | 0 | else if (!strcmp(hval, "s-maxage") && vsep) { |
3867 | 0 | sscanf(vsep+1, "%u", &max_age); |
3868 | 0 | } |
3869 | 0 | else if (!strcmp(hval, "must-revalidate")) cache_must_revalidate = GF_TRUE; |
3870 | 0 | else if (!strcmp(hval, "proxy-revalidate")) cache_must_revalidate = GF_TRUE; |
3871 | |
|
3872 | 0 | if (vsep) vsep[0] = '='; |
3873 | 0 | if (!hsep) break; |
3874 | 0 | hsep[0] = ','; |
3875 | 0 | hval = hsep+1; |
3876 | 0 | } |
3877 | 0 | } |
3878 | 0 | else if (!stricmp(hdr->name, "ETag")) { |
3879 | 0 | if (sess->rsp_code<300) |
3880 | 0 | gf_cache_set_etag_on_server(sess->cache_entry, hdr->value); |
3881 | 0 | } |
3882 | 0 | else if (!stricmp(hdr->name, "Last-Modified")) { |
3883 | 0 | if (sess->rsp_code<300) |
3884 | 0 | gf_cache_set_last_modified_on_server(sess->cache_entry, hdr->value); |
3885 | 0 | } |
3886 | 0 | else if (!stricmp(hdr->name, "Transfer-Encoding")) { |
3887 | 0 | if (!stricmp(hdr->value, "chunked") |
3888 | | #ifdef GPAC_HAS_CURL |
3889 | | && !sess->curl_hnd |
3890 | | #endif |
3891 | 0 | ) |
3892 | 0 | sess->chunked = GF_TRUE; |
3893 | 0 | } |
3894 | 0 | else if (!stricmp(hdr->name, "X-UserProfileID") ) { |
3895 | 0 | gf_opts_set_key("core", "user-profileid", hdr->value); |
3896 | 0 | } |
3897 | 0 | else if (!stricmp(hdr->name, "Connection") ) { |
3898 | 0 | if (strstr(hdr->value, "close")) |
3899 | 0 | connection_closed = GF_TRUE; |
3900 | 0 | else if (strstr(hdr->value, "Keep-Alive")) |
3901 | 0 | connection_keep_alive = GF_TRUE; |
3902 | 0 | } |
3903 | 0 | else if (!stricmp(hdr->name, "Keep-Alive") ) { |
3904 | 0 | char *tout = strstr(hdr->value, "timeout="); |
3905 | 0 | if (tout) { |
3906 | 0 | char c=0; |
3907 | 0 | s32 end = gf_token_find(tout, 0, (u32) strlen(tout), ", "); |
3908 | 0 | if (end>=0) { |
3909 | 0 | c = tout[end]; |
3910 | 0 | tout[end] = 0; |
3911 | 0 | } |
3912 | 0 | connection_timeout = atoi(tout+8); |
3913 | 0 | if (end>=0) tout[end] = c; |
3914 | 0 | } |
3915 | 0 | if (strstr(hdr->value, "close")) |
3916 | 0 | connection_closed = GF_TRUE; |
3917 | 0 | } |
3918 | | #ifdef GPAC_HAS_HTTP2 |
3919 | | else if (!stricmp(hdr->name, "Upgrade") ) { |
3920 | | if (!sess->dm->disable_http2 && !gf_opts_get_bool("core", "no-h2c") && !strncmp(hdr->value,"h2c", 3)) { |
3921 | | upgrade_to_http2 = GF_TRUE; |
3922 | | } |
3923 | | } |
3924 | | #endif |
3925 | |
|
3926 | 0 | if (sess->status==GF_NETIO_DISCONNECTED) return GF_OK; |
3927 | 0 | } |
3928 | | |
3929 | 0 | if ((sess->flags & GF_NETIO_SESSION_AUTO_CACHE) && !ContentLength && (sess->rsp_code>=200) && (sess->rsp_code<300) ) { |
3930 | 0 | sess->use_cache_file = GF_FALSE; |
3931 | 0 | if (sess->cache_entry) { |
3932 | 0 | gf_cache_entry_set_delete_files_when_deleted(sess->cache_entry); |
3933 | 0 | gf_cache_remove_session_from_cache_entry(sess->cache_entry, sess); |
3934 | 0 | sess->cache_entry = NULL; |
3935 | 0 | } |
3936 | 0 | } |
3937 | |
|
3938 | 0 | sess->flags &= ~GF_NETIO_SESSION_NO_STORE; |
3939 | 0 | if (cache_no_store) { |
3940 | 0 | gf_cache_entry_set_delete_files_when_deleted(sess->cache_entry); |
3941 | 0 | sess->flags |= GF_NETIO_SESSION_NO_STORE; |
3942 | |
|
3943 | 0 | if (sess->cache_entry && !ContentLength && !sess->chunked && (sess->rsp_code<300) |
3944 | | #ifdef GPAC_HTTPMUX |
3945 | | && !sess->hmux_sess |
3946 | | #endif |
3947 | 0 | ) { |
3948 | 0 | sess->use_cache_file = GF_FALSE; |
3949 | 0 | gf_cache_remove_session_from_cache_entry(sess->cache_entry, sess); |
3950 | 0 | sess->cache_entry = NULL; |
3951 | 0 | } |
3952 | 0 | } |
3953 | 0 | else if (sess->cache_entry) { |
3954 | 0 | if (max_age) max_age += delta_age; |
3955 | 0 | gf_cache_set_max_age(sess->cache_entry, max_age, cache_must_revalidate); |
3956 | 0 | } |
3957 | | |
3958 | |
|
3959 | 0 | if (no_range) first_byte = 0; |
3960 | | |
3961 | |
|
3962 | 0 | gf_cache_set_headers_processed(sess->cache_entry); |
3963 | |
|
3964 | 0 | if (connection_keep_alive |
3965 | 0 | && !gf_opts_get_bool("core", "no-timeout") |
3966 | 0 | && !sess->server_mode |
3967 | | #ifdef GPAC_HTTPMUX |
3968 | | && !sess->hmux_sess |
3969 | | #endif |
3970 | 0 | ) { |
3971 | 0 | sess->connection_timeout_ms = connection_timeout*1000; |
3972 | 0 | } |
3973 | 0 | } else { |
3974 | | //server mode, start timers as soon as we see the request headers |
3975 | 0 | sess->last_fetch_time = sess->request_start_time = gf_sys_clock_high_res(); |
3976 | 0 | } |
3977 | | |
3978 | | //if we issued an open-range from end of file till unknown we may get a 416. If the server is indicating |
3979 | | //resource size and it matches our range, move to 206 |
3980 | 0 | if ((sess->rsp_code==416) && (sess->range_start==sess->full_resource_size)) { |
3981 | 0 | sess->rsp_code = 206; |
3982 | 0 | ContentLength = 0; |
3983 | 0 | has_content_length = GF_TRUE; |
3984 | 0 | } |
3985 | | //if no start range, a server may reply with 200 if open end range or if end range is file size |
3986 | | //move this to 200 to avoid triggering a byte range not supported detection |
3987 | 0 | else if (sess->needs_range && (sess->rsp_code==200) && !sess->range_start && (!sess->range_end || (sess->range_end+1==ContentLength))) { |
3988 | 0 | sess->rsp_code = 206; |
3989 | 0 | } |
3990 | |
|
3991 | 0 | par.msg_type = GF_NETIO_PARSE_REPLY; |
3992 | 0 | par.error = GF_OK; |
3993 | 0 | par.reply = sess->rsp_code; |
3994 | 0 | par.value = comp; |
3995 | | /* |
3996 | | * If response is correct, it means our credentials are correct |
3997 | | */ |
3998 | 0 | if (sess->creds && sess->rsp_code != 304) |
3999 | 0 | sess->creds->valid = GF_TRUE; |
4000 | |
|
4001 | | #ifdef GPAC_HAS_HTTP2 |
4002 | | if ((sess->rsp_code == 101) && upgrade_to_http2) { |
4003 | | char *body = NULL; |
4004 | | u32 body_len = 0; |
4005 | | if (bytesRead > BodyStart) { |
4006 | | body = sHTTP + BodyStart; |
4007 | | body_len = bytesRead - BodyStart; |
4008 | | } |
4009 | | return http2_do_upgrade(sess, body, body_len); |
4010 | | } |
4011 | | if (sess->h2_upgrade_state<4) |
4012 | | sess->h2_upgrade_state = 2; |
4013 | | #endif |
4014 | | |
4015 | | |
4016 | | /*try to flush body */ |
4017 | 0 | if ((sess->rsp_code>=300) |
4018 | | #ifdef GPAC_HTTPMUX |
4019 | | && !sess->hmux_sess |
4020 | | #endif |
4021 | | #ifdef GPAC_HAS_CURL |
4022 | | && !sess->curl_hnd |
4023 | | #endif |
4024 | 0 | ) { |
4025 | 0 | u32 start = gf_sys_clock(); |
4026 | 0 | while (BodyStart + ContentLength > (u32) bytesRead) { |
4027 | 0 | e = gf_dm_read_data(sess, sHTTP + bytesRead, buf_size - bytesRead, &res); |
4028 | 0 | switch (e) { |
4029 | 0 | case GF_IP_NETWORK_EMPTY: |
4030 | 0 | break; |
4031 | 0 | case GF_OK: |
4032 | 0 | bytesRead += res; |
4033 | 0 | break; |
4034 | 0 | default: |
4035 | 0 | start=0; |
4036 | 0 | break; |
4037 | 0 | } |
4038 | 0 | if (gf_sys_clock()-start>100) |
4039 | 0 | break; |
4040 | | |
4041 | | //does not fit in our buffer, too bad we'll kill the connection |
4042 | 0 | if (bytesRead == buf_size) |
4043 | 0 | break; |
4044 | 0 | } |
4045 | | |
4046 | 0 | if (BodyStart + ContentLength > (u32) bytesRead) { |
4047 | 0 | ContentLength = 0; |
4048 | | //cannot flush, discard socket |
4049 | 0 | sess->connection_close = GF_TRUE; |
4050 | 0 | } |
4051 | 0 | } |
4052 | | |
4053 | | #ifdef GPAC_HTTPMUX |
4054 | | if (sess->hmux_sess) { |
4055 | | u32 count = gf_list_count(sess->headers); |
4056 | | for (i=0; i<count; i++) { |
4057 | | GF_HTTPHeader *hdr = gf_list_get(sess->headers, i); |
4058 | | if (!stricmp(hdr->name, ":method")) { |
4059 | | method = http_parse_method(hdr->value); |
4060 | | sess->rsp_code = 200; |
4061 | | } |
4062 | | else if (!stricmp(hdr->name, ":path")) { |
4063 | | if (sess->orig_url) gf_free(sess->orig_url); |
4064 | | sess->orig_url = gf_strdup(hdr->value); |
4065 | | } |
4066 | | } |
4067 | | } |
4068 | | #endif |
4069 | | #ifdef GPAC_HAS_HTTP2 |
4070 | | else if (sess->server_mode |
4071 | | && !gf_opts_get_bool("core", "no-h2") |
4072 | | && !gf_opts_get_bool("core", "no-h2c") |
4073 | | && !(sess->flags & GF_NETIO_SESSION_USE_QUIC) |
4074 | | ) { |
4075 | | Bool is_upgradeable = GF_FALSE; |
4076 | | char *h2_settings = NULL; |
4077 | | u32 count = gf_list_count(sess->headers); |
4078 | | for (i=0; i<count; i++) { |
4079 | | GF_HTTPHeader *hdr = gf_list_get(sess->headers, i); |
4080 | | if (!stricmp(hdr->name, "Upgrade")) { |
4081 | | if (strstr(hdr->value, "h2c")) |
4082 | | is_upgradeable = GF_TRUE; |
4083 | | } |
4084 | | else if (!stricmp(hdr->name, "HTTP2-Settings")) { |
4085 | | h2_settings = hdr->value; |
4086 | | } |
4087 | | } |
4088 | | |
4089 | | if (is_upgradeable && h2_settings) { |
4090 | | u32 len = (u32) strlen(h2_settings); |
4091 | | sess->h2_upgrade_settings = gf_malloc(sizeof(char) * len * 2); |
4092 | | sess->h2_upgrade_settings_len = gf_base64_decode(h2_settings, len, sess->h2_upgrade_settings, len*2); |
4093 | | } |
4094 | | } |
4095 | | #endif |
4096 | | |
4097 | 0 | if (sess->server_mode) { |
4098 | 0 | if (ContentLength) { |
4099 | 0 | par.data = sHTTP + BodyStart; |
4100 | 0 | par.size = ContentLength; |
4101 | 0 | } else if ((BodyStart < (s32) bytesRead) |
4102 | | #ifdef GPAC_HTTPMUX |
4103 | | && !sess->hmux_sess |
4104 | | #endif |
4105 | 0 | ) { |
4106 | 0 | if (sess->init_data) gf_free(sess->init_data); |
4107 | 0 | sess->init_data_size = 0; |
4108 | 0 | sess->init_data = NULL; |
4109 | |
|
4110 | 0 | gf_dm_data_received(sess, (u8 *) sHTTP + BodyStart, bytesRead - BodyStart, GF_TRUE, NULL, NULL); |
4111 | 0 | } |
4112 | |
|
4113 | 0 | sess->request_start_time = gf_sys_clock_high_res(); |
4114 | |
|
4115 | 0 | par.reply = method; |
4116 | 0 | gf_dm_sess_user_io(sess, &par); |
4117 | 0 | sess->status = GF_NETIO_DATA_TRANSFERED; |
4118 | 0 | SET_LAST_ERR(GF_OK) |
4119 | 0 | return GF_OK; |
4120 | 0 | } |
4121 | | //remember if we can keep the session alive after the transfer is done |
4122 | 0 | sess->connection_close = connection_closed; |
4123 | 0 | gf_assert(sess->rsp_code); |
4124 | | |
4125 | |
|
4126 | 0 | switch (sess->rsp_code) { |
4127 | | //100 continue |
4128 | 0 | case 100: |
4129 | 0 | break; |
4130 | 0 | case 200: |
4131 | 0 | case 201: |
4132 | 0 | case 202: |
4133 | 0 | case 206: |
4134 | 0 | gf_dm_sess_user_io(sess, &par); |
4135 | 0 | e = GF_OK; |
4136 | 0 | if (sess->proxy_enabled==2) { |
4137 | 0 | sess->proxy_enabled=0; |
4138 | 0 | if (sess->dm) |
4139 | 0 | gf_list_add(sess->dm->skip_proxy_servers, gf_strdup(sess->server_name)); |
4140 | 0 | } |
4141 | 0 | sess->nb_redirect=0; |
4142 | 0 | break; |
4143 | | /*redirection: extract the new location*/ |
4144 | 0 | case 301: |
4145 | 0 | case 302: |
4146 | 0 | case 303: |
4147 | 0 | case 307: |
4148 | 0 | if ((sess->nb_redirect > 4) || !new_location || !strlen(new_location) ) { |
4149 | 0 | gf_dm_sess_user_io(sess, &par); |
4150 | 0 | e = GF_URL_ERROR; |
4151 | 0 | goto exit; |
4152 | 0 | } |
4153 | 0 | while ( |
4154 | 0 | (new_location[strlen(new_location)-1] == '\n') |
4155 | 0 | || (new_location[strlen(new_location)-1] == '\r') ) |
4156 | 0 | new_location[strlen(new_location)-1] = 0; |
4157 | | |
4158 | | /*reset and reconnect*/ |
4159 | 0 | gf_dm_disconnect(sess, HTTP_CLOSE); |
4160 | 0 | sess->status = GF_NETIO_SETUP; |
4161 | 0 | sess->nb_redirect++; |
4162 | 0 | e = gf_dm_sess_setup_from_url(sess, new_location, GF_FALSE); |
4163 | 0 | if (e) { |
4164 | 0 | sess->status = GF_NETIO_STATE_ERROR; |
4165 | 0 | SET_LAST_ERR(e) |
4166 | 0 | gf_dm_sess_notify_state(sess, sess->status, e); |
4167 | 0 | } |
4168 | 0 | gf_free(new_location); |
4169 | 0 | return e; |
4170 | 0 | case 304: |
4171 | 0 | { |
4172 | 0 | gf_assert(sess->cache_entry); |
4173 | 0 | gf_assert(!sess->cached_file); |
4174 | | |
4175 | | //special case for resources stored as persistent (mpd, init seg): we don't push the data |
4176 | 0 | if (gf_cache_entry_persistent(sess->cache_entry)) { |
4177 | 0 | gf_dm_sess_notify_state(sess, GF_NETIO_PARSE_REPLY, GF_OK); |
4178 | | |
4179 | | /* Cache file is the most recent */ |
4180 | 0 | sess->status = GF_NETIO_DATA_TRANSFERED; |
4181 | 0 | SET_LAST_ERR(GF_OK) |
4182 | 0 | gf_dm_sess_notify_state(sess, GF_NETIO_DATA_TRANSFERED, GF_OK); |
4183 | 0 | gf_dm_disconnect(sess, HTTP_NO_CLOSE); |
4184 | 0 | return GF_OK; |
4185 | 0 | } |
4186 | | |
4187 | 0 | sess->status = GF_NETIO_PARSE_REPLY; |
4188 | 0 | if (!gf_cache_is_mem(sess->cache_entry)) { |
4189 | 0 | sess->cached_file = gf_cache_open_read(sess->cache_entry); |
4190 | 0 | if (!sess->cached_file) { |
4191 | 0 | GF_LOG(GF_LOG_INFO, GF_LOG_HTTP, ("[%s] FAILED to open cache file %s for reading contents !\n", sess->log_name, gf_cache_get_cache_filename(sess->cache_entry))); |
4192 | | /* Ooops, no cache, redownload everything ! */ |
4193 | 0 | gf_dm_disconnect(sess, HTTP_NO_CLOSE); |
4194 | 0 | sess->status = GF_NETIO_SETUP; |
4195 | 0 | e = gf_dm_sess_setup_from_url(sess, sess->orig_url, GF_FALSE); |
4196 | 0 | sess->total_size = gf_cache_get_cache_filesize(sess->cache_entry); |
4197 | 0 | if (e) { |
4198 | 0 | sess->status = GF_NETIO_STATE_ERROR; |
4199 | 0 | SET_LAST_ERR(e) |
4200 | 0 | gf_dm_sess_notify_state(sess, sess->status, e); |
4201 | 0 | } |
4202 | 0 | return e; |
4203 | 0 | } |
4204 | 0 | } else { |
4205 | | //we read from mem cache |
4206 | 0 | sess->local_cache_only = GF_TRUE; |
4207 | 0 | } |
4208 | 0 | sess->status = GF_NETIO_DATA_EXCHANGE; |
4209 | 0 | sess->total_size = ContentLength = gf_cache_get_cache_filesize(sess->cache_entry); |
4210 | |
|
4211 | 0 | gf_dm_sess_user_io(sess, &par); |
4212 | 0 | sess->status = GF_NETIO_DATA_EXCHANGE; |
4213 | 0 | e = GF_EOS; |
4214 | 0 | break; |
4215 | 0 | } |
4216 | 0 | case 401: |
4217 | 0 | { |
4218 | 0 | if (sess->creds && sess->creds->valid) { |
4219 | 0 | gf_opts_set_key("credentials", sess->creds->site, NULL); |
4220 | 0 | sess->creds->valid = GF_FALSE; |
4221 | 0 | } |
4222 | 0 | Bool secure = GF_FALSE; |
4223 | 0 | #ifdef GPAC_HAS_SSL |
4224 | 0 | if (sess->ssl) secure = GF_TRUE; |
4225 | 0 | #endif |
4226 | | /* Do we have a credentials struct ? */ |
4227 | 0 | sess->creds = gf_user_credentials_register(sess->dm, secure, sess->server_name, NULL, NULL, GF_FALSE); |
4228 | 0 | if (!sess->creds) { |
4229 | | /* User credentials have not been filled properly, we have to abort */ |
4230 | 0 | gf_dm_disconnect(sess, HTTP_CLOSE); |
4231 | 0 | sess->status = GF_NETIO_STATE_ERROR; |
4232 | 0 | par.error = GF_AUTHENTICATION_FAILURE; |
4233 | 0 | par.msg_type = GF_NETIO_DISCONNECTED; |
4234 | 0 | gf_dm_sess_user_io(sess, &par); |
4235 | 0 | e = GF_AUTHENTICATION_FAILURE; |
4236 | 0 | SET_LAST_ERR(e) |
4237 | 0 | goto exit; |
4238 | 0 | } |
4239 | 0 | gf_dm_disconnect(sess, HTTP_NO_CLOSE); |
4240 | 0 | sess->status = GF_NETIO_SETUP; |
4241 | |
|
4242 | 0 | if (sess->creds->req_state==GF_CREDS_STATE_PENDING) { |
4243 | | //force a wait for reply until we have resolved user pass |
4244 | 0 | sess->status = GF_NETIO_WAIT_FOR_REPLY; |
4245 | 0 | return GF_OK; |
4246 | 0 | } |
4247 | | |
4248 | 0 | e = gf_dm_sess_setup_from_url(sess, sess->orig_url, GF_FALSE); |
4249 | 0 | if (e) { |
4250 | 0 | sess->status = GF_NETIO_STATE_ERROR; |
4251 | 0 | SET_LAST_ERR(e) |
4252 | 0 | gf_dm_sess_notify_state(sess, sess->status, e); |
4253 | 0 | } |
4254 | 0 | return e; |
4255 | 0 | } |
4256 | 0 | case 400: |
4257 | 0 | case 501: |
4258 | | /* Method not implemented ! */ |
4259 | 0 | if (sess->http_read_type == HEAD) { |
4260 | | /* Since HEAD is not understood by this server, we use a GET instead */ |
4261 | 0 | sess->http_read_type = GET; |
4262 | 0 | sess->flags |= GF_NETIO_SESSION_NOT_CACHED; |
4263 | 0 | gf_dm_disconnect(sess, HTTP_NO_CLOSE); |
4264 | 0 | sess->status = GF_NETIO_SETUP; |
4265 | 0 | sess->server_only_understand_get = GF_TRUE; |
4266 | 0 | GF_LOG(GF_LOG_INFO, GF_LOG_HTTP, ("Method not supported, try with GET.\n")); |
4267 | 0 | e = gf_dm_sess_setup_from_url(sess, sess->orig_url, GF_FALSE); |
4268 | 0 | if (e) { |
4269 | 0 | sess->status = GF_NETIO_STATE_ERROR; |
4270 | 0 | SET_LAST_ERR(e) |
4271 | 0 | gf_dm_sess_notify_state(sess, sess->status, e); |
4272 | 0 | } |
4273 | 0 | return e; |
4274 | 0 | } |
4275 | | |
4276 | 0 | gf_dm_sess_user_io(sess, &par); |
4277 | 0 | notify_error_body(sess, sHTTP, bytesRead, BodyStart); |
4278 | 0 | e = GF_REMOTE_SERVICE_ERROR; |
4279 | 0 | goto exit; |
4280 | | |
4281 | 0 | case 503: |
4282 | | /*retry without proxy*/ |
4283 | 0 | if (sess->proxy_enabled==1) { |
4284 | 0 | sess->proxy_enabled=2; |
4285 | 0 | gf_dm_disconnect(sess, HTTP_CLOSE); |
4286 | 0 | sess->status = GF_NETIO_SETUP; |
4287 | 0 | return GF_OK; |
4288 | 0 | } |
4289 | | //fall-through |
4290 | | |
4291 | | /* case 204: |
4292 | | case 504: |
4293 | | case 404: |
4294 | | case 403: |
4295 | | case 416: |
4296 | | */ |
4297 | 0 | default: |
4298 | 0 | gf_dm_sess_user_io(sess, &par); |
4299 | 0 | if ((BodyStart < (s32) bytesRead)) { |
4300 | 0 | sHTTP[bytesRead] = 0; |
4301 | 0 | GF_LOG(GF_LOG_WARNING, GF_LOG_HTTP, ("[%s] Failure - body: %s\n", sess->log_name, sHTTP + BodyStart)); |
4302 | 0 | } |
4303 | 0 | notify_error_body(sess, sHTTP, bytesRead, BodyStart); |
4304 | |
|
4305 | 0 | switch (sess->rsp_code) { |
4306 | 0 | case 204: e = GF_EOS; break; |
4307 | | /* File not found */ |
4308 | 0 | case 404: |
4309 | | //too early |
4310 | 0 | case 425: |
4311 | 0 | e = GF_URL_ERROR; break; |
4312 | | /* Forbidden */ |
4313 | 0 | case 403: e = GF_AUTHENTICATION_FAILURE; break; |
4314 | | /* Range not accepted */ |
4315 | 0 | case 416: |
4316 | 0 | e = GF_SERVICE_ERROR; |
4317 | 0 | break; |
4318 | 0 | case 504: e = GF_URL_ERROR; break; |
4319 | 0 | default: |
4320 | 0 | if (sess->rsp_code>=500) e = GF_REMOTE_SERVICE_ERROR; |
4321 | 0 | else e = GF_SERVICE_ERROR; |
4322 | 0 | break; |
4323 | 0 | } |
4324 | 0 | goto exit; |
4325 | 0 | } |
4326 | | |
4327 | 0 | if (sess->http_read_type != GET) |
4328 | 0 | sess->use_cache_file = GF_FALSE; |
4329 | |
|
4330 | 0 | if (sess->http_read_type==HEAD) { |
4331 | 0 | gf_dm_disconnect(sess, HTTP_NO_CLOSE); |
4332 | 0 | gf_dm_sess_notify_state(sess, GF_NETIO_DATA_TRANSFERED, GF_OK); |
4333 | 0 | sess->http_read_type = GET; |
4334 | 0 | return GF_OK; |
4335 | 0 | } |
4336 | | |
4337 | | |
4338 | 0 | mime_type = gf_cache_get_mime_type(sess->cache_entry); |
4339 | 0 | if (!ContentLength && mime_type && ((strstr(mime_type, "ogg") || (!strcmp(mime_type, "audio/mpeg"))))) { |
4340 | 0 | if (0 == sess->icy_metaint) |
4341 | 0 | sess->icy_metaint = -1; |
4342 | 0 | sess->use_cache_file = GF_FALSE; |
4343 | 0 | } |
4344 | |
|
4345 | 0 | #ifndef GPAC_DISABLE_LOG |
4346 | 0 | if (e) { |
4347 | 0 | GF_LOG(GF_LOG_ERROR, GF_LOG_HTTP, ("[%s] Error processing rely from %s: %s\n", sess->log_name, sess->server_name, gf_error_to_string(e) ) ); |
4348 | 0 | } else { |
4349 | 0 | GF_LOG(GF_LOG_DEBUG, GF_LOG_HTTP, ("[%s] Reply processed from %s\n", sess->log_name, sess->server_name ) ); |
4350 | 0 | } |
4351 | 0 | #endif |
4352 | | |
4353 | | //in HTTP2 we may resume to setup state if we got a refused stream |
4354 | 0 | if (sess->status == GF_NETIO_SETUP) { |
4355 | 0 | return GF_OK; |
4356 | 0 | } |
4357 | | |
4358 | | |
4359 | | /*some servers may reply without content length, but we MUST have it*/ |
4360 | 0 | if (e) goto exit; |
4361 | 0 | if (sess->icy_metaint != 0) { |
4362 | 0 | gf_assert( ! sess->use_cache_file ); |
4363 | 0 | GF_LOG(GF_LOG_INFO, GF_LOG_HTTP, ("[%s] ICY protocol detected\n", sess->log_name)); |
4364 | 0 | if (mime_type && !stricmp(mime_type, "video/nsv")) { |
4365 | 0 | gf_cache_set_mime_type(sess->cache_entry, "audio/aac"); |
4366 | 0 | } |
4367 | 0 | sess->icy_bytes = 0; |
4368 | 0 | sess->total_size = SIZE_IN_STREAM; |
4369 | 0 | sess->status = GF_NETIO_DATA_EXCHANGE; |
4370 | 0 | } else if (!ContentLength && !has_content_length && !sess->chunked |
4371 | | #ifdef GPAC_HTTPMUX |
4372 | | && !sess->hmux_sess |
4373 | | #endif |
4374 | | #ifdef GPAC_HAS_CURL |
4375 | | && !sess->curl_hnd |
4376 | | #endif |
4377 | 0 | ) { |
4378 | 0 | if (sess->http_read_type == GET) { |
4379 | 0 | sess->total_size = SIZE_IN_STREAM; |
4380 | 0 | sess->use_cache_file = GF_FALSE; |
4381 | 0 | sess->status = GF_NETIO_DATA_EXCHANGE; |
4382 | 0 | sess->bytes_done = 0; |
4383 | 0 | } else { |
4384 | 0 | gf_dm_sess_notify_state(sess, GF_NETIO_DATA_TRANSFERED, GF_OK); |
4385 | 0 | gf_dm_disconnect(sess, HTTP_NO_CLOSE); |
4386 | 0 | return GF_OK; |
4387 | 0 | } |
4388 | | #ifdef GPAC_HTTPMUX |
4389 | | } else if (sess->hmux_sess && !ContentLength && (sess->http_read_type != GET)) { |
4390 | | gf_dm_sess_notify_state(sess, GF_NETIO_DATA_TRANSFERED, GF_OK); |
4391 | | gf_dm_disconnect(sess, HTTP_NO_CLOSE); |
4392 | | return GF_OK; |
4393 | | #endif |
4394 | 0 | } else { |
4395 | 0 | sess->total_size = ContentLength; |
4396 | 0 | if (sess->use_cache_file && !sess->cached_file && (sess->http_read_type == GET)) { |
4397 | |
|
4398 | 0 | e = gf_cache_open_write_cache(sess->cache_entry, sess); |
4399 | 0 | if (e) { |
4400 | 0 | GF_LOG(GF_LOG_ERROR, GF_LOG_HTTP, ( "[CACHE] Failed to open cache, error=%d\n", e)); |
4401 | 0 | goto exit; |
4402 | 0 | } |
4403 | 0 | } |
4404 | 0 | sess->status = GF_NETIO_DATA_EXCHANGE; |
4405 | 0 | sess->bytes_done = 0; |
4406 | 0 | if (!ContentLength && has_content_length) { |
4407 | 0 | gf_dm_sess_notify_state(sess, GF_NETIO_DATA_TRANSFERED, GF_OK); |
4408 | 0 | gf_dm_disconnect(sess, HTTP_NO_CLOSE); |
4409 | 0 | sess->status = GF_NETIO_DATA_TRANSFERED; |
4410 | 0 | return GF_OK; |
4411 | 0 | } |
4412 | 0 | } |
4413 | | |
4414 | | /* we may have existing data in this buffer ... */ |
4415 | | #ifdef GPAC_HTTPMUX |
4416 | | if (!e && sess->hmux_sess) { |
4417 | | hmux_flush_internal_data(sess, GF_TRUE); |
4418 | | } else |
4419 | | #endif |
4420 | 0 | if (!e && (BodyStart < (s32) bytesRead)) { |
4421 | 0 | u32 rewrite_size=0; |
4422 | 0 | if (sess->init_data) gf_free(sess->init_data); |
4423 | 0 | sess->init_data_size = 0; |
4424 | 0 | sess->init_data = NULL; |
4425 | |
|
4426 | 0 | gf_dm_data_received(sess, (u8 *) sHTTP + BodyStart, bytesRead - BodyStart, GF_TRUE, &rewrite_size, NULL); |
4427 | |
|
4428 | 0 | if (sess->chunked) |
4429 | 0 | gf_dm_sess_estimate_chunk_rate(sess, rewrite_size); |
4430 | 0 | } |
4431 | 0 | exit: |
4432 | 0 | if (e) { |
4433 | 0 | if (e<0) { |
4434 | 0 | GF_LOG((e==GF_URL_ERROR) ? GF_LOG_INFO : GF_LOG_WARNING, GF_LOG_HTTP, ("[%s] Error parsing reply for URL %s: %s (code %d)\n", sess->log_name, sess->orig_url, gf_error_to_string(e), sess->rsp_code )); |
4435 | 0 | } else { |
4436 | 0 | e = GF_OK; |
4437 | 0 | } |
4438 | 0 | gf_cache_entry_set_delete_files_when_deleted(sess->cache_entry); |
4439 | 0 | gf_cache_remove_entry_from_session(sess); |
4440 | 0 | sess->cache_entry = NULL; |
4441 | 0 | gf_dm_disconnect(sess, HTTP_NO_CLOSE); |
4442 | 0 | if ((e<0) && connection_closed) |
4443 | 0 | sess->status = GF_NETIO_STATE_ERROR; |
4444 | 0 | else |
4445 | 0 | sess->status = GF_NETIO_DATA_TRANSFERED; |
4446 | 0 | SET_LAST_ERR(e) |
4447 | 0 | gf_dm_sess_notify_state(sess, sess->status, e); |
4448 | 0 | return e; |
4449 | 0 | } |
4450 | | /*DO NOT call parse_body yet, as the final user may not be connected to our session*/ |
4451 | 0 | return GF_OK; |
4452 | 0 | } |
4453 | | |
4454 | | /** |
4455 | | * Default performing behavior |
4456 | | \param sess The session |
4457 | | */ |
4458 | | void http_do_requests(GF_DownloadSession *sess) |
4459 | 0 | { |
4460 | 0 | sess->http_buf[0] = 0; |
4461 | |
|
4462 | 0 | if (sess->reused_cache_entry) { |
4463 | | //main session is done downloading, notify - to do we should send progress events on this session also ... |
4464 | 0 | if (!gf_cache_is_in_progress(sess->cache_entry)) { |
4465 | 0 | GF_NETIO_Parameter par; |
4466 | 0 | gf_dm_disconnect(sess, HTTP_NO_CLOSE); |
4467 | 0 | sess->reused_cache_entry = GF_FALSE; |
4468 | 0 | memset(&par, 0, sizeof(GF_NETIO_Parameter)); |
4469 | 0 | par.msg_type = GF_NETIO_DATA_TRANSFERED; |
4470 | 0 | par.error = GF_OK; |
4471 | 0 | gf_dm_sess_user_io(sess, &par); |
4472 | 0 | } |
4473 | 0 | return; |
4474 | 0 | } |
4475 | | |
4476 | 0 | if (sess->async_buf_size && (gf_dm_sess_flush_async(sess, GF_FALSE) == GF_IP_NETWORK_EMPTY)) { |
4477 | 0 | return; |
4478 | 0 | } |
4479 | | |
4480 | 0 | switch (sess->status) { |
4481 | 0 | case GF_NETIO_CONNECTED: |
4482 | 0 | if (sess->server_mode) { |
4483 | 0 | wait_for_header_and_parse(sess); |
4484 | 0 | } else { |
4485 | 0 | http_send_headers(sess); |
4486 | 0 | } |
4487 | 0 | break; |
4488 | 0 | case GF_NETIO_WAIT_FOR_REPLY: |
4489 | 0 | if (sess->server_mode) { |
4490 | 0 | http_send_headers(sess); |
4491 | 0 | } else { |
4492 | 0 | wait_for_header_and_parse(sess); |
4493 | 0 | } |
4494 | 0 | break; |
4495 | 0 | case GF_NETIO_DATA_EXCHANGE: |
4496 | 0 | if (sess->server_mode) { |
4497 | 0 | sess->status = GF_NETIO_CONNECTED; |
4498 | 0 | break; |
4499 | 0 | } |
4500 | | /*session has been reassigned, resend data retrieved in first GET reply to user but don't write to cache*/ |
4501 | 0 | if (sess->reassigned) { |
4502 | |
|
4503 | 0 | if (sess->icy_metaint > 0) { |
4504 | | //we are reparsing init data, reset icy status |
4505 | 0 | sess->icy_bytes = 0; |
4506 | 0 | gf_icy_skip_data(sess, sess->init_data, sess->init_data_size); |
4507 | 0 | } else { |
4508 | 0 | GF_NETIO_Parameter par; |
4509 | 0 | par.msg_type = GF_NETIO_DATA_EXCHANGE; |
4510 | 0 | par.error = GF_OK; |
4511 | 0 | par.data = sess->init_data; |
4512 | 0 | par.size = sess->init_data_size; |
4513 | 0 | gf_dm_sess_user_io(sess, &par); |
4514 | 0 | } |
4515 | 0 | sess->reassigned = GF_FALSE; |
4516 | 0 | } |
4517 | 0 | http_parse_remaining_body(sess); |
4518 | 0 | break; |
4519 | 0 | default: |
4520 | 0 | break; |
4521 | 0 | } |
4522 | 0 | } |
4523 | | |
4524 | | GF_EXPORT |
4525 | | void gf_dm_sess_set_max_rate(GF_DownloadSession *sess, u32 max_rate) |
4526 | 0 | { |
4527 | 0 | if (sess) { |
4528 | 0 | sess->max_data_rate = max_rate/8; |
4529 | 0 | sess->rate_regulated = GF_FALSE; |
4530 | 0 | } |
4531 | 0 | } |
4532 | | GF_EXPORT |
4533 | | u32 gf_dm_sess_get_max_rate(GF_DownloadSession *sess) |
4534 | 0 | { |
4535 | 0 | return sess ? 8*sess->max_data_rate : 0; |
4536 | 0 | } |
4537 | | GF_EXPORT |
4538 | | Bool gf_dm_sess_is_regulated(GF_DownloadSession *sess) |
4539 | 0 | { |
4540 | 0 | return sess ? sess->rate_regulated : GF_FALSE; |
4541 | 0 | } |
4542 | | |
4543 | | |
4544 | | /** |
4545 | | * NET IO for MPD, we don't need this anymore since mime-type can be given by session |
4546 | | */ |
4547 | | static void wget_NetIO(void *cbk, GF_NETIO_Parameter *param) |
4548 | 0 | { |
4549 | 0 | FILE * f = (FILE*) cbk; |
4550 | | |
4551 | | /*handle service message*/ |
4552 | 0 | if (param->msg_type == GF_NETIO_DATA_EXCHANGE) { |
4553 | 0 | s32 written = (u32) gf_fwrite( param->data, param->size, f); |
4554 | 0 | if (written != param->size) { |
4555 | 0 | GF_LOG(GF_LOG_ERROR, GF_LOG_HTTP, ("Failed to write data on disk\n")); |
4556 | 0 | } |
4557 | 0 | } |
4558 | 0 | } |
4559 | | |
4560 | | GF_EXPORT |
4561 | | GF_Err gf_dm_wget(const char *url, const char *filename, u64 start_range, u64 end_range, char **redirected_url) |
4562 | 0 | { |
4563 | 0 | GF_Err e; |
4564 | 0 | GF_DownloadManager * dm = gf_dm_new(NULL); |
4565 | 0 | if (!dm) |
4566 | 0 | return GF_OUT_OF_MEM; |
4567 | 0 | e = gf_dm_wget_with_cache(dm, url, filename, start_range, end_range, redirected_url); |
4568 | 0 | gf_dm_del(dm); |
4569 | 0 | return e; |
4570 | 0 | } |
4571 | | |
4572 | | GF_EXPORT |
4573 | | GF_Err gf_dm_wget_with_cache(GF_DownloadManager * dm, const char *url, const char *filename, u64 start_range, u64 end_range, char **redirected_url) |
4574 | 0 | { |
4575 | 0 | GF_Err e; |
4576 | 0 | FILE * f; |
4577 | 0 | GF_DownloadSession *dnload; |
4578 | 0 | if (!filename || !url || !dm) |
4579 | 0 | return GF_BAD_PARAM; |
4580 | 0 | f = gf_fopen(filename, "wb"); |
4581 | 0 | if (!f) { |
4582 | 0 | GF_LOG(GF_LOG_ERROR, GF_LOG_HTTP, ("[WGET] Failed to open file %s for write.\n", filename)); |
4583 | 0 | return GF_IO_ERR; |
4584 | 0 | } |
4585 | 0 | dnload = gf_dm_sess_new_simple(dm, (char *)url, GF_NETIO_SESSION_NOT_THREADED, &wget_NetIO, f, &e); |
4586 | 0 | if (!dnload) { |
4587 | 0 | return GF_BAD_PARAM; |
4588 | 0 | } |
4589 | 0 | dnload->use_cache_file = GF_FALSE; |
4590 | 0 | dnload->force_data_write_callback = GF_TRUE; |
4591 | 0 | if (end_range) { |
4592 | 0 | dnload->range_start = start_range; |
4593 | 0 | dnload->range_end = end_range; |
4594 | 0 | dnload->needs_range = GF_TRUE; |
4595 | 0 | } |
4596 | 0 | while (e == GF_OK) { |
4597 | 0 | e = gf_dm_sess_process(dnload); |
4598 | 0 | if (e && (e!=GF_IP_NETWORK_EMPTY)) |
4599 | 0 | break; |
4600 | 0 | if (dnload->status>=GF_NETIO_DATA_TRANSFERED) break; |
4601 | 0 | e = GF_OK; |
4602 | 0 | if (dnload->connect_pending) |
4603 | 0 | gf_sleep(100); |
4604 | 0 | } |
4605 | 0 | if (e==GF_OK) |
4606 | 0 | gf_cache_set_content_length(dnload->cache_entry, dnload->total_size); |
4607 | 0 | e |= gf_cache_close_write_cache(dnload->cache_entry, dnload, (e == GF_OK) ? GF_TRUE : GF_FALSE); |
4608 | 0 | gf_fclose(f); |
4609 | |
|
4610 | 0 | if (redirected_url) { |
4611 | 0 | if (dnload->orig_url_before_redirect) *redirected_url = gf_strdup(dnload->orig_url); |
4612 | 0 | } |
4613 | 0 | gf_dm_sess_del(dnload); |
4614 | 0 | return e; |
4615 | 0 | } |
4616 | | |
4617 | | GF_EXPORT |
4618 | | const char *gf_dm_sess_get_resource_name(GF_DownloadSession *dnload) |
4619 | 0 | { |
4620 | 0 | return dnload ? dnload->orig_url : NULL; |
4621 | 0 | } |
4622 | | |
4623 | | |
4624 | | |
4625 | | GF_EXPORT |
4626 | | void gf_dm_set_data_rate(GF_DownloadManager *dm, u32 rate_in_bits_per_sec) |
4627 | 0 | { |
4628 | 0 | if (rate_in_bits_per_sec == 0xFFFFFFFF) { |
4629 | 0 | dm->simulate_no_connection=GF_TRUE; |
4630 | 0 | } else { |
4631 | 0 | char opt[100]; |
4632 | 0 | dm->simulate_no_connection=GF_FALSE; |
4633 | 0 | dm->limit_data_rate = rate_in_bits_per_sec/8; |
4634 | |
|
4635 | 0 | sprintf(opt, "%d", rate_in_bits_per_sec); |
4636 | | //temporary store of maxrate |
4637 | 0 | gf_opts_set_key("temp", "maxrate", opt); |
4638 | 0 | } |
4639 | 0 | } |
4640 | | |
4641 | | GF_EXPORT |
4642 | | u32 gf_dm_get_data_rate(GF_DownloadManager *dm) |
4643 | 0 | { |
4644 | 0 | return dm->limit_data_rate*8; |
4645 | 0 | } |
4646 | | |
4647 | | GF_EXPORT |
4648 | | u32 gf_dm_get_global_rate(GF_DownloadManager *dm) |
4649 | 0 | { |
4650 | 0 | u32 ret = 0; |
4651 | 0 | u32 i, count; |
4652 | 0 | if (!dm) return 0; |
4653 | 0 | gf_mx_p(dm->cache_mx); |
4654 | 0 | count = gf_list_count(dm->all_sessions); |
4655 | |
|
4656 | 0 | for (i=0; i<count; i++) { |
4657 | 0 | GF_DownloadSession *sess = (GF_DownloadSession*)gf_list_get(dm->all_sessions, i); |
4658 | 0 | if (sess->status >= GF_NETIO_DATA_TRANSFERED) { |
4659 | 0 | if (sess->total_size==sess->bytes_done) { |
4660 | | //do not aggregate session if done/interrupted since more than 1/2 a sec |
4661 | 0 | if (gf_sys_clock_high_res() - sess->start_time > 500000) { |
4662 | 0 | continue; |
4663 | 0 | } |
4664 | 0 | } |
4665 | 0 | } |
4666 | 0 | ret += sess->bytes_per_sec; |
4667 | 0 | } |
4668 | 0 | gf_mx_v(dm->cache_mx); |
4669 | 0 | return 8*ret; |
4670 | 0 | } |
4671 | | |
4672 | | GF_HTTPSessionType gf_dm_sess_is_hmux(GF_DownloadSession *sess) |
4673 | 0 | { |
4674 | | #ifdef GPAC_HTTPMUX |
4675 | | if (sess->hmux_sess) { |
4676 | | if (sess->hmux_sess->net_sess && sess->hmux_sess->net_sess->flags & GF_NETIO_SESSION_USE_QUIC) |
4677 | | return GF_SESS_TYPE_HTTP3; |
4678 | | if (sess->hmux_sess->net_sess && !sess->hmux_sess->net_sess->sock) |
4679 | | return GF_SESS_TYPE_HTTP3; |
4680 | | return GF_SESS_TYPE_HTTP2; |
4681 | | } |
4682 | | #endif |
4683 | 0 | return GF_SESS_TYPE_HTTP; |
4684 | 0 | } |
4685 | | |
4686 | | Bool gf_dm_sess_use_tls(GF_DownloadSession * sess) |
4687 | 0 | { |
4688 | 0 | #ifdef GPAC_HAS_SSL |
4689 | 0 | if (sess->ssl) |
4690 | 0 | return GF_TRUE; |
4691 | 0 | #endif |
4692 | | |
4693 | | #ifdef GPAC_HTTPMUX |
4694 | | if (sess->hmux_sess && (sess->hmux_sess->net_sess->flags & GF_NETIO_SESSION_USE_QUIC)) |
4695 | | return GF_TRUE; |
4696 | | #endif |
4697 | 0 | return GF_FALSE; |
4698 | 0 | } |
4699 | | |
4700 | | u32 gf_dm_sess_get_resource_size(GF_DownloadSession * sess) |
4701 | 0 | { |
4702 | 0 | if (!sess) return 0; |
4703 | 0 | if (sess->full_resource_size) return sess->full_resource_size; |
4704 | 0 | if (sess->needs_range) return 0; |
4705 | 0 | return sess->total_size; |
4706 | 0 | } |
4707 | | |
4708 | | GF_EXPORT |
4709 | | const char *gf_dm_sess_get_header(GF_DownloadSession *sess, const char *name) |
4710 | 0 | { |
4711 | 0 | u32 i, count; |
4712 | 0 | if( !sess || !name) return NULL; |
4713 | 0 | count = gf_list_count(sess->headers); |
4714 | 0 | for (i=0; i<count; i++) { |
4715 | 0 | GF_HTTPHeader *header = (GF_HTTPHeader*)gf_list_get(sess->headers, i); |
4716 | 0 | if (!stricmp(header->name, name)) return header->value; |
4717 | 0 | } |
4718 | 0 | return NULL; |
4719 | 0 | } |
4720 | | |
4721 | | GF_EXPORT |
4722 | | GF_Err gf_dm_sess_enum_headers(GF_DownloadSession *sess, u32 *idx, const char **hdr_name, const char **hdr_val) |
4723 | 0 | { |
4724 | 0 | GF_HTTPHeader *hdr; |
4725 | 0 | if( !sess || !idx || !hdr_name || !hdr_val) |
4726 | 0 | return GF_BAD_PARAM; |
4727 | 0 | hdr = gf_list_get(sess->headers, *idx); |
4728 | 0 | if (!hdr) return GF_EOS; |
4729 | 0 | (*idx) = (*idx) + 1; |
4730 | 0 | (*hdr_name) = hdr->name; |
4731 | 0 | (*hdr_val) = hdr->value; |
4732 | 0 | return GF_OK; |
4733 | 0 | } |
4734 | | |
4735 | | GF_EXPORT |
4736 | | GF_Err gf_dm_sess_get_header_sizes_and_times(GF_DownloadSession *sess, u32 *req_hdr_size, u32 *rsp_hdr_size, u32 *connect_time, u32 *reply_time, u32 *download_time) |
4737 | 0 | { |
4738 | 0 | if (!sess) |
4739 | 0 | return GF_BAD_PARAM; |
4740 | | |
4741 | 0 | if (req_hdr_size) *req_hdr_size = sess->req_hdr_size; |
4742 | 0 | if (rsp_hdr_size) *rsp_hdr_size = sess->rsp_hdr_size; |
4743 | 0 | if (connect_time) *connect_time = sess->connect_time; |
4744 | 0 | if (reply_time) *reply_time = sess->reply_time; |
4745 | 0 | if (download_time) *download_time = sess->total_time_since_req; |
4746 | 0 | return GF_OK; |
4747 | 0 | } |
4748 | | |
4749 | | GF_EXPORT |
4750 | | void gf_dm_sess_force_memory_mode(GF_DownloadSession *sess, u32 force_keep) |
4751 | 0 | { |
4752 | 0 | if (sess) { |
4753 | 0 | sess->flags |= GF_NETIO_SESSION_MEMORY_CACHE; |
4754 | 0 | sess->flags &= ~GF_NETIO_SESSION_NOT_CACHED; |
4755 | 0 | if (force_keep==1) { |
4756 | 0 | sess->flags |= GF_NETIO_SESSION_KEEP_CACHE; |
4757 | 0 | } else if (force_keep==2) |
4758 | 0 | sess->flags |= GF_NETIO_SESSION_KEEP_FIRST_CACHE; |
4759 | 0 | } |
4760 | 0 | } |
4761 | | |
4762 | | |
4763 | | static GF_Err gf_dm_sess_flush_async_close(GF_DownloadSession *sess, Bool no_select, Bool for_close) |
4764 | 0 | { |
4765 | 0 | if (!sess) return GF_OK; |
4766 | 0 | if (sess->status==GF_NETIO_STATE_ERROR) return sess->last_error; |
4767 | | |
4768 | 0 | if (!no_select && sess->sock && (gf_sk_select(sess->sock, GF_SK_SELECT_WRITE)!=GF_OK)) { |
4769 | 0 | return sess->async_buf_size ? GF_IP_NETWORK_EMPTY : GF_OK; |
4770 | 0 | } |
4771 | 0 | GF_Err ret = GF_OK; |
4772 | |
|
4773 | | #ifdef GPAC_HTTPMUX |
4774 | | if(sess->hmux_sess) |
4775 | | ret = sess->hmux_sess->async_flush(sess, for_close); |
4776 | | |
4777 | | //if H2 flush the parent session holding the http2 session |
4778 | | if (sess->hmux_sess) |
4779 | | sess = sess->hmux_sess->net_sess; |
4780 | | #endif |
4781 | |
|
4782 | 0 | if (sess->async_buf_size) { |
4783 | 0 | GF_Err e = dm_sess_write(sess, sess->async_buf, sess->async_buf_size); |
4784 | 0 | if (e) return e; |
4785 | 0 | if (sess->async_buf_size) return GF_IP_NETWORK_EMPTY; |
4786 | 0 | } |
4787 | 0 | return ret; |
4788 | 0 | } |
4789 | | |
4790 | | GF_Err gf_dm_sess_flush_async(GF_DownloadSession *sess, Bool no_select) |
4791 | 0 | { |
4792 | 0 | return gf_dm_sess_flush_async_close(sess, no_select, GF_FALSE); |
4793 | 0 | } |
4794 | | GF_Err gf_dm_sess_flush_close(GF_DownloadSession *sess) |
4795 | 0 | { |
4796 | 0 | return gf_dm_sess_flush_async_close(sess, GF_TRUE, GF_TRUE); |
4797 | 0 | } |
4798 | | |
4799 | | u32 gf_dm_sess_async_pending(GF_DownloadSession *sess) |
4800 | 0 | { |
4801 | 0 | if (!sess) return 0; |
4802 | | #ifdef GPAC_HTTPMUX |
4803 | | if (sess->local_buf_len) return sess->local_buf_len; |
4804 | | if (sess->hmux_sess) |
4805 | | sess = sess->hmux_sess->net_sess; |
4806 | | #endif |
4807 | 0 | return sess ? sess->async_buf_size : 0; |
4808 | 0 | } |
4809 | | |
4810 | | GF_EXPORT |
4811 | | GF_Err gf_dm_sess_send(GF_DownloadSession *sess, u8 *data, u32 size) |
4812 | 0 | { |
4813 | 0 | GF_Err e = GF_OK; |
4814 | |
|
4815 | | #ifdef GPAC_HTTPMUX |
4816 | | if (sess->hmux_sess) { |
4817 | | return hmux_send_payload(sess, data, size); |
4818 | | } |
4819 | | #endif |
4820 | |
|
4821 | 0 | if (!data || !size) { |
4822 | 0 | if (sess->put_state) { |
4823 | 0 | sess->put_state = 2; |
4824 | 0 | sess->status = GF_NETIO_WAIT_FOR_REPLY; |
4825 | 0 | return GF_OK; |
4826 | 0 | } |
4827 | 0 | return GF_OK; |
4828 | 0 | } |
4829 | | |
4830 | 0 | e = dm_sess_write(sess, data, size); |
4831 | |
|
4832 | 0 | if (e==GF_IP_CONNECTION_CLOSED) { |
4833 | 0 | sess_connection_closed(sess); |
4834 | 0 | sess->status = GF_NETIO_STATE_ERROR; |
4835 | 0 | return e; |
4836 | 0 | } |
4837 | 0 | else if (e==GF_IP_NETWORK_EMPTY) { |
4838 | 0 | if (sess->flags & GF_NETIO_SESSION_NO_BLOCK) |
4839 | 0 | return GF_OK; |
4840 | | |
4841 | 0 | return gf_dm_sess_send(sess, data, size); |
4842 | 0 | } |
4843 | 0 | return e; |
4844 | 0 | } |
4845 | | |
4846 | | GF_Socket *gf_dm_sess_get_socket(GF_DownloadSession *sess) |
4847 | 0 | { |
4848 | 0 | return sess ? sess->sock : NULL; |
4849 | 0 | } |
4850 | | |
4851 | | void gf_dm_sess_set_sock_group(GF_DownloadSession *sess, GF_SockGroup *sg) |
4852 | 0 | { |
4853 | 0 | if (sess) { |
4854 | 0 | if (sess->sock_group!=sg) { |
4855 | 0 | if (sess->sock_group) |
4856 | 0 | gf_sk_group_unregister(sess->sock_group, sess->sock); |
4857 | 0 | if (sg) |
4858 | 0 | gf_sk_group_register(sg, sess->sock); |
4859 | 0 | } |
4860 | 0 | sess->sock_group = sg; |
4861 | 0 | } |
4862 | 0 | } |
4863 | | |
4864 | | void gf_dm_sess_detach_async(GF_DownloadSession *sess) |
4865 | 0 | { |
4866 | 0 | if (sess->sock) { |
4867 | 0 | while (sess->async_buf_size) { |
4868 | 0 | gf_dm_sess_flush_async(sess, GF_FALSE); |
4869 | 0 | gf_sleep(1); |
4870 | 0 | } |
4871 | 0 | gf_sk_set_block_mode(sess->sock, GF_TRUE); |
4872 | 0 | } |
4873 | 0 | sess->flags |= GF_NETIO_SESSION_NO_BLOCK; |
4874 | | //FOR TEST ONLY |
4875 | 0 | sess->flags &= ~GF_NETIO_SESSION_NOT_THREADED; |
4876 | | //mutex may already be created for H2 sessions |
4877 | 0 | if (!sess->mx) |
4878 | 0 | sess->mx = gf_mx_new(sess->orig_url); |
4879 | 0 | } |
4880 | | |
4881 | | void gf_dm_sess_set_timeout(GF_DownloadSession *sess, u32 timeout) |
4882 | 0 | { |
4883 | 0 | if (sess) sess->request_timeout = 1000*timeout; |
4884 | 0 | } |
4885 | | |
4886 | | void gf_dm_sess_set_netcap_id(GF_DownloadSession *sess, const char *netcap_id) |
4887 | 0 | { |
4888 | 0 | if (sess) sess->netcap_id = netcap_id; |
4889 | 0 | } |
4890 | | |
4891 | | |
4892 | | #endif //GPAC_DISABLE_NETWORK |