Line data Source code
1 : /*
2 : * GPAC Multimedia Framework
3 : *
4 : * Authors: Jean Le Feuvre
5 : * Copyright (c) Telecom ParisTech 2005-2021
6 : * All rights reserved
7 : *
8 : * This file is part of GPAC / common tools 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 <gpac/download.h>
27 : #include <gpac/network.h>
28 : #include <gpac/token.h>
29 : #include <gpac/thread.h>
30 : #include <gpac/list.h>
31 : #include <gpac/base_coding.h>
32 : #include <gpac/tools.h>
33 : #include <gpac/cache.h>
34 : #include <gpac/filters.h>
35 :
36 : #ifndef GPAC_DISABLE_CORE_TOOLS
37 :
38 : #ifdef GPAC_HAS_SSL
39 : #include <openssl/ssl.h>
40 : #include <openssl/x509.h>
41 : #include <openssl/x509v3.h>
42 : #include <openssl/err.h>
43 : #include <openssl/rand.h>
44 : #endif
45 :
46 : #ifdef GPAC_HAS_HTTP2
47 : #if defined(_MSC_VER)
48 : typedef SSIZE_T ssize_t;
49 : #define NGHTTP2_STATICLIB
50 : #else
51 : #if defined(WIN32) && defined(GPAC_STATIC_BUILD)
52 : #define NGHTTP2_STATICLIB
53 : #endif
54 : #endif
55 : #include <nghttp2/nghttp2.h>
56 :
57 : #if !defined(__GNUC__)
58 : # if defined(_WIN32_WCE) || defined (WIN32)
59 : #pragma comment(lib, "nghttp2")
60 : # endif
61 : #endif
62 :
63 : #endif
64 :
65 :
66 :
67 : #ifdef __USE_POSIX
68 : #include <unistd.h>
69 : #endif
70 :
71 : #define SIZE_IN_STREAM ( 2 << 29 )
72 :
73 :
74 : #define SESSION_RETRY_COUNT 20
75 :
76 : #include <gpac/revision.h>
77 : #define GF_DOWNLOAD_AGENT_NAME "GPAC/"GPAC_VERSION "-rev" GPAC_GIT_REVISION
78 :
79 : //let's be agressive with socket buffer size
80 : #define GF_DOWNLOAD_BUFFER_SIZE 131072
81 :
82 :
83 : #ifdef GPAC_HAS_HTTP2
84 : #define HTTP2_BUFFER_SETTINGS_SIZE 128
85 :
86 :
87 : typedef struct
88 : {
89 : u8 * data;
90 : u32 size, alloc, offset;
91 : } h2_reagg_buffer;
92 :
93 : typedef struct
94 : {
95 : Bool do_shutdown;
96 : GF_List *sessions;
97 : nghttp2_session *ng_sess;
98 :
99 : GF_DownloadSession *net_sess;
100 : GF_Mutex *mx;
101 : Bool copy;
102 : } GF_H2_Session;
103 :
104 :
105 : #endif
106 :
107 : static void gf_dm_data_received(GF_DownloadSession *sess, u8 *payload, u32 payload_size, Bool store_in_init, u32 *rewrite_size, u8 *original_payload);
108 : static GF_Err gf_dm_read_data(GF_DownloadSession *sess, char *data, u32 data_size, u32 *out_read);
109 :
110 : static void gf_dm_connect(GF_DownloadSession *sess);
111 : GF_Err gf_dm_sess_send(GF_DownloadSession *sess, u8 *data, u32 size);
112 :
113 : /*internal flags*/
114 : enum
115 : {
116 : GF_DOWNLOAD_SESSION_USE_SSL = 1<<10,
117 : GF_DOWNLOAD_SESSION_THREAD_DEAD = 1<<11
118 : };
119 :
120 : typedef struct __gf_user_credentials
121 : {
122 : char site[1024];
123 : char username[50];
124 : char digest[1024];
125 : Bool valid;
126 : } gf_user_credentials_struct;
127 :
128 : enum REQUEST_TYPE
129 : {
130 : GET = 0,
131 : HEAD = 1,
132 : OTHER = 2
133 : };
134 :
135 : /*!the structure used to store an HTTP header*/
136 : typedef struct
137 : {
138 : char *name;
139 : char *value;
140 : } GF_HTTPHeader;
141 :
142 :
143 : /**
144 : * This structure handles partial downloads
145 : */
146 : typedef struct __partialDownloadStruct {
147 : char * url;
148 : u64 startOffset;
149 : u64 endOffset;
150 : char * filename;
151 : } GF_PartialDownload ;
152 :
153 : typedef struct
154 : {
155 : struct __gf_download_session *sess;
156 : } GF_SessTask;
157 :
158 : struct __gf_download_session
159 : {
160 : /*this is always 0 and helps differenciating downloads from other interfaces (interfaceType != 0)*/
161 : u32 reserved;
162 :
163 : struct __gf_download_manager *dm;
164 : GF_Thread *th;
165 : GF_Mutex *mx;
166 : GF_SessTask *ftask;
167 :
168 : Bool in_callback, destroy;
169 : u32 proxy_enabled;
170 : Bool allow_direct_reuse;
171 :
172 : char *server_name;
173 : u16 port;
174 :
175 : char *orig_url;
176 : char *orig_url_before_redirect;
177 : char *remote_path;
178 : gf_user_credentials_struct * creds;
179 : char cookie[GF_MAX_PATH];
180 : DownloadedCacheEntry cache_entry;
181 : Bool reused_cache_entry, from_cache_only;
182 :
183 : //mime type, only used when the session is not cached.
184 : char *mime_type;
185 : GF_List *headers;
186 :
187 : GF_Socket *sock;
188 : u32 num_retry;
189 : GF_NetIOStatus status;
190 :
191 : u32 flags;
192 : u32 total_size, bytes_done, icy_metaint, icy_count, icy_bytes;
193 : u64 start_time;
194 :
195 : u32 bytes_per_sec;
196 : u64 start_time_utc;
197 : Bool last_chunk_found;
198 : Bool connection_close;
199 : Bool is_range_continuation;
200 : /*0: no cache reconfig before next GET request: 1: try to rematch the cache entry: 2: force to create a new cache entry (for byte-range cases)*/
201 : u32 needs_cache_reconfig;
202 : /* Range information if needed for the download (cf flag) */
203 : Bool needs_range;
204 : u64 range_start, range_end;
205 :
206 : u32 connect_time, ssl_setup_time, reply_time, total_time_since_req, req_hdr_size, rsp_hdr_size;
207 :
208 : /*0: GET
209 : 1: HEAD
210 : 2: all the rest
211 : */
212 : enum REQUEST_TYPE http_read_type;
213 :
214 : GF_Err last_error;
215 : char *init_data;
216 : u32 init_data_size;
217 : Bool server_only_understand_get;
218 : /* True if cache file must be stored on disk */
219 : Bool use_cache_file;
220 : Bool disable_cache;
221 : /*forces notification of data exchange to be sent regardless of threading mode*/
222 : Bool force_data_write_callback;
223 : #ifdef GPAC_HAS_SSL
224 : SSL *ssl;
225 : #endif
226 :
227 : void (*do_requests)(struct __gf_download_session *);
228 :
229 : /*callback for data reception - may not be NULL*/
230 : gf_dm_user_io user_proc;
231 : void *usr_cbk;
232 : Bool reassigned;
233 :
234 : Bool chunked;
235 : u32 nb_left_in_chunk;
236 : u32 current_chunk_size;
237 : u64 current_chunk_start;
238 :
239 : u64 request_start_time, last_fetch_time;
240 : /*private extension*/
241 : void *ext;
242 :
243 : char *remaining_data;
244 : u32 remaining_data_size;
245 :
246 : u32 head_timeout, request_timeout;
247 :
248 : Bool local_cache_only;
249 : Bool server_mode;
250 : //0: not PUT/POST, 1: waiting for body to be completed, 2: body done
251 : u32 put_state;
252 :
253 : u64 last_cap_rate_time;
254 : u64 last_cap_rate_bytes;
255 : u32 last_cap_rate_bytes_per_sec;
256 :
257 : u64 last_chunk_start_time;
258 : u32 chunk_wnd_dur;
259 : u32 chunk_bytes, chunk_header_bytes, cumulated_chunk_header_bytes;
260 : //in bytes per seconds
261 : Double cumulated_chunk_rate;
262 :
263 : #ifdef GPAC_HAS_HTTP2
264 : //HTTP/2 session used by this download session. f not NULL, the mutex, socket and ssl context are moved along sessions
265 : GF_H2_Session *h2_sess;
266 : int32_t h2_stream_id;
267 : h2_reagg_buffer h2_buf;
268 :
269 : nghttp2_data_provider data_io;
270 : u8 *h2_send_data;
271 : u32 h2_send_data_len;
272 :
273 : u8 *h2_upgrade_settings;
274 : u32 h2_upgrade_settings_len;
275 : u8 h2_headers_seen, h2_ready_to_send, h2_is_eos, h2_data_paused, h2_upgrade_state, h2_data_done, h2_switch_sess;
276 : #endif
277 : };
278 :
279 :
280 : struct __gf_download_manager
281 : {
282 : GF_Mutex *cache_mx;
283 : char *cache_directory;
284 :
285 : Bool (*get_user_password)(void *usr_cbk, const char *site_url, char *usr_name, char *password);
286 : void *usr_cbk;
287 :
288 : GF_List *sessions;
289 : Bool disable_cache, simulate_no_connection, allow_offline_cache, clean_cache;
290 : u32 limit_data_rate, read_buf_size;
291 : u64 max_cache_size;
292 : Bool allow_broken_certificate;
293 :
294 : GF_List *skip_proxy_servers;
295 : GF_List *credentials;
296 : GF_List *cache_entries;
297 : /* FIXME : should be placed in DownloadedCacheEntry maybe... */
298 : GF_List *partial_downloads;
299 : #ifdef GPAC_HAS_SSL
300 : SSL_CTX *ssl_ctx;
301 : #endif
302 :
303 : GF_FilterSession *filter_session;
304 :
305 : #ifdef GPAC_HAS_HTTP2
306 : Bool disable_http2;
307 : #endif
308 :
309 : Bool (*local_cache_url_provider_cbk)(void *udta, char *url, Bool cache_destroy);
310 : void *lc_udta;
311 : };
312 :
313 : #ifdef GPAC_HAS_SSL
314 :
315 11 : static void init_prng (void)
316 : {
317 : char namebuf[256];
318 : const char *random_file;
319 :
320 22 : if (RAND_status ()) return;
321 :
322 0 : namebuf[0] = '\0';
323 0 : random_file = RAND_file_name (namebuf, sizeof (namebuf));
324 :
325 0 : if (random_file && *random_file)
326 0 : RAND_load_file(random_file, 16384);
327 :
328 0 : if (RAND_status ()) return;
329 : }
330 :
331 : #endif
332 :
333 :
334 : /*HTTP2 callbacks*/
335 : #ifdef GPAC_HAS_HTTP2
336 :
337 : #ifdef GPAC_HAS_SSL
338 : /* NPN TLS extension client callback. We check that server advertised
339 : the HTTP/2 protocol the nghttp2 library supports. If not, exit
340 : the program. */
341 : static int h2_select_next_proto_cb(SSL *ssl , unsigned char **out,
342 : unsigned char *outlen, const unsigned char *in,
343 : unsigned int inlen, void *arg)
344 : {
345 : if (nghttp2_select_next_protocol(out, outlen, in, inlen) <= 0) {
346 : GF_LOG(GF_LOG_DEBUG, GF_LOG_HTTP, ("[HTTP/2] Server did not advertise " NGHTTP2_PROTO_VERSION_ID));
347 : }
348 : return SSL_TLSEXT_ERR_OK;
349 : }
350 : #endif
351 :
352 : //detach session from HTTP2 session - the session mutex SHALL be grabbed before calling this
353 : void h2_detach_session(GF_H2_Session *h2_sess, GF_DownloadSession *sess)
354 : {
355 : if (!h2_sess || !sess) return;
356 : assert(sess->h2_sess == h2_sess);
357 : assert(sess->mx);
358 :
359 : gf_list_del_item(h2_sess->sessions, sess);
360 : if (!gf_list_count(h2_sess->sessions)) {
361 : if (sess->sock) {
362 : GF_LOG(GF_LOG_DEBUG, GF_LOG_HTTP, ("[Downloader] closing socket\n"));
363 : gf_sk_del(sess->sock);
364 : sess->sock = NULL;
365 : }
366 : #ifdef GPAC_HAS_SSL
367 : if (sess->ssl) {
368 : GF_LOG(GF_LOG_DEBUG, GF_LOG_HTTP, ("[Downloader] shut down SSL context\n"));
369 : SSL_shutdown(sess->ssl);
370 : SSL_free(sess->ssl);
371 : sess->ssl = NULL;
372 : }
373 : #endif
374 : //destroy h2 session
375 : nghttp2_session_del(h2_sess->ng_sess);
376 : gf_list_del(h2_sess->sessions);
377 : gf_mx_v(h2_sess->mx);
378 : gf_mx_del(sess->mx);
379 : gf_free(h2_sess);
380 : } else {
381 : GF_DownloadSession *asess = gf_list_get(h2_sess->sessions, 0);
382 : assert(asess->h2_sess == h2_sess);
383 : assert(asess->sock);
384 :
385 : h2_sess->net_sess = asess;
386 : sess->sock = NULL;
387 : #ifdef GPAC_HAS_SSL
388 : sess->ssl = NULL;
389 : #endif
390 : gf_mx_v(sess->mx);
391 : }
392 :
393 : if (sess->h2_buf.data) {
394 : gf_free(sess->h2_buf.data);
395 : memset(&sess->h2_buf, 0, sizeof(h2_reagg_buffer));
396 : }
397 : sess->h2_sess = NULL;
398 : sess->mx = NULL;
399 : }
400 :
401 : static GF_Err h2_session_send(GF_DownloadSession *sess)
402 : {
403 : assert(sess->h2_sess);
404 : int rv = nghttp2_session_send(sess->h2_sess->ng_sess);
405 : if (rv != 0) {
406 : GF_LOG(GF_LOG_ERROR, GF_LOG_HTTP, ("[HTTP/2] session_send error : %s\n", nghttp2_strerror(rv)));
407 : if (sess->status != GF_NETIO_STATE_ERROR) {
408 : sess->status = GF_NETIO_STATE_ERROR;
409 : if (rv==NGHTTP2_ERR_NOMEM) sess->last_error = GF_OUT_OF_MEM;
410 : else sess->last_error = GF_SERVICE_ERROR;
411 : }
412 : return sess->last_error;
413 : }
414 : // GF_LOG(GF_LOG_DEBUG, GF_LOG_HTTP, ("[HTTP/2] session_send OK\n"));
415 : return GF_OK;
416 : }
417 :
418 : static GF_DownloadSession *h2_get_session(void *user_data, s32 stream_id, Bool can_reassign)
419 : {
420 : u32 i, nb_sess;
421 : GF_DownloadSession *first_not_assigned = NULL;
422 : GF_H2_Session *h2sess = (GF_H2_Session *)user_data;
423 :
424 : nb_sess = gf_list_count(h2sess->sessions);
425 : for (i=0;i<nb_sess; i++) {
426 : GF_DownloadSession *s = gf_list_get(h2sess->sessions, i);
427 : if (s->h2_stream_id == stream_id)
428 : return s;
429 :
430 : if (s->server_mode && !s->h2_stream_id && !first_not_assigned) {
431 : first_not_assigned = s;
432 : }
433 : }
434 : if (can_reassign && first_not_assigned) {
435 : first_not_assigned->h2_stream_id = stream_id;
436 : GF_LOG(GF_LOG_DEBUG, GF_LOG_HTTP, ("[HTTP/2] reassigning old server session to new stream %d\n", stream_id));
437 : assert(first_not_assigned->data_io.source.ptr);
438 : first_not_assigned->total_size = first_not_assigned->bytes_done = 0;
439 : first_not_assigned->status = GF_NETIO_CONNECTED;
440 : return first_not_assigned;
441 : }
442 : return NULL;
443 : }
444 :
445 : static int h2_header_callback(nghttp2_session *session,
446 : const nghttp2_frame *frame, const uint8_t *name,
447 : size_t namelen, const uint8_t *value,
448 : size_t valuelen, uint8_t flags ,
449 : void *user_data)
450 : {
451 : GF_DownloadSession *sess;
452 :
453 : switch (frame->hd.type) {
454 : case NGHTTP2_HEADERS:
455 : sess = h2_get_session(user_data, frame->hd.stream_id, GF_FALSE);
456 : if (!sess)
457 : return NGHTTP2_ERR_CALLBACK_FAILURE;
458 : if (
459 : (!sess->server_mode && (frame->headers.cat == NGHTTP2_HCAT_RESPONSE))
460 : || (sess->server_mode && ((frame->headers.cat == NGHTTP2_HCAT_HEADERS) || (frame->headers.cat == NGHTTP2_HCAT_REQUEST)))
461 : ) {
462 : GF_HTTPHeader *hdrp;
463 :
464 : GF_SAFEALLOC(hdrp, GF_HTTPHeader);
465 : if (hdrp) {
466 : GF_LOG(GF_LOG_DEBUG, GF_LOG_HTTP, ("[HTTP/2] stream_id %d got header %s: %s\n", sess->h2_stream_id, name, value));
467 : hdrp->name = gf_strdup(name);
468 : hdrp->value = gf_strdup(value);
469 : gf_list_add(sess->headers, hdrp);
470 : }
471 : break;
472 : }
473 : }
474 : return 0;
475 : }
476 :
477 : static int h2_begin_headers_callback(nghttp2_session *session, const nghttp2_frame *frame, void *user_data)
478 : {
479 : GF_DownloadSession *sess = h2_get_session(user_data, frame->hd.stream_id, GF_TRUE);
480 : if (!sess) {
481 : GF_H2_Session *h2sess = (GF_H2_Session *)user_data;
482 : GF_DownloadSession *par_sess = h2sess->net_sess;
483 : assert(par_sess);
484 : if (!par_sess->server_mode)
485 : return NGHTTP2_ERR_CALLBACK_FAILURE;
486 :
487 :
488 : if (par_sess->user_proc) {
489 : GF_NETIO_Parameter param;
490 : memset(¶m, 0, sizeof(GF_NETIO_Parameter));
491 : param.msg_type = GF_NETIO_REQUEST_SESSION;
492 : par_sess->in_callback = GF_TRUE;
493 : param.sess = par_sess;
494 : param.reply = frame->hd.stream_id;
495 : par_sess->user_proc(par_sess->usr_cbk, ¶m);
496 : par_sess->in_callback = GF_FALSE;
497 :
498 : if (param.error == GF_OK)
499 : sess = h2_get_session(user_data, frame->hd.stream_id, GF_FALSE);
500 : }
501 :
502 : if (!sess)
503 : return NGHTTP2_ERR_CALLBACK_FAILURE;
504 : }
505 :
506 : switch (frame->hd.type) {
507 : case NGHTTP2_HEADERS:
508 : if (sess->server_mode) {
509 : GF_LOG(GF_LOG_DEBUG, GF_LOG_HTTP, ("[HTTP/2] stream_id %d header callback\n", frame->hd.stream_id));
510 : } else {
511 : GF_LOG(GF_LOG_DEBUG, GF_LOG_HTTP, ("[HTTP/2] stream_id %d (%s) header callback\n", frame->hd.stream_id, sess->remote_path));
512 : }
513 : break;
514 : }
515 : return 0;
516 : }
517 :
518 : static int h2_frame_recv_callback(nghttp2_session *session, const nghttp2_frame *frame, void *user_data)
519 : {
520 : GF_DownloadSession *sess;
521 : switch (frame->hd.type) {
522 : case NGHTTP2_HEADERS:
523 : sess = h2_get_session(user_data, frame->hd.stream_id, GF_FALSE);
524 : if (!sess)
525 : return NGHTTP2_ERR_CALLBACK_FAILURE;
526 :
527 : if (
528 : (!sess->server_mode && (frame->headers.cat == NGHTTP2_HCAT_RESPONSE))
529 : || (sess->server_mode && ((frame->headers.cat == NGHTTP2_HCAT_HEADERS) || (frame->headers.cat == NGHTTP2_HCAT_REQUEST)))
530 : ) {
531 : sess->h2_headers_seen = 1;
532 : if (sess->server_mode) {
533 : GF_LOG(GF_LOG_DEBUG, GF_LOG_HTTP, ("[HTTP/2] All headers received for stream ID %d\n", sess->h2_stream_id));
534 : } else {
535 : GF_LOG(GF_LOG_DEBUG, GF_LOG_HTTP, ("[HTTP/2] All headers received for stream ID %d\n", sess->h2_stream_id));
536 : }
537 : }
538 : break;
539 : case NGHTTP2_DATA:
540 : if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
541 : sess = h2_get_session(user_data, frame->hd.stream_id, GF_FALSE);
542 : //if no session with such ID this means we got all our bytes and considered the session done, do not throw and error
543 : if (sess) {
544 : GF_LOG(GF_LOG_DEBUG, GF_LOG_HTTP, ("[HTTP/2] stream_id %d (%s) data done\n", frame->hd.stream_id, sess->remote_path ? sess->remote_path : sess->orig_url));
545 : sess->h2_data_done = 1;
546 : }
547 : }
548 : break;
549 : case NGHTTP2_RST_STREAM:
550 : sess = h2_get_session(user_data, frame->hd.stream_id, GF_FALSE);
551 : // cancel from remote peer, signal if not done
552 : if (sess && sess->server_mode && !sess->h2_is_eos) {
553 : GF_NETIO_Parameter param;
554 : GF_LOG(GF_LOG_DEBUG, GF_LOG_HTTP, ("[HTTP/2] stream_id %d (%s) canceled\n", frame->hd.stream_id, sess->remote_path ? sess->remote_path : sess->orig_url));
555 : memset(¶m, 0, sizeof(GF_NETIO_Parameter));
556 : param.msg_type = GF_NETIO_CANCEL_STREAM;
557 : gf_mx_p(sess->mx);
558 : sess->in_callback = GF_TRUE;
559 : param.sess = sess;
560 : sess->user_proc(sess->usr_cbk, ¶m);
561 : sess->in_callback = GF_FALSE;
562 : gf_mx_v(sess->mx);
563 : }
564 : break;
565 : }
566 :
567 : return 0;
568 : }
569 :
570 : static int h2_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags, int32_t stream_id, const uint8_t *data, size_t len, void *user_data)
571 : {
572 : GF_DownloadSession *sess = h2_get_session(user_data, stream_id, GF_FALSE);
573 : if (!sess)
574 : return NGHTTP2_ERR_CALLBACK_FAILURE;
575 :
576 : if (sess->h2_buf.size + len > sess->h2_buf.alloc) {
577 : sess->h2_buf.alloc = sess->h2_buf.size + (u32) len;
578 : sess->h2_buf.data = gf_realloc(sess->h2_buf.data, sizeof(u8) * sess->h2_buf.alloc);
579 : if (!sess->h2_buf.data) return NGHTTP2_ERR_NOMEM;
580 : }
581 : memcpy(sess->h2_buf.data + sess->h2_buf.size, data, len);
582 : sess->h2_buf.size += (u32) len;
583 : GF_LOG(GF_LOG_DEBUG, GF_LOG_HTTP, ("[HTTP/2] stream_id %d received %d bytes - flags %d\n", sess->h2_stream_id, len, flags));
584 : return 0;
585 : }
586 :
587 : static int h2_stream_close_callback(nghttp2_session *session, int32_t stream_id, uint32_t error_code, void *user_data)
588 : {
589 : Bool do_retry = GF_FALSE;
590 : GF_DownloadSession *sess = h2_get_session(user_data, stream_id, GF_FALSE);
591 : if (!sess)
592 : return 0;
593 :
594 : gf_mx_p(sess->mx);
595 :
596 : if (error_code==NGHTTP2_REFUSED_STREAM)
597 : do_retry = GF_TRUE;
598 : else if (sess->h2_sess->do_shutdown && !sess->server_mode && !sess->bytes_done)
599 : do_retry = GF_TRUE;
600 :
601 : if (do_retry) {
602 : sess->h2_sess->do_shutdown = GF_TRUE;
603 : sess->h2_switch_sess = GF_TRUE;
604 : sess->status = GF_NETIO_SETUP;
605 : sess->last_error = GF_OK;
606 : gf_mx_v(sess->mx);
607 : return 0;
608 : }
609 :
610 : if (error_code) {
611 : GF_LOG(GF_LOG_ERROR, GF_LOG_HTTP, ("[HTTP/2] stream_id %d (%s) closed with error_code=%d\n", stream_id, sess->remote_path ? sess->remote_path : sess->orig_url, error_code));
612 : sess->status = GF_NETIO_STATE_ERROR;
613 : sess->last_error = GF_IP_NETWORK_FAILURE;
614 : } else {
615 : GF_LOG(GF_LOG_DEBUG, GF_LOG_HTTP, ("[HTTP/2] stream_id %d (%s) closed\n", stream_id, sess->remote_path ? sess->remote_path : sess->orig_url));
616 : //keep status in DATA_EXCHANGE as this frame might have been pushed while processing another session
617 : }
618 : //stream closed
619 : sess->h2_stream_id = 0;
620 : gf_mx_v(sess->mx);
621 : return 0;
622 : }
623 :
624 : static int h2_error_callback(nghttp2_session *session, const char *msg, size_t len, void *user_data)
625 : {
626 : GF_LOG(GF_LOG_ERROR, GF_LOG_HTTP, ("[HTTP/2] error %s\n", msg));
627 : return 0;
628 : }
629 :
630 : static ssize_t h2_write_data(GF_DownloadSession *sess, const uint8_t *data, size_t length)
631 : {
632 : GF_Err e;
633 : #ifdef GPAC_HAS_SSL
634 : if (sess->ssl) {
635 : assert(length);
636 : int res = SSL_write(sess->ssl, data, (int) length);
637 : if (res <= 0) {
638 : int err = SSL_get_error(sess->ssl, res);
639 : if (err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ) {
640 : return NGHTTP2_ERR_WOULDBLOCK;
641 : } else {
642 : //err = errno;
643 : return NGHTTP2_ERR_SESSION_CLOSING;
644 : }
645 : }
646 : return res;
647 : }
648 : #endif
649 :
650 : e = gf_sk_send(sess->sock, data, (u32) length);
651 : switch (e) {
652 : case GF_OK:
653 : return length;
654 : case GF_IP_SOCK_WOULD_BLOCK:
655 : return NGHTTP2_ERR_WOULDBLOCK;
656 : case GF_IP_CONNECTION_CLOSED:
657 : return NGHTTP2_ERR_EOF;
658 : default:
659 : break;
660 : }
661 : return NGHTTP2_ERR_CALLBACK_FAILURE;
662 : }
663 :
664 : static ssize_t h2_send_callback(nghttp2_session *session, const uint8_t *data, size_t length, int flags, void *user_data)
665 : {
666 : GF_H2_Session *h2sess = (GF_H2_Session *)user_data;
667 : GF_DownloadSession *sess = h2sess->net_sess;
668 :
669 : return h2_write_data(sess, data, length);
670 : }
671 :
672 : static int h2_before_frame_send_callback(nghttp2_session *session, const nghttp2_frame *frame, void *user_data)
673 : {
674 : GF_DownloadSession *sess = h2_get_session(user_data, frame->hd.stream_id, GF_FALSE);
675 : if (!sess)
676 : return 0;
677 : sess->h2_ready_to_send = 1;
678 : return 0;
679 : }
680 :
681 :
682 : static ssize_t h2_data_source_read_callback(nghttp2_session *session, int32_t stream_id, uint8_t *buf, size_t length, uint32_t *data_flags, nghttp2_data_source *source, void *user_data)
683 : {
684 : GF_DownloadSession *sess = (GF_DownloadSession *) source->ptr;
685 :
686 : if (!sess->h2_send_data_len) {
687 : sess->h2_send_data = NULL;
688 : if (sess->h2_is_eos) {
689 : *data_flags = NGHTTP2_DATA_FLAG_EOF;
690 : return 0;
691 : }
692 : sess->h2_data_paused = 1;
693 : return NGHTTP2_ERR_DEFERRED;
694 : }
695 :
696 : if (sess->h2_sess->copy) {
697 : u32 copy = (sess->h2_send_data_len > length) ? (u32) length : sess->h2_send_data_len;
698 : memcpy(buf, sess->h2_send_data, copy);
699 : sess->h2_send_data += copy;
700 : sess->h2_send_data_len -= copy;
701 : return copy;
702 : }
703 :
704 : *data_flags = NGHTTP2_DATA_FLAG_NO_COPY;
705 : if (sess->h2_send_data_len > length)
706 : return length;
707 : return sess->h2_send_data_len;
708 : }
709 :
710 : static void h2_flush_send(GF_DownloadSession *sess)
711 : {
712 : char h2_flush[1024];
713 : u32 res;
714 :
715 : while (sess->h2_send_data) {
716 : h2_session_send(sess);
717 : //read any frame pending from remote peer (window update and co)
718 : gf_dm_read_data(sess, h2_flush, 1023, &res);
719 :
720 : //error or regular eos
721 : if (!sess->h2_stream_id)
722 : break;
723 : if (sess->status==GF_NETIO_STATE_ERROR)
724 : break;
725 : }
726 : }
727 :
728 : static char padding[256];
729 :
730 : static int h2_send_data_callback(nghttp2_session *session, nghttp2_frame *frame, const uint8_t *framehd, size_t length, nghttp2_data_source *source, void *user_data)
731 : { ssize_t rv;
732 : GF_DownloadSession *sess = (GF_DownloadSession *) source->ptr;
733 :
734 : assert(sess->h2_send_data_len);
735 : assert(sess->h2_send_data_len >= length);
736 :
737 : rv = h2_write_data(sess, (u8 *) framehd, 9);
738 : if (rv<0) goto err;
739 :
740 : if (frame->data.padlen > 0) {
741 : u32 padlen = (u32) frame->data.padlen - 1;
742 : rv = h2_write_data(sess, padding, padlen);
743 : if (rv<0) goto err;
744 : }
745 : rv = h2_write_data(sess, (u8 *) sess->h2_send_data, length);
746 : if (rv<0) goto err;
747 :
748 : sess->h2_send_data += (u32) length;
749 : sess->h2_send_data_len -= (u32) length;
750 : return 0;
751 : err:
752 :
753 : sess->status = GF_NETIO_STATE_ERROR;
754 : sess->last_error = GF_IP_NETWORK_FAILURE;
755 : return NGHTTP2_ERR_CALLBACK_FAILURE;
756 : }
757 :
758 : static void h2_initialize_session(GF_DownloadSession *sess)
759 : {
760 : int rv;
761 : nghttp2_settings_entry iv[2] = {
762 : {NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100},
763 : {NGHTTP2_SETTINGS_ENABLE_PUSH, 0}
764 : };
765 : char szMXName[100];
766 : nghttp2_session_callbacks *callbacks;
767 :
768 : nghttp2_session_callbacks_new(&callbacks);
769 : nghttp2_session_callbacks_set_send_callback(callbacks, h2_send_callback);
770 : nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks, h2_frame_recv_callback);
771 : nghttp2_session_callbacks_set_before_frame_send_callback(callbacks, h2_before_frame_send_callback);
772 :
773 : nghttp2_session_callbacks_set_on_data_chunk_recv_callback(callbacks, h2_data_chunk_recv_callback);
774 : nghttp2_session_callbacks_set_on_stream_close_callback(callbacks, h2_stream_close_callback);
775 : nghttp2_session_callbacks_set_on_header_callback(callbacks, h2_header_callback);
776 : nghttp2_session_callbacks_set_on_begin_headers_callback(callbacks, h2_begin_headers_callback);
777 : nghttp2_session_callbacks_set_error_callback(callbacks, h2_error_callback);
778 :
779 : GF_SAFEALLOC(sess->h2_sess, GF_H2_Session)
780 : sess->h2_sess->sessions = gf_list_new();
781 : sess->h2_sess->copy = gf_opts_get_bool("core", "h2-copy");
782 : if (!sess->h2_sess->copy)
783 : nghttp2_session_callbacks_set_send_data_callback(callbacks, h2_send_data_callback);
784 :
785 : if (sess->server_mode) {
786 : nghttp2_session_server_new(&sess->h2_sess->ng_sess, callbacks, sess->h2_sess);
787 : } else {
788 : nghttp2_session_client_new(&sess->h2_sess->ng_sess, callbacks, sess->h2_sess);
789 : }
790 : nghttp2_session_callbacks_del(callbacks);
791 : sess->h2_sess->net_sess = sess;
792 : gf_list_add(sess->h2_sess->sessions, sess);
793 :
794 : sprintf(szMXName, "http2_%p", sess->h2_sess);
795 : sess->h2_sess->mx = gf_mx_new(szMXName);
796 : sess->mx = sess->h2_sess->mx;
797 : sess->chunked = GF_FALSE;
798 :
799 : sess->data_io.read_callback = h2_data_source_read_callback;
800 : sess->data_io.source.ptr = sess;
801 :
802 : if (sess->server_mode) {
803 : sess->h2_stream_id = 1;
804 : if (sess->h2_upgrade_settings) {
805 : rv = nghttp2_session_upgrade2(sess->h2_sess->ng_sess, sess->h2_upgrade_settings, sess->h2_upgrade_settings_len, 0, sess);
806 : gf_free(sess->h2_upgrade_settings);
807 : sess->h2_upgrade_settings = NULL;
808 :
809 : if (rv) {
810 : sess->status = GF_NETIO_STATE_ERROR;
811 : sess->last_error = (rv==NGHTTP2_ERR_NOMEM) ? GF_OUT_OF_MEM : GF_REMOTE_SERVICE_ERROR;
812 : return;
813 : }
814 : }
815 : }
816 :
817 : /* client 24 bytes magic string will be sent by nghttp2 library */
818 : rv = nghttp2_submit_settings(sess->h2_sess->ng_sess, NGHTTP2_FLAG_NONE, iv, GF_ARRAY_LENGTH(iv));
819 : if (rv != 0) {
820 : sess->status = GF_NETIO_STATE_ERROR;
821 : sess->last_error = (rv==NGHTTP2_ERR_NOMEM) ? GF_OUT_OF_MEM : GF_SERVICE_ERROR;
822 : return;
823 : }
824 : h2_session_send(sess);
825 : }
826 :
827 :
828 : #define NV_HDR(_hdr, _name, _value) { \
829 : _hdr.name = (uint8_t *)_name;\
830 : _hdr.value = (uint8_t *)_value;\
831 : _hdr.namelen = (u32) strlen(_name);\
832 : _hdr.valuelen = (u32) strlen(_value);\
833 : _hdr.flags = NGHTTP2_NV_FLAG_NONE;\
834 : }
835 :
836 : static GF_Err h2_submit_request(GF_DownloadSession *sess, char *req_name, const char *url, const char *param_string, Bool has_body)
837 : {
838 : u32 nb_hdrs, i;
839 : char *hostport = NULL;
840 : char *path = NULL;
841 : char port[20];
842 : nghttp2_nv *hdrs;
843 :
844 : nb_hdrs = gf_list_count(sess->headers);
845 : hdrs = gf_malloc(sizeof(nghttp2_nv) * (nb_hdrs + 4));
846 :
847 : NV_HDR(hdrs[0], ":method", req_name);
848 : NV_HDR(hdrs[1], ":scheme", "https");
849 :
850 : gf_dynstrcat(&hostport, sess->server_name, NULL);
851 : sprintf(port, ":%d", sess->port);
852 : gf_dynstrcat(&hostport, port, NULL);
853 : NV_HDR(hdrs[2], ":authority", hostport);
854 :
855 : if (param_string) {
856 : gf_dynstrcat(&path, url, NULL);
857 : if (strchr(sess->remote_path, '?')) {
858 : gf_dynstrcat(&path, param_string, "&");
859 : } else {
860 : gf_dynstrcat(&path, param_string, "?");
861 : }
862 : NV_HDR(hdrs[3], ":path", path);
863 : } else {
864 : NV_HDR(hdrs[3], ":path", url);
865 : }
866 :
867 : for (i=0; i<nb_hdrs; i++) {
868 : GF_HTTPHeader *hdr = gf_list_get(sess->headers, i);
869 : NV_HDR(hdrs[4+i], hdr->name, hdr->value);
870 : }
871 : if (has_body) {
872 : assert(sess->data_io.read_callback);
873 : assert(sess->data_io.source.ptr != NULL);
874 : }
875 :
876 : sess->h2_data_done = 0;
877 : sess->h2_headers_seen = 0;
878 : sess->h2_stream_id = nghttp2_submit_request(sess->h2_sess->ng_sess, NULL, hdrs, nb_hdrs+4, has_body ? &sess->data_io : NULL, sess);
879 : sess->h2_ready_to_send = 0;
880 :
881 : #ifndef GPAC_DISABLE_LOGS
882 : if (gf_log_tool_level_on(GF_LOG_HTTP, GF_LOG_DEBUG)) {
883 : GF_LOG(GF_LOG_DEBUG, GF_LOG_HTTP, ("[HTTP/2] send request (has_body %d) for new stream_id %d:\n", has_body, sess->h2_stream_id));
884 : for (i=0; i<nb_hdrs+4; i++) {
885 : GF_LOG(GF_LOG_DEBUG, GF_LOG_HTTP, ("\t%s: %s\n", hdrs[i].name, hdrs[i].value));
886 : }
887 : }
888 : #endif
889 :
890 : gf_free(hdrs);
891 : gf_free(hostport);
892 : if (path) gf_free(path);
893 :
894 : if (sess->h2_stream_id < 0) {
895 : return GF_IP_NETWORK_FAILURE;
896 : }
897 :
898 : return GF_OK;
899 : }
900 :
901 :
902 : static void h2_flush_data(GF_DownloadSession *sess, Bool store_in_init)
903 : {
904 : gf_dm_data_received(sess, (u8 *) sess->h2_buf.data, sess->h2_buf.size, store_in_init, NULL, NULL);
905 : sess->h2_buf.size = 0;
906 : }
907 :
908 : static void h2_flush_data_ex(GF_DownloadSession *sess, u8 *obuffer, u32 size, u32 *nb_bytes)
909 : {
910 : u32 copy, nb_b_pck;
911 : u8 *data;
912 : *nb_bytes = 0;
913 : if (!sess->h2_buf.size)
914 : return;
915 :
916 : assert(sess->h2_buf.offset<=sess->h2_buf.size);
917 :
918 : nb_b_pck = sess->h2_buf.size - sess->h2_buf.offset;
919 : if (nb_b_pck > size)
920 : copy = size;
921 : else
922 : copy = nb_b_pck;
923 :
924 : data = sess->h2_buf.data + sess->h2_buf.offset;
925 : memcpy(obuffer, data, copy);
926 : *nb_bytes = copy;
927 : gf_dm_data_received(sess, (u8 *) data, copy, GF_FALSE, NULL, NULL);
928 :
929 : if (copy < nb_b_pck) {
930 : sess->h2_buf.offset += copy;
931 : } else {
932 : sess->h2_buf.size = sess->h2_buf.offset = 0;
933 : }
934 : assert(sess->h2_buf.offset<=sess->h2_buf.size);
935 : }
936 :
937 :
938 : #endif
939 :
940 : static void sess_connection_closed(GF_DownloadSession *sess)
941 : {
942 : #ifdef GPAC_HAS_HTTP2
943 : if (sess->h2_sess) {
944 : sess->h2_sess->do_shutdown = GF_TRUE;
945 : sess->h2_switch_sess = GF_TRUE;
946 : }
947 : #endif
948 : }
949 :
950 : /*
951 : * Private methods of cache
952 : */
953 :
954 : //Writes data to the cache. A call to gf_cache_open_write_cache should have been issued before calling this function.
955 : GF_Err gf_cache_write_to_cache( const DownloadedCacheEntry entry, const GF_DownloadSession * sess, const char * data, const u32 size, GF_Mutex *mx);
956 :
957 : /**
958 : * \brief Close the write file pointer of cache
959 : * This function also flushes all buffers, so cache will always be consistent after
960 : \param entry The entry to use
961 : \param sess The download session
962 : \param success 1 if cache write is success, false otherwise
963 : \param GF_OK is everything went fine, GF_BAD_PARAM if entry is NULL, GF_IO_ERR if a failure occurs
964 : */
965 : GF_Err gf_cache_close_write_cache( const DownloadedCacheEntry entry, const GF_DownloadSession * sess, Bool success);
966 :
967 : /**
968 : * \brief Open the write file pointer of cache
969 : * This function prepares calls for gf_cache_write_to_cache
970 : \param entry The entry to use
971 : \param sess The download session
972 : \param GF_OK is everything went fine, GF_BAD_PARAM if entry is NULL, GF_IO_ERR if a failure occurs
973 : */
974 : GF_Err gf_cache_open_write_cache( const DownloadedCacheEntry entry, const GF_DownloadSession * sess );
975 :
976 : /*modify end range when chaining byte-range requests*/
977 : void gf_cache_set_end_range(DownloadedCacheEntry entry, u64 range_end);
978 :
979 : /*returns 1 if cache is currently open for write*/
980 : Bool gf_cache_is_in_progress(const DownloadedCacheEntry entry);
981 :
982 : /**
983 : * Find a User's credentials for a given site
984 : */
985 822 : static gf_user_credentials_struct* gf_find_user_credentials_for_site(GF_DownloadManager *dm, const char *server_name) {
986 : u32 count, i;
987 822 : if (!dm || !dm->credentials || !server_name || !strlen(server_name))
988 : return NULL;
989 822 : count = gf_list_count( dm->credentials);
990 822 : for (i = 0 ; i < count; i++) {
991 0 : gf_user_credentials_struct * cred = (gf_user_credentials_struct*)gf_list_get(dm->credentials, i );
992 : assert( cred );
993 0 : if (!strcmp(cred->site, server_name))
994 : return cred;
995 : }
996 : return NULL;
997 : }
998 :
999 : /**
1000 : * \brief Saves the digest for authentication of password and username
1001 : \param dm The download manager
1002 : \param creds The credentials to fill
1003 : \param GF_OK if info has been filled, GF_BAD_PARAM if creds == NULL or dm == NULL, GF_AUTHENTICATION_FAILURE if user did not filled the info.
1004 : */
1005 5 : static GF_Err gf_user_credentials_save_digest( GF_DownloadManager * dm, gf_user_credentials_struct * creds, const char * password) {
1006 : int size;
1007 : char pass_buf[1024], range_buf[1024];
1008 5 : if (!dm || !creds || !password)
1009 : return GF_BAD_PARAM;
1010 0 : sprintf(pass_buf, "%s:%s", creds->username, password);
1011 0 : size = gf_base64_encode(pass_buf, (u32) strlen(pass_buf), range_buf, 1024);
1012 0 : range_buf[size] = 0;
1013 0 : strcpy(creds->digest, range_buf);
1014 0 : creds->valid = GF_TRUE;
1015 0 : return GF_OK;
1016 : }
1017 :
1018 : /**
1019 : * \brief Asks the user for credentials for given site
1020 : \param dm The download manager
1021 : \param creds The credentials to fill
1022 : \param GF_OK if info has been filled, GF_BAD_PARAM if creds == NULL or dm == NULL, GF_AUTHENTICATION_FAILURE if user did not filled the info.
1023 : */
1024 5 : static GF_Err gf_user_credentials_ask_password( GF_DownloadManager * dm, gf_user_credentials_struct * creds)
1025 : {
1026 : char szPASS[50];
1027 5 : if (!dm || !creds)
1028 : return GF_BAD_PARAM;
1029 : memset(szPASS, 0, 50);
1030 0 : if (!dm->get_user_password || !dm->get_user_password(dm->usr_cbk, creds->site, creds->username, szPASS)) {
1031 : return GF_AUTHENTICATION_FAILURE;
1032 : }
1033 0 : return gf_user_credentials_save_digest(dm, creds, szPASS);
1034 : }
1035 :
1036 0 : static gf_user_credentials_struct * gf_user_credentials_register(GF_DownloadManager * dm, const char * server_name, const char * username, const char * password, Bool valid)
1037 : {
1038 : gf_user_credentials_struct * creds;
1039 0 : if (!dm)
1040 : return NULL;
1041 : assert( server_name );
1042 0 : creds = gf_find_user_credentials_for_site(dm, server_name);
1043 : /* If none found, we create one */
1044 0 : if (!creds) {
1045 0 : creds = (gf_user_credentials_struct*)gf_malloc(sizeof( gf_user_credentials_struct));
1046 0 : if (!creds)
1047 : return NULL;
1048 0 : gf_list_insert(dm->credentials, creds, 0);
1049 : }
1050 0 : creds->valid = valid;
1051 0 : strncpy(creds->username, username ? username : "", 49);
1052 0 : creds->username[49] = 0;
1053 0 : strcpy(creds->site, server_name);
1054 0 : if (username && password && valid)
1055 0 : gf_user_credentials_save_digest(dm, creds, password);
1056 : else {
1057 0 : if (GF_OK != gf_user_credentials_ask_password(dm, creds)) {
1058 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_HTTP,
1059 : ("[HTTP] Failed to get password information.\n"));
1060 0 : gf_list_rem( dm->credentials, 0);
1061 0 : gf_free( creds );
1062 : creds = NULL;
1063 : }
1064 : }
1065 : return creds;
1066 : }
1067 :
1068 : #ifdef GPAC_HAS_SSL
1069 :
1070 : static Bool _ssl_is_initialized = GF_FALSE;
1071 :
1072 : /*!
1073 : * initialize the SSL library once for all download managers
1074 : \param GF_FALSE if everyhing is OK, GF_TRUE otherwise
1075 : */
1076 11 : Bool gf_ssl_init_lib() {
1077 11 : if (_ssl_is_initialized)
1078 : return GF_FALSE;
1079 11 : GF_LOG(GF_LOG_DEBUG, GF_LOG_HTTP, ("[HTTPS] Initializing SSL library...\n"));
1080 11 : init_prng();
1081 11 : if (RAND_status() != 1) {
1082 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_HTTP, ("[HTTPS] Error while initializing Random Number generator, failed to init SSL !\n"));
1083 : return GF_TRUE;
1084 : }
1085 :
1086 : /* per https://www.openssl.org/docs/man1.1.0/ssl/OPENSSL_init_ssl.html
1087 : ** As of version 1.1.0 OpenSSL will automatically allocate all resources that it needs so no explicit initialisation is required.
1088 : ** Similarly it will also automatically deinitialise as required.
1089 : */
1090 : #if OPENSSL_VERSION_NUMBER < 0x10100000L
1091 : SSL_library_init();
1092 : SSL_load_error_strings();
1093 : SSLeay_add_all_algorithms();
1094 : SSLeay_add_ssl_algorithms();
1095 : #endif
1096 :
1097 11 : _ssl_is_initialized = GF_TRUE;
1098 11 : GF_LOG(GF_LOG_DEBUG, GF_LOG_HTTP, ("[HTTPS] Initalization of SSL library complete.\n"));
1099 : return GF_FALSE;
1100 : }
1101 :
1102 10 : static int ssl_init(GF_DownloadManager *dm, u32 mode)
1103 : {
1104 : #if OPENSSL_VERSION_NUMBER > 0x00909000
1105 : const
1106 : #endif
1107 : SSL_METHOD *meth;
1108 :
1109 10 : if (!dm) return 0;
1110 10 : gf_mx_p(dm->cache_mx);
1111 : /* The SSL has already been initialized. */
1112 10 : if (dm->ssl_ctx) {
1113 0 : gf_mx_v(dm->cache_mx);
1114 0 : return 1;
1115 : }
1116 : /* Init the PRNG. If that fails, bail out. */
1117 10 : if (gf_ssl_init_lib()) {
1118 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_HTTP, ("[HTTPS] Failed to properly initialize SSL library\n"));
1119 : goto error;
1120 : }
1121 :
1122 10 : switch (mode) {
1123 : #if OPENSSL_VERSION_NUMBER < 0x10100000L
1124 : case 0:
1125 : meth = SSLv23_client_method();
1126 : break;
1127 : #if 0 /*SSL v2 is no longer supported in OpenSSL 1.0*/
1128 : case 1:
1129 : meth = SSLv2_client_method();
1130 : break;
1131 : #endif
1132 : case 2:
1133 : meth = SSLv3_client_method();
1134 : break;
1135 : case 3:
1136 : meth = TLSv1_client_method();
1137 : break;
1138 : #else /* for openssl 1.1+ this is the preferred method */
1139 10 : case 0:
1140 10 : meth = TLS_client_method();
1141 : break;
1142 : #endif
1143 : default:
1144 : goto error;
1145 : }
1146 :
1147 10 : dm->ssl_ctx = SSL_CTX_new(meth);
1148 10 : if (!dm->ssl_ctx) goto error;
1149 10 : SSL_CTX_set_default_verify_paths(dm->ssl_ctx);
1150 10 : SSL_CTX_load_verify_locations (dm->ssl_ctx, NULL, NULL);
1151 : /* SSL_VERIFY_NONE instructs OpenSSL not to abort SSL_connect if the
1152 : certificate is invalid. We verify the certificate separately in
1153 : ssl_check_certificate, which provides much better diagnostics
1154 : than examining the error stack after a failed SSL_connect. */
1155 10 : SSL_CTX_set_verify(dm->ssl_ctx, SSL_VERIFY_NONE, NULL);
1156 :
1157 : #ifdef GPAC_HAS_HTTP2
1158 : if (!dm->disable_http2) {
1159 : SSL_CTX_set_next_proto_select_cb(dm->ssl_ctx, h2_select_next_proto_cb, NULL);
1160 : #if OPENSSL_VERSION_NUMBER >= 0x10002000L
1161 : SSL_CTX_set_alpn_protos(dm->ssl_ctx, (const unsigned char *)"\x02h2", 3);
1162 : #endif
1163 : }
1164 : #endif
1165 :
1166 : /* Since fd_write unconditionally assumes partial writes (and handles them correctly),
1167 : allow them in OpenSSL. */
1168 10 : SSL_CTX_set_mode(dm->ssl_ctx, SSL_MODE_ENABLE_PARTIAL_WRITE);
1169 10 : gf_mx_v(dm->cache_mx);
1170 10 : return 1;
1171 0 : error:
1172 0 : if (dm->ssl_ctx) SSL_CTX_free(dm->ssl_ctx);
1173 0 : dm->ssl_ctx = NULL;
1174 0 : gf_mx_v(dm->cache_mx);
1175 0 : return 0;
1176 : }
1177 :
1178 : #ifdef GPAC_HAS_HTTP2
1179 :
1180 : static unsigned char next_proto_list[256];
1181 : static size_t next_proto_list_len;
1182 :
1183 : #ifndef OPENSSL_NO_NEXTPROTONEG
1184 : static int next_proto_cb(SSL *ssl, const unsigned char **data, unsigned int *len, void *arg)
1185 : {
1186 : *data = next_proto_list;
1187 : *len = (unsigned int)next_proto_list_len;
1188 : return SSL_TLSEXT_ERR_OK;
1189 : }
1190 : #endif //OPENSSL_NO_NEXTPROTONEG
1191 :
1192 : #if OPENSSL_VERSION_NUMBER >= 0x10002000L
1193 : static int alpn_select_proto_cb(SSL *ssl, const unsigned char **out, unsigned char *outlen, const unsigned char *in, unsigned int inlen, void *arg)
1194 : {
1195 : int rv = nghttp2_select_next_protocol((unsigned char **)out, outlen, in, inlen);
1196 : if (rv != 1) {
1197 : return SSL_TLSEXT_ERR_NOACK;
1198 : }
1199 : return SSL_TLSEXT_ERR_OK;
1200 : }
1201 : #endif /* OPENSSL_VERSION_NUMBER >= 0x10002000L */
1202 :
1203 : #endif
1204 :
1205 :
1206 1 : void *gf_ssl_server_context_new(const char *cert, const char *key)
1207 : {
1208 : const SSL_METHOD *method;
1209 : SSL_CTX *ctx;
1210 :
1211 1 : method = SSLv23_server_method();
1212 :
1213 1 : ctx = SSL_CTX_new(method);
1214 1 : if (!ctx) {
1215 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("Unable to create SSL context\n"));
1216 0 : ERR_print_errors_fp(stderr);
1217 0 : return NULL;
1218 : }
1219 : SSL_CTX_set_ecdh_auto(ctx, 1);
1220 1 : if (SSL_CTX_use_certificate_file(ctx, cert, SSL_FILETYPE_PEM) <= 0) {
1221 0 : ERR_print_errors_fp(stderr);
1222 0 : SSL_CTX_free(ctx);
1223 0 : return NULL;
1224 : }
1225 1 : if (SSL_CTX_use_PrivateKey_file(ctx, key, SSL_FILETYPE_PEM) <= 0 ) {
1226 0 : ERR_print_errors_fp(stderr);
1227 0 : SSL_CTX_free(ctx);
1228 0 : return NULL;
1229 : }
1230 :
1231 : #ifdef GPAC_HAS_HTTP2
1232 : if (!gf_opts_get_bool("core", "no-h2")) {
1233 : next_proto_list[0] = NGHTTP2_PROTO_VERSION_ID_LEN;
1234 : memcpy(&next_proto_list[1], NGHTTP2_PROTO_VERSION_ID, NGHTTP2_PROTO_VERSION_ID_LEN);
1235 : next_proto_list_len = 1 + NGHTTP2_PROTO_VERSION_ID_LEN;
1236 :
1237 : SSL_CTX_set_next_protos_advertised_cb(ctx, next_proto_cb, NULL);
1238 :
1239 : #if OPENSSL_VERSION_NUMBER >= 0x10002000L
1240 : SSL_CTX_set_alpn_select_cb(ctx, alpn_select_proto_cb, NULL);
1241 : #endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
1242 : }
1243 :
1244 : #endif
1245 :
1246 : return ctx;
1247 : }
1248 :
1249 1 : void gf_ssl_server_context_del(void *ssl_ctx)
1250 : {
1251 1 : SSL_CTX_free(ssl_ctx);
1252 1 : }
1253 1 : void *gf_ssl_new(void *ssl_ctx, GF_Socket *client_sock, GF_Err *e)
1254 : {
1255 : SSL *ssl;
1256 1 : ssl = SSL_new(ssl_ctx);
1257 1 : if (!ssl) {
1258 0 : *e = GF_IO_ERR;
1259 0 : return NULL;
1260 : }
1261 1 : SSL_set_fd(ssl, gf_sk_get_handle(client_sock) );
1262 1 : if (SSL_accept(ssl) <= 0) {
1263 0 : ERR_print_errors_fp(stderr);
1264 0 : SSL_shutdown(ssl);
1265 0 : SSL_free(ssl);
1266 0 : *e = GF_AUTHENTICATION_FAILURE;
1267 0 : return NULL;
1268 : }
1269 1 : *e = GF_OK;
1270 1 : return ssl;
1271 : }
1272 0 : void gf_ssl_del(void *ssl)
1273 : {
1274 0 : SSL_shutdown(ssl);
1275 0 : SSL_free(ssl);
1276 0 : }
1277 :
1278 163 : static GF_Err gf_ssl_write(void *ssl_ctx, const u8 *buffer, u32 size)
1279 : {
1280 : u32 idx=0;
1281 163 : s32 nb_tls_blocks = size/16000;
1282 489 : while (nb_tls_blocks>=0) {
1283 : u32 len, to_write = 16000;
1284 163 : if (nb_tls_blocks==0)
1285 163 : to_write = size - idx*16000;
1286 :
1287 163 : len = SSL_write(ssl_ctx, buffer + idx*16000, to_write);
1288 163 : nb_tls_blocks--;
1289 163 : idx++;
1290 :
1291 163 : if (len != to_write) {
1292 : return GF_IP_NETWORK_FAILURE;
1293 : }
1294 : }
1295 : return GF_OK;
1296 : }
1297 :
1298 :
1299 : #endif /* GPAC_HAS_SSL */
1300 :
1301 :
1302 153 : static Bool gf_dm_is_local(GF_DownloadManager *dm, const char *url)
1303 : {
1304 153 : if (!strnicmp(url, "file://", 7)) return GF_TRUE;
1305 153 : if (!strstr(url, "://")) return GF_TRUE;
1306 : return GF_FALSE;
1307 : }
1308 :
1309 153 : static Bool gf_dm_can_handle_url(GF_DownloadManager *dm, const char *url)
1310 : {
1311 153 : if (!strnicmp(url, "http://", 7)) return GF_TRUE;
1312 : #ifdef GPAC_HAS_SSL
1313 27 : if (!strnicmp(url, "https://", 8)) return GF_TRUE;
1314 : #endif
1315 : return GF_FALSE;
1316 : }
1317 :
1318 : /*!
1319 : * Finds an existing entry in the cache for a given URL
1320 : \param sess The session configured with the URL
1321 : \param NULL if none found, the DownloadedCacheEntry otherwise
1322 : */
1323 808 : DownloadedCacheEntry gf_dm_find_cached_entry_by_url(GF_DownloadSession * sess)
1324 : {
1325 : u32 i, count;
1326 : assert( sess && sess->dm && sess->dm->cache_entries );
1327 808 : gf_mx_p( sess->dm->cache_mx );
1328 808 : count = gf_list_count(sess->dm->cache_entries);
1329 3265 : for (i = 0 ; i < count; i++) {
1330 : const char * url;
1331 2601 : DownloadedCacheEntry e = (DownloadedCacheEntry)gf_list_get(sess->dm->cache_entries, i);
1332 : assert(e);
1333 2601 : url = gf_cache_get_url(e);
1334 : assert( url );
1335 2601 : if (strcmp(url, sess->orig_url)) continue;
1336 :
1337 179 : if (! sess->is_range_continuation) {
1338 179 : if (sess->range_start != gf_cache_get_start_range(e)) continue;
1339 149 : if (sess->range_end != gf_cache_get_end_range(e)) continue;
1340 : }
1341 : /*OK that's ours*/
1342 144 : gf_mx_v( sess->dm->cache_mx );
1343 144 : return e;
1344 : }
1345 664 : gf_mx_v( sess->dm->cache_mx );
1346 664 : return NULL;
1347 : }
1348 :
1349 : /**
1350 : * Creates a new cache entry
1351 : */
1352 : DownloadedCacheEntry gf_cache_create_entry( GF_DownloadManager * dm, const char * cache_directory, const char * url, u64 start_range, u64 end_range, Bool mem_storage, GF_Mutex *mx);
1353 :
1354 : /*!
1355 : * Removes a session for a DownloadedCacheEntry
1356 : \param entry The entry
1357 : \param sess The session to remove
1358 : \param the number of sessions left in the cached entry, -1 if one of the parameters is wrong
1359 : */
1360 : s32 gf_cache_remove_session_from_cache_entry(DownloadedCacheEntry entry, GF_DownloadSession * sess);
1361 :
1362 : Bool gf_cache_set_mime(const DownloadedCacheEntry entry, const char *mime);
1363 : Bool gf_cache_set_range(const DownloadedCacheEntry entry, u64 size, u64 start_range, u64 end_range);
1364 : Bool gf_cache_set_content(const DownloadedCacheEntry entry, GF_Blob *blob, Bool copy, GF_Mutex *mx);
1365 : Bool gf_cache_set_headers(const DownloadedCacheEntry entry, const char *headers);
1366 : Bool gf_cache_set_downtime(const DownloadedCacheEntry entry, u32 download_time_ms);
1367 :
1368 :
1369 : /**
1370 : * Removes a cache entry from cache and performs a cleanup if possible.
1371 : * If the cache entry is marked for deletion and has no sessions associated with it, it will be
1372 : * removed (so some modules using a streaming like cache will still work).
1373 : */
1374 1076 : static void gf_dm_remove_cache_entry_from_session(GF_DownloadSession * sess) {
1375 1076 : if (sess && sess->cache_entry) {
1376 804 : gf_cache_remove_session_from_cache_entry(sess->cache_entry, sess);
1377 804 : if (sess->dm
1378 : /*JLF - not sure what the rationale of this test is, and it prevents cleanup of cache entry
1379 : which then results to crash when restarting the session (entry->writeFilePtr is not set back to NULL)*/
1380 804 : && gf_cache_entry_is_delete_files_when_deleted(sess->cache_entry)
1381 :
1382 441 : && (0 == gf_cache_get_sessions_count_for_cache_entry(sess->cache_entry)))
1383 : {
1384 : u32 i, count;
1385 441 : gf_mx_p( sess->dm->cache_mx );
1386 441 : count = gf_list_count( sess->dm->cache_entries );
1387 1931 : for (i = 0; i < count; i++) {
1388 1931 : DownloadedCacheEntry ex = (DownloadedCacheEntry)gf_list_get(sess->dm->cache_entries, i);
1389 1931 : if (ex == sess->cache_entry) {
1390 441 : gf_list_rem(sess->dm->cache_entries, i);
1391 441 : gf_cache_delete_entry( sess->cache_entry );
1392 441 : sess->cache_entry = NULL;
1393 441 : break;
1394 : }
1395 : }
1396 441 : gf_mx_v( sess->dm->cache_mx );
1397 : }
1398 : }
1399 1076 : }
1400 :
1401 : /*!
1402 : * Adds a session to a DownloadedCacheEntry.
1403 : * implemented in cache.c
1404 : \param entry The entry
1405 : \param sess The session to add
1406 : \param the number of sessions in the cached entry, -1 if one of the parameters is wrong
1407 : */
1408 : s32 gf_cache_add_session_to_cache_entry(DownloadedCacheEntry entry, GF_DownloadSession * sess);
1409 : Bool gf_cache_entry_persistent(const DownloadedCacheEntry entry);
1410 : void gf_cache_entry_set_persistent(const DownloadedCacheEntry entry);
1411 :
1412 : static void gf_dm_sess_notify_state(GF_DownloadSession *sess, GF_NetIOStatus dnload_status, GF_Err error);
1413 :
1414 852 : static void gf_dm_configure_cache(GF_DownloadSession *sess)
1415 : {
1416 : DownloadedCacheEntry entry;
1417 852 : GF_LOG(GF_LOG_DEBUG, GF_LOG_CACHE, ("[Downloader] gf_dm_configure_cache(%p), cached=%s URL=%s\n", sess, (sess->flags & GF_NETIO_SESSION_NOT_CACHED) ? "no" : "yes", sess->orig_url ));
1418 852 : gf_dm_remove_cache_entry_from_session(sess);
1419 : //session is not cached and we don't cache the first URL
1420 852 : if ((sess->flags & GF_NETIO_SESSION_NOT_CACHED) && !(sess->flags & GF_NETIO_SESSION_KEEP_FIRST_CACHE)) {
1421 44 : sess->reused_cache_entry = GF_FALSE;
1422 44 : if (sess->cache_entry)
1423 0 : gf_cache_close_write_cache(sess->cache_entry, sess, GF_FALSE);
1424 :
1425 44 : sess->cache_entry = NULL;
1426 : } else {
1427 : Bool found = GF_FALSE;
1428 : u32 i, count;
1429 808 : entry = gf_dm_find_cached_entry_by_url(sess);
1430 808 : if (!entry) {
1431 664 : if (sess->local_cache_only) {
1432 4 : sess->cache_entry = NULL;
1433 4 : sess->last_error = GF_URL_ERROR;
1434 4 : return;
1435 : }
1436 : /* We found the existing session */
1437 660 : if (sess->cache_entry) {
1438 : Bool delete_cache = GF_TRUE;
1439 :
1440 106 : if (sess->flags & GF_NETIO_SESSION_KEEP_CACHE) {
1441 : delete_cache = GF_FALSE;
1442 : }
1443 106 : if (gf_cache_entry_persistent(sess->cache_entry))
1444 : delete_cache = GF_FALSE;
1445 :
1446 : /*! indicate we can destroy file upon destruction, except if disabled at session level*/
1447 26 : if (delete_cache)
1448 11 : gf_cache_entry_set_delete_files_when_deleted(sess->cache_entry);
1449 :
1450 106 : if (!gf_cache_entry_persistent(sess->cache_entry) && !gf_cache_get_sessions_count_for_cache_entry(sess->cache_entry)) {
1451 26 : gf_mx_p( sess->dm->cache_mx );
1452 : /* No session attached anymore... we can delete it */
1453 26 : gf_list_del_item(sess->dm->cache_entries, sess->cache_entry);
1454 26 : gf_mx_v( sess->dm->cache_mx );
1455 26 : gf_cache_delete_entry(sess->cache_entry);
1456 : }
1457 106 : sess->cache_entry = NULL;
1458 : }
1459 660 : entry = gf_cache_create_entry(sess->dm, sess->dm->cache_directory, sess->orig_url, sess->range_start, sess->range_end, (sess->flags&GF_NETIO_SESSION_MEMORY_CACHE) ? GF_TRUE : GF_FALSE, sess->dm->cache_mx);
1460 660 : gf_mx_p( sess->dm->cache_mx );
1461 660 : gf_list_add(sess->dm->cache_entries, entry);
1462 660 : gf_mx_v( sess->dm->cache_mx );
1463 660 : sess->is_range_continuation = GF_FALSE;
1464 : }
1465 : assert( entry );
1466 804 : sess->cache_entry = entry;
1467 804 : sess->reused_cache_entry = gf_cache_is_in_progress(entry);
1468 804 : count = gf_list_count(sess->dm->sessions);
1469 2815 : for (i=0; i<count; i++) {
1470 2011 : GF_DownloadSession *a_sess = (GF_DownloadSession*)gf_list_get(sess->dm->sessions, i);
1471 : assert(a_sess);
1472 2011 : if (a_sess==sess) continue;
1473 1228 : if (a_sess->cache_entry==entry) {
1474 : found = GF_TRUE;
1475 : break;
1476 : }
1477 : }
1478 804 : if (!found) {
1479 804 : sess->reused_cache_entry = GF_FALSE;
1480 804 : if (sess->cache_entry)
1481 804 : gf_cache_close_write_cache(sess->cache_entry, sess, GF_FALSE);
1482 : }
1483 804 : gf_cache_add_session_to_cache_entry(sess->cache_entry, sess);
1484 804 : if (sess->needs_range)
1485 55 : gf_cache_set_range(sess->cache_entry, 0, sess->range_start, sess->range_end);
1486 804 : GF_LOG(GF_LOG_INFO, GF_LOG_HTTP, ("[CACHE] Cache setup to %p %s\n", sess, gf_cache_get_cache_filename(sess->cache_entry)));
1487 :
1488 804 : if (sess->cache_entry) {
1489 804 : if (sess->flags & GF_NETIO_SESSION_KEEP_FIRST_CACHE) {
1490 91 : sess->flags &= ~GF_NETIO_SESSION_KEEP_FIRST_CACHE;
1491 91 : gf_cache_entry_set_persistent(sess->cache_entry);
1492 : }
1493 804 : if ((sess->flags & GF_NETIO_SESSION_MEMORY_CACHE) && (sess->flags & GF_NETIO_SESSION_KEEP_CACHE) ) {
1494 123 : gf_cache_entry_set_persistent(sess->cache_entry);
1495 : }
1496 : }
1497 :
1498 804 : if ( (sess->allow_direct_reuse || sess->dm->allow_offline_cache) && !gf_cache_check_if_cache_file_is_corrupted(sess->cache_entry)
1499 : ) {
1500 18 : sess->from_cache_only = GF_TRUE;
1501 18 : sess->connect_time = 0;
1502 18 : sess->status = GF_NETIO_CONNECTED;
1503 18 : GF_LOG(GF_LOG_DEBUG, GF_LOG_HTTP, ("[HTTP] using existing cache entry\n"));
1504 18 : gf_dm_sess_notify_state(sess, GF_NETIO_CONNECTED, GF_OK);
1505 : }
1506 : }
1507 : }
1508 :
1509 520 : void gf_dm_delete_cached_file_entry(const GF_DownloadManager * dm, const char * url)
1510 : {
1511 : GF_Err e;
1512 : u32 count, i;
1513 : char * realURL;
1514 : GF_URL_Info info;
1515 520 : if (!url || !dm)
1516 520 : return;
1517 520 : gf_mx_p( dm->cache_mx );
1518 520 : gf_dm_url_info_init(&info);
1519 520 : e = gf_dm_get_url_info(url, &info, NULL);
1520 520 : if (e != GF_OK) {
1521 1 : gf_mx_v( dm->cache_mx );
1522 1 : gf_dm_url_info_del(&info);
1523 1 : return;
1524 : }
1525 519 : realURL = gf_strdup(info.canonicalRepresentation);
1526 519 : gf_dm_url_info_del(&info);
1527 : assert( realURL );
1528 519 : count = gf_list_count(dm->cache_entries);
1529 2162 : for (i = 0 ; i < count; i++) {
1530 : const char * e_url;
1531 2162 : DownloadedCacheEntry cache_ent = (DownloadedCacheEntry)gf_list_get(dm->cache_entries, i);
1532 : assert(cache_ent);
1533 2162 : e_url = gf_cache_get_url(cache_ent);
1534 : assert( e_url );
1535 2162 : if (!strcmp(e_url, realURL)) {
1536 : /* We found the existing session */
1537 519 : gf_cache_entry_set_delete_files_when_deleted(cache_ent);
1538 519 : if (0 == gf_cache_get_sessions_count_for_cache_entry( cache_ent )) {
1539 : /* No session attached anymore... we can delete it */
1540 6 : gf_list_rem(dm->cache_entries, i);
1541 6 : gf_cache_delete_entry(cache_ent);
1542 : }
1543 : /* If deleted or not, we don't search further */
1544 519 : gf_mx_v( dm->cache_mx );
1545 519 : gf_free(realURL);
1546 519 : return;
1547 : }
1548 : }
1549 : /* If we are heren it means we did not found this URL in cache */
1550 0 : gf_mx_v( dm->cache_mx );
1551 0 : gf_free(realURL);
1552 0 : GF_LOG(GF_LOG_DEBUG, GF_LOG_HTTP, ("[CACHE] Cannot find URL %s, cache file won't be deleted.\n", url));
1553 : }
1554 :
1555 : GF_EXPORT
1556 520 : void gf_dm_delete_cached_file_entry_session(const GF_DownloadSession * sess, const char * url) {
1557 520 : if (sess && sess->dm && url) {
1558 520 : GF_LOG(GF_LOG_INFO, GF_LOG_HTTP, ("[CACHE] Requesting deletion for %s\n", url));
1559 520 : gf_dm_delete_cached_file_entry(sess->dm, url);
1560 520 : if (sess->local_cache_only && sess->dm->local_cache_url_provider_cbk)
1561 14 : sess->dm->local_cache_url_provider_cbk(sess->dm->lc_udta, (char *) url, GF_TRUE);
1562 : }
1563 520 : }
1564 :
1565 1240 : void gf_dm_sess_set_header(GF_DownloadSession *sess, const char *name, const char *value)
1566 : {
1567 : GF_HTTPHeader *hdr;
1568 1240 : if (!sess) return;
1569 :
1570 : #ifdef GPAC_HAS_HTTP2
1571 : if (sess->h2_sess || sess->h2_upgrade_settings) {
1572 : if (!stricmp(name, "Transfer-Encoding"))
1573 : return;
1574 : if (!stricmp(name, "Connection")) return;
1575 : if (!stricmp(name, "Keep-Alive")) return;
1576 : }
1577 : #endif
1578 :
1579 1240 : GF_SAFEALLOC(hdr, GF_HTTPHeader)
1580 1240 : if (hdr) {
1581 1240 : hdr->name = gf_strdup(name);
1582 1240 : hdr->value = gf_strdup(value);
1583 1240 : gf_list_add(sess->headers, hdr);
1584 : }
1585 : }
1586 :
1587 213 : GF_Err gf_dm_sess_send_reply(GF_DownloadSession *sess, u32 reply_code, const char *response_body, Bool no_body)
1588 : {
1589 : u32 i, count;
1590 : GF_Err e;
1591 : char szFmt[50];
1592 213 : char *rsp_buf = NULL;
1593 213 : if (!sess || !sess->server_mode) return GF_BAD_PARAM;
1594 :
1595 213 : count = gf_list_count(sess->headers);
1596 :
1597 : #ifdef GPAC_HAS_HTTP2
1598 : if (sess->h2_upgrade_settings) {
1599 : u32 len;
1600 : assert(!sess->h2_sess);
1601 : gf_dynstrcat(&rsp_buf, "HTTP/1.1 101 Switching Protocols\r\n"
1602 : "Connection: Upgrade\r\n"
1603 : "Upgrade: h2c\r\n\r\n", NULL);
1604 :
1605 :
1606 : len = (u32) strlen(rsp_buf);
1607 : e = gf_sk_send(sess->sock, rsp_buf, len);
1608 : gf_free(rsp_buf);
1609 : rsp_buf = NULL;
1610 :
1611 : h2_initialize_session(sess);
1612 : }
1613 :
1614 : if (sess->h2_sess) {
1615 : nghttp2_nv *hdrs;
1616 :
1617 : if (response_body) {
1618 : no_body = GF_FALSE;
1619 : sess->h2_send_data = (u8 *) response_body;
1620 : sess->h2_send_data_len = (u32) strlen(response_body);
1621 : sess->h2_is_eos = 1;
1622 : } else if (!no_body) {
1623 : switch (reply_code) {
1624 : case 200:
1625 : case 206:
1626 : no_body = GF_FALSE;
1627 : sess->h2_is_eos = 0;
1628 : break;
1629 : default:
1630 : no_body = GF_TRUE;
1631 : break;
1632 : }
1633 : }
1634 :
1635 : hdrs = gf_malloc(sizeof(nghttp2_nv) * (count + 1) );
1636 :
1637 : sprintf(szFmt, "%d", reply_code);
1638 : NV_HDR(hdrs[0], ":status", szFmt);
1639 : GF_LOG(GF_LOG_INFO, GF_LOG_HTTP, ("[HTTP/2] send reply for stream_id %d (body %d) headers:\n:status: %s\n", sess->h2_stream_id, !no_body, szFmt));
1640 : for (i=0; i<count; i++) {
1641 : GF_HTTPHeader *hdr = gf_list_get(sess->headers, i);
1642 : NV_HDR(hdrs[i+1], hdr->name, hdr->value)
1643 : GF_LOG(GF_LOG_DEBUG, GF_LOG_HTTP, ("%s: %s\n", hdr->name, hdr->value));
1644 : }
1645 :
1646 :
1647 : gf_mx_p(sess->mx);
1648 :
1649 : int rv = nghttp2_submit_response(sess->h2_sess->ng_sess, sess->h2_stream_id, hdrs, count+1, no_body ? NULL : &sess->data_io);
1650 :
1651 : gf_free(hdrs);
1652 :
1653 : if (rv != 0) {
1654 : gf_mx_v(sess->mx);
1655 : GF_LOG(GF_LOG_ERROR, GF_LOG_HTTP, ("[HTTP/2] Failed to submit reply: %s\n", nghttp2_strerror(rv)));
1656 : return GF_SERVICE_ERROR;
1657 : }
1658 : h2_session_send(sess);
1659 : //in case we have a body already setup with this reply
1660 : h2_flush_send(sess);
1661 :
1662 : gf_mx_v(sess->mx);
1663 :
1664 : //h2_stream_id may still be 0 at this point (typically reply to PUT/POST)
1665 : return GF_OK;
1666 : }
1667 : #endif
1668 :
1669 :
1670 :
1671 : sprintf(szFmt, "HTTP/1.1 %d ", reply_code);
1672 213 : gf_dynstrcat(&rsp_buf, szFmt, NULL);
1673 213 : switch (reply_code) {
1674 0 : case 400: gf_dynstrcat(&rsp_buf, "Bad Request", NULL); break;
1675 0 : case 403: gf_dynstrcat(&rsp_buf, "Forbidden", NULL); break;
1676 0 : case 405: gf_dynstrcat(&rsp_buf, "Not Allowed", NULL); break;
1677 0 : case 416: gf_dynstrcat(&rsp_buf, "Requested Range Not Satisfiable", NULL); break;
1678 0 : case 411: gf_dynstrcat(&rsp_buf, "Length Required", NULL); break;
1679 0 : case 404: gf_dynstrcat(&rsp_buf, "Not Found", NULL); break;
1680 0 : case 501: gf_dynstrcat(&rsp_buf, "Not Implemented", NULL); break;
1681 0 : case 500: gf_dynstrcat(&rsp_buf, "Internal Server Error", NULL); break;
1682 0 : case 304: gf_dynstrcat(&rsp_buf, "Not Modified", NULL); break;
1683 0 : case 204: gf_dynstrcat(&rsp_buf, "No Content", NULL); break;
1684 30 : case 206: gf_dynstrcat(&rsp_buf, "Partial Content", NULL); break;
1685 174 : case 200: gf_dynstrcat(&rsp_buf, "OK", NULL); break;
1686 9 : case 201: gf_dynstrcat(&rsp_buf, "Created", NULL); break;
1687 0 : default:
1688 0 : gf_dynstrcat(&rsp_buf, "OK", NULL); break;
1689 : }
1690 213 : gf_dynstrcat(&rsp_buf, "\r\n", NULL);
1691 213 : if (!rsp_buf) return GF_OUT_OF_MEM;
1692 :
1693 1240 : for (i=0; i<count; i++) {
1694 1240 : GF_HTTPHeader *hdr = gf_list_get(sess->headers, i);
1695 1240 : gf_dynstrcat(&rsp_buf, hdr->name, NULL);
1696 1240 : gf_dynstrcat(&rsp_buf, ": ", NULL);
1697 1240 : gf_dynstrcat(&rsp_buf, hdr->value, NULL);
1698 1240 : gf_dynstrcat(&rsp_buf, "\r\n", NULL);
1699 : }
1700 213 : gf_dynstrcat(&rsp_buf, "\r\n", NULL);
1701 213 : if (!rsp_buf) return GF_OUT_OF_MEM;
1702 :
1703 213 : GF_LOG(GF_LOG_INFO, GF_LOG_HTTP, ("[HTTP] send reply for %s: %s\n", sess->orig_url, rsp_buf));
1704 :
1705 213 : if (response_body) {
1706 2 : gf_dynstrcat(&rsp_buf, response_body, NULL);
1707 2 : if (!rsp_buf) return GF_OUT_OF_MEM;
1708 : }
1709 :
1710 213 : count = (u32) strlen(rsp_buf);
1711 : #ifdef GPAC_HAS_SSL
1712 213 : if (sess->ssl) {
1713 1 : e = gf_ssl_write(sess->ssl, rsp_buf, count);
1714 : } else
1715 : #endif
1716 212 : e = gf_sk_send(sess->sock, rsp_buf, count);
1717 :
1718 213 : gf_free(rsp_buf);
1719 213 : return e;
1720 : }
1721 :
1722 4194 : void gf_dm_sess_clear_headers(GF_DownloadSession *sess)
1723 : {
1724 23348 : while (gf_list_count(sess->headers)) {
1725 14960 : GF_HTTPHeader *hdr = (GF_HTTPHeader*)gf_list_last(sess->headers);
1726 14960 : gf_list_rem_last(sess->headers);
1727 14960 : gf_free(hdr->name);
1728 14960 : gf_free(hdr->value);
1729 14960 : gf_free(hdr);
1730 : }
1731 4194 : if (sess->mime_type) {
1732 2 : gf_free(sess->mime_type);
1733 2 : sess->mime_type = NULL;
1734 : }
1735 4194 : }
1736 :
1737 : typedef enum
1738 : {
1739 : HTTP_NO_CLOSE=0,
1740 : HTTP_CLOSE,
1741 : HTTP_RESET_CONN,
1742 : } HTTPCloseType;
1743 :
1744 1073 : static void gf_dm_disconnect(GF_DownloadSession *sess, HTTPCloseType close_type)
1745 : {
1746 : assert( sess );
1747 1073 : if (sess->connection_close) close_type = HTTP_RESET_CONN;
1748 1073 : sess->connection_close = GF_FALSE;
1749 1073 : sess->remaining_data_size = 0;
1750 :
1751 1073 : if (sess->status >= GF_NETIO_DISCONNECTED) {
1752 176 : if (close_type && sess->use_cache_file && sess->cache_entry) {
1753 122 : gf_cache_close_write_cache(sess->cache_entry, sess, GF_FALSE);
1754 : }
1755 : return;
1756 : }
1757 897 : GF_LOG(GF_LOG_DEBUG, GF_LOG_CORE, ("[Downloader] gf_dm_disconnect(%p)\n", sess ));
1758 :
1759 897 : gf_mx_p(sess->mx);
1760 :
1761 897 : if (!sess->server_mode) {
1762 863 : Bool do_close = (close_type || !(sess->flags & GF_NETIO_SESSION_PERSISTENT)) ? GF_TRUE : GF_FALSE;
1763 : #ifdef GPAC_HAS_HTTP2
1764 : if (sess->h2_sess) {
1765 : do_close = (close_type==HTTP_RESET_CONN) ? GF_TRUE : GF_FALSE;
1766 : }
1767 : #endif
1768 :
1769 :
1770 : if (do_close) {
1771 : #ifdef GPAC_HAS_HTTP2
1772 : if (sess->h2_sess) {
1773 : sess->h2_sess->do_shutdown = GF_TRUE;
1774 : h2_detach_session(sess->h2_sess, sess);
1775 : }
1776 : #endif
1777 :
1778 : #ifdef GPAC_HAS_SSL
1779 79 : if (sess->ssl) {
1780 7 : SSL_shutdown(sess->ssl);
1781 7 : SSL_free(sess->ssl);
1782 7 : sess->ssl = NULL;
1783 : }
1784 : #endif
1785 79 : if (sess->sock) {
1786 : GF_Socket * sx = sess->sock;
1787 57 : sess->sock = NULL;
1788 57 : gf_sk_del(sx);
1789 : }
1790 : }
1791 :
1792 863 : if (close_type && sess->use_cache_file && sess->cache_entry) {
1793 56 : gf_cache_close_write_cache(sess->cache_entry, sess, GF_FALSE);
1794 : }
1795 : }
1796 :
1797 897 : sess->status = GF_NETIO_DISCONNECTED;
1798 897 : if (sess->num_retry) sess->num_retry--;
1799 :
1800 897 : gf_mx_v(sess->mx);
1801 : }
1802 :
1803 : GF_EXPORT
1804 206 : void gf_dm_sess_del(GF_DownloadSession *sess)
1805 : {
1806 206 : if (!sess)
1807 : return;
1808 :
1809 204 : GF_LOG(GF_LOG_DEBUG, GF_LOG_HTTP, ("[Downloader] Destroy session URL %s\n", sess->orig_url));
1810 : /*self-destruction, let the download manager destroy us*/
1811 204 : if ((sess->th || sess->ftask) && sess->in_callback) {
1812 0 : sess->destroy = GF_TRUE;
1813 0 : return;
1814 : }
1815 204 : gf_dm_disconnect(sess, HTTP_CLOSE);
1816 204 : gf_dm_sess_clear_headers(sess);
1817 :
1818 : /*if threaded wait for thread exit*/
1819 204 : if (sess->th) {
1820 0 : while (!(sess->flags & GF_DOWNLOAD_SESSION_THREAD_DEAD))
1821 0 : gf_sleep(1);
1822 0 : gf_th_stop(sess->th);
1823 0 : gf_th_del(sess->th);
1824 0 : sess->th = NULL;
1825 : }
1826 :
1827 204 : if (sess->dm) {
1828 157 : gf_mx_p(sess->dm->cache_mx);
1829 157 : gf_list_del_item(sess->dm->sessions, sess);
1830 157 : gf_mx_v(sess->dm->cache_mx);
1831 : }
1832 :
1833 204 : gf_dm_remove_cache_entry_from_session(sess);
1834 204 : sess->cache_entry = NULL;
1835 204 : if (sess->orig_url) gf_free(sess->orig_url);
1836 204 : if (sess->orig_url_before_redirect) gf_free(sess->orig_url_before_redirect);
1837 204 : if (sess->server_name) gf_free(sess->server_name);
1838 204 : sess->server_name = NULL;
1839 204 : if (sess->remote_path) gf_free(sess->remote_path);
1840 : /* Credentials are stored into the sess->dm */
1841 204 : if (sess->creds) sess->creds = NULL;
1842 204 : if (sess->init_data) gf_free(sess->init_data);
1843 204 : if (sess->remaining_data) gf_free(sess->remaining_data);
1844 :
1845 204 : sess->orig_url = sess->server_name = sess->remote_path;
1846 204 : sess->creds = NULL;
1847 :
1848 : #ifdef GPAC_HAS_HTTP2
1849 : if (sess->h2_sess) {
1850 : gf_mx_p(sess->mx);
1851 : h2_detach_session(sess->h2_sess, sess);
1852 : gf_mx_v(sess->mx);
1853 : }
1854 :
1855 : if (sess->h2_upgrade_settings)
1856 : gf_free(sess->h2_upgrade_settings);
1857 : #endif
1858 :
1859 :
1860 : #ifdef GPAC_HAS_SSL
1861 : //in server mode SSL context is managed by caller
1862 204 : if (sess->ssl) {
1863 23 : GF_LOG(GF_LOG_DEBUG, GF_LOG_HTTP, ("[Downloader] shut down SSL context\n"));
1864 23 : SSL_shutdown(sess->ssl);
1865 23 : SSL_free(sess->ssl);
1866 23 : sess->ssl = NULL;
1867 : }
1868 : #endif
1869 204 : if (sess->sock) {
1870 129 : GF_LOG(GF_LOG_DEBUG, GF_LOG_HTTP, ("[Downloader] closing socket\n"));
1871 129 : gf_sk_del(sess->sock);
1872 : }
1873 204 : gf_list_del(sess->headers);
1874 :
1875 204 : gf_mx_del(sess->mx);
1876 :
1877 :
1878 204 : if (sess->ftask) {
1879 1 : sess->ftask->sess = NULL;
1880 1 : sess->ftask = NULL;
1881 : }
1882 204 : gf_free(sess);
1883 : }
1884 :
1885 : void http_do_requests(GF_DownloadSession *sess);
1886 :
1887 1213 : static void gf_dm_sess_notify_state(GF_DownloadSession *sess, GF_NetIOStatus dnload_status, GF_Err error)
1888 : {
1889 1213 : if (sess->user_proc) {
1890 : GF_NETIO_Parameter par;
1891 128 : sess->in_callback = GF_TRUE;
1892 : memset(&par, 0, sizeof(GF_NETIO_Parameter));
1893 128 : par.msg_type = dnload_status;
1894 128 : par.error = error;
1895 128 : par.sess = sess;
1896 128 : par.reply = 200;
1897 128 : sess->user_proc(sess->usr_cbk, &par);
1898 128 : sess->in_callback = GF_FALSE;
1899 : }
1900 1213 : }
1901 :
1902 : static void gf_dm_sess_user_io(GF_DownloadSession *sess, GF_NETIO_Parameter *par)
1903 : {
1904 21877 : if (sess->user_proc) {
1905 930 : sess->in_callback = GF_TRUE;
1906 930 : par->sess = sess;
1907 930 : sess->user_proc(sess->usr_cbk, par);
1908 930 : sess->in_callback = GF_FALSE;
1909 : }
1910 : }
1911 :
1912 : #if 0 //unused
1913 : /*!
1914 : \brief is download manager thread dead?
1915 : *
1916 : *Indicates whether the thread has ended
1917 : \param sess the download session
1918 : */
1919 : Bool gf_dm_is_thread_dead(GF_DownloadSession *sess)
1920 : {
1921 : if (!sess) return GF_TRUE;
1922 : return (sess->flags & GF_DOWNLOAD_SESSION_THREAD_DEAD) ? GF_TRUE : GF_FALSE;
1923 : }
1924 : #endif
1925 :
1926 : GF_EXPORT
1927 1 : GF_Err gf_dm_sess_last_error(GF_DownloadSession *sess)
1928 : {
1929 1 : if (!sess)
1930 : return GF_BAD_PARAM;
1931 0 : return sess->last_error;
1932 : }
1933 :
1934 : GF_EXPORT
1935 4138 : void gf_dm_url_info_init(GF_URL_Info * info)
1936 : {
1937 : memset(info, 0, sizeof(GF_URL_Info));
1938 4138 : }
1939 :
1940 : GF_EXPORT
1941 2769 : void gf_dm_url_info_del(GF_URL_Info * info) {
1942 2769 : if (!info)
1943 : return;
1944 2769 : if (info->canonicalRepresentation)
1945 1368 : gf_free(info->canonicalRepresentation);
1946 2769 : if (info->password)
1947 0 : gf_free(info->password);
1948 2769 : if (info->userName)
1949 0 : gf_free(info->userName);
1950 2769 : if (info->remotePath)
1951 1398 : gf_free(info->remotePath);
1952 2769 : if (info->server_name)
1953 1398 : gf_free(info->server_name);
1954 2769 : gf_dm_url_info_init(info);
1955 : }
1956 :
1957 : /**
1958 : \param url The url to parse for protocol
1959 : \param info The info to fill
1960 : \param Returns the offset in url of the protocol found -1 if not found
1961 : */
1962 1399 : static s32 gf_dm_parse_protocol(const char * url, GF_URL_Info * info) {
1963 : assert(info);
1964 : assert(url);
1965 1399 : if (!strnicmp(url, "http://", 7)) {
1966 1134 : info->port = 80;
1967 1134 : info->protocol = "http://";
1968 : return 7;
1969 : }
1970 265 : else if (!strnicmp(url, "https://", 8)) {
1971 234 : info->port = 443;
1972 : #ifndef GPAC_HAS_SSL
1973 : return -1;
1974 : #endif
1975 234 : info->protocol = "https://";
1976 : return 8;
1977 : }
1978 31 : else if (!strnicmp(url, "ftp://", 6)) {
1979 0 : info->port = 21;
1980 0 : info->protocol = "ftp://";
1981 : return -1;
1982 : }
1983 : return -1;
1984 : }
1985 :
1986 : GF_EXPORT
1987 1369 : GF_Err gf_dm_get_url_info(const char * url, GF_URL_Info * info, const char * baseURL) {
1988 : char *tmp, *tmp_url, *current_pos, *urlConcatenateWithBaseURL, *ipv6;
1989 : char * copyOfUrl;
1990 : s32 proto_offset;
1991 1369 : gf_dm_url_info_del(info);
1992 : urlConcatenateWithBaseURL = NULL;
1993 1369 : proto_offset = gf_dm_parse_protocol(url, info);
1994 1369 : if (proto_offset > 0) {
1995 1338 : url += proto_offset;
1996 : } else {
1997 : /*relative URL*/
1998 31 : if (!strstr(url, "://")) {
1999 : u32 i;
2000 30 : info->protocol = "file://";
2001 30 : if (baseURL) {
2002 30 : urlConcatenateWithBaseURL = gf_url_concatenate(baseURL, url);
2003 : /*relative file path*/
2004 30 : if (!strstr(baseURL, "://")) {
2005 0 : info->canonicalRepresentation = urlConcatenateWithBaseURL;
2006 0 : return GF_OK;
2007 : }
2008 30 : proto_offset = gf_dm_parse_protocol(urlConcatenateWithBaseURL, info);
2009 : } else {
2010 : proto_offset = -1;
2011 : }
2012 :
2013 30 : if (proto_offset < 0) {
2014 : tmp = urlConcatenateWithBaseURL;
2015 : assert( ! info->remotePath );
2016 0 : info->remotePath = gf_url_percent_encode(tmp);
2017 0 : gf_free( urlConcatenateWithBaseURL );
2018 : urlConcatenateWithBaseURL = NULL;
2019 :
2020 0 : if (!info->remotePath) {
2021 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_HTTP, ("[Network] No supported protocol for url %s\n", url));
2022 0 : gf_dm_url_info_del(info);
2023 0 : return GF_BAD_PARAM;
2024 : }
2025 0 : for (i=0; i<strlen(info->remotePath); i++)
2026 0 : if (info->remotePath[i]=='\\') info->remotePath[i]='/';
2027 0 : info->canonicalRepresentation = (char*)gf_malloc(strlen(info->protocol) + strlen(info->remotePath) + 1);
2028 0 : strcpy(info->canonicalRepresentation, info->protocol);
2029 0 : strcat(info->canonicalRepresentation, info->remotePath);
2030 :
2031 0 : return GF_OK;
2032 : } else {
2033 : /* We continue the parsing as usual */
2034 30 : url = urlConcatenateWithBaseURL + proto_offset;
2035 : }
2036 : } else {
2037 1 : GF_LOG(GF_LOG_WARNING, GF_LOG_HTTP, ("[Network] No supported protocol for url %s\n", url));
2038 1 : gf_dm_url_info_del(info);
2039 1 : return GF_BAD_PARAM;
2040 : }
2041 : }
2042 : assert( proto_offset >= 0 );
2043 1368 : tmp = strchr(url, '/');
2044 : assert( !info->remotePath );
2045 1368 : info->remotePath = gf_url_percent_encode(tmp ? tmp : "/");
2046 1368 : if (tmp) {
2047 1368 : tmp[0] = 0;
2048 1368 : copyOfUrl = gf_strdup(url);
2049 1368 : tmp[0] = '/';
2050 : } else {
2051 0 : copyOfUrl = gf_strdup(url);
2052 : }
2053 : tmp_url = copyOfUrl;
2054 : current_pos = tmp_url;
2055 1368 : tmp = strrchr(tmp_url, '@');
2056 1368 : if (tmp) {
2057 0 : current_pos = tmp + 1;
2058 : assert( ! info->server_name );
2059 0 : info->server_name = gf_strdup(current_pos);
2060 0 : tmp[0] = 0;
2061 0 : tmp = strchr(tmp_url, ':');
2062 :
2063 0 : if (tmp) {
2064 0 : tmp[0] = 0;
2065 0 : info->password = gf_strdup(tmp+1);
2066 : }
2067 0 : info->userName = gf_strdup(tmp_url);
2068 : } else {
2069 : assert( ! info->server_name );
2070 1368 : info->server_name = gf_strdup(tmp_url);
2071 : }
2072 :
2073 : //scan for port number after IPv6 address ']' end char
2074 1368 : ipv6 = strrchr(current_pos, ']');
2075 1368 : tmp = strrchr(ipv6 ? ipv6 : current_pos, ':');
2076 :
2077 1368 : if (tmp) {
2078 978 : info->port = atoi(tmp+1);
2079 489 : tmp[0] = 0;
2080 489 : if (info->server_name) {
2081 489 : gf_free(info->server_name);
2082 : }
2083 489 : info->server_name = gf_strdup(current_pos);
2084 : }
2085 :
2086 : /* builds orig_url */
2087 : /* We dont't want orig_url to contain user/passwords for security reasons or mismatch in cache hit */
2088 : {
2089 : char port[8];
2090 1368 : snprintf(port, sizeof(port)-1, ":%d", info->port);
2091 1368 : info->canonicalRepresentation = (char*)gf_malloc(strlen(info->protocol)+strlen(info->server_name)+1+strlen(port)+strlen(info->remotePath));
2092 1368 : strcpy(info->canonicalRepresentation, info->protocol);
2093 1368 : strcat(info->canonicalRepresentation, info->server_name);
2094 1368 : if (info->port!=80)
2095 515 : strcat(info->canonicalRepresentation, port);
2096 1368 : strcat(info->canonicalRepresentation, info->remotePath);
2097 : }
2098 1368 : gf_free(copyOfUrl);
2099 1368 : if (urlConcatenateWithBaseURL)
2100 30 : gf_free(urlConcatenateWithBaseURL);
2101 : return GF_OK;
2102 : }
2103 :
2104 : char *gf_cache_get_forced_headers(const DownloadedCacheEntry entry);
2105 : u32 gf_cache_get_downtime(const DownloadedCacheEntry entry);
2106 : Bool gf_cache_is_done(const DownloadedCacheEntry entry);
2107 : Bool gf_cache_is_deleted(const DownloadedCacheEntry entry);
2108 :
2109 46 : static void gf_dm_sess_reload_cached_headers(GF_DownloadSession *sess)
2110 : {
2111 : char *hdrs;
2112 :
2113 46 : if (!sess || !sess->local_cache_only) return;
2114 :
2115 46 : hdrs = gf_cache_get_forced_headers(sess->cache_entry);
2116 :
2117 46 : gf_dm_sess_clear_headers(sess);
2118 149 : while (hdrs) {
2119 99 : char *sep2, *sepL = strstr(hdrs, "\r\n");
2120 99 : if (sepL) sepL[0] = 0;
2121 99 : sep2 = strchr(hdrs, ':');
2122 99 : if (sep2) {
2123 : GF_HTTPHeader *hdr;
2124 57 : GF_SAFEALLOC(hdr, GF_HTTPHeader);
2125 57 : if (!hdr) break;
2126 57 : sep2[0]=0;
2127 57 : hdr->name = gf_strdup(hdrs);
2128 57 : sep2[0]=':';
2129 57 : sep2++;
2130 57 : while (sep2[0]==' ') sep2++;
2131 57 : hdr->value = gf_strdup(sep2);
2132 57 : gf_list_add(sess->headers, hdr);
2133 : }
2134 99 : if (!sepL) break;
2135 57 : sepL[0] = '\r';
2136 57 : hdrs = sepL + 2;
2137 : }
2138 : }
2139 :
2140 : GF_EXPORT
2141 849 : GF_Err gf_dm_sess_setup_from_url(GF_DownloadSession *sess, const char *url, Bool allow_direct_reuse)
2142 : {
2143 : Bool socket_changed = GF_FALSE;
2144 : GF_URL_Info info;
2145 : Bool free_proto = GF_FALSE;
2146 : char *sep_frag=NULL;
2147 849 : if (!url)
2148 : return GF_BAD_PARAM;
2149 :
2150 849 : gf_dm_sess_clear_headers(sess);
2151 849 : sess->allow_direct_reuse = allow_direct_reuse;
2152 849 : gf_dm_url_info_init(&info);
2153 :
2154 849 : if (!sess->sock)
2155 : socket_changed = GF_TRUE;
2156 663 : else if (sess->status>GF_NETIO_DISCONNECTED)
2157 : socket_changed = GF_TRUE;
2158 :
2159 : assert(sess->status != GF_NETIO_WAIT_FOR_REPLY);
2160 : assert(sess->status != GF_NETIO_DATA_EXCHANGE);
2161 :
2162 : //strip fragment
2163 849 : sep_frag = strchr(url, '#');
2164 849 : if (sep_frag) sep_frag[0]=0;
2165 849 : sess->last_error = gf_dm_get_url_info(url, &info, sess->orig_url);
2166 849 : if (sess->last_error) {
2167 0 : if (sep_frag) sep_frag[0]='#';
2168 0 : return sess->last_error;
2169 : }
2170 :
2171 849 : if (!strstr(url, "://")) {
2172 : char c, *sep;
2173 30 : gf_dm_url_info_del(&info);
2174 30 : info.port = sess->port;
2175 30 : info.server_name = sess->server_name ? gf_strdup(sess->server_name) : NULL;
2176 30 : info.remotePath = gf_strdup(url);
2177 30 : sep = strstr(sess->orig_url_before_redirect, "://");
2178 : assert(sep);
2179 30 : c = sep[3];
2180 30 : sep[3] = 0;
2181 30 : info.protocol = gf_strdup(sess->orig_url_before_redirect);
2182 30 : sep[3] = c;
2183 : free_proto = GF_TRUE;
2184 : }
2185 :
2186 849 : if (sess->port != info.port) {
2187 : socket_changed = GF_TRUE;
2188 157 : sess->port = info.port;
2189 : }
2190 :
2191 849 : if (sess->from_cache_only) {
2192 : socket_changed = GF_TRUE;
2193 18 : sess->from_cache_only = GF_FALSE;
2194 18 : if (sess->cache_entry) {
2195 18 : gf_dm_remove_cache_entry_from_session(sess);
2196 18 : sess->cache_entry = NULL;
2197 : }
2198 : }
2199 :
2200 849 : if (!strcmp("http://", info.protocol) || !strcmp("https://", info.protocol)) {
2201 849 : if (sess->do_requests != http_do_requests) {
2202 157 : sess->do_requests = http_do_requests;
2203 : socket_changed = GF_TRUE;
2204 : }
2205 849 : if (!strcmp("https://", info.protocol)) {
2206 138 : if (!(sess->flags & GF_DOWNLOAD_SESSION_USE_SSL)) {
2207 28 : sess->flags |= GF_DOWNLOAD_SESSION_USE_SSL;
2208 : socket_changed = GF_TRUE;
2209 : }
2210 711 : } else if (sess->flags & GF_DOWNLOAD_SESSION_USE_SSL) {
2211 0 : sess->flags &= ~GF_DOWNLOAD_SESSION_USE_SSL;
2212 : socket_changed = GF_TRUE;
2213 : }
2214 : } else {
2215 0 : sess->do_requests = NULL;
2216 : }
2217 :
2218 849 : if (sess->server_name && info.server_name && !strcmp(sess->server_name, info.server_name)) {
2219 : } else {
2220 : socket_changed = GF_TRUE;
2221 161 : if (sess->server_name) gf_free(sess->server_name);
2222 161 : sess->server_name = info.server_name ? gf_strdup(info.server_name) : NULL;
2223 : }
2224 :
2225 849 : if (info.canonicalRepresentation) {
2226 819 : if (sess->orig_url) gf_free(sess->orig_url);
2227 819 : sess->orig_url = gf_strdup(info.canonicalRepresentation);
2228 : }
2229 :
2230 849 : if (!sess->orig_url_before_redirect)
2231 157 : sess->orig_url_before_redirect = gf_strdup(url);
2232 :
2233 849 : if (sess->remote_path) gf_free(sess->remote_path);
2234 849 : sess->remote_path = gf_strdup(info.remotePath);
2235 :
2236 849 : if (sess->status==GF_NETIO_STATE_ERROR)
2237 : socket_changed = GF_TRUE;
2238 :
2239 848 : if (!socket_changed && info.userName && !strcmp(info.userName, sess->creds->username)) {
2240 : } else {
2241 849 : sess->creds = NULL;
2242 849 : if (info.userName) {
2243 0 : if (! sess->dm) {
2244 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_HTTP, ("[HTTP] Did not found any download manager, credentials not supported\n"));
2245 : } else
2246 0 : sess->creds = gf_user_credentials_register(sess->dm, sess->server_name, info.userName, info.password, info.userName && info.password);
2247 : }
2248 : }
2249 849 : if (free_proto) gf_free((char *) info.protocol);
2250 849 : gf_dm_url_info_del(&info);
2251 849 : if (sep_frag) sep_frag[0]='#';
2252 :
2253 : #ifdef GPAC_HAS_HTTP2
2254 : if (sess->h2_sess) {
2255 : if (sess->h2_sess->do_shutdown)
2256 : socket_changed = GF_TRUE;
2257 : sess->h2_buf.size = 0;
2258 : sess->h2_buf.offset = 0;
2259 : }
2260 : #endif
2261 :
2262 849 : if (sess->sock && !socket_changed) {
2263 641 : sess->status = GF_NETIO_CONNECTED;
2264 641 : sess->num_retry = SESSION_RETRY_COUNT;
2265 641 : sess->needs_cache_reconfig = 1;
2266 : } else {
2267 :
2268 : #ifdef GPAC_HAS_HTTP2
2269 : if (sess->h2_sess) {
2270 : gf_mx_p(sess->mx);
2271 : h2_detach_session(sess->h2_sess, sess);
2272 : gf_mx_v(sess->mx);
2273 : }
2274 : #endif
2275 :
2276 208 : if (sess->sock) {
2277 22 : gf_sk_del(sess->sock);
2278 22 : sess->sock = NULL;
2279 : }
2280 208 : sess->status = GF_NETIO_SETUP;
2281 : #ifdef GPAC_HAS_SSL
2282 208 : if (sess->ssl) {
2283 1 : SSL_shutdown(sess->ssl);
2284 1 : SSL_free(sess->ssl);
2285 1 : sess->ssl = NULL;
2286 : }
2287 : #endif
2288 : }
2289 849 : sess->total_size = 0;
2290 849 : sess->bytes_done = 0;
2291 : //could be not-0 after a byte-range request using chunk transfer
2292 849 : sess->remaining_data_size = 0;
2293 :
2294 849 : sess->local_cache_only = GF_FALSE;
2295 849 : if (sess->dm && sess->dm->local_cache_url_provider_cbk) {
2296 45 : Bool res = sess->dm->local_cache_url_provider_cbk(sess->dm->lc_udta, (char *)url, GF_FALSE);
2297 45 : if (res == GF_TRUE) {
2298 35 : sess->local_cache_only = GF_TRUE;
2299 35 : gf_free(sess->orig_url);
2300 35 : sess->orig_url = gf_strdup(url);
2301 35 : sess->last_error = GF_OK;
2302 35 : sess->use_cache_file = GF_TRUE;
2303 35 : gf_dm_configure_cache(sess);
2304 35 : sess->bytes_done = 0;
2305 35 : if (sess->cache_entry && gf_cache_is_deleted(sess->cache_entry)) {
2306 0 : sess->status = GF_NETIO_DATA_TRANSFERED;
2307 0 : sess->last_error = GF_URL_REMOVED;
2308 : //return GF_OK;
2309 35 : } else if (! gf_cache_is_done(sess->cache_entry)) {
2310 4 : sess->total_size = 0;
2311 4 : sess->status = GF_NETIO_DATA_EXCHANGE;
2312 : } else {
2313 31 : sess->total_size = gf_cache_get_content_length(sess->cache_entry);
2314 31 : sess->bytes_done = sess->total_size;
2315 31 : sess->status = GF_NETIO_DATA_TRANSFERED;
2316 : }
2317 :
2318 35 : sess->total_time_since_req = gf_cache_get_downtime(sess->cache_entry);
2319 35 : if (sess->total_time_since_req)
2320 18 : sess->bytes_per_sec = (u32) ((1000 * (u64) sess->bytes_done) / sess->total_time_since_req);
2321 : else
2322 17 : sess->bytes_per_sec = 0;
2323 35 : gf_dm_sess_reload_cached_headers(sess);
2324 : }
2325 : }
2326 849 : if (sess->last_error)
2327 : return sess->last_error;
2328 845 : return gf_dm_sess_set_range(sess, 0, 0, GF_TRUE);
2329 : }
2330 :
2331 :
2332 0 : Bool gf_dm_session_do_task(GF_DownloadSession *sess)
2333 : {
2334 : Bool do_run = GF_TRUE;
2335 :
2336 0 : if (sess->destroy) {
2337 : do_run = GF_FALSE;
2338 : } else {
2339 0 : gf_mx_p(sess->mx);
2340 0 : if (sess->status >= GF_NETIO_DISCONNECTED) {
2341 : do_run = GF_FALSE;
2342 : } else {
2343 0 : if (sess->status < GF_NETIO_CONNECTED) {
2344 0 : gf_dm_connect(sess);
2345 : } else {
2346 0 : sess->do_requests(sess);
2347 : }
2348 : }
2349 0 : gf_mx_v(sess->mx);
2350 : }
2351 0 : if (do_run) return GF_TRUE;
2352 :
2353 : /*destroy all session but keep connection active*/
2354 0 : gf_dm_disconnect(sess, HTTP_NO_CLOSE);
2355 0 : sess->status = GF_NETIO_STATE_ERROR;
2356 0 : sess->last_error = GF_OK;
2357 0 : return GF_FALSE;
2358 : }
2359 :
2360 1 : Bool gf_dm_session_task(GF_FilterSession *fsess, void *callback, u32 *reschedule_ms)
2361 : {
2362 : GF_SessTask *task = callback;
2363 1 : GF_DownloadSession *sess = task->sess;
2364 1 : if (!sess) {
2365 1 : gf_free(task);
2366 1 : return GF_FALSE;
2367 : }
2368 0 : Bool ret = gf_dm_session_do_task(sess);
2369 0 : if (ret) {
2370 0 : *reschedule_ms = 1;
2371 0 : return GF_TRUE;
2372 : }
2373 : assert(sess->ftask);
2374 0 : gf_free(sess->ftask);
2375 0 : sess->ftask = NULL;
2376 0 : if (sess->destroy)
2377 0 : gf_dm_sess_del(sess);
2378 : return GF_FALSE;
2379 : }
2380 :
2381 0 : static u32 gf_dm_session_thread(void *par)
2382 : {
2383 : GF_DownloadSession *sess = (GF_DownloadSession *)par;
2384 0 : if (!sess) return 0;
2385 :
2386 0 : GF_LOG(GF_LOG_DEBUG, GF_LOG_CORE, ("[Downloader] Entering thread ID %d\n", gf_th_id() ));
2387 0 : sess->flags &= ~GF_DOWNLOAD_SESSION_THREAD_DEAD;
2388 0 : while (!sess->destroy) {
2389 0 : Bool ret = gf_dm_session_do_task(sess);
2390 0 : if (!ret) break;
2391 0 : gf_sleep(0);
2392 : }
2393 0 : sess->flags |= GF_DOWNLOAD_SESSION_THREAD_DEAD;
2394 0 : if (sess->destroy)
2395 0 : gf_dm_sess_del(sess);
2396 : return 1;
2397 : }
2398 :
2399 204 : static GF_DownloadSession *gf_dm_sess_new_internal(GF_DownloadManager * dm, const char *url, u32 dl_flags,
2400 : gf_dm_user_io user_io,
2401 : void *usr_cbk,
2402 : GF_Socket *server,
2403 : GF_Err *e)
2404 : {
2405 : GF_DownloadSession *sess;
2406 :
2407 204 : GF_SAFEALLOC(sess, GF_DownloadSession);
2408 204 : if (!sess) {
2409 : return NULL;
2410 : }
2411 204 : sess->headers = gf_list_new();
2412 204 : sess->flags = dl_flags;
2413 204 : if (sess->flags & GF_NETIO_SESSION_NOTIFY_DATA)
2414 1 : sess->force_data_write_callback = GF_TRUE;
2415 204 : sess->user_proc = user_io;
2416 204 : sess->usr_cbk = usr_cbk;
2417 204 : sess->creds = NULL;
2418 :
2419 204 : if (!gf_opts_get_key("core", "head-timeout")) {
2420 204 : sess->head_timeout = 5000;
2421 : } else {
2422 0 : sess->head_timeout = gf_opts_get_int("core", "head-timeout");
2423 : }
2424 :
2425 204 : sess->request_timeout = gf_opts_get_int("core", "req-timeout");
2426 204 : if (!sess->request_timeout) sess->request_timeout = 20000;
2427 :
2428 204 : sess->chunk_wnd_dur = gf_opts_get_int("core", "cte-rate-wnd") * 1000;
2429 204 : if (!sess->chunk_wnd_dur) sess->chunk_wnd_dur = 20000;
2430 :
2431 204 : sess->dm = dm;
2432 204 : if (server) {
2433 47 : sess->sock = server;
2434 47 : sess->flags = GF_NETIO_SESSION_NOT_THREADED;
2435 47 : sess->status = GF_NETIO_CONNECTED;
2436 47 : sess->server_mode = GF_TRUE;
2437 47 : sess->do_requests = http_do_requests;
2438 47 : if (e) *e = GF_OK;
2439 : return sess;
2440 : }
2441 :
2442 157 : if (!sess->head_timeout) sess->server_only_understand_get = GF_TRUE;
2443 157 : if (dm)
2444 157 : sess->disable_cache = dm->disable_cache;
2445 :
2446 157 : if (! (dl_flags & GF_NETIO_SESSION_NOT_THREADED)) {
2447 1 : sess->mx = gf_mx_new(url);
2448 1 : if (!sess->mx) {
2449 0 : gf_free(sess);
2450 0 : return NULL;
2451 : }
2452 : }
2453 :
2454 157 : *e = gf_dm_sess_setup_from_url(sess, url, GF_FALSE);
2455 157 : if (*e) {
2456 2 : GF_LOG(GF_LOG_ERROR, GF_LOG_HTTP, ("[Downloader] failed to create session for %s: %s\n", url, gf_error_to_string(*e)));
2457 2 : gf_dm_sess_del(sess);
2458 2 : return NULL;
2459 : }
2460 : assert( sess );
2461 155 : sess->num_retry = SESSION_RETRY_COUNT;
2462 : /*threaded session must be started with gf_dm_sess_process*/
2463 155 : return sess;
2464 : }
2465 :
2466 : GF_EXPORT
2467 47 : GF_DownloadSession *gf_dm_sess_new_server(GF_Socket *server,
2468 : void *ssl_sock_ctx,
2469 : gf_dm_user_io user_io,
2470 : void *usr_cbk,
2471 : GF_Err *e)
2472 : {
2473 : GF_DownloadSession *sess;
2474 :
2475 : #if defined(GPAC_HAS_HTTP2) && defined(GPAC_HAS_SSL)
2476 : Bool h2_negotiated = GF_FALSE;
2477 : if (ssl_sock_ctx) {
2478 : const unsigned char *alpn = NULL;
2479 : unsigned int alpnlen = 0;
2480 : SSL_get0_next_proto_negotiated(ssl_sock_ctx, &alpn, &alpnlen);
2481 : #if OPENSSL_VERSION_NUMBER >= 0x10002000L
2482 : if (alpn == NULL) {
2483 : SSL_get0_alpn_selected(ssl_sock_ctx, &alpn, &alpnlen);
2484 : }
2485 : #endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
2486 :
2487 : if (alpn && (alpnlen == 2) && !memcmp("h2", alpn, 2)) {
2488 : h2_negotiated = GF_TRUE;
2489 : }
2490 : }
2491 : #endif //GPAC_HAS_HTTP2 && GPAC_HAS_SSL
2492 :
2493 47 : sess = gf_dm_sess_new_internal(NULL, NULL, 0, user_io, usr_cbk, server, e);
2494 :
2495 : #ifdef GPAC_HAS_SSL
2496 47 : if (sess) {
2497 47 : sess->ssl = ssl_sock_ctx;
2498 :
2499 : #if defined(GPAC_HAS_HTTP2)
2500 : if (h2_negotiated) {
2501 : h2_initialize_session(sess);
2502 : }
2503 : #endif
2504 : }
2505 : #endif
2506 47 : return sess;
2507 : }
2508 :
2509 0 : GF_DownloadSession *gf_dm_sess_new_subsession(GF_DownloadSession *sess, u32 stream_id, void *usr_cbk, GF_Err *e)
2510 : {
2511 : #ifdef GPAC_HAS_HTTP2
2512 : GF_DownloadSession *sub_sess;
2513 : if (!sess->h2_sess || !stream_id) return NULL;
2514 : gf_mx_p(sess->mx);
2515 : sub_sess = gf_dm_sess_new_internal(NULL, NULL, 0, sess->user_proc, usr_cbk, sess->sock, e);
2516 : if (!sub_sess) {
2517 : gf_mx_v(sess->mx);
2518 : return NULL;
2519 : }
2520 : gf_list_add(sess->h2_sess->sessions, sub_sess);
2521 : #ifdef GPAC_HAS_SSL
2522 : sub_sess->ssl = sess->ssl;
2523 : #endif
2524 : sub_sess->h2_sess = sess->h2_sess;
2525 : if (sub_sess->mx) gf_mx_del(sub_sess->mx);
2526 : sub_sess->mx = sess->h2_sess->mx;
2527 : sub_sess->h2_stream_id = stream_id;
2528 : sub_sess->status = GF_NETIO_CONNECTED;
2529 : sub_sess->data_io.read_callback = h2_data_source_read_callback;
2530 : sub_sess->data_io.source.ptr = sub_sess;
2531 : gf_mx_v(sess->mx);
2532 : return sub_sess;
2533 : #else
2534 0 : return NULL;
2535 : #endif
2536 : }
2537 :
2538 0 : u32 gf_dm_sess_subsession_count(GF_DownloadSession *sess)
2539 : {
2540 : #ifdef GPAC_HAS_HTTP2
2541 : if (sess->h2_sess)
2542 : return gf_list_count(sess->h2_sess->sessions);
2543 : #endif
2544 0 : return 1;
2545 : }
2546 :
2547 :
2548 207 : void gf_dm_sess_server_reset(GF_DownloadSession *sess)
2549 : {
2550 207 : if (!sess->server_mode) return;
2551 :
2552 207 : gf_dm_sess_clear_headers(sess);
2553 207 : sess->total_size = sess->bytes_done = 0;
2554 207 : sess->chunk_bytes = 0;
2555 207 : sess->chunk_header_bytes = 0;
2556 207 : sess->chunked = GF_FALSE;
2557 207 : sess->status = GF_NETIO_CONNECTED;
2558 : }
2559 :
2560 :
2561 : GF_EXPORT
2562 157 : GF_DownloadSession *gf_dm_sess_new_simple(GF_DownloadManager * dm, const char *url, u32 dl_flags,
2563 : gf_dm_user_io user_io,
2564 : void *usr_cbk,
2565 : GF_Err *e)
2566 : {
2567 157 : return gf_dm_sess_new_internal(dm, url, dl_flags, user_io, usr_cbk, NULL, e);
2568 : }
2569 : GF_EXPORT
2570 153 : GF_DownloadSession *gf_dm_sess_new(GF_DownloadManager *dm, const char *url, u32 dl_flags,
2571 : gf_dm_user_io user_io,
2572 : void *usr_cbk,
2573 : GF_Err *e)
2574 : {
2575 : GF_DownloadSession *sess;
2576 153 : *e = GF_OK;
2577 153 : if (gf_dm_is_local(dm, url)) {
2578 0 : *e = GF_NOT_SUPPORTED;
2579 0 : return NULL;
2580 : }
2581 :
2582 153 : if (!gf_dm_can_handle_url(dm, url)) {
2583 0 : *e = GF_NOT_SUPPORTED;
2584 0 : return NULL;
2585 : }
2586 153 : sess = gf_dm_sess_new_simple(dm, url, dl_flags, user_io, usr_cbk, e);
2587 153 : if (sess && dm) {
2588 151 : sess->dm = dm;
2589 151 : gf_mx_p(dm->cache_mx);
2590 151 : gf_list_add(dm->sessions, sess);
2591 151 : gf_mx_v(dm->cache_mx);
2592 : }
2593 : return sess;
2594 : }
2595 :
2596 147426 : static GF_Err gf_dm_read_data(GF_DownloadSession *sess, char *data, u32 data_size, u32 *out_read)
2597 : {
2598 : GF_Err e;
2599 :
2600 147426 : if (sess->dm && sess->dm->simulate_no_connection) {
2601 0 : if (sess->sock) {
2602 0 : sess->status = GF_NETIO_DISCONNECTED;
2603 : }
2604 : return GF_IP_NETWORK_FAILURE;
2605 : }
2606 :
2607 : if (!sess)
2608 : return GF_BAD_PARAM;
2609 :
2610 147426 : gf_mx_p(sess->mx);
2611 147426 : if (!sess->sock) {
2612 0 : sess->status = GF_NETIO_DISCONNECTED;
2613 0 : gf_mx_v(sess->mx);
2614 0 : return GF_IP_CONNECTION_CLOSED;
2615 : }
2616 :
2617 147426 : *out_read = 0;
2618 :
2619 : #ifdef GPAC_HAS_SSL
2620 147426 : if (sess->ssl) {
2621 : s32 size;
2622 :
2623 : //receive on null buffer (select only, check if data available)
2624 14374 : e = gf_sk_receive(sess->sock, NULL, 0, NULL);
2625 : //empty and no pending bytes in SSL, network empty
2626 26422 : if ((e==GF_IP_NETWORK_EMPTY) &&
2627 : #if 1
2628 12048 : !SSL_pending(sess->ssl)
2629 : #else
2630 : //no support for SSL_has_pending in old libSSL and same result can be achieved with SSL_pending
2631 : !SSL_has_pending(sess->ssl)
2632 : #endif
2633 : ) {
2634 12045 : gf_mx_v(sess->mx);
2635 12045 : return e;
2636 : }
2637 2329 : size = SSL_read(sess->ssl, data, data_size);
2638 2329 : if (size < 0) {
2639 0 : int err = SSL_get_error(sess->ssl, size);
2640 0 : if (err==SSL_ERROR_SSL) {
2641 : /*
2642 : char msg[1024];
2643 : SSL_load_error_strings();
2644 : ERR_error_string_n(ERR_get_error(), msg, sizeof(msg));
2645 : GF_LOG(GF_LOG_ERROR, GF_LOG_HTTP, ("[SSL] Cannot read, error %s\n", msg));
2646 : */
2647 : e = GF_IO_ERR;
2648 : } else {
2649 0 : e = gf_sk_probe(sess->sock);
2650 : }
2651 2329 : } else if (!size)
2652 : e = GF_IP_NETWORK_EMPTY;
2653 : else {
2654 : e = GF_OK;
2655 2328 : data[size] = 0;
2656 2328 : *out_read = size;
2657 : }
2658 : } else
2659 : #endif
2660 :
2661 133052 : e = gf_sk_receive(sess->sock, data, data_size, out_read);
2662 :
2663 : #ifdef GPAC_HAS_HTTP2
2664 : if (sess->h2_sess) {
2665 : if (*out_read > 0) {
2666 : ssize_t read_len = nghttp2_session_mem_recv(sess->h2_sess->ng_sess, data, *out_read);
2667 : if(read_len < 0 ) {
2668 : GF_LOG(GF_LOG_ERROR, GF_LOG_HTTP, ("[HTTP/2] nghttp2_session_mem_recv error: %s\n", nghttp2_strerror((int) read_len)));
2669 : return GF_IO_ERR;
2670 : }
2671 : }
2672 : /* send pending frames - h2_sess may be NULL at this point if the connection was reset during processing of nghttp2_session_mem_recv
2673 : this typically happens if we have a refused stream
2674 : */
2675 : if (sess->h2_sess)
2676 : h2_session_send(sess);
2677 : }
2678 : #endif //GPAC_HAS_HTTP2
2679 :
2680 135381 : if (*out_read)
2681 9129 : sess->last_fetch_time = gf_sys_clock_high_res();
2682 :
2683 135381 : gf_mx_v(sess->mx);
2684 135381 : return e;
2685 : }
2686 :
2687 :
2688 : #ifdef GPAC_HAS_SSL
2689 :
2690 : #define LWR(x) ('A' <= (x) && (x) <= 'Z' ? (x) - 32 : (x))
2691 :
2692 66 : static Bool rfc2818_match(const char *pattern, const char *string)
2693 : {
2694 : char d;
2695 : u32 i=0, k=0;
2696 902 : while (1) {
2697 968 : char c = LWR(pattern[i]);
2698 968 : if (c == '\0') break;
2699 :
2700 938 : if (c=='*') {
2701 : /*remove *** patterns*/
2702 12 : while (c == '*') {
2703 6 : i++;
2704 6 : c = LWR(pattern[i]);
2705 : }
2706 : /*look for same c character*/
2707 : while (1) {
2708 282 : d = LWR(string[k]);
2709 144 : if (d == '\0') break;
2710 : /*matched c character, check following substrings*/
2711 144 : if ((d == c) && rfc2818_match (&pattern[i], &string[k]))
2712 : return GF_TRUE;
2713 138 : else if (d == '.')
2714 : return GF_FALSE;
2715 :
2716 138 : k++;
2717 : }
2718 0 : return (c == '\0') ? GF_TRUE : GF_FALSE;
2719 : } else {
2720 932 : if (c != LWR(string[k]))
2721 : return GF_FALSE;
2722 : }
2723 902 : i++;
2724 : k++;
2725 : }
2726 30 : return (string[k]=='\0') ? GF_TRUE : GF_FALSE;
2727 : }
2728 : #undef LWR
2729 :
2730 : #endif
2731 :
2732 161 : static void gf_dm_connect(GF_DownloadSession *sess)
2733 : {
2734 : GF_Err e;
2735 : u16 proxy_port = 0;
2736 : const char *proxy;
2737 :
2738 : #ifdef GPAC_HAS_HTTP2
2739 :
2740 : if (sess->h2_switch_sess) {
2741 : sess->h2_switch_sess = 0;
2742 : gf_mx_p(sess->mx);
2743 : h2_detach_session(sess->h2_sess, sess);
2744 : gf_mx_v(sess->mx);
2745 :
2746 : if (sess->num_retry) {
2747 : sess->last_error = GF_OK;
2748 : sess->num_retry--;
2749 : GF_LOG(GF_LOG_WARNING, GF_LOG_HTTP, ("[HTTP/2] stream_id %d (%s) refused by server, retrying and marking session as no longer available\n", sess->h2_stream_id, sess->remote_path ? sess->remote_path : sess->orig_url));
2750 :
2751 : sess->h2_stream_id = 0;
2752 : } else {
2753 : GF_LOG(GF_LOG_ERROR, GF_LOG_HTTP, ("[HTTP/2] stream_id %d (%s) refused by server after all retries, marking session as no longer available\n", sess->h2_stream_id, sess->remote_path ? sess->remote_path : sess->orig_url));
2754 : sess->status = GF_NETIO_STATE_ERROR;
2755 : sess->last_error = GF_REMOTE_SERVICE_ERROR;
2756 : sess->h2_stream_id = 0;
2757 : return;
2758 : }
2759 : }
2760 : assert(!sess->h2_sess);
2761 :
2762 : if (sess->dm && !sess->dm->disable_http2) {
2763 : u32 i, count = gf_list_count(sess->dm->sessions);
2764 : for (i=0; i<count; i++) {
2765 : GF_DownloadSession *a_sess = gf_list_get(sess->dm->sessions, i);
2766 : if (!a_sess->h2_sess) continue;
2767 : if (a_sess->h2_sess->do_shutdown) continue;
2768 : if (strcmp(a_sess->server_name, sess->server_name)) continue;
2769 :
2770 : GF_LOG(GF_LOG_DEBUG, GF_LOG_HTTP, ("[HTTP/2] associating session %s to existing http2 session\n", sess->remote_path ? sess->remote_path : sess->orig_url));
2771 :
2772 : if (sess->mx) gf_mx_del(sess->mx);
2773 : sess->h2_sess = a_sess->h2_sess;
2774 : sess->mx = a_sess->h2_sess->mx;
2775 : sess->sock = a_sess->sock;
2776 : #ifdef GPAC_HAS_SSL
2777 : sess->ssl = a_sess->ssl;
2778 : #endif
2779 : sess->data_io.read_callback = h2_data_source_read_callback;
2780 : sess->data_io.source.ptr = sess;
2781 : gf_list_add(sess->h2_sess->sessions, sess);
2782 :
2783 : if (sess->allow_direct_reuse) {
2784 : gf_dm_configure_cache(sess);
2785 : if (sess->from_cache_only) return;
2786 : }
2787 :
2788 : sess->connect_time = 0;
2789 : sess->status = GF_NETIO_CONNECTED;
2790 : gf_dm_sess_notify_state(sess, GF_NETIO_CONNECTED, GF_OK);
2791 : gf_dm_configure_cache(sess);
2792 : return;
2793 : }
2794 : }
2795 : #endif
2796 :
2797 :
2798 161 : if (!sess->sock) {
2799 161 : sess->num_retry = 40;
2800 161 : sess->sock = gf_sk_new(GF_SOCK_TYPE_TCP);
2801 : }
2802 :
2803 : /*connect*/
2804 161 : sess->status = GF_NETIO_SETUP;
2805 161 : gf_dm_sess_notify_state(sess, sess->status, GF_OK);
2806 :
2807 : /*PROXY setup*/
2808 161 : if (sess->proxy_enabled!=2) {
2809 : proxy = NULL;
2810 161 : if (gf_opts_get_bool("core", "proxy-on")) {
2811 : u32 i;
2812 : Bool use_proxy=GF_TRUE;
2813 0 : for (i=0; i<gf_list_count(sess->dm->skip_proxy_servers); i++) {
2814 0 : char *skip = (char*)gf_list_get(sess->dm->skip_proxy_servers, i);
2815 0 : if (!strcmp(skip, sess->server_name)) {
2816 : use_proxy=GF_FALSE;
2817 : break;
2818 : }
2819 : }
2820 0 : if (use_proxy) {
2821 0 : proxy_port = gf_opts_get_int("core", "proxy-port");
2822 0 : if (!proxy_port) proxy_port = 80;
2823 0 : proxy = gf_opts_get_key("core", "proxy-name");
2824 0 : sess->proxy_enabled = 1;
2825 : } else {
2826 : proxy = NULL;
2827 : }
2828 : } else {
2829 : proxy = NULL;
2830 161 : sess->proxy_enabled = 0;
2831 : }
2832 : } else {
2833 : proxy = NULL;
2834 : }
2835 :
2836 :
2837 0 : if (!proxy) {
2838 161 : proxy = sess->server_name;
2839 161 : proxy_port = sess->port;
2840 : }
2841 161 : GF_LOG(GF_LOG_INFO, GF_LOG_HTTP, ("[HTTP] Connecting to %s:%d\n", proxy, proxy_port));
2842 :
2843 161 : if (sess->status == GF_NETIO_SETUP) {
2844 : u64 now;
2845 161 : if (sess->dm && sess->dm->simulate_no_connection) {
2846 0 : sess->status = GF_NETIO_STATE_ERROR;
2847 0 : sess->last_error = GF_IP_NETWORK_FAILURE;
2848 0 : gf_dm_sess_notify_state(sess, sess->status, sess->last_error);
2849 0 : return;
2850 : }
2851 :
2852 161 : now = gf_sys_clock_high_res();
2853 161 : e = gf_sk_connect(sess->sock, (char *) proxy, proxy_port, NULL);
2854 :
2855 : /*retry*/
2856 161 : if ((e == GF_IP_SOCK_WOULD_BLOCK) && sess->num_retry) {
2857 0 : sess->status = GF_NETIO_SETUP;
2858 0 : sess->num_retry--;
2859 0 : return;
2860 : }
2861 :
2862 : /*failed*/
2863 161 : if (e) {
2864 0 : if (!sess->cache_entry && sess->dm && sess->dm->allow_offline_cache) {
2865 0 : gf_dm_configure_cache(sess);
2866 0 : if (sess->from_cache_only) return;
2867 : }
2868 0 : sess->status = GF_NETIO_STATE_ERROR;
2869 0 : sess->last_error = e;
2870 0 : gf_dm_sess_notify_state(sess, sess->status, e);
2871 0 : return;
2872 : }
2873 161 : if (sess->allow_direct_reuse) {
2874 12 : gf_dm_configure_cache(sess);
2875 12 : if (sess->from_cache_only) return;
2876 : }
2877 :
2878 161 : sess->connect_time = (u32) (gf_sys_clock_high_res() - now);
2879 161 : sess->status = GF_NETIO_CONNECTED;
2880 161 : GF_LOG(GF_LOG_INFO, GF_LOG_HTTP, ("[HTTP] Connected to %s:%d\n", proxy, proxy_port));
2881 161 : gf_dm_sess_notify_state(sess, GF_NETIO_CONNECTED, GF_OK);
2882 : // gf_sk_set_buffer_size(sess->sock, GF_TRUE, GF_DOWNLOAD_BUFFER_SIZE);
2883 : // gf_sk_set_buffer_size(sess->sock, GF_FALSE, GF_DOWNLOAD_BUFFER_SIZE);
2884 : }
2885 :
2886 : #ifdef GPAC_HAS_SSL
2887 161 : if (!sess->ssl && (sess->flags & GF_DOWNLOAD_SESSION_USE_SSL)) {
2888 30 : u64 now = gf_sys_clock_high_res();
2889 30 : if (sess->dm && !sess->dm->ssl_ctx)
2890 10 : ssl_init(sess->dm, 0);
2891 : /*socket is connected, configure SSL layer*/
2892 30 : if (sess->dm && sess->dm->ssl_ctx) {
2893 : int ret;
2894 : X509 *cert;
2895 : Bool success;
2896 :
2897 30 : sess->ssl = SSL_new(sess->dm->ssl_ctx);
2898 30 : SSL_set_fd(sess->ssl, gf_sk_get_handle(sess->sock));
2899 30 : SSL_ctrl(sess->ssl, SSL_CTRL_SET_TLSEXT_HOSTNAME, TLSEXT_NAMETYPE_host_name, (void*) proxy);
2900 :
2901 30 : SSL_set_connect_state(sess->ssl);
2902 30 : ret = SSL_connect(sess->ssl);
2903 30 : if (ret<=0) {
2904 0 : ret = SSL_get_error(sess->ssl, ret);
2905 0 : if (ret==SSL_ERROR_SSL) {
2906 : char msg[1024];
2907 0 : SSL_load_error_strings();
2908 0 : ERR_error_string_n(ERR_get_error(), msg, sizeof(msg));
2909 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_HTTP, ("[SSL] Cannot connect, error %s\n", msg));
2910 0 : sess->last_error = GF_SERVICE_ERROR;
2911 : } else {
2912 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_HTTP, ("[SSL] Cannot connect, error %d\n", ret));
2913 0 : sess->last_error = GF_REMOTE_SERVICE_ERROR;
2914 : }
2915 : } else {
2916 30 : GF_LOG(GF_LOG_DEBUG, GF_LOG_HTTP, ("[SSL] connected\n"));
2917 :
2918 :
2919 : #ifdef GPAC_HAS_HTTP2
2920 : if (!sess->dm->disable_http2) {
2921 : const u8 *alpn = NULL;
2922 : u32 alpnlen = 0;
2923 : SSL_get0_next_proto_negotiated(sess->ssl, &alpn, &alpnlen);
2924 : #if OPENSSL_VERSION_NUMBER >= 0x10002000L
2925 : if (alpn == NULL) {
2926 : SSL_get0_alpn_selected(sess->ssl, &alpn, &alpnlen);
2927 : }
2928 : #endif
2929 : if (alpn == NULL || alpnlen != 2 || memcmp("h2", alpn, 2) != 0) {
2930 : GF_LOG(GF_LOG_DEBUG, GF_LOG_HTTP, ("[SSL] HTTP/2 is not negotiated\n"));
2931 : } else {
2932 : h2_initialize_session(sess);
2933 : }
2934 : }
2935 : #endif
2936 : }
2937 :
2938 30 : cert = SSL_get_peer_certificate(sess->ssl);
2939 : /*if we have a cert, check it*/
2940 30 : if (cert) {
2941 : long vresult;
2942 30 : SSL_set_verify_result(sess->ssl, 0);
2943 30 : vresult = SSL_get_verify_result(sess->ssl);
2944 :
2945 30 : if (vresult == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY) {
2946 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_HTTP, ("[SSL] Cannot locate issuer's certificate on the local system, will not attempt to validate\n"));
2947 0 : SSL_set_verify_result(sess->ssl, 0);
2948 0 : vresult = SSL_get_verify_result(sess->ssl);
2949 : }
2950 :
2951 30 : if (vresult == X509_V_OK) {
2952 : char common_name[256];
2953 : STACK_OF(GENERAL_NAME) *altnames;
2954 : GF_List* valid_names;
2955 : int i;
2956 :
2957 30 : valid_names = gf_list_new();
2958 :
2959 30 : common_name[0] = 0;
2960 30 : X509_NAME_get_text_by_NID(X509_get_subject_name(cert), NID_commonName, common_name, sizeof (common_name));
2961 30 : gf_list_add(valid_names, common_name);
2962 :
2963 30 : altnames = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
2964 30 : if (altnames) {
2965 202 : for (i = 0; i < sk_GENERAL_NAME_num(altnames); ++i) {
2966 : const GENERAL_NAME *altname = sk_GENERAL_NAME_value(altnames, i);
2967 86 : if (altname->type == GEN_DNS)
2968 : {
2969 : #if OPENSSL_VERSION_NUMBER < 0x10100000L
2970 : unsigned char *altname_str = ASN1_STRING_data(altname->d.ia5);
2971 : #else
2972 86 : unsigned char *altname_str = (unsigned char *)ASN1_STRING_get0_data(altname->d.ia5);
2973 : #endif
2974 86 : gf_list_add(valid_names, altname_str);
2975 : }
2976 : }
2977 : }
2978 :
2979 : success = GF_FALSE;
2980 30 : for (i = 0; i < (int)gf_list_count(valid_names); ++i) {
2981 60 : const char *valid_name = (const char*) gf_list_get(valid_names, i);
2982 60 : if (rfc2818_match(valid_name, sess->server_name)) {
2983 : success = GF_TRUE;
2984 30 : GF_LOG(GF_LOG_INFO, GF_LOG_HTTP, ("[SSL] Hostname %s matches %s\n", sess->server_name, valid_name));
2985 : break;
2986 : }
2987 : }
2988 : if (!success) {
2989 0 : if (sess->dm && sess->dm->allow_broken_certificate) {
2990 : success = GF_TRUE;
2991 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_HTTP, ("[SSL] Mismatch in certificate names: expected %s\n", sess->server_name));
2992 : } else {
2993 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_HTTP, ("[SSL] Mismatch in certificate names, try using -broken-cert: expected %s\n", sess->server_name));
2994 : }
2995 : #ifndef GPAC_DISABLE_LOG
2996 0 : for (i = 0; i < (int)gf_list_count(valid_names); ++i) {
2997 0 : const char *valid_name = (const char*) gf_list_get(valid_names, i);
2998 0 : GF_LOG(success ? GF_LOG_DEBUG : GF_LOG_ERROR, GF_LOG_HTTP, ("[SSL] Tried name: %s\n", valid_name));
2999 : }
3000 : #endif
3001 : }
3002 :
3003 30 : gf_list_del(valid_names);
3004 30 : GENERAL_NAMES_free(altnames);
3005 : } else {
3006 : success = GF_FALSE;
3007 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_HTTP, ("[SSL] Error verifying certificate %x\n", vresult));
3008 : }
3009 :
3010 30 : X509_free(cert);
3011 :
3012 30 : if (!success) {
3013 0 : gf_dm_disconnect(sess, HTTP_RESET_CONN);
3014 0 : sess->status = GF_NETIO_STATE_ERROR;
3015 0 : sess->last_error = GF_AUTHENTICATION_FAILURE;
3016 0 : gf_dm_sess_notify_state(sess, sess->status, sess->last_error);
3017 : }
3018 : }
3019 :
3020 30 : sess->ssl_setup_time = (u32) (gf_sys_clock_high_res() - now);
3021 : }
3022 : }
3023 : #endif
3024 :
3025 : /*this should be done when building HTTP GET request in case we have range directives*/
3026 161 : gf_dm_configure_cache(sess);
3027 :
3028 : }
3029 :
3030 101 : DownloadedCacheEntry gf_dm_refresh_cache_entry(GF_DownloadSession *sess)
3031 : {
3032 : Bool go;
3033 : u32 timer = 0;
3034 : u32 flags;
3035 101 : if (!sess) return NULL;
3036 101 : flags = sess->flags;
3037 101 : sess->flags |= GF_NETIO_SESSION_NOT_CACHED;
3038 : go = GF_TRUE;
3039 101 : while (go) {
3040 101 : switch (sess->status) {
3041 : /*setup download*/
3042 0 : case GF_NETIO_SETUP:
3043 0 : gf_dm_connect(sess);
3044 0 : break;
3045 0 : case GF_NETIO_WAIT_FOR_REPLY:
3046 0 : if (timer == 0)
3047 0 : timer = gf_sys_clock();
3048 : {
3049 0 : u32 timer2 = gf_sys_clock();
3050 0 : if (timer2 - timer > 5000) {
3051 : GF_Err e;
3052 : /* Since HEAD is not understood by this server, we use a GET instead */
3053 0 : sess->http_read_type = GET;
3054 0 : sess->flags |= GF_NETIO_SESSION_NOT_CACHED;
3055 0 : gf_dm_disconnect(sess, HTTP_NO_CLOSE);
3056 0 : sess->status = GF_NETIO_SETUP;
3057 0 : sess->server_only_understand_get = GF_TRUE;
3058 0 : GF_LOG(GF_LOG_INFO, GF_LOG_HTTP, ("gf_dm_refresh_cache_entry() : Timeout with HEAD, try with GET\n"));
3059 0 : e = gf_dm_sess_setup_from_url(sess, sess->orig_url, GF_FALSE);
3060 0 : if (e) {
3061 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_HTTP, ("gf_dm_refresh_cache_entry() : Error with GET %d\n", e));
3062 0 : sess->status = GF_NETIO_STATE_ERROR;
3063 0 : sess->last_error = e;
3064 0 : gf_dm_sess_notify_state(sess, sess->status, e);
3065 : } else {
3066 : timer = 0;
3067 0 : continue;
3068 : }
3069 : }
3070 : }
3071 : case GF_NETIO_CONNECTED:
3072 0 : sess->do_requests(sess);
3073 0 : break;
3074 : case GF_NETIO_DATA_EXCHANGE:
3075 : case GF_NETIO_DISCONNECTED:
3076 : case GF_NETIO_STATE_ERROR:
3077 : case GF_NETIO_DATA_TRANSFERED:
3078 : go = GF_FALSE;
3079 : break;
3080 : default:
3081 : break;
3082 : }
3083 : }
3084 101 : sess->flags = flags;
3085 101 : if (sess->status==GF_NETIO_STATE_ERROR) return NULL;
3086 101 : return sess->cache_entry;
3087 : }
3088 :
3089 : GF_EXPORT
3090 734 : const char *gf_dm_sess_mime_type(GF_DownloadSession *sess)
3091 : {
3092 : DownloadedCacheEntry entry;
3093 734 : if (sess->cache_entry) {
3094 732 : const char * oldMimeIfAny = gf_cache_get_mime_type(sess->cache_entry);
3095 732 : if (oldMimeIfAny)
3096 : return oldMimeIfAny;
3097 : }
3098 101 : entry = gf_dm_refresh_cache_entry (sess);
3099 101 : if (!entry)
3100 2 : return sess->mime_type;
3101 : assert( entry == sess->cache_entry && entry);
3102 99 : return gf_cache_get_mime_type( sess->cache_entry );
3103 : }
3104 :
3105 : GF_EXPORT
3106 941 : GF_Err gf_dm_sess_set_range(GF_DownloadSession *sess, u64 start_range, u64 end_range, Bool discontinue_cache)
3107 : {
3108 941 : if (!sess)
3109 : return GF_BAD_PARAM;
3110 941 : if (sess->cache_entry) {
3111 745 : if (!discontinue_cache) {
3112 40 : if (gf_cache_get_end_range(sess->cache_entry) + 1 != start_range)
3113 : discontinue_cache = GF_TRUE;
3114 : }
3115 745 : if (sess->sock) {
3116 709 : if (sess->status != GF_NETIO_CONNECTED) {
3117 40 : if (sess->status != GF_NETIO_DISCONNECTED) {
3118 : return GF_BAD_PARAM;
3119 : }
3120 : }
3121 : }
3122 745 : if (!sess->local_cache_only) {
3123 714 : sess->status = sess->sock ? GF_NETIO_CONNECTED : GF_NETIO_SETUP;
3124 714 : sess->num_retry = SESSION_RETRY_COUNT;
3125 :
3126 714 : if (!discontinue_cache) {
3127 40 : gf_cache_set_end_range(sess->cache_entry, end_range);
3128 : /*remember this in case we get disconnected*/
3129 40 : sess->is_range_continuation = GF_TRUE;
3130 : } else {
3131 674 : sess->needs_cache_reconfig = 1;
3132 674 : sess->reused_cache_entry = GF_FALSE;
3133 : }
3134 : }
3135 : } else {
3136 196 : if ((sess->status != GF_NETIO_SETUP) && (sess->status != GF_NETIO_CONNECTED))
3137 : return GF_BAD_PARAM;
3138 : }
3139 941 : sess->range_start = start_range;
3140 941 : sess->range_end = end_range;
3141 941 : sess->needs_range = (start_range || end_range) ? GF_TRUE : GF_FALSE;
3142 941 : return GF_OK;
3143 : }
3144 :
3145 : #ifdef GPAC_HAS_HTTP2
3146 : static void gf_dm_sess_flush_input(GF_DownloadSession *sess)
3147 : {
3148 : char sHTTP[GF_DOWNLOAD_BUFFER_SIZE+1];
3149 : u32 res;
3150 : sHTTP[0] = 0;
3151 : GF_Err e = gf_dm_read_data(sess, sHTTP, GF_DOWNLOAD_BUFFER_SIZE, &res);
3152 : switch (e) {
3153 : case GF_IP_NETWORK_EMPTY:
3154 : case GF_OK:
3155 : case GF_IP_SOCK_WOULD_BLOCK:
3156 : return;
3157 : default:
3158 : sess->status = GF_NETIO_STATE_ERROR;
3159 : sess->last_error = e;
3160 : return;
3161 : }
3162 : }
3163 : #endif
3164 :
3165 : GF_EXPORT
3166 538 : GF_Err gf_dm_sess_process(GF_DownloadSession *sess)
3167 : {
3168 : Bool go;
3169 :
3170 : /*if session is threaded, start thread*/
3171 538 : if (! (sess->flags & GF_NETIO_SESSION_NOT_THREADED)) {
3172 1 : if (sess->dm->filter_session && !gf_opts_get_bool("core", "dm-threads")) {
3173 1 : GF_SAFEALLOC(sess->ftask, GF_SessTask);
3174 1 : if (!sess->ftask) return GF_OUT_OF_MEM;
3175 1 : sess->ftask->sess = sess;
3176 1 : gf_fs_post_user_task(sess->dm->filter_session, gf_dm_session_task, sess->ftask, "download");
3177 1 : return GF_OK;
3178 : }
3179 0 : if (sess->th) {
3180 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_HTTP, ("[HTTP] Session already started - ignoring start\n"));
3181 : return GF_OK;
3182 : }
3183 0 : sess->th = gf_th_new(sess->orig_url);
3184 0 : if (!sess->th) return GF_OUT_OF_MEM;
3185 0 : gf_th_run(sess->th, gf_dm_session_thread, sess);
3186 0 : return GF_OK;
3187 : }
3188 :
3189 537 : if (sess->put_state==2) {
3190 46 : if (sess->status==GF_NETIO_DATA_TRANSFERED)
3191 0 : sess->status = GF_NETIO_WAIT_FOR_REPLY;
3192 : }
3193 :
3194 : /*otherwise do a synchronous download*/
3195 : go = GF_TRUE;
3196 : while (go) {
3197 14145 : switch (sess->status) {
3198 : /*setup download*/
3199 27 : case GF_NETIO_SETUP:
3200 27 : gf_dm_connect(sess);
3201 27 : break;
3202 11233 : case GF_NETIO_WAIT_FOR_REPLY:
3203 : case GF_NETIO_CONNECTED:
3204 11233 : sess->do_requests(sess);
3205 11233 : if (sess->server_mode) {
3206 253 : if (sess->status == GF_NETIO_STATE_ERROR) {
3207 37 : sess->status = GF_NETIO_DISCONNECTED;
3208 37 : sess->last_error = GF_IP_CONNECTION_CLOSED;
3209 : sess_connection_closed(sess);
3210 : go = GF_FALSE;
3211 216 : } else if (sess->last_error==GF_IP_NETWORK_EMPTY) {
3212 : go = GF_FALSE;
3213 : }
3214 : }
3215 : break;
3216 2385 : case GF_NETIO_DATA_EXCHANGE:
3217 2385 : if (sess->put_state==2) {
3218 0 : sess->status = GF_NETIO_DATA_TRANSFERED;
3219 : go = GF_FALSE;
3220 : break;
3221 : }
3222 2385 : sess->do_requests(sess);
3223 2385 : break;
3224 : case GF_NETIO_DATA_TRANSFERED:
3225 : #ifdef GPAC_HAS_HTTP2
3226 : if (sess->h2_sess && sess->server_mode) {
3227 : gf_dm_sess_flush_input(sess);
3228 : h2_session_send(sess);
3229 : }
3230 : #endif
3231 : go = GF_FALSE;
3232 : break;
3233 : case GF_NETIO_DISCONNECTED:
3234 : case GF_NETIO_STATE_ERROR:
3235 : go = GF_FALSE;
3236 : break;
3237 :
3238 : case GF_NETIO_GET_METHOD:
3239 : case GF_NETIO_GET_HEADER:
3240 : case GF_NETIO_GET_CONTENT:
3241 : case GF_NETIO_PARSE_HEADER:
3242 : case GF_NETIO_PARSE_REPLY:
3243 : break;
3244 :
3245 0 : default:
3246 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_HTTP, ("[Downloader] Session in unknown state !! - aborting\n"));
3247 : go = GF_FALSE;
3248 : break;
3249 : }
3250 : }
3251 537 : return sess->last_error;
3252 : }
3253 :
3254 : GF_EXPORT
3255 209 : GF_Err gf_dm_sess_process_headers(GF_DownloadSession *sess)
3256 : {
3257 : Bool go;
3258 : go = GF_TRUE;
3259 209 : while (go) {
3260 16075 : switch (sess->status) {
3261 : /*setup download*/
3262 3 : case GF_NETIO_SETUP:
3263 3 : gf_dm_connect(sess);
3264 3 : break;
3265 15863 : case GF_NETIO_WAIT_FOR_REPLY:
3266 : case GF_NETIO_CONNECTED:
3267 15863 : sess->do_requests(sess);
3268 :
3269 15863 : if (sess->reused_cache_entry && sess->cache_entry && gf_cache_are_headers_processed(sess->cache_entry) ) {
3270 0 : sess->status = GF_NETIO_DATA_EXCHANGE;
3271 : }
3272 : break;
3273 : case GF_NETIO_DATA_EXCHANGE:
3274 : case GF_NETIO_DATA_TRANSFERED:
3275 : case GF_NETIO_DISCONNECTED:
3276 : case GF_NETIO_STATE_ERROR:
3277 : go = GF_FALSE;
3278 : break;
3279 : default:
3280 : break;
3281 : }
3282 : }
3283 209 : return sess->last_error;
3284 : }
3285 :
3286 : static Bool gf_dm_needs_to_delete_cache(GF_DownloadManager * dm)
3287 : {
3288 : if (!dm) return GF_FALSE;
3289 150 : return dm->clean_cache;
3290 : }
3291 :
3292 : #ifdef BUGGY_gf_cache_cleanup_cache
3293 : /*!
3294 : * Cleans up the cache at start and stop.
3295 : * Note that this method will perform any cleanup if
3296 : * Configuration section [Downloader]/CleanCache is not set, meaning
3297 : * that methods that create a "fake" GF_DownloadManager such as
3298 : * gf_dm_wget() are not impacted and won't cleanup the cache
3299 : *
3300 : * FIXME: should be probably threaded to avoid too long start time
3301 : \param dm The GF_DownloadManager
3302 : */
3303 : static void gf_cache_cleanup_cache(GF_DownloadManager * dm) {
3304 : if (gf_dm_needs_to_delete_cache(dm)) {
3305 : gf_cache_delete_all_cached_files(dm->cache_directory);
3306 : }
3307 : }
3308 : #endif
3309 :
3310 : typedef struct
3311 : {
3312 : Bool check_size;
3313 : u64 out_size;
3314 : } cache_probe;
3315 :
3316 :
3317 151 : static void gf_dm_clean_cache(GF_DownloadManager *dm)
3318 : {
3319 151 : u64 out_size = gf_cache_get_size(dm->cache_directory);
3320 151 : if (out_size >= dm->max_cache_size) {
3321 1 : GF_LOG(dm->max_cache_size ? GF_LOG_WARNING : GF_LOG_INFO, GF_LOG_HTTP, ("[Cache] Cache size %d exceeds max allowed %d, deleting entire cache\n", out_size, dm->max_cache_size));
3322 1 : gf_cache_delete_all_cached_files(dm->cache_directory);
3323 : }
3324 151 : }
3325 :
3326 : GF_EXPORT
3327 151 : GF_DownloadManager *gf_dm_new(GF_FilterSession *fsess)
3328 : {
3329 : const char *opt;
3330 : const char * default_cache_dir;
3331 : GF_DownloadManager *dm;
3332 151 : GF_SAFEALLOC(dm, GF_DownloadManager);
3333 151 : if (!dm) {
3334 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_HTTP, ("[Downloader] Failed to allocate downloader\n"));
3335 : return NULL;
3336 : }
3337 151 : dm->sessions = gf_list_new();
3338 151 : dm->cache_entries = gf_list_new();
3339 151 : dm->credentials = gf_list_new();
3340 151 : dm->skip_proxy_servers = gf_list_new();
3341 151 : dm->partial_downloads = gf_list_new();
3342 151 : dm->cache_mx = gf_mx_new("download_manager_cache_mx");
3343 151 : dm->filter_session = fsess;
3344 : default_cache_dir = NULL;
3345 151 : gf_mx_p( dm->cache_mx );
3346 :
3347 : #ifdef GPAC_HAS_HTTP2
3348 : dm->disable_http2 = gf_opts_get_bool("core", "no-h2");
3349 : #endif
3350 :
3351 151 : opt = gf_opts_get_key("core", "cache");
3352 :
3353 : retry_cache:
3354 151 : if (!opt) {
3355 0 : default_cache_dir = gf_get_default_cache_directory();
3356 : opt = default_cache_dir;
3357 : }
3358 151 : if (opt[strlen(opt)-1] != GF_PATH_SEPARATOR) {
3359 150 : dm->cache_directory = (char *) gf_malloc(sizeof(char)* (strlen(opt)+2));
3360 : sprintf(dm->cache_directory, "%s%c", opt, GF_PATH_SEPARATOR);
3361 : } else {
3362 1 : dm->cache_directory = gf_strdup(opt);
3363 : }
3364 :
3365 : //check cache exists
3366 151 : if (!default_cache_dir) {
3367 : FILE *test;
3368 : char szTemp[GF_MAX_PATH];
3369 151 : strcpy(szTemp, dm->cache_directory);
3370 : strcat(szTemp, "gpaccache.test");
3371 151 : test = gf_fopen(szTemp, "wb");
3372 151 : if (!test) {
3373 0 : gf_mkdir(dm->cache_directory);
3374 0 : test = gf_fopen(szTemp, "wb");
3375 0 : if (!test) {
3376 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_HTTP, ("[Cache] Cannot write to %s directory, using system temp cache\n", dm->cache_directory ));
3377 0 : gf_free(dm->cache_directory);
3378 0 : dm->cache_directory = NULL;
3379 : opt = NULL;
3380 0 : goto retry_cache;
3381 : }
3382 : }
3383 151 : if (test) {
3384 151 : gf_fclose(test);
3385 151 : gf_file_delete(szTemp);
3386 : }
3387 : }
3388 :
3389 : /*use it in in BYTES per second*/
3390 151 : dm->limit_data_rate = gf_opts_get_int("core", "maxrate") / 8;
3391 :
3392 151 : dm->read_buf_size = GF_DOWNLOAD_BUFFER_SIZE;
3393 : //when rate is limited, use smaller smaller read size
3394 151 : if (dm->limit_data_rate) {
3395 0 : dm->read_buf_size = 1024;
3396 : }
3397 :
3398 151 : dm->disable_cache = gf_opts_get_bool("core", "no-cache");
3399 :
3400 151 : dm->allow_offline_cache = gf_opts_get_bool("core", "offline-cache");
3401 :
3402 151 : dm->clean_cache = GF_FALSE;
3403 151 : dm->allow_broken_certificate = GF_FALSE;
3404 151 : if ( gf_opts_get_bool("core", "clean-cache")) {
3405 1 : dm->clean_cache = GF_TRUE;
3406 1 : dm->max_cache_size=0;
3407 1 : gf_dm_clean_cache(dm);
3408 : } else {
3409 150 : dm->max_cache_size = gf_opts_get_int("core", "cache-size");
3410 150 : if (dm->max_cache_size) {
3411 150 : gf_dm_clean_cache(dm);
3412 : }
3413 : }
3414 151 : dm->allow_broken_certificate = gf_opts_get_bool("core", "broken-cert");
3415 :
3416 151 : gf_mx_v( dm->cache_mx );
3417 :
3418 : #ifdef GPAC_HAS_SSL
3419 151 : dm->ssl_ctx = NULL;
3420 : #endif
3421 : /* TODO: Not ready for now, we should find a locking strategy between several GPAC instances...
3422 : * gf_cache_cleanup_cache(dm);
3423 : */
3424 151 : return dm;
3425 : }
3426 :
3427 : GF_EXPORT
3428 1 : void gf_dm_set_auth_callback(GF_DownloadManager *dm,
3429 : Bool (*get_user_password)(void *usr_cbk, const char *site_url, char *usr_name, char *password),
3430 : void *usr_cbk)
3431 : {
3432 1 : if (dm) {
3433 1 : dm->get_user_password = get_user_password;
3434 1 : dm->usr_cbk = usr_cbk;
3435 : }
3436 1 : }
3437 :
3438 : GF_EXPORT
3439 150 : void gf_dm_del(GF_DownloadManager *dm)
3440 : {
3441 150 : if (!dm)
3442 : return;
3443 : assert( dm->sessions);
3444 : assert( dm->cache_mx );
3445 150 : gf_mx_p( dm->cache_mx );
3446 :
3447 300 : while (gf_list_count(dm->partial_downloads)) {
3448 0 : GF_PartialDownload * entry = (GF_PartialDownload*)gf_list_get( dm->partial_downloads, 0);
3449 0 : gf_list_rem( dm->partial_downloads, 0);
3450 : assert( entry->filename );
3451 0 : gf_file_delete( entry->filename );
3452 0 : gf_free(entry->filename );
3453 0 : entry->filename = NULL;
3454 0 : entry->url = NULL;
3455 0 : gf_free( entry );
3456 : }
3457 :
3458 : /*destroy all pending sessions*/
3459 150 : while (gf_list_count(dm->sessions)) {
3460 0 : GF_DownloadSession *sess = (GF_DownloadSession *) gf_list_get(dm->sessions, 0);
3461 0 : gf_dm_sess_del(sess);
3462 : }
3463 150 : gf_list_del(dm->sessions);
3464 150 : dm->sessions = NULL;
3465 : assert( dm->skip_proxy_servers );
3466 300 : while (gf_list_count(dm->skip_proxy_servers)) {
3467 0 : char *serv = (char*)gf_list_get(dm->skip_proxy_servers, 0);
3468 0 : gf_list_rem(dm->skip_proxy_servers, 0);
3469 0 : gf_free(serv);
3470 : }
3471 150 : gf_list_del(dm->skip_proxy_servers);
3472 150 : dm->skip_proxy_servers = NULL;
3473 : assert( dm->credentials);
3474 300 : while (gf_list_count(dm->credentials)) {
3475 0 : gf_user_credentials_struct * cred = (gf_user_credentials_struct*)gf_list_get( dm->credentials, 0);
3476 0 : gf_list_rem( dm->credentials, 0);
3477 0 : gf_free( cred );
3478 : }
3479 150 : gf_list_del( dm->credentials);
3480 150 : dm->credentials = NULL;
3481 : assert( dm->cache_entries );
3482 : {
3483 : /* Deletes DownloadedCacheEntry and associated files if required */
3484 : Bool delete_my_files = gf_dm_needs_to_delete_cache(dm);
3485 509 : while (gf_list_count(dm->cache_entries)) {
3486 209 : const DownloadedCacheEntry entry = (const DownloadedCacheEntry)gf_list_get( dm->cache_entries, 0);
3487 209 : gf_list_rem( dm->cache_entries, 0);
3488 209 : if (delete_my_files)
3489 9 : gf_cache_entry_set_delete_files_when_deleted(entry);
3490 209 : gf_cache_delete_entry(entry);
3491 : }
3492 150 : gf_list_del( dm->cache_entries );
3493 150 : dm->cache_entries = NULL;
3494 : }
3495 :
3496 150 : gf_list_del( dm->partial_downloads );
3497 150 : dm->partial_downloads = NULL;
3498 : /* TODO: Not ready for now, we should find a locking strategy between several GPAC instances...
3499 : * gf_cache_cleanup_cache(dm);
3500 : */
3501 150 : if (dm->cache_directory)
3502 150 : gf_free(dm->cache_directory);
3503 150 : dm->cache_directory = NULL;
3504 :
3505 : #ifdef GPAC_HAS_SSL
3506 150 : if (dm->ssl_ctx) SSL_CTX_free(dm->ssl_ctx);
3507 : #endif
3508 : /* Stored elsewhere, no need to free */
3509 150 : gf_mx_v( dm->cache_mx );
3510 150 : gf_mx_del( dm->cache_mx);
3511 150 : dm->cache_mx = NULL;
3512 150 : gf_free(dm);
3513 : }
3514 :
3515 : /*!
3516 : * Skip ICY metadata from SHOUTCAST or ICECAST streams.
3517 : * Data will be skipped and parsed and sent as a GF_NETIO_Parameter to the user_io,
3518 : * so modules interrested by those streams may use the data
3519 : \param sess The GF_DownloadSession
3520 : \param data last data received
3521 : \param nbBytes The number of bytes contained into data
3522 : */
3523 4 : static void gf_icy_skip_data(GF_DownloadSession * sess, const char * data, u32 nbBytes)
3524 : {
3525 : u32 icy_metaint;
3526 4 : if (!sess || !data ) return;
3527 :
3528 4 : icy_metaint = sess->icy_metaint;
3529 : assert( icy_metaint > 0 );
3530 12 : while (nbBytes) {
3531 4 : if (sess->icy_bytes == icy_metaint) {
3532 0 : sess->icy_count = 1 + 16* (u8) data[0];
3533 : /*skip icy metadata*/
3534 0 : if (sess->icy_count > nbBytes) {
3535 0 : sess->icy_count -= nbBytes;
3536 : nbBytes = 0;
3537 : } else {
3538 0 : if (sess->icy_count > 1) {
3539 : GF_NETIO_Parameter par;
3540 : char szData[4096];
3541 : memset(szData, 0, 4096);
3542 0 : memcpy(szData, data+1, sess->icy_count-1);
3543 0 : szData[sess->icy_count] = 0;
3544 :
3545 0 : par.error = GF_OK;
3546 0 : par.msg_type = GF_NETIO_PARSE_HEADER;
3547 0 : par.name = "icy-meta";
3548 0 : par.value = szData;
3549 0 : par.sess = sess;
3550 0 : GF_LOG(GF_LOG_INFO, GF_LOG_HTTP, ("[ICY] Found metainfo in stream=%s, (every %d bytes)\n", szData, icy_metaint));
3551 : gf_dm_sess_user_io(sess, &par);
3552 : } else {
3553 0 : GF_LOG(GF_LOG_DEBUG, GF_LOG_HTTP, ("[ICY] Empty metainfo in stream, (every %d bytes)\n", icy_metaint));
3554 : }
3555 0 : nbBytes -= sess->icy_count;
3556 0 : data += sess->icy_count;
3557 0 : sess->icy_count = 0;
3558 0 : sess->icy_bytes = 0;
3559 : }
3560 : } else {
3561 : GF_NETIO_Parameter par;
3562 4 : u32 left = icy_metaint - sess->icy_bytes;
3563 4 : if (left > nbBytes) {
3564 : left = nbBytes;
3565 4 : sess->icy_bytes += left;
3566 : nbBytes = 0;
3567 : } else {
3568 0 : sess->icy_bytes = icy_metaint;
3569 0 : nbBytes -= left;
3570 : }
3571 :
3572 4 : par.msg_type = GF_NETIO_DATA_EXCHANGE;
3573 4 : par.data = data;
3574 4 : par.size = left;
3575 : gf_dm_sess_user_io(sess, &par);
3576 :
3577 4 : data += left;
3578 : }
3579 : }
3580 : }
3581 :
3582 :
3583 2387 : static char *gf_dm_get_chunk_data(GF_DownloadSession *sess, Bool first_chunk_in_payload, char *body_start, u32 *payload_size, u32 *header_size)
3584 : {
3585 : u32 size;
3586 : s32 res;
3587 : char *te_header, *sep;
3588 :
3589 2387 : if (!sess || !body_start) return NULL;
3590 2387 : if (!sess->chunked) return body_start;
3591 :
3592 2387 : if (sess->nb_left_in_chunk) {
3593 230 : if (sess->nb_left_in_chunk > *payload_size) {
3594 118 : sess->nb_left_in_chunk -= (*payload_size);
3595 118 : GF_LOG(GF_LOG_DEBUG, GF_LOG_HTTP, ("[HTTP] Chunk encoding: still %d bytes to get\n", sess->nb_left_in_chunk));
3596 : } else {
3597 112 : *payload_size = sess->nb_left_in_chunk;
3598 112 : sess->nb_left_in_chunk = 0;
3599 112 : GF_LOG(GF_LOG_DEBUG, GF_LOG_HTTP, ("[HTTP] Chunk encoding: last bytes in chunk received\n"));
3600 : }
3601 230 : *header_size = 0;
3602 : return body_start;
3603 : }
3604 :
3605 : if (*payload_size == 2) {
3606 : *header_size = 0;
3607 : }
3608 2157 : *header_size = 0;
3609 : /*skip remaining CRLF from previous chunk if any*/
3610 2157 : if (*payload_size >= 2) {
3611 2157 : if ((body_start[0]=='\r') && (body_start[1]=='\n')) {
3612 2089 : body_start += 2;
3613 2089 : *header_size = 2;
3614 : }
3615 2157 : if (*payload_size <= 4) {
3616 991 : *header_size = 0;
3617 : return NULL;
3618 : }
3619 1166 : te_header = strstr((char *) body_start, "\r\n");
3620 : } else {
3621 : //not enough bytes to read CRLF, don't bother parsing
3622 : te_header = NULL;
3623 : }
3624 :
3625 : //cannot parse now, copy over the bytes
3626 1166 : if (!te_header) {
3627 0 : *header_size = 0;
3628 0 : GF_LOG(GF_LOG_DEBUG, GF_LOG_HTTP, ("[HTTP] Chunk encoding: current buffer does not contain enough bytes (%d) to read the size\n", *payload_size));
3629 : return NULL;
3630 : }
3631 :
3632 1166 : te_header[0] = 0;
3633 : //assert(strlen(body_start));
3634 1166 : *header_size += (u32) (strlen(body_start)) + 2;
3635 :
3636 1166 : sep = strchr(body_start, ';');
3637 1166 : if (sep) sep[0] = 0;
3638 1166 : res = sscanf(body_start, "%x", &size);
3639 1166 : if (res<0) {
3640 0 : te_header[0] = '\r';
3641 0 : if (sep) sep[0] = ';';
3642 0 : *header_size = 0;
3643 0 : *payload_size = 0;
3644 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_HTTP, ("[HTTP] Chunk encoding: fail to read chunk size from buffer %s, aborting\n", body_start));
3645 : return NULL;
3646 : }
3647 1166 : if (sep) sep[0] = ';';
3648 1166 : *payload_size = size;
3649 :
3650 1166 : GF_LOG(GF_LOG_DEBUG, GF_LOG_HTTP, ("[HTTP] Chunk Start: Header \"%s\" - header size %d - payload size %d (bytes done %d) at UTC "LLD"\n", body_start, 2+strlen(body_start), size, sess->bytes_done, gf_net_get_utc()));
3651 :
3652 1166 : te_header[0] = '\r';
3653 1166 : if (!size)
3654 60 : sess->last_chunk_found = GF_TRUE;
3655 :
3656 1166 : sess->current_chunk_size = size;
3657 1166 : sess->current_chunk_start = gf_sys_clock_high_res();
3658 1166 : return te_header+2;
3659 : }
3660 :
3661 :
3662 8651 : static void dm_sess_update_download_rate(GF_DownloadSession * sess)
3663 : {
3664 8651 : if (!sess->bytes_done) {
3665 0 : sess->bytes_per_sec = 0;
3666 0 : return;
3667 : }
3668 :
3669 : //session is chunked and we have reached our first full window
3670 8651 : if (sess->chunked && sess->cumulated_chunk_rate) {
3671 : /*use our cumulated weighted rate in bytes per seconds, and divide by total size*/
3672 1167 : sess->bytes_per_sec = (u32) (sess->cumulated_chunk_rate / (sess->bytes_done + sess->cumulated_chunk_header_bytes) );
3673 :
3674 : #ifndef GPAC_DISABLE_LOG
3675 1167 : if (gf_log_tool_level_on(GF_LOG_HTTP, GF_LOG_DEBUG)) {
3676 31 : u64 runtime = (gf_sys_clock_high_res() - sess->request_start_time);
3677 31 : if (!runtime) runtime=1;
3678 31 : u32 kbps = (u32) ((1000000 * (u64) (sess->bytes_done + sess->cumulated_chunk_header_bytes)) / runtime) / 125;
3679 :
3680 31 : GF_LOG(GF_LOG_DEBUG, GF_LOG_HTTP, ("[HTTP] bandwidth estimation: download time "LLD" us - bytes %u - chunk rate %u kbps (overall rate rate %u kbps)\n", runtime, sess->bytes_done, sess->bytes_per_sec / 125, kbps));
3681 : }
3682 : #endif
3683 : } else {
3684 : /*compute bps starting from request send time*/
3685 7484 : u64 runtime = (gf_sys_clock_high_res() - sess->request_start_time);
3686 7484 : if (!runtime) runtime=1;
3687 :
3688 7484 : sess->bytes_per_sec = (u32) ((1000000 * (u64) sess->bytes_done) / runtime);
3689 7484 : GF_LOG(GF_LOG_DEBUG, GF_LOG_HTTP, ("[HTTP] bandwidth estimation: download time "LLD" us - bytes %u - rate %u kbps\n", runtime, sess->bytes_done, sess->bytes_per_sec / 125));
3690 : }
3691 : }
3692 :
3693 :
3694 9681 : static void gf_dm_data_received(GF_DownloadSession *sess, u8 *payload, u32 payload_size, Bool store_in_init, u32 *rewrite_size, u8 *original_payload)
3695 : {
3696 : u32 nbBytes, remaining, hdr_size;
3697 : u8 *data;
3698 : Bool first_chunk_in_payload = GF_TRUE;
3699 : Bool flush_chunk = GF_FALSE;
3700 : GF_NETIO_Parameter par;
3701 :
3702 9681 : nbBytes = payload_size;
3703 9681 : hdr_size = 0;
3704 : remaining = 0;
3705 9681 : if (!payload)
3706 0 : return; //nothing to do
3707 9681 : if (sess->chunked) {
3708 2387 : data = (u8 *) gf_dm_get_chunk_data(sess, first_chunk_in_payload, (char *) payload, &nbBytes, &hdr_size);
3709 2387 : if (!hdr_size && !data) {
3710 : /* keep the data and wait for the rest */
3711 991 : sess->remaining_data_size = nbBytes;
3712 991 : sess->remaining_data = (char *)gf_realloc(sess->remaining_data, nbBytes * sizeof(char));
3713 991 : memcpy(sess->remaining_data, payload, nbBytes);
3714 : payload_size = 0;
3715 991 : payload = NULL;
3716 1396 : } else if (hdr_size + nbBytes > payload_size) {
3717 : /* chunk header is processed but we will need several TCP frames to get the entire chunk*/
3718 116 : remaining = nbBytes + hdr_size - payload_size;
3719 : assert(payload_size >= hdr_size);
3720 116 : nbBytes = payload_size - hdr_size;
3721 : payload_size = 0;
3722 : payload = NULL;
3723 116 : sess->chunk_header_bytes += hdr_size;
3724 : } else {
3725 1280 : payload_size -= hdr_size + nbBytes;
3726 1280 : payload += hdr_size + nbBytes;
3727 : flush_chunk = GF_TRUE;
3728 1280 : sess->chunk_header_bytes += hdr_size;
3729 : }
3730 :
3731 : /*chunk transfer is done*/
3732 2387 : if (sess->last_chunk_found) {
3733 60 : sess->total_size = sess->bytes_done;
3734 : }
3735 : } else {
3736 : data = payload;
3737 : remaining = payload_size = 0;
3738 : }
3739 :
3740 9681 : if (data && nbBytes && store_in_init) {
3741 542 : sess->init_data = (char *) gf_realloc(sess->init_data , sizeof(char) * (sess->init_data_size + nbBytes) );
3742 542 : memcpy(sess->init_data+sess->init_data_size, data, nbBytes);
3743 542 : sess->init_data_size += nbBytes;
3744 : }
3745 :
3746 : //we have some new bytes received
3747 9681 : if (nbBytes && !sess->remaining_data_size) {
3748 8617 : sess->bytes_done += nbBytes;
3749 8617 : dm_sess_update_download_rate(sess);
3750 :
3751 8617 : GF_LOG(GF_LOG_DEBUG, GF_LOG_HTTP, ("[HTTP] url %s received %d new bytes (%d kbps)\n", sess->orig_url, nbBytes, 8*sess->bytes_per_sec/1000));
3752 8617 : if (sess->total_size && (sess->bytes_done > sess->total_size)) {
3753 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_HTTP, ("[HTTP] url %s received more bytes than planned!! Got %d bytes vs %d content length\n", sess->orig_url, sess->bytes_done , sess->total_size ));
3754 0 : sess->bytes_done = sess->total_size;
3755 : }
3756 :
3757 8617 : if (sess->icy_metaint > 0)
3758 4 : gf_icy_skip_data(sess, (char *) data, nbBytes);
3759 : else {
3760 8613 : if (sess->use_cache_file)
3761 4293 : gf_cache_write_to_cache( sess->cache_entry, sess, (char *) data, nbBytes, sess->dm->cache_mx);
3762 :
3763 8613 : par.msg_type = GF_NETIO_DATA_EXCHANGE;
3764 8613 : par.error = GF_OK;
3765 8613 : par.data = (char *) data;
3766 8613 : par.size = nbBytes;
3767 8613 : par.reply = flush_chunk;
3768 : gf_dm_sess_user_io(sess, &par);
3769 : }
3770 : }
3771 : //and we're done
3772 9681 : if (sess->total_size && (sess->bytes_done == sess->total_size)) {
3773 : u64 run_time;
3774 :
3775 : #if 0 //def GPAC_HAS_HTTP2
3776 : if (0 && sess->h2_sess && sess->h2_stream_id)
3777 : return;
3778 : #endif
3779 :
3780 774 : if (sess->use_cache_file) {
3781 746 : gf_cache_close_write_cache(sess->cache_entry, sess, GF_TRUE);
3782 746 : GF_LOG(GF_LOG_DEBUG, GF_LOG_HTTP,
3783 : ("[CACHE] url %s saved as %s\n", gf_cache_get_url(sess->cache_entry), gf_cache_get_cache_filename(sess->cache_entry)));
3784 : }
3785 :
3786 774 : gf_dm_disconnect(sess, HTTP_NO_CLOSE);
3787 774 : par.msg_type = GF_NETIO_DATA_TRANSFERED;
3788 774 : par.error = GF_OK;
3789 :
3790 : gf_dm_sess_user_io(sess, &par);
3791 774 : sess->total_time_since_req = (u32) (gf_sys_clock_high_res() - sess->request_start_time);
3792 774 : run_time = gf_sys_clock_high_res() - sess->start_time;
3793 :
3794 774 : GF_LOG(GF_LOG_INFO, GF_LOG_HTTP, ("[HTTP] %s (%d bytes) downloaded in "LLU" us (%d kbps) (%d us since request - got response in %d us)\n", gf_file_basename(gf_cache_get_url(sess->cache_entry)), sess->bytes_done,
3795 : run_time, 8*sess->bytes_per_sec/1000, sess->total_time_since_req, sess->reply_time));
3796 :
3797 774 : if (sess->chunked && (payload_size==2))
3798 : payload_size=0;
3799 : }
3800 :
3801 9681 : if (rewrite_size && sess->chunked && data) {
3802 1308 : if (original_payload) {
3803 : //use memmove since regions overlap
3804 1295 : memmove(original_payload + *rewrite_size, data, nbBytes);
3805 : }
3806 1308 : *rewrite_size += nbBytes;
3807 : }
3808 :
3809 9681 : if (!sess->nb_left_in_chunk && remaining) {
3810 116 : sess->nb_left_in_chunk = remaining;
3811 9565 : } else if (payload_size) {
3812 1070 : gf_dm_data_received(sess, payload, payload_size, store_in_init, rewrite_size, original_payload);
3813 : }
3814 : }
3815 :
3816 1 : static Bool dm_exceeds_cap_rate(GF_DownloadManager * dm)
3817 : {
3818 : u32 cumul_rate = 0;
3819 : // u32 nb_sess = 0;
3820 1 : u64 now = gf_sys_clock_high_res();
3821 1 : u32 i, count = gf_list_count(dm->sessions);
3822 :
3823 : //check if this fits with all other sessions
3824 0 : for (i=0; i<count; i++) {
3825 0 : GF_DownloadSession * sess = (GF_DownloadSession*)gf_list_get(dm->sessions, i);
3826 :
3827 : //session not running done
3828 0 : if (sess->status != GF_NETIO_DATA_EXCHANGE) continue;
3829 :
3830 : //compute average rate on a window of 200 ms
3831 : //we cannot just use sess->bytes_per_sec because the rate limit might be changed dynamically
3832 : //so we need a recent history, not the session history
3833 : //note that we don't try to use the estimated bps of chunk transfer when capping
3834 0 : if (!sess->last_cap_rate_time) {
3835 : u64 runtime;
3836 0 : sess->last_cap_rate_time = sess->request_start_time;
3837 0 : sess->last_cap_rate_bytes = sess->bytes_done;
3838 :
3839 : /*compute bps starting from request send time, do not call update_download_rate as we don't want the chunk transfer rate*/
3840 0 : runtime = (gf_sys_clock_high_res() - sess->request_start_time);
3841 0 : if (!runtime) runtime=1;
3842 0 : sess->last_cap_rate_bytes_per_sec = (u32) ((1000000 * (u64) sess->bytes_done) / runtime);
3843 0 : } else if (now > sess->last_cap_rate_time) {
3844 0 : u64 time = now - sess->last_cap_rate_time;
3845 0 : u64 bytes = sess->bytes_done - sess->last_cap_rate_bytes;
3846 0 : sess->last_cap_rate_bytes_per_sec = (u32) ((1000000 * (u64) bytes) / time);
3847 0 : if (time > 200000) {
3848 : //this is an approximation we don't know precisely when these were received
3849 : //and we don't really care since next rate estimation will be really high anyway and will exceed cap
3850 0 : sess->last_cap_rate_bytes = sess->bytes_done;
3851 0 : sess->last_cap_rate_time = now;
3852 : }
3853 : } else {
3854 : return GF_TRUE;
3855 : }
3856 0 : cumul_rate += sess->last_cap_rate_bytes_per_sec;
3857 : //nb_sess ++;
3858 : }
3859 1 : if ( cumul_rate >= dm->limit_data_rate)
3860 : return GF_TRUE;
3861 :
3862 : return GF_FALSE;
3863 : }
3864 :
3865 14422 : static void gf_dm_sess_estimate_chunk_rate(GF_DownloadSession *sess, u32 nb_bytes)
3866 : {
3867 14422 : u64 now = gf_sys_clock_high_res();
3868 14422 : sess->chunk_bytes += nb_bytes;
3869 14422 : if ((now > sess->last_chunk_start_time + sess->chunk_wnd_dur) || (sess->total_size==sess->bytes_done) ) {
3870 1836 : if (sess->chunk_bytes) {
3871 196 : u32 tot_bytes = sess->chunk_bytes + sess->chunk_header_bytes;
3872 : //compute rate in bytes per seconds
3873 196 : Double rate = 1000000.0 * tot_bytes;
3874 196 : rate /= (now - sess->last_chunk_start_time);
3875 :
3876 : //cumulated rate is the weighted sum of our probe rates, the weight being the number of bytes
3877 : //when comuting the bitrate, we will divide by the total size
3878 196 : sess->cumulated_chunk_rate += rate * tot_bytes;
3879 :
3880 196 : sess->chunk_bytes = 0;
3881 196 : sess->cumulated_chunk_header_bytes += sess->chunk_header_bytes;
3882 196 : sess->chunk_header_bytes = 0;
3883 :
3884 : //we are done, update rate
3885 196 : if (sess->total_size==sess->bytes_done)
3886 34 : dm_sess_update_download_rate(sess);
3887 : }
3888 1836 : sess->last_chunk_start_time = now;
3889 : }
3890 14422 : }
3891 :
3892 : const u8 *gf_cache_get_content(const DownloadedCacheEntry entry, u32 *size);
3893 : void gf_cache_release_content(const DownloadedCacheEntry entry);
3894 :
3895 : GF_EXPORT
3896 136471 : GF_Err gf_dm_sess_fetch_data(GF_DownloadSession *sess, char *buffer, u32 buffer_size, u32 *read_size)
3897 : {
3898 : u32 size;
3899 : GF_Err e;
3900 :
3901 136471 : if (!buffer || !buffer_size) {
3902 0 : if (sess->put_state) {
3903 0 : sess->put_state = 2;
3904 0 : sess->status = GF_NETIO_WAIT_FOR_REPLY;
3905 0 : return GF_OK;
3906 : }
3907 : return GF_BAD_PARAM;
3908 : }
3909 136471 : if (sess->th)
3910 : return GF_BAD_PARAM;
3911 136471 : if (sess->status == GF_NETIO_DISCONNECTED) {
3912 322 : if (!sess->init_data_size)
3913 : return GF_EOS;
3914 : }
3915 136149 : else if (sess->status == GF_NETIO_STATE_ERROR) {
3916 0 : return sess->last_error;
3917 : }
3918 136149 : else if (sess->status > GF_NETIO_DATA_TRANSFERED)
3919 : return GF_BAD_PARAM;
3920 :
3921 136433 : *read_size = 0;
3922 136433 : if (sess->status == GF_NETIO_DATA_TRANSFERED) {
3923 290 : if (!sess->server_mode)
3924 : return GF_EOS;
3925 276 : if (!sess->init_data_size && sess->total_size && (sess->total_size==sess->bytes_done))
3926 : return GF_EOS;
3927 268 : sess->status = GF_NETIO_DATA_EXCHANGE;
3928 : }
3929 :
3930 136411 : if (sess->status == GF_NETIO_SETUP) {
3931 131 : gf_dm_connect(sess);
3932 131 : if (sess->last_error)
3933 : return sess->last_error;
3934 : e = GF_OK;
3935 136280 : } else if (sess->status < GF_NETIO_DATA_EXCHANGE) {
3936 41883 : sess->do_requests(sess);
3937 41883 : e = sess->last_error;
3938 : }
3939 : /*we're running but we had data previously*/
3940 94397 : else if (sess->init_data) {
3941 : e = GF_OK;
3942 503 : if (sess->init_data_size<=buffer_size) {
3943 457 : memcpy(buffer, sess->init_data, sizeof(char)*sess->init_data_size);
3944 457 : *read_size = sess->init_data_size;
3945 457 : gf_free(sess->init_data);
3946 457 : sess->init_data = NULL;
3947 457 : if (sess->init_data_size==sess->total_size)
3948 : e = GF_EOS;
3949 457 : sess->init_data_size = 0;
3950 : } else {
3951 46 : memcpy(buffer, sess->init_data, sizeof(char)*buffer_size);
3952 46 : *read_size = buffer_size;
3953 46 : sess->init_data_size -= buffer_size;
3954 46 : memmove(sess->init_data, sess->init_data+buffer_size, sizeof(char)*sess->init_data_size);
3955 : e = GF_OK;
3956 : }
3957 93894 : } else if (sess->local_cache_only) {
3958 : u32 to_copy, data_size;
3959 : const u8 *ptr;
3960 : e = GF_OK;
3961 : assert(sess->cache_entry);
3962 : //always refresh total size
3963 20857 : sess->total_size = gf_cache_get_content_length(sess->cache_entry);
3964 :
3965 20857 : ptr = gf_cache_get_content(sess->cache_entry, &data_size);
3966 41614 : if (!ptr) return GF_OUT_OF_MEM;
3967 :
3968 20857 : if (sess->bytes_done >= data_size) {
3969 20757 : *read_size = 0;
3970 20757 : gf_cache_release_content(sess->cache_entry);
3971 20757 : if (gf_cache_is_done(sess->cache_entry)) {
3972 1 : sess->status = GF_NETIO_DATA_TRANSFERED;
3973 1 : return GF_EOS;
3974 : }
3975 : return GF_IP_NETWORK_EMPTY;
3976 : }
3977 100 : to_copy = data_size - sess->bytes_done;
3978 100 : if (to_copy > buffer_size) to_copy = buffer_size;
3979 :
3980 100 : memcpy(buffer, ptr + sess->bytes_done, to_copy);
3981 100 : sess->bytes_done += to_copy;
3982 100 : *read_size = to_copy;
3983 100 : if (gf_cache_is_done(sess->cache_entry))
3984 2 : sess->status = GF_NETIO_DATA_TRANSFERED;
3985 : else
3986 98 : sess->total_size = 0;
3987 100 : gf_cache_release_content(sess->cache_entry);
3988 : } else {
3989 :
3990 73037 : if (sess->dm && sess->dm->limit_data_rate) {
3991 0 : if (dm_exceeds_cap_rate(sess->dm))
3992 : return GF_IP_NETWORK_EMPTY;
3993 :
3994 0 : if (buffer_size > sess->dm->read_buf_size)
3995 : buffer_size = sess->dm->read_buf_size;
3996 : }
3997 :
3998 : e = GF_OK;
3999 73037 : *read_size = 0;
4000 : u32 nb_read = 0;
4001 : //perform a loop, mostly for chunk-tranfer mode where a server may push a lot of small TCP frames,
4002 : //we want to flush everything as fast as possible
4003 7958 : while (1) {
4004 80995 : u32 single_read = 0;
4005 :
4006 80995 : if (sess->remaining_data && sess->remaining_data_size) {
4007 14571 : if (nb_read + sess->remaining_data_size >= buffer_size) {
4008 0 : if (!nb_read) {
4009 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_HTTP, ("[HTTP] No HTTP chunk header found for %d bytes, assuming broken chunk transfer and aborting\n", sess->remaining_data_size));
4010 0 : return GF_NON_COMPLIANT_BITSTREAM;
4011 : }
4012 73037 : break;
4013 : }
4014 14571 : memcpy(buffer + nb_read, sess->remaining_data, sess->remaining_data_size);
4015 66424 : } else if (nb_read >= buffer_size) {
4016 : break;
4017 : }
4018 76920 : e = gf_dm_read_data(sess, buffer + nb_read + sess->remaining_data_size, buffer_size - sess->remaining_data_size - nb_read, &single_read);
4019 76920 : if (e<0) {
4020 : assert(single_read==0);
4021 : break;
4022 : }
4023 :
4024 7958 : size = sess->remaining_data_size + single_read;
4025 7958 : sess->remaining_data_size = 0;
4026 7958 : single_read = 0;
4027 :
4028 : #ifdef GPAC_HAS_HTTP2
4029 : if (!sess->h2_sess)
4030 : #endif
4031 7958 : gf_dm_data_received(sess, (u8 *) buffer + nb_read, size, GF_FALSE, &single_read, buffer + nb_read);
4032 :
4033 :
4034 7958 : if (!sess->chunked)
4035 6677 : single_read = size;
4036 :
4037 7958 : nb_read += single_read;
4038 : }
4039 :
4040 : #ifdef GPAC_HAS_HTTP2
4041 : if (sess->h2_sess) {
4042 : nb_read = 0;
4043 : h2_flush_data_ex(sess, buffer, buffer_size, &nb_read);
4044 : h2_session_send(sess);
4045 :
4046 : //stream is over and all data flushed, move to GF_NETIO_DATA_TRANSFERED in client mode
4047 : if (sess->h2_data_done && !sess->h2_buf.size && !sess->server_mode) {
4048 : sess->status = GF_NETIO_DATA_TRANSFERED;
4049 : }
4050 : }
4051 : #endif
4052 :
4053 73037 : *read_size = nb_read;
4054 : //we had data but last call to gf_dm_read_data may have returned network empty
4055 73037 : if (nb_read && (e<0))
4056 : e = GF_OK;
4057 :
4058 :
4059 : //estimate rate for chunk-transfer - we only do that for fetch_data
4060 73037 : if (sess->chunked
4061 : #ifdef GPAC_HAS_HTTP2
4062 : || sess->h2_sess
4063 : #endif
4064 : )
4065 14413 : gf_dm_sess_estimate_chunk_rate(sess, nb_read);
4066 :
4067 73037 : if (! (*read_size) && (e==GF_IP_NETWORK_EMPTY)) {
4068 : #ifdef GPAC_HAS_HTTP2
4069 : if (sess->h2_sess && (!sess->h2_stream_id || sess->h2_data_done) && sess->bytes_done && !sess->total_size) {
4070 : sess->status = GF_NETIO_DATA_TRANSFERED;
4071 : return GF_EOS;
4072 : }
4073 : #endif
4074 :
4075 67961 : e = gf_sk_probe(sess->sock);
4076 67961 : if ((e==GF_IP_CONNECTION_CLOSED) || (gf_sys_clock_high_res() - sess->last_fetch_time > 1000 * sess->request_timeout)
4077 : ) {
4078 1 : if (e==GF_IP_CONNECTION_CLOSED) {
4079 1 : sess->last_error = GF_IP_CONNECTION_CLOSED;
4080 : sess_connection_closed(sess);
4081 : } else {
4082 0 : sess->last_error = GF_IP_NETWORK_EMPTY;
4083 : }
4084 1 : sess->status = GF_NETIO_STATE_ERROR;
4085 1 : return GF_IP_NETWORK_EMPTY;
4086 : }
4087 : }
4088 : }
4089 :
4090 115653 : if (sess->server_mode && (sess->status == GF_NETIO_DATA_EXCHANGE)) {
4091 266 : sess->status = GF_NETIO_DATA_TRANSFERED;
4092 : }
4093 :
4094 : return e;
4095 : }
4096 :
4097 : GF_EXPORT
4098 135637 : 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)
4099 : {
4100 135637 : if (!sess)
4101 : return GF_BAD_PARAM;
4102 135637 : if (server) *server = sess->server_name;
4103 135637 : if (path) *path = sess->remote_path;
4104 135637 : if (total_size) {
4105 47764 : if (sess->total_size==SIZE_IN_STREAM) *total_size = 0;
4106 47761 : else *total_size = sess->total_size;
4107 : }
4108 135637 : if (bytes_done) *bytes_done = sess->bytes_done;
4109 135637 : if (bytes_per_sec) {
4110 135637 : if (sess->dm && sess->dm->limit_data_rate && sess->last_cap_rate_bytes_per_sec) {
4111 0 : *bytes_per_sec = sess->last_cap_rate_bytes_per_sec;
4112 : } else {
4113 135637 : *bytes_per_sec = sess->bytes_per_sec;
4114 : }
4115 : }
4116 :
4117 135637 : if (net_status) *net_status = sess->status;
4118 135637 : if (sess->status == GF_NETIO_DISCONNECTED) return GF_EOS;
4119 134724 : else if (sess->status == GF_NETIO_STATE_ERROR) return GF_SERVICE_ERROR;
4120 134724 : return GF_OK;
4121 : }
4122 :
4123 : GF_EXPORT
4124 133 : u64 gf_dm_sess_get_utc_start(GF_DownloadSession * sess)
4125 : {
4126 133 : if (!sess) return 0;
4127 133 : return sess->start_time_utc;
4128 : }
4129 :
4130 : GF_EXPORT
4131 1532 : const char *gf_dm_sess_get_cache_name(GF_DownloadSession * sess)
4132 : {
4133 1532 : if (!sess) return NULL;
4134 1532 : if (! sess->cache_entry || sess->needs_cache_reconfig) return NULL;
4135 1528 : if (!sess->use_cache_file) return NULL;
4136 1527 : return gf_cache_get_cache_filename(sess->cache_entry);
4137 : }
4138 :
4139 : #if 0 //unused
4140 : /*!
4141 : * Tells whether session can be cached on disk.
4142 : * Typically, when request has no content length, it deserves being streamed an cannot be cached
4143 : * (ICY or MPEG-streamed content
4144 : \param sess The session
4145 : \param True if a cache can be created
4146 : */
4147 : Bool gf_dm_sess_can_be_cached_on_disk(const GF_DownloadSession *sess)
4148 : {
4149 : if (!sess) return GF_FALSE;
4150 : return gf_cache_get_content_length(sess->cache_entry) != 0;
4151 : }
4152 : #endif
4153 :
4154 : GF_EXPORT
4155 43 : void gf_dm_sess_abort(GF_DownloadSession * sess)
4156 : {
4157 43 : if (sess) {
4158 42 : gf_mx_p(sess->mx);
4159 :
4160 : #ifdef GPAC_HAS_HTTP2
4161 : if (sess->h2_sess && (sess->status==GF_NETIO_DATA_EXCHANGE)) {
4162 : nghttp2_submit_rst_stream(sess->h2_sess->ng_sess, NGHTTP2_FLAG_NONE, sess->h2_stream_id, NGHTTP2_NO_ERROR);
4163 : h2_session_send(sess);
4164 : }
4165 : #endif
4166 42 : gf_dm_disconnect(sess, HTTP_CLOSE);
4167 42 : sess->status = GF_NETIO_STATE_ERROR;
4168 42 : gf_mx_v(sess->mx);
4169 : }
4170 43 : }
4171 :
4172 : #if 0 //unused
4173 : /*!
4174 : \brief gets private data
4175 : *
4176 : *Gets private data associated with the session.
4177 : \param sess the download session
4178 : \return the private data
4179 : \warning the private_data parameter is reserved for bandwidth statistics per service when used in the GPAC terminal.
4180 : */
4181 : void *gf_dm_sess_get_private(GF_DownloadSession * sess)
4182 : {
4183 : return sess ? sess->ext : NULL;
4184 : }
4185 :
4186 : /*!
4187 : \brief sets private data
4188 : *
4189 : *associate private data with the session.
4190 : \param sess the download session
4191 : \param private_data the private data
4192 : \warning the private_data parameter is reserved for bandwidth statistics per service when used in the GPAC terminal.
4193 : */
4194 : void gf_dm_sess_set_private(GF_DownloadSession * sess, void *private_data)
4195 : {
4196 : if (sess) sess->ext = private_data;
4197 : }
4198 : #endif
4199 :
4200 : /*!
4201 : * Sends the HTTP headers
4202 : \param sess The GF_DownloadSession
4203 : \param sHTTP buffer containing the request
4204 : \param GF_OK if everything went fine, the error otherwise
4205 : */
4206 840 : static GF_Err http_send_headers(GF_DownloadSession *sess, char * sHTTP) {
4207 : GF_Err e;
4208 : GF_NETIO_Parameter par;
4209 : Bool no_cache = GF_FALSE;
4210 : char range_buf[1024];
4211 : char pass_buf[1124];
4212 : char req_name[20];
4213 : const char *user_agent;
4214 : const char *url;
4215 : const char *user_profile;
4216 : const char *param_string;
4217 : Bool inject_icy = GF_FALSE;
4218 : u32 i, count;
4219 : GF_HTTPHeader *hdr;
4220 : Bool has_accept, has_connection, has_range, has_agent, has_language, send_profile, has_mime, has_chunk_transfer;
4221 : assert (sess->status == GF_NETIO_CONNECTED);
4222 :
4223 840 : gf_dm_sess_clear_headers(sess);
4224 : assert(sess->remaining_data_size == 0);
4225 :
4226 840 : if (sess->needs_cache_reconfig) {
4227 644 : gf_dm_configure_cache(sess);
4228 644 : sess->needs_cache_reconfig = 0;
4229 : }
4230 840 : if (sess->from_cache_only) {
4231 18 : sess->last_fetch_time = sess->request_start_time = gf_sys_clock_high_res();
4232 18 : sess->req_hdr_size = 0;
4233 18 : sess->status = GF_NETIO_WAIT_FOR_REPLY;
4234 18 : gf_dm_sess_notify_state(sess, GF_NETIO_WAIT_FOR_REPLY, GF_OK);
4235 18 : return GF_OK;
4236 : }
4237 :
4238 : /*setup authentification*/
4239 : strcpy(pass_buf, "");
4240 822 : sess->creds = gf_find_user_credentials_for_site( sess->dm, sess->server_name );
4241 822 : if (sess->creds && sess->creds->valid) {
4242 0 : sprintf(pass_buf, "Basic %s", sess->creds->digest);
4243 : }
4244 :
4245 822 : user_agent = gf_opts_get_key("core", "ua");
4246 822 : if (!user_agent) user_agent = GF_DOWNLOAD_AGENT_NAME;
4247 :
4248 822 : sess->put_state = 0;
4249 :
4250 822 : par.error = GF_OK;
4251 822 : par.msg_type = GF_NETIO_GET_METHOD;
4252 822 : par.name = NULL;
4253 : gf_dm_sess_user_io(sess, &par);
4254 822 : if (!par.name || sess->server_only_understand_get) {
4255 792 : par.name = "GET";
4256 : }
4257 :
4258 822 : strncpy(req_name, par.name, 19);
4259 822 : req_name[19] = 0;
4260 :
4261 822 : if (!strcmp(req_name, "GET")) {
4262 792 : sess->http_read_type = GET;
4263 : #ifdef GPAC_HAS_HTTP2
4264 : if (!sess->h2_sess)
4265 : #endif
4266 : inject_icy = GF_TRUE;
4267 30 : } else if (!strcmp(req_name, "HEAD")) sess->http_read_type = HEAD;
4268 30 : else sess->http_read_type = OTHER;
4269 :
4270 822 : if (!strcmp(req_name, "PUT") || !strcmp(req_name, "POST"))
4271 28 : sess->put_state = 1;
4272 :
4273 822 : url = (sess->proxy_enabled==1) ? sess->orig_url : sess->remote_path;
4274 :
4275 : /*get all headers*/
4276 822 : gf_dm_sess_clear_headers(sess);
4277 :
4278 :
4279 : #define PUSH_HDR(_name, _value) {\
4280 : GF_SAFEALLOC(hdr, GF_HTTPHeader)\
4281 : hdr->name = gf_strdup(_name);\
4282 : hdr->value = gf_strdup(_value);\
4283 : gf_list_add(sess->headers, hdr);\
4284 : }
4285 :
4286 : has_agent = has_accept = has_connection = has_range = has_language = has_mime = has_chunk_transfer = GF_FALSE;
4287 : while (1) {
4288 878 : par.msg_type = GF_NETIO_GET_HEADER;
4289 878 : par.value = NULL;
4290 : gf_dm_sess_user_io(sess, &par);
4291 878 : if (!par.value) break;
4292 :
4293 56 : if (!stricmp(par.name, "Connection")) {
4294 0 : if (!stricmp(par.value, "close"))
4295 : has_connection = GF_TRUE;
4296 : else
4297 0 : continue;
4298 : }
4299 56 : else if (!stricmp(par.name, "Transfer-Encoding")) {
4300 28 : if (!stricmp(par.value, "chunked"))
4301 : has_chunk_transfer = GF_TRUE;
4302 28 : continue;
4303 : }
4304 :
4305 56 : PUSH_HDR(par.name, par.value)
4306 :
4307 28 : if (!stricmp(par.name, "Accept")) has_accept = GF_TRUE;
4308 28 : else if (!stricmp(par.name, "Range")) has_range = GF_TRUE;
4309 28 : else if (!stricmp(par.name, "User-Agent")) has_agent = GF_TRUE;
4310 28 : else if (!stricmp(par.name, "Accept-Language")) has_language = GF_TRUE;
4311 28 : else if (!stricmp(par.name, "Content-Type")) has_mime = GF_TRUE;
4312 :
4313 28 : if (!par.msg_type) break;
4314 : }
4315 1644 : if (!has_agent) PUSH_HDR("User-Agent", user_agent)
4316 :
4317 : /*no mime and POST/PUT, default to octet stream*/
4318 824 : if (!has_mime && (sess->http_read_type==OTHER)) PUSH_HDR("Content-Type", "application/octet-stream")
4319 :
4320 1614 : if (!has_accept && (sess->http_read_type!=OTHER) ) PUSH_HDR("Accept", "*/*")
4321 :
4322 : #ifdef GPAC_HAS_HTTP2
4323 : if (sess->h2_sess)
4324 : has_connection = GF_TRUE;
4325 :
4326 : if (!has_connection && !sess->h2_sess
4327 : #ifdef GPAC_HAS_SSL
4328 : && !sess->ssl
4329 : #endif
4330 : && !sess->dm->disable_http2 && (sess->h2_upgrade_state!=2)
4331 : && !gf_opts_get_bool("core", "no-h2c")
4332 : ) {
4333 : u8 settings[HTTP2_BUFFER_SETTINGS_SIZE];
4334 : u32 settings_len;
4335 : u8 b64[100];
4336 : u32 b64len;
4337 :
4338 : nghttp2_settings_entry h2_settings[2] = {
4339 : {NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100},
4340 : {NGHTTP2_SETTINGS_ENABLE_PUSH, 0}
4341 : };
4342 :
4343 : PUSH_HDR("Connection", "Upgrade, HTTP2-Settings")
4344 : PUSH_HDR("Upgrade", "h2c")
4345 :
4346 :
4347 : settings_len = (u32) nghttp2_pack_settings_payload(settings, HTTP2_BUFFER_SETTINGS_SIZE, h2_settings, GF_ARRAY_LENGTH(h2_settings));
4348 : b64len = gf_base64_encode(settings, settings_len, b64, 100);
4349 : b64[b64len] = 0;
4350 : PUSH_HDR("HTTP2-Settings", b64)
4351 :
4352 : sess->h2_upgrade_state = 1;
4353 : inject_icy = GF_FALSE;
4354 : } else
4355 : #endif
4356 822 : if (sess->proxy_enabled==1) PUSH_HDR("Proxy-Connection", "Keep-alive")
4357 822 : else if (!has_connection) {
4358 1644 : PUSH_HDR("Connection", "Keep-Alive");
4359 : }
4360 :
4361 :
4362 822 : if (has_chunk_transfer
4363 : #ifdef GPAC_HAS_HTTP2
4364 : && !sess->h2_sess
4365 : #endif
4366 : ) {
4367 56 : PUSH_HDR("Transfer-Encoding", "chunked");
4368 28 : sess->chunked = GF_TRUE;
4369 : }
4370 :
4371 822 : if (!has_range && sess->needs_range) {
4372 94 : if (!sess->range_end)
4373 0 : sprintf(range_buf, "bytes="LLD"-", sess->range_start);
4374 : //if end is set to -1 use open end
4375 94 : else if (sess->range_end==(u64)-1)
4376 1 : sprintf(range_buf, "bytes="LLD"-", sess->range_start);
4377 : else
4378 93 : sprintf(range_buf, "bytes="LLD"-"LLD"", sess->range_start, sess->range_end);
4379 188 : PUSH_HDR("Range", range_buf)
4380 : no_cache = GF_TRUE;
4381 : }
4382 822 : if (!has_language) {
4383 822 : const char *opt = gf_opts_get_key("core", "lang");
4384 822 : if (opt) PUSH_HDR("Accept-Language", opt)
4385 : }
4386 :
4387 :
4388 822 : if (strlen(pass_buf)) {
4389 0 : PUSH_HDR("Authorization", pass_buf)
4390 : }
4391 :
4392 822 : par.msg_type = GF_NETIO_GET_CONTENT;
4393 822 : par.data = NULL;
4394 822 : par.size = 0;
4395 :
4396 : /*check if we have personalization info*/
4397 : send_profile = GF_FALSE;
4398 822 : user_profile = gf_opts_get_key("core", "user-profileid");
4399 :
4400 822 : if (user_profile) {
4401 0 : PUSH_HDR("X-UserProfileID", user_profile);
4402 822 : } else if ((sess->http_read_type == GET) || (sess->http_read_type == HEAD) ) {
4403 792 : user_profile = gf_opts_get_key("core", "user-profile");
4404 792 : if (user_profile && gf_file_exists(user_profile)) {
4405 0 : FILE *profile = gf_fopen(user_profile, "rb");
4406 0 : if (profile) {
4407 0 : par.size = (u32) gf_fsize(profile);
4408 0 : gf_fclose(profile);
4409 0 : sprintf(range_buf, "%d", par.size);
4410 0 : PUSH_HDR("Content-Length", range_buf);
4411 0 : PUSH_HDR("Content-Type", "text/xml");
4412 : send_profile = GF_TRUE;
4413 : }
4414 : }
4415 : }
4416 :
4417 :
4418 : if (!send_profile) {
4419 : gf_dm_sess_user_io(sess, &par);
4420 822 : if (par.data && par.size) {
4421 : sprintf(range_buf, "%d", par.size);
4422 0 : PUSH_HDR("Content-Length", range_buf);
4423 : } else {
4424 822 : par.data = NULL;
4425 822 : par.size = 0;
4426 : }
4427 : }
4428 :
4429 822 : if (inject_icy) {
4430 : /* This will force the server to respond with Icy-Metaint */
4431 1584 : PUSH_HDR("Icy-Metadata", "1");
4432 : }
4433 :
4434 822 : if (sess->http_read_type!=OTHER) {
4435 792 : const char *etag=NULL, *last_modif=NULL;
4436 :
4437 : /*cached headers are not appended in POST*/
4438 792 : if (!no_cache && !sess->disable_cache && (GF_OK < gf_cache_get_http_headers( sess->cache_entry, &etag, &last_modif)) ) {
4439 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_HTTP, ("Cache Entry : %p, FAILED to append cache directives.", sess->cache_entry));
4440 : }
4441 :
4442 891 : if (etag) PUSH_HDR("If-None-Match", "etag")
4443 807 : if (last_modif) PUSH_HDR("If-Modified-Since", last_modif)
4444 : }
4445 :
4446 :
4447 : //done gathering headers
4448 :
4449 822 : param_string = gf_opts_get_key("core", "query-string");
4450 :
4451 : #ifdef GPAC_HAS_HTTP2
4452 : if (sess->h2_sess) {
4453 : Bool has_body = GF_FALSE;
4454 :
4455 : gf_mx_p(sess->mx);
4456 :
4457 : sess->h2_send_data = NULL;
4458 : if (par.data && par.size) {
4459 : has_body = GF_TRUE;
4460 : sess->h2_send_data = (u8 *) par.data;
4461 : sess->h2_send_data_len = par.size;
4462 : sess->h2_is_eos = GF_TRUE;
4463 : } else if (sess->put_state==1) {
4464 : has_body = GF_TRUE;
4465 : }
4466 :
4467 : e = h2_submit_request(sess, req_name, url, param_string, has_body);
4468 :
4469 : sess->last_fetch_time = sess->request_start_time = gf_sys_clock_high_res();
4470 : if (!e)
4471 : e = h2_session_send(sess);
4472 :
4473 : //in case we have a body already setup with this request
4474 : h2_flush_send(sess);
4475 :
4476 : gf_mx_v(sess->mx);
4477 :
4478 : sess->h2_is_eos = GF_FALSE;
4479 : goto req_sent;
4480 : }
4481 : #endif // GPAC_HAS_HTTP2
4482 :
4483 822 : if (param_string) {
4484 0 : if (strchr(sess->remote_path, '?')) {
4485 0 : sprintf(sHTTP, "%s %s&%s HTTP/1.1\r\nHost: %s\r\n", req_name, url, param_string, sess->server_name);
4486 : } else {
4487 0 : sprintf(sHTTP, "%s %s?%s HTTP/1.1\r\nHost: %s\r\n", req_name, url, param_string, sess->server_name);
4488 : }
4489 : } else {
4490 822 : sprintf(sHTTP, "%s %s HTTP/1.1\r\nHost: %s\r\n", req_name, url, sess->server_name);
4491 : }
4492 :
4493 : //serialize headers
4494 822 : count = gf_list_count(sess->headers);
4495 4316 : for (i=0; i<count; i++) {
4496 3494 : hdr = gf_list_get(sess->headers, i);
4497 3494 : strcat(sHTTP, hdr->name);
4498 : strcat(sHTTP, ": ");
4499 3494 : strcat(sHTTP, hdr->value);
4500 : strcat(sHTTP, "\r\n");
4501 : }
4502 :
4503 : strcat(sHTTP, "\r\n");
4504 :
4505 822 : if (send_profile || par.data) {
4506 0 : u32 len = (u32) strlen(sHTTP);
4507 0 : char *tmp_buf = (char*)gf_malloc(sizeof(char)*(len+par.size+1));
4508 : strcpy(tmp_buf, sHTTP);
4509 0 : if (par.data) {
4510 0 : memcpy(tmp_buf+len, par.data, par.size);
4511 0 : tmp_buf[len+par.size] = 0;
4512 :
4513 0 : sess->put_state = 2;
4514 : } else {
4515 : FILE *profile;
4516 0 : user_profile = gf_opts_get_key("core", "user-profile");
4517 : assert (user_profile);
4518 0 : profile = gf_fopen(user_profile, "rt");
4519 0 : if (profile) {
4520 0 : s32 read = (s32) gf_fread(tmp_buf+len, par.size, profile);
4521 0 : if ((read<0) || (read< (s32) par.size)) {
4522 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_HTTP,
4523 : ("[HTTP] Error while loading UserProfile, size=%d, should be %d.", read, par.size));
4524 0 : for (; read < (s32) par.size; read++) {
4525 0 : tmp_buf[len + read] = 0;
4526 : }
4527 : }
4528 0 : gf_fclose(profile);
4529 : } else {
4530 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_HTTP, ("[HTTP] Error while loading Profile file %s.", user_profile));
4531 : }
4532 : }
4533 :
4534 0 : sess->last_fetch_time = sess->request_start_time = gf_sys_clock_high_res();
4535 0 : sess->req_hdr_size = len+par.size;
4536 :
4537 : #ifdef GPAC_HAS_SSL
4538 0 : if (sess->ssl) {
4539 0 : e = gf_ssl_write(sess->ssl, tmp_buf, len+par.size);
4540 : } else
4541 : #endif
4542 0 : e = gf_sk_send(sess->sock, tmp_buf, len+par.size);
4543 :
4544 0 : GF_LOG(GF_LOG_INFO, GF_LOG_HTTP, ("[HTTP] Sending request at UTC "LLD" %s\n\n", gf_net_get_utc(), tmp_buf));
4545 0 : gf_free(tmp_buf);
4546 : } else {
4547 822 : u32 len = (u32) strlen(sHTTP);
4548 :
4549 822 : sess->last_fetch_time = sess->request_start_time = gf_sys_clock_high_res();
4550 822 : sess->req_hdr_size = len;
4551 :
4552 : #ifdef GPAC_HAS_SSL
4553 822 : if (sess->ssl) {
4554 153 : e = gf_ssl_write(sess->ssl, sHTTP, len);
4555 : } else
4556 : #endif
4557 669 : e = gf_sk_send(sess->sock, sHTTP, len);
4558 :
4559 : #ifndef GPAC_DISABLE_LOG
4560 822 : if (e) {
4561 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_HTTP, ("[HTTP] Error sending request %s\n", gf_error_to_string(e) ));
4562 : } else {
4563 822 : GF_LOG(GF_LOG_INFO, GF_LOG_HTTP, ("[HTTP] Sending request at UTC "LLU" %s\n\n", gf_net_get_utc(), sHTTP));
4564 : }
4565 : #endif
4566 : }
4567 :
4568 :
4569 : #ifdef GPAC_HAS_HTTP2
4570 : req_sent:
4571 : #endif
4572 822 : gf_dm_sess_clear_headers(sess);
4573 :
4574 822 : if (e) {
4575 0 : sess->status = GF_NETIO_STATE_ERROR;
4576 0 : sess->last_error = e;
4577 0 : gf_dm_sess_notify_state(sess, GF_NETIO_STATE_ERROR, e);
4578 0 : return e;
4579 : }
4580 :
4581 822 : gf_dm_sess_notify_state(sess, GF_NETIO_WAIT_FOR_REPLY, GF_OK);
4582 822 : if (sess->put_state==1) {
4583 28 : sess->status = GF_NETIO_DATA_TRANSFERED;
4584 : } else {
4585 794 : sess->status = GF_NETIO_WAIT_FOR_REPLY;
4586 : }
4587 : return GF_OK;
4588 : }
4589 :
4590 :
4591 : /*!
4592 : * Parse the remaining part of body
4593 : \param sess The session
4594 : \param sHTTP the data buffer
4595 : \param The error code if any
4596 : */
4597 2385 : static GF_Err http_parse_remaining_body(GF_DownloadSession * sess, char * sHTTP)
4598 : {
4599 : GF_Err e;
4600 2385 : u32 buf_size = sess->dm ? sess->dm->read_buf_size : GF_DOWNLOAD_BUFFER_SIZE;
4601 :
4602 0 : while (1) {
4603 2385 : u32 prev_remaining_data_size, size=0, rewrite_size=0;
4604 2385 : if (sess->status>=GF_NETIO_DISCONNECTED)
4605 2385 : return GF_REMOTE_SERVICE_ERROR;
4606 :
4607 2385 : if (sess->dm && sess->dm->limit_data_rate && sess->bytes_per_sec) {
4608 0 : if (dm_exceeds_cap_rate(sess->dm)) {
4609 0 : gf_sleep(1);
4610 0 : return GF_OK;
4611 : }
4612 : }
4613 :
4614 : //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)
4615 2385 : if (sess->remaining_data && sess->remaining_data_size) {
4616 82 : if (sess->remaining_data_size >= buf_size) {
4617 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_HTTP, ("[HTTP] No HTTP chunk header found for %d bytes, assuming broken chunk transfer and aborting\n", sess->remaining_data_size));
4618 : return GF_NON_COMPLIANT_BITSTREAM;
4619 : }
4620 82 : memcpy(sHTTP, sess->remaining_data, sess->remaining_data_size);
4621 : }
4622 2385 : e = gf_dm_read_data(sess, sHTTP + sess->remaining_data_size, buf_size - sess->remaining_data_size, &size);
4623 2385 : if ((e != GF_IP_CONNECTION_CLOSED) && (!size || e == GF_IP_NETWORK_EMPTY)) {
4624 2233 : if (!sess->total_size && !sess->chunked && (gf_sys_clock_high_res() - sess->start_time > 5000000)) {
4625 0 : sess->total_size = sess->bytes_done;
4626 0 : gf_dm_sess_notify_state(sess, GF_NETIO_DATA_TRANSFERED, GF_OK);
4627 : assert(sess->server_name);
4628 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_HTTP, ("[HTTP] Disconnected from %s: %s\n", sess->server_name, gf_error_to_string(e)));
4629 0 : gf_dm_disconnect(sess, HTTP_NO_CLOSE);
4630 : }
4631 : return GF_OK;
4632 : }
4633 :
4634 152 : if (e) {
4635 0 : if (sess->sock && (e == GF_IP_CONNECTION_CLOSED)) {
4636 0 : u32 len = gf_cache_get_content_length(sess->cache_entry);
4637 0 : if (size > 0) {
4638 : #ifdef GPAC_HAS_HTTP2
4639 : if (sess->h2_sess) {
4640 : h2_flush_data(sess, GF_FALSE);
4641 : } else
4642 : #endif
4643 0 : gf_dm_data_received(sess, (u8 *) sHTTP, size, GF_FALSE, NULL, NULL);
4644 : }
4645 :
4646 0 : if ( ( (len == 0) && sess->use_cache_file) || sess->bytes_done) {
4647 0 : sess->total_size = sess->bytes_done;
4648 : // HTTP 1.1 without content length...
4649 0 : gf_dm_sess_notify_state(sess, GF_NETIO_DATA_TRANSFERED, GF_OK);
4650 : assert(sess->server_name);
4651 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_HTTP, ("[HTTP] Disconnected from %s: %s\n", sess->server_name, gf_error_to_string(e)));
4652 0 : if (sess->use_cache_file)
4653 0 : gf_cache_set_content_length(sess->cache_entry, sess->bytes_done);
4654 : e = GF_OK;
4655 : }
4656 : }
4657 0 : gf_dm_disconnect(sess, HTTP_CLOSE);
4658 0 : sess->last_error = e;
4659 0 : gf_dm_sess_notify_state(sess, sess->status, e);
4660 0 : return e;
4661 : }
4662 :
4663 152 : prev_remaining_data_size = sess->remaining_data_size;
4664 152 : sess->remaining_data_size = 0;
4665 :
4666 152 : sHTTP[size + prev_remaining_data_size] = 0;
4667 :
4668 : #ifdef GPAC_HAS_HTTP2
4669 : if (sess->h2_sess) {
4670 : h2_flush_data(sess, GF_FALSE);
4671 : if (sess->h2_data_done)
4672 : sess->status = GF_NETIO_DATA_TRANSFERED;
4673 : } else
4674 : #endif
4675 152 : gf_dm_data_received(sess, (u8 *) sHTTP, size + prev_remaining_data_size, GF_FALSE, &rewrite_size, NULL);
4676 :
4677 152 : if (sess->chunked)
4678 1 : gf_dm_sess_estimate_chunk_rate(sess, rewrite_size);
4679 :
4680 :
4681 : /*socket empty*/
4682 152 : if (size < buf_size) {
4683 : return GF_OK;
4684 : }
4685 : }
4686 : return GF_OK;
4687 : }
4688 :
4689 799 : static void notify_headers(GF_DownloadSession *sess, char * sHTTP, s32 bytesRead, s32 BodyStart)
4690 : {
4691 : GF_NETIO_Parameter par;
4692 : u32 i, count;
4693 :
4694 799 : count = gf_list_count(sess->headers);
4695 : memset(&par, 0, sizeof(GF_NETIO_Parameter));
4696 :
4697 9728 : for (i=0; i<count; i++) {
4698 8929 : GF_HTTPHeader *hdrp = (GF_HTTPHeader*)gf_list_get(sess->headers, i);
4699 8929 : par.name = hdrp->name;
4700 8929 : par.value = hdrp->value;
4701 :
4702 8929 : par.error = GF_OK;
4703 8929 : par.msg_type = GF_NETIO_PARSE_HEADER;
4704 : gf_dm_sess_user_io(sess, &par);
4705 : }
4706 :
4707 799 : if (sHTTP) {
4708 2 : sHTTP[bytesRead]=0;
4709 2 : par.error = GF_OK;
4710 2 : par.data = sHTTP + BodyStart;
4711 2 : par.size = (u32) strlen(par.data);
4712 2 : par.msg_type = GF_NETIO_DATA_EXCHANGE;
4713 : gf_dm_sess_user_io(sess, &par);
4714 : }
4715 799 : }
4716 :
4717 216 : static u32 http_parse_method(const char *comp)
4718 : {
4719 216 : if (!strcmp(comp, "GET")) return GF_HTTP_GET;
4720 30 : else if (!strcmp(comp, "HEAD")) return GF_HTTP_HEAD;
4721 30 : else if (!strcmp(comp, "OPTIONS")) return GF_HTTP_OPTIONS;
4722 30 : else if (!strcmp(comp, "PUT")) return GF_HTTP_PUT;
4723 2 : else if (!strcmp(comp, "POST")) return GF_HTTP_POST;
4724 2 : else if (!strcmp(comp, "DELETE")) return GF_HTTP_DELETE;
4725 0 : else if (!strcmp(comp, "CONNECT")) return GF_HTTP_CONNECT;
4726 0 : else if (!strcmp(comp, "TRACE")) return GF_HTTP_TRACE;
4727 0 : else return 0;
4728 : }
4729 :
4730 : /*!
4731 : * Waits for the response HEADERS, parse the information... and so on
4732 : \param sess The session
4733 : \param sHTTP the data buffer
4734 : */
4735 68139 : static GF_Err wait_for_header_and_parse(GF_DownloadSession *sess, char * sHTTP)
4736 : {
4737 : GF_NETIO_Parameter par;
4738 : s32 bytesRead, BodyStart;
4739 : u32 res, i, buf_size;
4740 : s32 LinePos, Pos;
4741 : u32 method=0;
4742 : u32 rsp_code=0, ContentLength, first_byte, last_byte, total_size, range, no_range;
4743 : Bool connection_closed = GF_FALSE;
4744 : char buf[1025];
4745 : char comp[400];
4746 : GF_Err e;
4747 : char * new_location;
4748 : const char * mime_type;
4749 : #ifdef GPAC_HAS_HTTP2
4750 : Bool upgrade_to_http2 = GF_FALSE;
4751 : #endif
4752 :
4753 :
4754 68139 : if (sess->server_mode) {
4755 : assert( sess->status == GF_NETIO_CONNECTED );
4756 : } else {
4757 : assert( sess->status == GF_NETIO_WAIT_FOR_REPLY );
4758 67886 : if (!(sess->flags & GF_NETIO_SESSION_NOT_CACHED)) {
4759 58643 : sess->use_cache_file = sess->dm->disable_cache ? GF_FALSE : GF_TRUE;
4760 : }
4761 : }
4762 68139 : bytesRead = res = 0;
4763 : new_location = NULL;
4764 :
4765 68139 : if (sess->from_cache_only) {
4766 18 : sess->reply_time = (u32) (gf_sys_clock_high_res() - sess->request_start_time);
4767 18 : sess->rsp_hdr_size = 0;
4768 18 : sess->total_size = sess->bytes_done = gf_cache_get_content_length(sess->cache_entry);
4769 :
4770 : memset(&par, 0, sizeof(GF_NETIO_Parameter));
4771 18 : par.msg_type = GF_NETIO_DATA_TRANSFERED;
4772 : par.error = GF_OK;
4773 : gf_dm_sess_user_io(sess, &par);
4774 18 : gf_dm_disconnect(sess, HTTP_NO_CLOSE);
4775 18 : return GF_OK;
4776 : }
4777 :
4778 68121 : buf_size = sess->dm ? sess->dm->read_buf_size : GF_DOWNLOAD_BUFFER_SIZE;
4779 :
4780 : //always set start time to the time at last attempt reply parsing
4781 68121 : sess->start_time = gf_sys_clock_high_res();
4782 68121 : sess->start_time_utc = gf_net_get_utc();
4783 68121 : sess->chunked = GF_FALSE;
4784 68121 : sess->last_chunk_found = GF_FALSE;
4785 :
4786 68121 : sess->last_chunk_start_time = sess->request_start_time;
4787 68121 : sess->chunk_bytes = 0;
4788 68121 : sess->cumulated_chunk_rate = 0;
4789 :
4790 : // gf_sk_reset(sess->sock);
4791 68121 : sHTTP[0] = 0;
4792 :
4793 : while (1) {
4794 68121 : e = gf_dm_read_data(sess, sHTTP + bytesRead, buf_size - bytesRead, &res);
4795 :
4796 : #ifdef GPAC_HAS_HTTP2
4797 : /* break as soon as we have a header frame*/
4798 : if (sess->h2_headers_seen) {
4799 : sess->h2_headers_seen = 0;
4800 : res = 0;
4801 : bytesRead = 0;
4802 : BodyStart = 0;
4803 : e = GF_OK;
4804 : break;
4805 : }
4806 : #endif
4807 :
4808 68121 : switch (e) {
4809 67102 : case GF_IP_NETWORK_EMPTY:
4810 67102 : if (!bytesRead) {
4811 67102 : e = gf_sk_probe(sess->sock);
4812 :
4813 67102 : if (e==GF_IP_CONNECTION_CLOSED) {
4814 37 : sess->last_error = GF_IP_CONNECTION_CLOSED;
4815 : sess_connection_closed(sess);
4816 37 : sess->status = GF_NETIO_STATE_ERROR;
4817 37 : return GF_IP_NETWORK_EMPTY;
4818 : }
4819 67065 : if (!sess->server_mode && (gf_sys_clock_high_res() - sess->request_start_time > 1000 * sess->request_timeout)) {
4820 3 : sess->last_error = GF_IP_NETWORK_EMPTY;
4821 3 : sess->status = GF_NETIO_STATE_ERROR;
4822 3 : return GF_IP_NETWORK_EMPTY;
4823 : }
4824 67062 : if (sess->server_mode)
4825 0 : sess->last_error = GF_IP_NETWORK_EMPTY;
4826 : return GF_OK;
4827 : }
4828 0 : if (sess->status==GF_NETIO_STATE_ERROR)
4829 0 : return sess->last_error;
4830 0 : if (!res && sess->status<=GF_NETIO_CONNECTED)
4831 : return GF_OK;
4832 :
4833 : #ifdef GPAC_HAS_HTTP2
4834 : //we may have received bytes (bytesRead>0) yet none for this session, return GF_IP_NETWORK_EMPTY if empty
4835 : if (sess->h2_sess)
4836 : return GF_IP_NETWORK_EMPTY;
4837 : #endif
4838 :
4839 0 : continue;
4840 : /*socket has been closed while configuring, retry (not sure if the server got the GET)*/
4841 0 : case GF_IP_CONNECTION_CLOSED:
4842 0 : if (sess->http_read_type == HEAD) {
4843 : /* Some servers such as shoutcast directly close connection if HEAD or an unknown method is issued */
4844 0 : sess->server_only_understand_get = GF_TRUE;
4845 : }
4846 0 : if (sess->server_mode) {
4847 0 : sess->last_error = GF_IP_CONNECTION_CLOSED;
4848 : sess_connection_closed(sess);
4849 0 : sess->status = GF_NETIO_DISCONNECTED;
4850 0 : GF_LOG(GF_LOG_INFO, GF_LOG_HTTP, ("[HTTP] Connection closed by client\n", sess->remote_path));
4851 : return GF_IP_CONNECTION_CLOSED;
4852 : }
4853 0 : GF_LOG(GF_LOG_INFO, GF_LOG_HTTP, ("[HTTP] Connection closed by server when processing %s - retrying\n", sess->remote_path));
4854 0 : gf_dm_disconnect(sess, HTTP_RESET_CONN);
4855 :
4856 0 : if (sess->num_retry)
4857 0 : sess->status = GF_NETIO_SETUP;
4858 : else {
4859 0 : sess->last_error = e;
4860 0 : sess->status = GF_NETIO_STATE_ERROR;
4861 : }
4862 : return e;
4863 1019 : case GF_OK:
4864 1019 : if (!res)
4865 : return GF_OK;
4866 : break;
4867 : default:
4868 : goto exit;
4869 : }
4870 1019 : bytesRead += res;
4871 :
4872 : #ifdef GPAC_HAS_HTTP2
4873 : //in case we got a refused stream
4874 : if (sess->status==GF_NETIO_SETUP) {
4875 : return GF_OK;
4876 : }
4877 : if (sess->h2_sess)
4878 : continue;
4879 : #endif
4880 :
4881 : //HTTP1.1 only
4882 :
4883 : //weird bug on some servers sending twice the last chunk
4884 1019 : if (bytesRead && !strncmp(sHTTP, "0\r\n\r\n", 5) ) {
4885 : bytesRead -= res;
4886 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_HTTP, ("[HTTP] End of chunk found while waiting server response when processing %s - retrying\n", sess->remote_path));
4887 : }
4888 :
4889 : /*locate body start*/
4890 1019 : BodyStart = gf_token_find(sHTTP, 0, bytesRead, "\r\n\r\n");
4891 1019 : if (BodyStart > 0) {
4892 1019 : BodyStart += 4;
4893 1019 : break;
4894 : }
4895 0 : BodyStart = gf_token_find(sHTTP, 0, bytesRead, "\n\n");
4896 0 : if (BodyStart > 0) {
4897 0 : BodyStart += 2;
4898 0 : break;
4899 : }
4900 : }
4901 :
4902 1019 : no_range = range = ContentLength = first_byte = last_byte = total_size = rsp_code = 0;
4903 :
4904 : #ifdef GPAC_HAS_HTTP2
4905 : if (!sess->h2_sess) {
4906 : #endif
4907 1019 : if (bytesRead < 0) {
4908 : e = GF_REMOTE_SERVICE_ERROR;
4909 : goto exit;
4910 : }
4911 :
4912 1019 : if (!BodyStart)
4913 : BodyStart = bytesRead;
4914 :
4915 1019 : sHTTP[BodyStart-1] = 0;
4916 1019 : GF_LOG(GF_LOG_INFO, GF_LOG_HTTP, ("[HTTP] %s\n\n", sHTTP));
4917 :
4918 1019 : sess->reply_time = (u32) (gf_sys_clock_high_res() - sess->request_start_time);
4919 1019 : sess->rsp_hdr_size = BodyStart;
4920 :
4921 1019 : LinePos = gf_token_get_line(sHTTP, 0, bytesRead, buf, 1024);
4922 1019 : Pos = gf_token_get(buf, 0, " \t\r\n", comp, 400);
4923 :
4924 : //TODO for HTTP2
4925 1019 : if (sess->server_mode) {
4926 216 : method = http_parse_method(comp);
4927 :
4928 216 : Pos = gf_token_get(buf, Pos, " \t\r\n", comp, 400);
4929 216 : if (sess->orig_url) gf_free(sess->orig_url);
4930 216 : sess->orig_url = gf_strdup(comp);
4931 216 : /*Pos = */gf_token_get(buf, Pos, " \t\r\n", comp, 400);
4932 216 : if ((strncmp("HTTP", comp, 4) != 0)) {
4933 : e = GF_REMOTE_SERVICE_ERROR;
4934 : goto exit;
4935 : }
4936 : //flush potential body except for PUT/POST
4937 216 : if ((method==GF_HTTP_PUT) || (method==GF_HTTP_POST))
4938 : rsp_code = 200;
4939 : else
4940 : rsp_code = 300;
4941 : } else {
4942 :
4943 803 : if (!strncmp("ICY", comp, 3)) {
4944 0 : sess->use_cache_file = GF_FALSE;
4945 : /*be prepared not to receive any mime type from ShoutCast servers*/
4946 0 : if (!gf_cache_get_mime_type(sess->cache_entry))
4947 0 : gf_cache_set_mime_type(sess->cache_entry, "audio/mpeg");
4948 803 : } else if ((strncmp("HTTP", comp, 4) != 0)) {
4949 : e = GF_REMOTE_SERVICE_ERROR;
4950 : goto exit;
4951 : }
4952 803 : Pos = gf_token_get(buf, Pos, " ", comp, 400);
4953 803 : if (Pos <= 0) {
4954 : e = GF_REMOTE_SERVICE_ERROR;
4955 : goto exit;
4956 : }
4957 803 : rsp_code = (u32) atoi(comp);
4958 803 : /*Pos = */gf_token_get(buf, Pos, " \r\n", comp, 400);
4959 :
4960 : }
4961 :
4962 : /* parse headers*/
4963 : while (1) {
4964 : GF_HTTPHeader *hdrp;
4965 : char *sep, *hdr_sep, *hdr, *hdr_val;
4966 11188 : if ( (s32) LinePos + 4 > BodyStart) break;
4967 10169 : LinePos = gf_token_get_line(sHTTP, LinePos , bytesRead, buf, 1024);
4968 10169 : if (LinePos < 0) break;
4969 :
4970 : hdr_sep = NULL;
4971 : hdr_val = NULL;
4972 : hdr = buf;
4973 10169 : sep = strchr(buf, ':');
4974 10169 : if (sep) {
4975 10169 : sep[0]=0;
4976 10169 : hdr_val = sep+1;
4977 10169 : while (hdr_val[0]==' ') hdr_val++;
4978 10169 : hdr_sep = strrchr(hdr_val, '\r');
4979 10169 : if (hdr_sep) hdr_sep[0] = 0;
4980 : }
4981 :
4982 10169 : GF_SAFEALLOC(hdrp, GF_HTTPHeader);
4983 10169 : if (hdrp) {
4984 10169 : hdrp->name = gf_strdup(hdr);
4985 10169 : hdrp->value = gf_strdup(hdr_val);
4986 10169 : gf_list_add(sess->headers, hdrp);
4987 : }
4988 :
4989 10169 : if (sep) sep[0]=':';
4990 10169 : if (hdr_sep) hdr_sep[0] = '\r';
4991 :
4992 10169 : if (sess->server_mode) {
4993 1194 : if (!stricmp(hdrp->name, "Transfer-Encoding") && !stricmp(hdrp->value, "chunked"))
4994 28 : sess->chunked = GF_TRUE;
4995 : }
4996 : }
4997 :
4998 : #ifdef GPAC_HAS_HTTP2
4999 : }
5000 : #endif
5001 :
5002 1019 : if (!sess->server_mode) {
5003 : Bool cache_no_store = GF_FALSE;
5004 : //default pre-processing of headers - needs cleanup, not all of these have to be parsed before checking reply code
5005 8975 : for (i=0; i<gf_list_count(sess->headers); i++) {
5006 : char *val;
5007 8975 : GF_HTTPHeader *hdr = (GF_HTTPHeader*)gf_list_get(sess->headers, i);
5008 :
5009 : #ifdef GPAC_HAS_HTTP2
5010 : if (!stricmp(hdr->name, ":status") ) {
5011 : rsp_code = (u32) atoi(hdr->value);
5012 : } else
5013 : #endif
5014 8975 : if (!stricmp(hdr->name, "Content-Length") ) {
5015 1470 : ContentLength = (u32) atoi(hdr->value);
5016 :
5017 735 : if ((rsp_code<300) && sess->cache_entry)
5018 729 : gf_cache_set_content_length(sess->cache_entry, ContentLength);
5019 :
5020 : }
5021 8240 : else if (!stricmp(hdr->name, "Content-Type")) {
5022 594 : char *mime = gf_strdup(hdr->value);
5023 0 : while (1) {
5024 594 : u32 len = (u32) strlen(mime);
5025 594 : char c = len ? mime[len-1] : 0;
5026 594 : if ((c=='\r') || (c=='\n')) {
5027 0 : mime[len-1] = 0;
5028 : } else {
5029 : break;
5030 : }
5031 : }
5032 594 : val = strchr(mime, ';');
5033 594 : if (val) val[0] = 0;
5034 :
5035 594 : strlwr(mime);
5036 594 : if (rsp_code<300) {
5037 588 : if (sess->cache_entry) {
5038 586 : gf_cache_set_mime_type(sess->cache_entry, mime);
5039 : } else {
5040 2 : sess->mime_type = mime;
5041 : mime = NULL;
5042 : }
5043 : }
5044 592 : if (mime) gf_free(mime);
5045 : }
5046 7646 : else if (!stricmp(hdr->name, "Content-Range")) {
5047 92 : if (!strnicmp(hdr->value, "bytes", 5)) {
5048 92 : val = hdr->value + 5;
5049 92 : if (val[0] == ':') val += 1;
5050 62 : while (val[0] == ' ') val += 1;
5051 :
5052 92 : if (val[0] == '*') {
5053 0 : sscanf(val, "*/%u", &total_size);
5054 : } else {
5055 92 : sscanf(val, "%u-%u/%u", &first_byte, &last_byte, &total_size);
5056 : }
5057 : }
5058 : }
5059 7554 : else if (!stricmp(hdr->name, "Accept-Ranges")) {
5060 559 : if (strstr(hdr->value, "none")) no_range = 1;
5061 : }
5062 6995 : else if (!stricmp(hdr->name, "Location"))
5063 2 : new_location = gf_strdup(hdr->value);
5064 6993 : else if (!strnicmp(hdr->name, "ice", 3) || !strnicmp(hdr->name, "icy", 3) ) {
5065 : /* For HTTP icy servers, we disable cache */
5066 3 : if (sess->icy_metaint == 0)
5067 1 : sess->icy_metaint = -1;
5068 3 : sess->use_cache_file = GF_FALSE;
5069 3 : if (!stricmp(hdr->name, "icy-metaint")) {
5070 0 : sess->icy_metaint = atoi(hdr->value);
5071 : }
5072 : }
5073 6990 : else if (!stricmp(hdr->name, "Cache-Control")) {
5074 95 : if (strstr(hdr->value, "no-store")) {
5075 : cache_no_store = GF_TRUE;
5076 : }
5077 : }
5078 6895 : else if (!stricmp(hdr->name, "ETag")) {
5079 707 : if (rsp_code<300)
5080 705 : gf_cache_set_etag_on_server(sess->cache_entry, hdr->value);
5081 : }
5082 6188 : else if (!stricmp(hdr->name, "Last-Modified")) {
5083 570 : if (rsp_code<300)
5084 568 : gf_cache_set_last_modified_on_server(sess->cache_entry, hdr->value);
5085 : }
5086 5618 : else if (!stricmp(hdr->name, "Transfer-Encoding")) {
5087 39 : if (!stricmp(hdr->value, "chunked"))
5088 39 : sess->chunked = GF_TRUE;
5089 : }
5090 5579 : else if (!stricmp(hdr->name, "X-UserProfileID") ) {
5091 0 : gf_opts_set_key("core", "user-profileid", hdr->value);
5092 : }
5093 5579 : else if (!stricmp(hdr->name, "Connection") ) {
5094 792 : if (strstr(hdr->value, "close"))
5095 : connection_closed = GF_TRUE;
5096 : }
5097 : #ifdef GPAC_HAS_HTTP2
5098 : else if (!stricmp(hdr->name, "Upgrade") ) {
5099 : if (!sess->dm->disable_http2 && !gf_opts_get_bool("core", "no-h2c") && !strncmp(hdr->value,"h2c", 3)) {
5100 : upgrade_to_http2 = GF_TRUE;
5101 : }
5102 : }
5103 : #endif
5104 :
5105 8975 : if (sess->status==GF_NETIO_DISCONNECTED) return GF_OK;
5106 : }
5107 :
5108 803 : if (cache_no_store) {
5109 4 : if (sess->cache_entry && !ContentLength && !sess->chunked && (rsp_code<300)
5110 : #ifdef GPAC_HAS_HTTP2
5111 : && !sess->h2_sess
5112 : #endif
5113 : ) {
5114 0 : sess->use_cache_file = GF_FALSE;
5115 0 : gf_cache_remove_session_from_cache_entry(sess->cache_entry, sess);
5116 0 : sess->cache_entry = NULL;
5117 : }
5118 : }
5119 :
5120 803 : if (no_range) first_byte = 0;
5121 :
5122 :
5123 803 : gf_cache_set_headers_processed(sess->cache_entry);
5124 : }
5125 :
5126 1019 : par.msg_type = GF_NETIO_PARSE_REPLY;
5127 1019 : par.error = GF_OK;
5128 1019 : par.reply = rsp_code;
5129 1019 : par.value = comp;
5130 : /*
5131 : * If response is correct, it means our credentials are correct
5132 : */
5133 1019 : if (sess->creds && rsp_code != 304)
5134 0 : sess->creds->valid = GF_TRUE;
5135 :
5136 : #ifdef GPAC_HAS_HTTP2
5137 : if ((rsp_code == 101) && upgrade_to_http2) {
5138 : int rv;
5139 : u8 settings[HTTP2_BUFFER_SETTINGS_SIZE];
5140 : u32 settings_len;
5141 :
5142 : nghttp2_settings_entry h2_settings[2] = {
5143 : {NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100},
5144 : {NGHTTP2_SETTINGS_ENABLE_PUSH, 0}
5145 : };
5146 : settings_len = (u32) nghttp2_pack_settings_payload(settings, HTTP2_BUFFER_SETTINGS_SIZE, h2_settings, GF_ARRAY_LENGTH(h2_settings));
5147 :
5148 : h2_initialize_session(sess);
5149 : sess->h2_stream_id = 1;
5150 : rv = nghttp2_session_upgrade2(sess->h2_sess->ng_sess, settings, settings_len, (sess->http_read_type==1) ? 1 : 0, sess);
5151 : if (rv < 0) {
5152 : GF_LOG(GF_LOG_ERROR, GF_LOG_HTTP, ("[HTTP/2] nghttp2_session_upgrade2 error: %s\n", nghttp2_strerror(rv)));
5153 : return GF_IP_NETWORK_FAILURE;
5154 : }
5155 : //push the body
5156 : if (bytesRead > BodyStart) {
5157 : rv = (int) nghttp2_session_mem_recv(sess->h2_sess->ng_sess, sHTTP + BodyStart , bytesRead - BodyStart);
5158 : if (rv < 0) {
5159 : GF_LOG(GF_LOG_ERROR, GF_LOG_HTTP, ("[HTTP/2] nghttp2_session_mem_recv error: %s\n", nghttp2_strerror(rv)));
5160 : return GF_IP_NETWORK_FAILURE;
5161 : }
5162 : //stay in WAIT_FOR_REPLY state and do not flush data, cache is not fully configured yet
5163 : }
5164 : //send pending frames
5165 : e = h2_session_send(sess);
5166 : if (e) return e;
5167 : sess->connection_close = GF_FALSE;
5168 : sess->h2_upgrade_state = 0;
5169 : return GF_OK;
5170 : }
5171 : if (sess->h2_upgrade_state)
5172 : sess->h2_upgrade_state = 2;
5173 : #endif
5174 :
5175 :
5176 : /*try to flush body */
5177 1019 : if ((rsp_code>=300)
5178 : #ifdef GPAC_HAS_HTTP2
5179 : && !sess->h2_sess
5180 : #endif
5181 : ) {
5182 194 : u32 start = gf_sys_clock();
5183 388 : while (BodyStart + ContentLength > (u32) bytesRead) {
5184 0 : e = gf_dm_read_data(sess, sHTTP + bytesRead, buf_size - bytesRead, &res);
5185 0 : switch (e) {
5186 : case GF_IP_NETWORK_EMPTY:
5187 : break;
5188 0 : case GF_OK:
5189 0 : bytesRead += res;
5190 0 : break;
5191 0 : default:
5192 : start=0;
5193 0 : break;
5194 : }
5195 0 : if (gf_sys_clock()-start>100)
5196 : break;
5197 :
5198 : //does not fit in our buffer, too bad we'll kill the connection
5199 0 : if (bytesRead == GF_DOWNLOAD_BUFFER_SIZE)
5200 : break;
5201 : }
5202 :
5203 194 : if (BodyStart + ContentLength > (u32) bytesRead) {
5204 : ContentLength = 0;
5205 : //cannot flush, discard socket
5206 0 : sess->connection_close = GF_TRUE;
5207 : }
5208 : }
5209 :
5210 : #ifdef GPAC_HAS_HTTP2
5211 : if (sess->h2_sess) {
5212 : u32 count = gf_list_count(sess->headers);
5213 : for (i=0; i<count; i++) {
5214 : GF_HTTPHeader *hdr = gf_list_get(sess->headers, i);
5215 : if (!stricmp(hdr->name, ":method")) {
5216 : method = http_parse_method(hdr->value);
5217 : rsp_code = 200;
5218 : }
5219 : else if (!stricmp(hdr->name, ":path")) {
5220 : if (sess->orig_url) gf_free(sess->orig_url);
5221 : sess->orig_url = gf_strdup(hdr->value);
5222 : }
5223 : }
5224 : } else if (sess->server_mode && !gf_opts_get_bool("core", "no-h2") && !gf_opts_get_bool("core", "no-h2c")) {
5225 : Bool is_upgradeable = GF_FALSE;
5226 : char *h2_settings = NULL;
5227 : u32 count = gf_list_count(sess->headers);
5228 : for (i=0; i<count; i++) {
5229 : GF_HTTPHeader *hdr = gf_list_get(sess->headers, i);
5230 : if (!stricmp(hdr->name, "Upgrade")) {
5231 : if (strstr(hdr->value, "h2c"))
5232 : is_upgradeable = GF_TRUE;
5233 : }
5234 : else if (!stricmp(hdr->name, "HTTP2-Settings")) {
5235 : h2_settings = hdr->value;
5236 : }
5237 : }
5238 :
5239 : if (is_upgradeable && h2_settings) {
5240 : u32 len = (u32) strlen(h2_settings);
5241 : sess->h2_upgrade_settings = gf_malloc(sizeof(char) * len * 2);
5242 : sess->h2_upgrade_settings_len = gf_base64_decode(h2_settings, len, sess->h2_upgrade_settings, len*2);
5243 : }
5244 : }
5245 : #endif
5246 :
5247 :
5248 1019 : if (sess->server_mode) {
5249 216 : if (ContentLength) {
5250 0 : par.data = sHTTP + BodyStart;
5251 0 : par.size = ContentLength;
5252 216 : } else if ((BodyStart < (s32) bytesRead)
5253 : #ifdef GPAC_HAS_HTTP2
5254 : && !sess->h2_sess
5255 : #endif
5256 : ) {
5257 27 : if (sess->init_data) gf_free(sess->init_data);
5258 27 : sess->init_data_size = 0;
5259 27 : sess->init_data = NULL;
5260 :
5261 27 : gf_dm_data_received(sess, (u8 *) sHTTP + BodyStart, bytesRead - BodyStart, GF_TRUE, NULL, NULL);
5262 : }
5263 :
5264 216 : sess->request_start_time = gf_sys_clock_high_res();
5265 :
5266 216 : par.reply = method;
5267 : gf_dm_sess_user_io(sess, &par);
5268 216 : sess->status = GF_NETIO_DATA_TRANSFERED;
5269 216 : return GF_OK;
5270 : }
5271 : //remember if we can keep the session alive after the transfer is done
5272 803 : sess->connection_close = connection_closed;
5273 : assert(rsp_code);
5274 :
5275 803 : switch (rsp_code) {
5276 : //100 continue
5277 : case 100:
5278 : break;
5279 : case 200:
5280 : case 201:
5281 : case 202:
5282 : case 206:
5283 : gf_dm_sess_user_io(sess, &par);
5284 : e = GF_OK;
5285 797 : if (sess->proxy_enabled==2) {
5286 0 : sess->proxy_enabled=0;
5287 0 : if (sess->dm)
5288 0 : gf_list_add(sess->dm->skip_proxy_servers, gf_strdup(sess->server_name));
5289 : }
5290 : break;
5291 : /*redirection: extract the new location*/
5292 2 : case 301:
5293 : case 302:
5294 : case 303:
5295 : case 307:
5296 2 : if (!new_location || !strlen(new_location) ) {
5297 : gf_dm_sess_user_io(sess, &par);
5298 : e = GF_URL_ERROR;
5299 : goto exit;
5300 : }
5301 2 : while (
5302 2 : (new_location[strlen(new_location)-1] == '\n')
5303 2 : || (new_location[strlen(new_location)-1] == '\r') )
5304 0 : new_location[strlen(new_location)-1] = 0;
5305 :
5306 : /*reset and reconnect*/
5307 2 : gf_dm_disconnect(sess, HTTP_CLOSE);
5308 2 : sess->status = GF_NETIO_SETUP;
5309 2 : e = gf_dm_sess_setup_from_url(sess, new_location, GF_FALSE);
5310 2 : if (e) {
5311 0 : sess->status = GF_NETIO_STATE_ERROR;
5312 0 : sess->last_error = e;
5313 0 : gf_dm_sess_notify_state(sess, sess->status, e);
5314 : }
5315 2 : gf_free(new_location);
5316 2 : return e;
5317 2 : case 304:
5318 : {
5319 2 : sess->status = GF_NETIO_PARSE_REPLY;
5320 : assert(sess->cache_entry);
5321 2 : sess->total_size = gf_cache_get_cache_filesize(sess->cache_entry);
5322 :
5323 2 : gf_dm_sess_notify_state(sess, GF_NETIO_PARSE_REPLY, GF_OK);
5324 :
5325 2 : gf_dm_disconnect(sess, HTTP_NO_CLOSE);
5326 2 : if (sess->user_proc) {
5327 : /* For modules that do not use cache and have problems with GF_NETIO_DATA_TRANSFERED ... */
5328 : const char * filename;
5329 : FILE * f;
5330 0 : filename = gf_cache_get_cache_filename(sess->cache_entry);
5331 0 : GF_LOG(GF_LOG_INFO, GF_LOG_HTTP, ("[HTTP] Sending data to modules from %s...\n", filename));
5332 0 : f = gf_fopen(filename, "rb");
5333 : assert(filename);
5334 0 : if (!f) {
5335 0 : GF_LOG(GF_LOG_INFO, GF_LOG_HTTP, ("[HTTP] FAILED to open cache file %s for reading contents !\n", filename));
5336 : /* Ooops, no cache, redowload everything ! */
5337 0 : gf_dm_disconnect(sess, HTTP_NO_CLOSE);
5338 0 : sess->status = GF_NETIO_SETUP;
5339 0 : e = gf_dm_sess_setup_from_url(sess, sess->orig_url, GF_FALSE);
5340 0 : sess->total_size = gf_cache_get_cache_filesize(sess->cache_entry);
5341 0 : if (e) {
5342 0 : sess->status = GF_NETIO_STATE_ERROR;
5343 0 : sess->last_error = e;
5344 0 : gf_dm_sess_notify_state(sess, sess->status, e);
5345 : }
5346 : return e;
5347 : }
5348 :
5349 0 : par.error = GF_OK;
5350 0 : par.msg_type = GF_NETIO_PARSE_HEADER;
5351 0 : par.name = "Content-Type";
5352 0 : par.value = (char *) gf_cache_get_mime_type(sess->cache_entry);
5353 : gf_dm_sess_user_io(sess, &par);
5354 :
5355 0 : sess->status = GF_NETIO_DATA_EXCHANGE;
5356 0 : if (! (sess->flags & GF_NETIO_SESSION_NOT_THREADED) || sess->force_data_write_callback) {
5357 : char file_cache_buff[16544];
5358 : s32 read = 0;
5359 0 : total_size = gf_cache_get_cache_filesize(sess->cache_entry);
5360 : do {
5361 0 : read = (s32) gf_fread(file_cache_buff, 16384, f);
5362 0 : if (read > 0) {
5363 0 : sess->bytes_done += read;
5364 0 : sess->total_size = total_size;
5365 0 : sess->bytes_per_sec = 0xFFFFFFFF;
5366 0 : par.size = read;
5367 0 : par.msg_type = GF_NETIO_DATA_EXCHANGE;
5368 0 : par.error = GF_EOS;
5369 0 : par.reply = 2;
5370 0 : par.data = file_cache_buff;
5371 : gf_dm_sess_user_io(sess, &par);
5372 : }
5373 0 : } while ( read > 0);
5374 : }
5375 0 : gf_fclose(f);
5376 0 : GF_LOG(GF_LOG_INFO, GF_LOG_HTTP, ("[HTTP] all data has been sent to modules from %s.\n", filename));
5377 : }
5378 : /* Cache file is the most recent */
5379 2 : sess->status = GF_NETIO_DATA_TRANSFERED;
5380 2 : par.error = GF_OK;
5381 2 : gf_dm_sess_notify_state(sess, GF_NETIO_DATA_TRANSFERED, GF_OK);
5382 2 : gf_dm_disconnect(sess, HTTP_NO_CLOSE);
5383 2 : return GF_OK;
5384 : }
5385 0 : case 401:
5386 : {
5387 : /* Do we have a credentials struct ? */
5388 0 : sess->creds = gf_user_credentials_register(sess->dm, sess->server_name, NULL, NULL, GF_FALSE);
5389 0 : if (!sess->creds) {
5390 : /* User credentials have not been filled properly, we have to abort */
5391 0 : gf_dm_disconnect(sess, HTTP_CLOSE);
5392 0 : sess->status = GF_NETIO_STATE_ERROR;
5393 0 : par.error = GF_AUTHENTICATION_FAILURE;
5394 0 : par.msg_type = GF_NETIO_DISCONNECTED;
5395 : gf_dm_sess_user_io(sess, &par);
5396 : e = GF_AUTHENTICATION_FAILURE;
5397 0 : sess->last_error = e;
5398 0 : goto exit;
5399 : }
5400 0 : gf_dm_disconnect(sess, HTTP_NO_CLOSE);
5401 0 : sess->status = GF_NETIO_SETUP;
5402 0 : e = gf_dm_sess_setup_from_url(sess, sess->orig_url, GF_FALSE);
5403 0 : if (e) {
5404 0 : sess->status = GF_NETIO_STATE_ERROR;
5405 0 : sess->last_error = e;
5406 0 : gf_dm_sess_notify_state(sess, sess->status, e);
5407 : }
5408 : return e;
5409 : }
5410 : case 404:
5411 : /* File not found */
5412 : gf_dm_sess_user_io(sess, &par);
5413 2 : if ((BodyStart < (s32) bytesRead)) {
5414 2 : sHTTP[bytesRead] = 0;
5415 2 : GF_LOG(GF_LOG_WARNING, GF_LOG_HTTP, ("[HTTP] Failure - body: %s\n", sHTTP + BodyStart));
5416 : }
5417 2 : notify_headers(sess, sHTTP, bytesRead, BodyStart);
5418 : e = GF_URL_ERROR;
5419 2 : goto exit;
5420 :
5421 : case 416:
5422 : /* Range not accepted */
5423 : gf_dm_sess_user_io(sess, &par);
5424 :
5425 0 : notify_headers(sess, sHTTP, bytesRead, BodyStart);
5426 : e = GF_SERVICE_ERROR;
5427 0 : goto exit;
5428 :
5429 0 : case 400:
5430 : case 501:
5431 : /* Method not implemented ! */
5432 0 : if (sess->http_read_type == HEAD) {
5433 : /* Since HEAD is not understood by this server, we use a GET instead */
5434 0 : sess->http_read_type = GET;
5435 0 : sess->flags |= GF_NETIO_SESSION_NOT_CACHED;
5436 0 : gf_dm_disconnect(sess, HTTP_NO_CLOSE);
5437 0 : sess->status = GF_NETIO_SETUP;
5438 0 : sess->server_only_understand_get = GF_TRUE;
5439 0 : GF_LOG(GF_LOG_INFO, GF_LOG_HTTP, ("Method not supported, try with GET.\n"));
5440 0 : e = gf_dm_sess_setup_from_url(sess, sess->orig_url, GF_FALSE);
5441 0 : if (e) {
5442 0 : sess->status = GF_NETIO_STATE_ERROR;
5443 0 : sess->last_error = e;
5444 0 : gf_dm_sess_notify_state(sess, sess->status, e);
5445 : }
5446 : return e;
5447 : }
5448 :
5449 : gf_dm_sess_user_io(sess, &par);
5450 0 : notify_headers(sess, sHTTP, bytesRead, BodyStart);
5451 : e = GF_REMOTE_SERVICE_ERROR;
5452 0 : goto exit;
5453 :
5454 0 : case 503:
5455 : /*retry without proxy*/
5456 0 : if (sess->proxy_enabled==1) {
5457 0 : sess->proxy_enabled=2;
5458 0 : gf_dm_disconnect(sess, HTTP_CLOSE);
5459 0 : sess->status = GF_NETIO_SETUP;
5460 0 : return GF_OK;
5461 : }
5462 : case 204:
5463 : gf_dm_sess_user_io(sess, &par);
5464 0 : notify_headers(sess, sHTTP, bytesRead, BodyStart);
5465 : e = GF_EOS;
5466 0 : goto exit;
5467 :
5468 : case 504:
5469 : /* gateway timeout, notified as URL error */
5470 : gf_dm_sess_user_io(sess, &par);
5471 0 : notify_headers(sess, sHTTP, bytesRead, BodyStart);
5472 : e = GF_URL_ERROR;
5473 0 : goto exit;
5474 :
5475 : default:
5476 : gf_dm_sess_user_io(sess, &par);
5477 0 : notify_headers(sess, sHTTP, bytesRead, BodyStart);
5478 : e = GF_REMOTE_SERVICE_ERROR;
5479 0 : goto exit;
5480 : }
5481 :
5482 797 : notify_headers(sess, NULL, bytesRead, BodyStart);
5483 :
5484 797 : if (sess->http_read_type != GET)
5485 27 : sess->use_cache_file = GF_FALSE;
5486 :
5487 797 : if (sess->http_read_type==HEAD) {
5488 0 : gf_dm_disconnect(sess, HTTP_NO_CLOSE);
5489 0 : gf_dm_sess_notify_state(sess, GF_NETIO_DATA_TRANSFERED, GF_OK);
5490 0 : sess->http_read_type = GET;
5491 0 : return GF_OK;
5492 : }
5493 :
5494 :
5495 797 : mime_type = gf_cache_get_mime_type(sess->cache_entry);
5496 797 : if (!ContentLength && mime_type && ((strstr(mime_type, "ogg") || (!strcmp(mime_type, "audio/mpeg"))))) {
5497 1 : if (0 == sess->icy_metaint)
5498 0 : sess->icy_metaint = -1;
5499 1 : sess->use_cache_file = GF_FALSE;
5500 : }
5501 :
5502 : #ifndef GPAC_DISABLE_LOG
5503 797 : if (e) {
5504 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_HTTP, ("[HTTP] Error processing rely from %s: %s\n", sess->server_name, gf_error_to_string(e) ) );
5505 : } else {
5506 797 : GF_LOG(GF_LOG_DEBUG, GF_LOG_HTTP, ("[HTTP] Reply processed from %s\n", sess->server_name ) );
5507 : }
5508 : #endif
5509 :
5510 : //in HTTP2 we may resume to setup state if we got a refused stream
5511 797 : if (sess->status == GF_NETIO_SETUP) {
5512 : return GF_OK;
5513 : }
5514 :
5515 :
5516 : /*some servers may reply without content length, but we MUST have it*/
5517 797 : if (e) goto exit;
5518 797 : if (sess->icy_metaint != 0) {
5519 : assert( ! sess->use_cache_file );
5520 1 : GF_LOG(GF_LOG_INFO, GF_LOG_HTTP, ("[HTTP] ICY protocol detected\n"));
5521 1 : if (mime_type && !stricmp(mime_type, "video/nsv")) {
5522 0 : gf_cache_set_mime_type(sess->cache_entry, "audio/aac");
5523 : }
5524 1 : sess->icy_bytes = 0;
5525 1 : sess->total_size = SIZE_IN_STREAM;
5526 1 : sess->status = GF_NETIO_DATA_EXCHANGE;
5527 796 : } else if (!ContentLength && !sess->chunked
5528 : #ifdef GPAC_HAS_HTTP2
5529 : && !sess->h2_sess
5530 : #endif
5531 : ) {
5532 27 : if (sess->http_read_type == GET) {
5533 0 : sess->total_size = SIZE_IN_STREAM;
5534 0 : sess->use_cache_file = GF_FALSE;
5535 0 : sess->status = GF_NETIO_DATA_EXCHANGE;
5536 0 : sess->bytes_done = 0;
5537 : } else {
5538 27 : gf_dm_sess_notify_state(sess, GF_NETIO_DATA_TRANSFERED, GF_OK);
5539 27 : gf_dm_disconnect(sess, HTTP_NO_CLOSE);
5540 27 : return GF_OK;
5541 : }
5542 : #ifdef GPAC_HAS_HTTP2
5543 : } else if (sess->h2_sess && !ContentLength && (sess->http_read_type != GET)) {
5544 : gf_dm_sess_notify_state(sess, GF_NETIO_DATA_TRANSFERED, GF_OK);
5545 : gf_dm_disconnect(sess, HTTP_NO_CLOSE);
5546 : return GF_OK;
5547 : #endif
5548 : } else {
5549 769 : sess->total_size = ContentLength;
5550 769 : if (sess->use_cache_file && sess->http_read_type == GET ) {
5551 767 : e = gf_cache_open_write_cache(sess->cache_entry, sess);
5552 767 : if (e) {
5553 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_HTTP, ( "[CACHE] Failed to open cache, error=%d\n", e));
5554 : goto exit;
5555 : }
5556 : }
5557 769 : sess->status = GF_NETIO_DATA_EXCHANGE;
5558 769 : sess->bytes_done = 0;
5559 : }
5560 :
5561 :
5562 : /* we may have existing data in this buffer ... */
5563 : #ifdef GPAC_HAS_HTTP2
5564 : if (!e && sess->h2_sess) {
5565 : h2_flush_data(sess, GF_TRUE);
5566 : } else
5567 : #endif
5568 770 : if (!e && (BodyStart < (s32) bytesRead)) {
5569 474 : u32 rewrite_size=0;
5570 474 : if (sess->init_data) gf_free(sess->init_data);
5571 474 : sess->init_data_size = 0;
5572 474 : sess->init_data = NULL;
5573 :
5574 474 : gf_dm_data_received(sess, (u8 *) sHTTP + BodyStart, bytesRead - BodyStart, GF_TRUE, &rewrite_size, NULL);
5575 :
5576 474 : if (sess->chunked)
5577 8 : gf_dm_sess_estimate_chunk_rate(sess, rewrite_size);
5578 : }
5579 1068 : exit:
5580 772 : if (e) {
5581 2 : if (e<0) {
5582 2 : GF_LOG((e==GF_URL_ERROR) ? GF_LOG_INFO : GF_LOG_WARNING, GF_LOG_HTTP, ("[HTTP] Error parsing reply for URL %s: %s (code %d)\n", sess->orig_url, gf_error_to_string(e), rsp_code ));
5583 : } else {
5584 : e = GF_OK;
5585 : }
5586 2 : gf_cache_entry_set_delete_files_when_deleted(sess->cache_entry);
5587 2 : gf_dm_remove_cache_entry_from_session(sess);
5588 2 : sess->cache_entry = NULL;
5589 2 : gf_dm_disconnect(sess, HTTP_NO_CLOSE);
5590 2 : if (connection_closed)
5591 0 : sess->status = GF_NETIO_STATE_ERROR;
5592 : else
5593 2 : sess->status = GF_NETIO_DATA_TRANSFERED;
5594 2 : sess->last_error = e;
5595 2 : gf_dm_sess_notify_state(sess, sess->status, e);
5596 2 : return e;
5597 : }
5598 : /*DO NOT call parse_body yet, as the final user may not be connected to our session*/
5599 : return GF_OK;
5600 : }
5601 :
5602 : /**
5603 : * Default performing behavior
5604 : \param sess The session
5605 : */
5606 71364 : void http_do_requests(GF_DownloadSession *sess)
5607 : {
5608 : char sHTTP[GF_DOWNLOAD_BUFFER_SIZE+1];
5609 71364 : sHTTP[0] = 0;
5610 :
5611 71364 : if (sess->reused_cache_entry) {
5612 : //main session is done downloading, notify - to do we should send progress events on this session also ...
5613 0 : if (!gf_cache_is_in_progress(sess->cache_entry)) {
5614 : GF_NETIO_Parameter par;
5615 0 : gf_dm_disconnect(sess, HTTP_NO_CLOSE);
5616 0 : sess->reused_cache_entry = GF_FALSE;
5617 : memset(&par, 0, sizeof(GF_NETIO_Parameter));
5618 0 : par.msg_type = GF_NETIO_DATA_TRANSFERED;
5619 : par.error = GF_OK;
5620 : gf_dm_sess_user_io(sess, &par);
5621 : }
5622 0 : return;
5623 : }
5624 :
5625 71364 : switch (sess->status) {
5626 1093 : case GF_NETIO_CONNECTED:
5627 1093 : if (sess->server_mode) {
5628 253 : wait_for_header_and_parse(sess, sHTTP);
5629 : } else {
5630 840 : http_send_headers(sess, sHTTP);
5631 : }
5632 : break;
5633 67886 : case GF_NETIO_WAIT_FOR_REPLY:
5634 67886 : if (sess->server_mode) {
5635 0 : http_send_headers(sess, sHTTP);
5636 : } else {
5637 67886 : wait_for_header_and_parse(sess, sHTTP);
5638 : }
5639 : break;
5640 2385 : case GF_NETIO_DATA_EXCHANGE:
5641 2385 : if (sess->server_mode) {
5642 0 : sess->status = GF_NETIO_CONNECTED;
5643 0 : break;
5644 : }
5645 : /*session has been reassigned, resend data retrieved in first GET reply to user but don't write to cache*/
5646 2385 : if (sess->reassigned) {
5647 :
5648 0 : if (sess->icy_metaint > 0) {
5649 : //we are reparsing init data, reset icy status
5650 0 : sess->icy_bytes = 0;
5651 0 : gf_icy_skip_data(sess, sess->init_data, sess->init_data_size);
5652 : } else {
5653 : GF_NETIO_Parameter par;
5654 0 : par.msg_type = GF_NETIO_DATA_EXCHANGE;
5655 0 : par.error = GF_OK;
5656 0 : par.data = sess->init_data;
5657 0 : par.size = sess->init_data_size;
5658 : gf_dm_sess_user_io(sess, &par);
5659 : }
5660 0 : sess->reassigned = GF_FALSE;
5661 : }
5662 2385 : http_parse_remaining_body(sess, sHTTP);
5663 2385 : break;
5664 : default:
5665 : break;
5666 : }
5667 : }
5668 :
5669 :
5670 : /**
5671 : * NET IO for MPD, we don't need this anymore since mime-type can be given by session
5672 : */
5673 72 : static void wget_NetIO(void *cbk, GF_NETIO_Parameter *param)
5674 : {
5675 : FILE * f = (FILE*) cbk;
5676 :
5677 : /*handle service message*/
5678 72 : if (param->msg_type == GF_NETIO_DATA_EXCHANGE) {
5679 4 : s32 written = (u32) gf_fwrite( param->data, param->size, f);
5680 4 : if (written != param->size) {
5681 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_HTTP, ("Failed to write data on disk\n"));
5682 : }
5683 : }
5684 72 : }
5685 :
5686 : GF_EXPORT
5687 4 : GF_Err gf_dm_wget(const char *url, const char *filename, u64 start_range, u64 end_range, char **redirected_url)
5688 : {
5689 : GF_Err e;
5690 4 : GF_DownloadManager * dm = gf_dm_new(NULL);
5691 4 : if (!dm)
5692 : return GF_OUT_OF_MEM;
5693 4 : e = gf_dm_wget_with_cache(dm, url, filename, start_range, end_range, redirected_url);
5694 4 : gf_dm_del(dm);
5695 4 : return e;
5696 : }
5697 :
5698 : GF_EXPORT
5699 4 : 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)
5700 : {
5701 : GF_Err e;
5702 : FILE * f;
5703 : GF_DownloadSession *dnload;
5704 4 : if (!filename || !url || !dm)
5705 : return GF_BAD_PARAM;
5706 4 : f = gf_fopen(filename, "wb");
5707 4 : if (!f) {
5708 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_HTTP, ("[WGET] Failed to open file %s for write.\n", filename));
5709 : return GF_IO_ERR;
5710 : }
5711 4 : dnload = gf_dm_sess_new_simple(dm, (char *)url, GF_NETIO_SESSION_NOT_THREADED, &wget_NetIO, f, &e);
5712 4 : if (!dnload) {
5713 : return GF_BAD_PARAM;
5714 : }
5715 4 : dnload->use_cache_file = GF_FALSE;
5716 4 : dnload->force_data_write_callback = GF_TRUE;
5717 4 : if (end_range) {
5718 0 : dnload->range_start = start_range;
5719 0 : dnload->range_end = end_range;
5720 0 : dnload->needs_range = GF_TRUE;
5721 : }
5722 4 : if (e == GF_OK) {
5723 4 : e = gf_dm_sess_process(dnload);
5724 : }
5725 4 : e |= gf_cache_close_write_cache(dnload->cache_entry, dnload, (e == GF_OK) ? GF_TRUE : GF_FALSE);
5726 4 : gf_fclose(f);
5727 :
5728 4 : if (redirected_url) {
5729 0 : if (dnload->orig_url_before_redirect) *redirected_url = gf_strdup(dnload->orig_url);
5730 : }
5731 4 : gf_dm_sess_del(dnload);
5732 4 : return e;
5733 : }
5734 :
5735 : #if 0 //unused
5736 :
5737 : /*
5738 : \brief fetches remote file in memory
5739 : *
5740 : *Fetches remote file in memory.
5741 : \param url the data to fetch
5742 : \param out_data output data (allocated by function)
5743 : \param out_size output data size
5744 : \param out_mime if not NULL, pointer will contain the mime type (allocated by function)
5745 : \return error code if any
5746 : */
5747 : GF_Err gf_dm_get_file_memory(const char *url, char **out_data, u32 *out_size, char **out_mime)
5748 : {
5749 : GF_Err e;
5750 : FILE * f;
5751 : char * f_fn = NULL;
5752 : GF_DownloadSession *dnload;
5753 : GF_DownloadManager *dm;
5754 :
5755 : if (!url || !out_data || !out_size)
5756 : return GF_BAD_PARAM;
5757 : f = gf_file_temp(&f_fn);
5758 : if (!f) {
5759 : GF_LOG(GF_LOG_ERROR, GF_LOG_HTTP, ("[WGET] Failed to create temp file for write.\n"));
5760 : return GF_IO_ERR;
5761 : }
5762 :
5763 : dm = gf_dm_new(NULL);
5764 : if (!dm) {
5765 : gf_fclose(f);
5766 : gf_file_delete(f_fn);
5767 : return GF_OUT_OF_MEM;
5768 : }
5769 :
5770 : dnload = gf_dm_sess_new_simple(dm, (char *)url, GF_NETIO_SESSION_NOT_THREADED, &wget_NetIO, f, &e);
5771 : if (!dnload) {
5772 : gf_dm_del(dm);
5773 : gf_fclose(f);
5774 : gf_file_delete(f_fn);
5775 : return GF_BAD_PARAM;
5776 : }
5777 : dnload->use_cache_file = GF_FALSE;
5778 : dnload->disable_cache = GF_TRUE;
5779 : if (!e)
5780 : e = gf_dm_sess_process(dnload);
5781 :
5782 : if (!e)
5783 : e = gf_cache_close_write_cache(dnload->cache_entry, dnload, e == GF_OK);
5784 :
5785 : if (!e) {
5786 : u32 size = (u32) gf_ftell(f);
5787 : s32 read;
5788 : *out_size = size;
5789 : *out_data = (char*)gf_malloc(sizeof(char)* ( 1 + size));
5790 : gf_fseek(f, 0, SEEK_SET);
5791 : read = (s32) gf_fread(*out_data, size, f);
5792 : if (read != size) {
5793 : gf_free(*out_data);
5794 : e = GF_IO_ERR;
5795 : } else {
5796 : (*out_data)[size] = 0;
5797 : if (out_mime) {
5798 : const char *mime = gf_dm_sess_mime_type(dnload);
5799 : if (mime) *out_mime = gf_strdup(mime);
5800 : }
5801 : }
5802 : }
5803 : gf_fclose(f);
5804 : gf_file_delete(f_fn);
5805 : gf_free(f_fn);
5806 : gf_dm_sess_del(dnload);
5807 : gf_dm_del(dm);
5808 : return e;
5809 : }
5810 : #endif
5811 :
5812 : GF_EXPORT
5813 276 : const char *gf_dm_sess_get_resource_name(GF_DownloadSession *dnload)
5814 : {
5815 276 : return dnload ? dnload->orig_url : NULL;
5816 : }
5817 :
5818 :
5819 : #if 0 //unused
5820 : /*!
5821 : \brief Get session original resource url
5822 : *
5823 : *Returns the original resource URL before any redirection associated with the session
5824 : \param sess the download session
5825 : \return the associated URL
5826 : */
5827 : const char *gf_dm_sess_get_original_resource_name(GF_DownloadSession *dnload)
5828 : {
5829 : if (dnload) return dnload->orig_url_before_redirect ? dnload->orig_url_before_redirect : dnload->orig_url;
5830 : return NULL;
5831 : }
5832 :
5833 : /*!
5834 : \brief fetch session status
5835 : *
5836 : *Fetch the session current status
5837 : \param sess the download session
5838 : \return the session status*/
5839 : u32 gf_dm_sess_get_status(GF_DownloadSession *dnload)
5840 : {
5841 : return dnload ? dnload->status : GF_NETIO_STATE_ERROR;
5842 : }
5843 :
5844 :
5845 : /*!
5846 : \brief Reset session
5847 : *
5848 : *Resets the session for new processing of the same url
5849 : \param sess the download session
5850 : \return error code if any
5851 : */
5852 : GF_Err gf_dm_sess_reset(GF_DownloadSession *sess)
5853 : {
5854 : if (!sess)
5855 : return GF_BAD_PARAM;
5856 : sess->status = GF_NETIO_SETUP;
5857 : sess->needs_range = GF_FALSE;
5858 : sess->range_start = sess->range_end = 0;
5859 : sess->bytes_done = sess->bytes_per_sec = 0;
5860 : if (sess->init_data) gf_free(sess->init_data);
5861 : sess->init_data = NULL;
5862 : sess->init_data_size = 0;
5863 : sess->last_error = GF_OK;
5864 : sess->total_size = 0;
5865 : sess->start_time = 0;
5866 : sess->start_time_utc = 0;
5867 : sess->max_chunk_size = 0;
5868 : sess->max_chunk_bytes_per_sec = 0;
5869 : return GF_OK;
5870 : }
5871 :
5872 : /*!
5873 : * Get a range of a cache entry file
5874 : \param sess The session
5875 : \param startOffset The first byte of the request to get
5876 : \param endOffset The last byte of request to get
5877 : \param The temporary name for the file created to have a range of the file
5878 : */
5879 : const char * gf_cache_get_cache_filename_range( const GF_DownloadSession * sess, u64 startOffset, u64 endOffset ) {
5880 : u32 i, count;
5881 : if (!sess || !sess->dm || endOffset < startOffset)
5882 : return NULL;
5883 : count = gf_list_count(sess->dm->partial_downloads);
5884 : for (i = 0 ; i < count ; i++) {
5885 : GF_PartialDownload * pd = (GF_PartialDownload*)gf_list_get(sess->dm->partial_downloads, i);
5886 : assert( pd->filename && pd->url);
5887 : if (!strcmp(pd->url, sess->orig_url) && pd->startOffset == startOffset && pd->endOffset == endOffset) {
5888 : /* File already created, just return the file */
5889 : return pd->filename;
5890 : }
5891 : }
5892 : {
5893 : /* Not found, we are gonna create the file */
5894 : char * newFilename;
5895 : GF_PartialDownload * partial;
5896 : FILE * fw, *fr;
5897 : u32 maxLen;
5898 : const char * orig = gf_cache_get_cache_filename(sess->cache_entry);
5899 : if (orig == NULL)
5900 : return NULL;
5901 : /* 22 if 1G + 1G + 2 dashes */
5902 : maxLen = (u32) strlen(orig) + 22;
5903 : newFilename = (char*)gf_malloc( maxLen );
5904 : if (newFilename == NULL)
5905 : return NULL;
5906 : snprintf(newFilename, maxLen, "%s " LLU LLU, orig, startOffset, endOffset);
5907 : fw = gf_fopen(newFilename, "wb");
5908 : if (!fw) {
5909 : GF_LOG(GF_LOG_ERROR, GF_LOG_HTTP, ("[CACHE] Cannot open partial cache file %s for write\n", newFilename));
5910 : gf_free( newFilename );
5911 : return NULL;
5912 : }
5913 : fr = gf_fopen(orig, "rb");
5914 : if (!fr) {
5915 : GF_LOG(GF_LOG_ERROR, GF_LOG_HTTP, ("[CACHE] Cannot open full cache file %s\n", orig));
5916 : gf_free( newFilename );
5917 : gf_fclose( fw );
5918 : }
5919 : /* Now, we copy ! */
5920 : {
5921 : char copyBuff[GF_DOWNLOAD_BUFFER_SIZE+1];
5922 : s64 read, write, total;
5923 : total = endOffset - startOffset;
5924 : read = gf_fseek(fr, startOffset, SEEK_SET);
5925 : if (read != startOffset) {
5926 : GF_LOG(GF_LOG_ERROR, GF_LOG_HTTP, ("[CACHE] Cannot seek at right start offset in %s\n", orig));
5927 : gf_fclose( fr );
5928 : gf_fclose( fw );
5929 : gf_free( newFilename );
5930 : return NULL;
5931 : }
5932 : do {
5933 : read = gf_fread(copyBuff, MIN(sizeof(copyBuff), (size_t) total), fr);
5934 : if (read > 0) {
5935 : total-= read;
5936 : write = gf_fwrite(copyBuff, (size_t) read, fw);
5937 : if (write != read) {
5938 : /* Something bad happened */
5939 : gf_fclose( fw );
5940 : gf_fclose (fr );
5941 : gf_free( newFilename );
5942 : return NULL;
5943 : }
5944 : } else {
5945 : if (read < 0) {
5946 : gf_fclose( fw );
5947 : gf_fclose( fr );
5948 : gf_free( newFilename );
5949 : return NULL;
5950 : }
5951 : }
5952 : } while (total > 0);
5953 : gf_fclose( fr );
5954 : gf_fclose (fw);
5955 : partial = (GF_PartialDownload*)gf_malloc( sizeof(GF_PartialDownload));
5956 : if (partial == NULL) {
5957 : gf_free(newFilename);
5958 : return NULL;
5959 : }
5960 : partial->filename = newFilename;
5961 : partial->url = sess->orig_url;
5962 : partial->endOffset = endOffset;
5963 : partial->startOffset = startOffset;
5964 : gf_list_add(sess->dm->partial_downloads, partial);
5965 : return newFilename;
5966 : }
5967 : }
5968 : }
5969 :
5970 : /*!
5971 : * Reassigns session flags and callbacks. This is only possible if the session is not threaded.
5972 : \param sess The session
5973 : \param flags The new flags for the session - if flags is 0xFFFFFFFF, existing flags are not modified
5974 : \param user_io The new callback function
5975 : \param cbk The new user data to ba used in the callback function
5976 : \param GF_OK or error
5977 : */
5978 : GF_Err gf_dm_sess_reassign(GF_DownloadSession *sess, u32 flags, gf_dm_user_io user_io, void *cbk)
5979 : {
5980 : /*shall only be called for non-threaded sessions!! */
5981 : if (sess->th)
5982 : return GF_BAD_PARAM;
5983 :
5984 : if (flags == 0xFFFFFFFF) {
5985 : sess->user_proc = user_io;
5986 : sess->usr_cbk = cbk;
5987 : return GF_OK;
5988 : }
5989 :
5990 : if (sess->flags & GF_DOWNLOAD_SESSION_USE_SSL) flags |= GF_DOWNLOAD_SESSION_USE_SSL;
5991 : sess->flags = flags;
5992 : if (sess->flags & GF_NETIO_SESSION_NOTIFY_DATA)
5993 : sess->force_data_write_callback = GF_TRUE;
5994 :
5995 : sess->user_proc = user_io;
5996 : sess->usr_cbk = cbk;
5997 : sess->reassigned = sess->init_data ? GF_TRUE : GF_FALSE;
5998 : sess->num_retry = SESSION_RETRY_COUNT;
5999 :
6000 : if (sess->status==GF_NETIO_DISCONNECTED)
6001 : sess->status = GF_NETIO_SETUP;
6002 :
6003 : /*threaded session shall be started with gf_dm_sess_process*/
6004 : return GF_OK;
6005 : }
6006 : #endif
6007 :
6008 :
6009 : GF_EXPORT
6010 1 : void gf_dm_set_data_rate(GF_DownloadManager *dm, u32 rate_in_bits_per_sec)
6011 : {
6012 1 : if (rate_in_bits_per_sec == 0xFFFFFFFF) {
6013 0 : dm->simulate_no_connection=GF_TRUE;
6014 : } else {
6015 : char opt[100];
6016 1 : dm->simulate_no_connection=GF_FALSE;
6017 1 : dm->limit_data_rate = rate_in_bits_per_sec/8;
6018 :
6019 : sprintf(opt, "%d", rate_in_bits_per_sec);
6020 1 : gf_opts_set_key("core", "maxrate", opt);
6021 :
6022 1 : dm->read_buf_size = GF_DOWNLOAD_BUFFER_SIZE;
6023 : //when rate is limited, use smaller smaller read size
6024 1 : if (dm->limit_data_rate) dm->read_buf_size = 1024;
6025 :
6026 : #ifdef GPAC_ENABLE_COVERAGE
6027 1 : if (gf_sys_is_cov_mode()) {
6028 1 : dm_exceeds_cap_rate(dm);
6029 : }
6030 : #endif
6031 :
6032 : }
6033 1 : }
6034 :
6035 : GF_EXPORT
6036 2 : u32 gf_dm_get_data_rate(GF_DownloadManager *dm)
6037 : {
6038 2 : return dm->limit_data_rate*8;
6039 : }
6040 :
6041 : GF_EXPORT
6042 4 : u32 gf_dm_get_global_rate(GF_DownloadManager *dm)
6043 : {
6044 : u32 ret = 0;
6045 : u32 i, count;
6046 4 : if (!dm) return 0;
6047 4 : gf_mx_p(dm->cache_mx);
6048 4 : count = gf_list_count(dm->sessions);
6049 :
6050 12 : for (i=0; i<count; i++) {
6051 8 : GF_DownloadSession *sess = (GF_DownloadSession*)gf_list_get(dm->sessions, i);
6052 8 : if (sess->status >= GF_NETIO_DATA_TRANSFERED) {
6053 7 : if (sess->total_size==sess->bytes_done) {
6054 : //do not aggregate session if done/interrupted since more than 1/2 a sec
6055 7 : if (gf_sys_clock_high_res() - sess->start_time > 500000) {
6056 3 : continue;
6057 : }
6058 : }
6059 : }
6060 5 : ret += sess->bytes_per_sec;
6061 : }
6062 4 : gf_mx_v(dm->cache_mx);
6063 4 : return 8*ret;
6064 : }
6065 :
6066 218 : Bool gf_dm_sess_is_h2(GF_DownloadSession *sess)
6067 : {
6068 : #ifdef GPAC_HAS_HTTP2
6069 : if (sess->h2_sess) return GF_TRUE;
6070 : #endif
6071 218 : return GF_FALSE;
6072 : }
6073 :
6074 : GF_EXPORT
6075 838 : const char *gf_dm_sess_get_header(GF_DownloadSession *sess, const char *name)
6076 : {
6077 : u32 i, count;
6078 838 : if( !sess || !name) return NULL;
6079 809 : count = gf_list_count(sess->headers);
6080 5082 : for (i=0; i<count; i++) {
6081 4476 : GF_HTTPHeader *header = (GF_HTTPHeader*)gf_list_get(sess->headers, i);
6082 4476 : if (!stricmp(header->name, name)) return header->value;
6083 : }
6084 : return NULL;
6085 : }
6086 :
6087 : GF_EXPORT
6088 353 : GF_Err gf_dm_sess_enum_headers(GF_DownloadSession *sess, u32 *idx, const char **hdr_name, const char **hdr_val)
6089 : {
6090 : GF_HTTPHeader *hdr;
6091 353 : if( !sess || !idx || !hdr_name || !hdr_val)
6092 : return GF_BAD_PARAM;
6093 353 : hdr = gf_list_get(sess->headers, *idx);
6094 353 : if (!hdr) return GF_EOS;
6095 326 : (*idx) = (*idx) + 1;
6096 326 : (*hdr_name) = hdr->name;
6097 326 : (*hdr_val) = hdr->value;
6098 326 : return GF_OK;
6099 : }
6100 :
6101 : GF_EXPORT
6102 1 : 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)
6103 : {
6104 1 : if (!sess)
6105 : return GF_BAD_PARAM;
6106 :
6107 1 : if (req_hdr_size) *req_hdr_size = sess->req_hdr_size;
6108 1 : if (rsp_hdr_size) *rsp_hdr_size = sess->rsp_hdr_size;
6109 1 : if (connect_time) *connect_time = sess->connect_time;
6110 1 : if (reply_time) *reply_time = sess->reply_time;
6111 1 : if (download_time) *download_time = sess->total_time_since_req;
6112 : return GF_OK;
6113 : }
6114 :
6115 : GF_EXPORT
6116 68 : void gf_dm_sess_force_memory_mode(GF_DownloadSession *sess, u32 force_keep)
6117 : {
6118 68 : if (sess) {
6119 68 : sess->flags |= GF_NETIO_SESSION_MEMORY_CACHE;
6120 68 : if (force_keep==1)
6121 39 : sess->flags |= GF_NETIO_SESSION_KEEP_CACHE;
6122 29 : else if (force_keep==2)
6123 29 : sess->flags |= GF_NETIO_SESSION_KEEP_FIRST_CACHE;
6124 : }
6125 68 : }
6126 :
6127 : GF_EXPORT
6128 6 : GF_Err gf_dm_set_localcache_provider(GF_DownloadManager *dm, Bool (*local_cache_url_provider_cbk)(void *udta, char *url, Bool is_cache_destroy), void *lc_udta)
6129 : {
6130 6 : if (!dm)
6131 : return GF_BAD_PARAM;
6132 6 : dm->local_cache_url_provider_cbk = local_cache_url_provider_cbk;
6133 6 : dm->lc_udta = lc_udta;
6134 6 : return GF_OK;
6135 :
6136 : }
6137 :
6138 : GF_EXPORT
6139 43 : const DownloadedCacheEntry gf_dm_add_cache_entry(GF_DownloadManager *dm, const char *szURL, GF_Blob *blob, u64 start_range, u64 end_range, const char *mime, Bool clone_memory, u32 download_time_ms)
6140 : {
6141 : u32 i, count;
6142 : DownloadedCacheEntry the_entry = NULL;
6143 :
6144 43 : gf_mx_p(dm->cache_mx );
6145 43 : if (blob)
6146 32 : GF_LOG(GF_LOG_INFO, GF_LOG_CACHE, ("[HTTP] Pushing %s to cache "LLU" bytes (done %s)\n", szURL, blob->size, (blob->flags & GF_BLOB_IN_TRANSFER) ? "no" : "yes"));
6147 43 : count = gf_list_count(dm->cache_entries);
6148 121 : for (i = 0 ; i < count; i++) {
6149 : const char * url;
6150 96 : DownloadedCacheEntry e = (DownloadedCacheEntry)gf_list_get(dm->cache_entries, i);
6151 : assert(e);
6152 96 : url = gf_cache_get_url(e);
6153 : assert( url );
6154 96 : if (strcmp(url, szURL)) continue;
6155 :
6156 18 : if (end_range) {
6157 0 : if (start_range != gf_cache_get_start_range(e)) continue;
6158 0 : if (end_range != gf_cache_get_end_range(e)) continue;
6159 : }
6160 : /*OK that's ours*/
6161 : the_entry = e;
6162 : break;
6163 : }
6164 43 : if (!the_entry) {
6165 25 : the_entry = gf_cache_create_entry(dm, "", szURL, 0, 0, GF_TRUE, dm->cache_mx);
6166 25 : if (!the_entry) {
6167 0 : gf_mx_v(dm->cache_mx );
6168 0 : return NULL;
6169 : }
6170 25 : gf_list_add(dm->cache_entries, the_entry);
6171 : }
6172 :
6173 43 : gf_cache_set_mime(the_entry, mime);
6174 43 : if (blob && ! (blob->flags & GF_BLOB_IN_TRANSFER))
6175 24 : gf_cache_set_range(the_entry, blob->size, start_range, end_range);
6176 :
6177 43 : gf_cache_set_content(the_entry, blob, clone_memory ? GF_TRUE : GF_FALSE, dm->cache_mx);
6178 43 : gf_cache_set_downtime(the_entry, download_time_ms);
6179 43 : gf_mx_v(dm->cache_mx );
6180 43 : return the_entry;
6181 : }
6182 :
6183 : GF_EXPORT
6184 40 : GF_Err gf_dm_force_headers(GF_DownloadManager *dm, const DownloadedCacheEntry entry, const char *headers)
6185 : {
6186 : u32 i, count;
6187 : Bool res;
6188 40 : if (!entry)
6189 : return GF_BAD_PARAM;
6190 40 : gf_mx_p(dm->cache_mx);
6191 40 : res = gf_cache_set_headers(entry, headers);
6192 40 : count = gf_list_count(dm->sessions);
6193 94 : for (i=0; i<count; i++) {
6194 54 : GF_DownloadSession *sess = gf_list_get(dm->sessions, i);
6195 54 : if (sess->cache_entry != entry) continue;
6196 11 : gf_dm_sess_reload_cached_headers(sess);
6197 : }
6198 :
6199 : #ifdef GPAC_ENABLE_COVERAGE
6200 40 : if (!count && gf_sys_is_cov_mode()) {
6201 : gf_dm_sess_reload_cached_headers(NULL);
6202 : gf_dm_refresh_cache_entry(NULL);
6203 : gf_dm_session_thread(NULL);
6204 5 : gf_user_credentials_save_digest(NULL, NULL, NULL);
6205 5 : gf_user_credentials_ask_password(NULL, NULL);
6206 : gf_user_credentials_register(NULL, NULL, NULL, NULL, GF_FALSE);
6207 5 : gf_cache_are_headers_processed(NULL);
6208 5 : gf_cache_get_start_range(NULL);
6209 5 : gf_cache_get_end_range(NULL);
6210 5 : gf_cache_get_content_length(NULL);
6211 5 : gf_cache_set_end_range(NULL, 0);
6212 5 : gf_cache_get_forced_headers(NULL);
6213 5 : gf_cache_get_downtime(NULL);
6214 : }
6215 : #endif
6216 :
6217 40 : gf_mx_v(dm->cache_mx);
6218 40 : if (res) return GF_OK;
6219 0 : return GF_BAD_PARAM;
6220 : }
6221 :
6222 : GF_EXPORT
6223 1033 : GF_Err gf_dm_sess_send(GF_DownloadSession *sess, u8 *data, u32 size)
6224 : {
6225 : GF_Err e = GF_OK;
6226 :
6227 : #ifdef GPAC_HAS_HTTP2
6228 : if (sess->h2_sess) {
6229 : if (sess->h2_send_data)
6230 : return GF_SERVICE_ERROR;
6231 : if (!sess->h2_stream_id)
6232 : return GF_SERVICE_ERROR;
6233 :
6234 : gf_mx_p(sess->mx);
6235 :
6236 : GF_LOG(GF_LOG_DEBUG, GF_LOG_HTTP, ("[HTTP/2] Sending %d bytes on stream_id %d\n", size, sess->h2_stream_id));
6237 :
6238 : sess->h2_send_data = data;
6239 : sess->h2_send_data_len = size;
6240 : if (sess->h2_data_paused) {
6241 : sess->h2_data_paused = 0;
6242 : nghttp2_session_resume_data(sess->h2_sess->ng_sess, sess->h2_stream_id);
6243 : }
6244 : //if no data, signal end of stream, otherwise regular send
6245 : if (!data || !size) {
6246 : sess->h2_is_eos = 1;
6247 : h2_session_send(sess);
6248 : //stream_id is not yet 0 in case of PUT/PUSH, stream is closed once we get reply from server
6249 : } else {
6250 : sess->h2_is_eos = 0;
6251 : //send the data
6252 : h2_flush_send(sess);
6253 : }
6254 : sess->h2_is_eos = 0;
6255 :
6256 : gf_mx_v(sess->mx);
6257 :
6258 : if (!data || !size) {
6259 : if (sess->put_state) {
6260 : sess->put_state = 2;
6261 : sess->status = GF_NETIO_WAIT_FOR_REPLY;
6262 : return GF_OK;
6263 : }
6264 : }
6265 : return GF_OK;
6266 : }
6267 : #endif
6268 :
6269 1033 : if (!data || !size) {
6270 191 : if (sess->put_state) {
6271 28 : sess->put_state = 2;
6272 28 : sess->status = GF_NETIO_WAIT_FOR_REPLY;
6273 28 : return GF_OK;
6274 : }
6275 : return GF_OK;
6276 : }
6277 :
6278 : #ifdef GPAC_HAS_SSL
6279 842 : if (sess->ssl) {
6280 9 : e = gf_ssl_write(sess->ssl, data, size);
6281 : } else
6282 : #endif
6283 833 : e = gf_sk_send(sess->sock, data, size);
6284 :
6285 842 : if (e==GF_IP_CONNECTION_CLOSED) {
6286 : sess_connection_closed(sess);
6287 2 : sess->status = GF_NETIO_STATE_ERROR;
6288 2 : return e;
6289 : }
6290 840 : else if (e==GF_IP_SOCK_WOULD_BLOCK) {
6291 : return gf_dm_sess_send(sess, data, size);
6292 : }
6293 : return e;
6294 : }
6295 :
6296 0 : void gf_dm_sess_flush_h2(GF_DownloadSession *sess)
6297 : {
6298 : #ifdef GPAC_HAS_HTTP2
6299 : u64 in_time;
6300 : u32 res;
6301 : char h2_flush[2024];
6302 : if (!sess->h2_sess) return;
6303 :
6304 : in_time = gf_sys_clock_high_res();
6305 : while (nghttp2_session_want_read(sess->h2_sess->ng_sess)) {
6306 : if (gf_sys_clock_high_res() - in_time > 100000)
6307 : break;
6308 :
6309 : //read any frame pending from remote peer (window update and co)
6310 : gf_dm_read_data(sess, h2_flush, 1023, &res);
6311 : }
6312 : #endif
6313 0 : }
6314 :
6315 :
6316 : #endif
|