1 /*
2 * GPAC Multimedia Framework
3 *
4 * Authors: Jean Le Feuvre
5 * Copyright (c) Telecom ParisTech 2005-2020
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
45 #endif
46
47 #ifdef __USE_POSIX
48 #include <unistd.h>
49 #endif
50
51 #define SIZE_IN_STREAM ( 2 << 29 )
52
53
54 #define SESSION_RETRY_COUNT 20
55
56 #include <gpac/revision.h>
57 #define GF_DOWNLOAD_AGENT_NAME "GPAC/"GPAC_VERSION "-rev" GPAC_GIT_REVISION
58
59 //let's be agressive with socket buffer size
60 #define GF_DOWNLOAD_BUFFER_SIZE 131072
61
62
63 static void gf_dm_connect(GF_DownloadSession *sess);
64
65 /*internal flags*/
66 enum
67 {
68 GF_DOWNLOAD_SESSION_USE_SSL = 1<<10,
69 GF_DOWNLOAD_SESSION_THREAD_DEAD = 1<<11
70 };
71
72 typedef struct __gf_user_credentials
73 {
74 char site[1024];
75 char username[50];
76 char digest[1024];
77 Bool valid;
78 } gf_user_credentials_struct;
79
80 enum REQUEST_TYPE
81 {
82 GET = 0,
83 HEAD = 1,
84 OTHER = 2
85 };
86
87 /*!the structure used to store an HTTP header*/
88 typedef struct
89 {
90 char *name;
91 char *value;
92 } GF_HTTPHeader;
93
94
95 /**
96 * This structure handles partial downloads
97 */
98 typedef struct __partialDownloadStruct {
99 char * url;
100 u64 startOffset;
101 u64 endOffset;
102 char * filename;
103 } GF_PartialDownload ;
104
105 typedef struct
106 {
107 struct __gf_download_session *sess;
108 } GF_SessTask;
109
110 struct __gf_download_session
111 {
112 /*this is always 0 and helps differenciating downloads from other interfaces (interfaceType != 0)*/
113 u32 reserved;
114
115 struct __gf_download_manager *dm;
116 GF_Thread *th;
117 GF_Mutex *mx;
118 GF_SessTask *ftask;
119
120 Bool in_callback, destroy;
121 u32 proxy_enabled;
122 Bool allow_direct_reuse;
123
124 char *server_name;
125 u16 port;
126
127 char *orig_url;
128 char *orig_url_before_redirect;
129 char *remote_path;
130 gf_user_credentials_struct * creds;
131 char cookie[GF_MAX_PATH];
132 DownloadedCacheEntry cache_entry;
133 Bool reused_cache_entry, from_cache_only;
134
135 //mime type, only used when the session is not cached.
136 char *mime_type;
137 GF_List *headers;
138
139 GF_Socket *sock;
140 u32 num_retry;
141 GF_NetIOStatus status;
142
143 u32 flags;
144 u32 total_size, bytes_done, icy_metaint, icy_count, icy_bytes;
145 u64 start_time;
146 u64 chunk_run_time;
147 u64 active_time, in_time, idle_time;
148
149 u32 bytes_per_sec;
150 u64 start_time_utc;
151 Bool last_chunk_found;
152 Bool connection_close;
153 Bool is_range_continuation;
154 /*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)*/
155 u32 needs_cache_reconfig;
156 /* Range information if needed for the download (cf flag) */
157 Bool needs_range;
158 u64 range_start, range_end;
159
160 u32 connect_time, ssl_setup_time, reply_time, total_time_since_req, req_hdr_size, rsp_hdr_size;
161
162 /*0: GET
163 1: HEAD
164 2: all the rest
165 */
166 enum REQUEST_TYPE http_read_type;
167
168 GF_Err last_error;
169 char *init_data;
170 u32 init_data_size;
171 Bool server_only_understand_get;
172 /* True if cache file must be stored on disk */
173 Bool use_cache_file;
174 Bool disable_cache;
175 /*forces notification of data exchange to be sent regardless of threading mode*/
176 Bool force_data_write_callback;
177 #ifdef GPAC_HAS_SSL
178 SSL *ssl;
179 #endif
180
181 void (*do_requests)(struct __gf_download_session *);
182
183 /*callback for data reception - may not be NULL*/
184 gf_dm_user_io user_proc;
185 void *usr_cbk;
186 Bool reassigned;
187
188 Bool chunked;
189 u32 nb_left_in_chunk;
190 u32 current_chunk_size;
191 u64 current_chunk_start;
192
193 u64 request_start_time;
194 /*private extension*/
195 void *ext;
196
197 char *remaining_data;
198 u32 remaining_data_size;
199
200 u32 head_timeout, request_timeout;
201
202 Bool local_cache_only;
203 Bool server_mode;
204 //0: not PUT/POST, 1: waiting for body to be completed, 2: body done
205 u32 put_state;
206 };
207
208 struct __gf_download_manager
209 {
210 GF_Mutex *cache_mx;
211 char *cache_directory;
212
213 Bool (*get_user_password)(void *usr_cbk, const char *site_url, char *usr_name, char *password);
214 void *usr_cbk;
215
216 GF_List *sessions;
217 Bool disable_cache, simulate_no_connection, allow_offline_cache, clean_cache;
218 u32 limit_data_rate, read_buf_size;
219 u64 max_cache_size;
220 Bool allow_broken_certificate;
221
222 GF_List *skip_proxy_servers;
223 GF_List *credentials;
224 GF_List *cache_entries;
225 /* FIXME : should be placed in DownloadedCacheEntry maybe... */
226 GF_List *partial_downloads;
227 #ifdef GPAC_HAS_SSL
228 SSL_CTX *ssl_ctx;
229 #endif
230
231 GF_FilterSession *filter_session;
232
233
234 Bool (*local_cache_url_provider_cbk)(void *udta, char *url, Bool cache_destroy);
235 void *lc_udta;
236 };
237
238 #ifdef GPAC_HAS_SSL
239
init_prng(void)240 static void init_prng (void)
241 {
242 char namebuf[256];
243 const char *random_file;
244
245 if (RAND_status ()) return;
246
247 namebuf[0] = '\0';
248 random_file = RAND_file_name (namebuf, sizeof (namebuf));
249
250 if (random_file && *random_file)
251 RAND_load_file(random_file, 16384);
252
253 if (RAND_status ()) return;
254 }
255
256 #endif
257
258 /*
259 * Private methods of cache
260 */
261
262 /**
263 * \brief Write data to cache
264 * Writes data to the cache. A call to gf_cache_open_write_cache should have been issued before calling this function.
265 \param entry The entry to use
266 \param sess The download session
267 \param data data to write
268 \param size number of elements to write
269 \param GF_OK is everything went fine, GF_BAD_PARAM if cache has not been opened, GF_IO_ERR if a failure occurs
270 */
271 GF_Err gf_cache_write_to_cache( const DownloadedCacheEntry entry, const GF_DownloadSession * sess, const char * data, const u32 size);
272
273 /**
274 * \brief Close the write file pointer of cache
275 * This function also flushes all buffers, so cache will always be consistent after
276 \param entry The entry to use
277 \param sess The download session
278 \param success 1 if cache write is success, false otherwise
279 \param GF_OK is everything went fine, GF_BAD_PARAM if entry is NULL, GF_IO_ERR if a failure occurs
280 */
281 GF_Err gf_cache_close_write_cache( const DownloadedCacheEntry entry, const GF_DownloadSession * sess, Bool success);
282
283 /**
284 * \brief Open the write file pointer of cache
285 * This function prepares calls for gf_cache_write_to_cache
286 \param entry The entry to use
287 \param sess The download session
288 \param GF_OK is everything went fine, GF_BAD_PARAM if entry is NULL, GF_IO_ERR if a failure occurs
289 */
290 GF_Err gf_cache_open_write_cache( const DownloadedCacheEntry entry, const GF_DownloadSession * sess );
291
292 /*modify end range when chaining byte-range requests*/
293 void gf_cache_set_end_range(DownloadedCacheEntry entry, u64 range_end);
294
295 /*returns 1 if cache is currently open for write*/
296 Bool gf_cache_is_in_progress(const DownloadedCacheEntry entry);
297
298 /**
299 * Find a User's credentials for a given site
300 */
gf_find_user_credentials_for_site(GF_DownloadManager * dm,const char * server_name)301 static gf_user_credentials_struct* gf_find_user_credentials_for_site(GF_DownloadManager *dm, const char *server_name) {
302 u32 count, i;
303 if (!dm || !dm->credentials || !server_name || !strlen(server_name))
304 return NULL;
305 count = gf_list_count( dm->credentials);
306 for (i = 0 ; i < count; i++) {
307 gf_user_credentials_struct * cred = (gf_user_credentials_struct*)gf_list_get(dm->credentials, i );
308 assert( cred );
309 if (!strcmp(cred->site, server_name))
310 return cred;
311 }
312 return NULL;
313 }
314
315 /**
316 * \brief Saves the digest for authentication of password and username
317 \param dm The download manager
318 \param creds The credentials to fill
319 \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.
320 */
gf_user_credentials_save_digest(GF_DownloadManager * dm,gf_user_credentials_struct * creds,const char * password)321 static GF_Err gf_user_credentials_save_digest( GF_DownloadManager * dm, gf_user_credentials_struct * creds, const char * password) {
322 int size;
323 char pass_buf[1024], range_buf[1024];
324 if (!dm || !creds || !password)
325 return GF_BAD_PARAM;
326 sprintf(pass_buf, "%s:%s", creds->username, password);
327 size = gf_base64_encode(pass_buf, (u32) strlen(pass_buf), range_buf, 1024);
328 range_buf[size] = 0;
329 strcpy(creds->digest, range_buf);
330 creds->valid = GF_TRUE;
331 return GF_OK;
332 }
333
334 /**
335 * \brief Asks the user for credentials for given site
336 \param dm The download manager
337 \param creds The credentials to fill
338 \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.
339 */
gf_user_credentials_ask_password(GF_DownloadManager * dm,gf_user_credentials_struct * creds)340 static GF_Err gf_user_credentials_ask_password( GF_DownloadManager * dm, gf_user_credentials_struct * creds)
341 {
342 char szPASS[50];
343 if (!dm || !creds)
344 return GF_BAD_PARAM;
345 memset(szPASS, 0, 50);
346 if (!dm->get_user_password || !dm->get_user_password(dm->usr_cbk, creds->site, creds->username, szPASS)) {
347 return GF_AUTHENTICATION_FAILURE;
348 }
349 return gf_user_credentials_save_digest(dm, creds, szPASS);
350 }
351
gf_user_credentials_register(GF_DownloadManager * dm,const char * server_name,const char * username,const char * password,Bool valid)352 static gf_user_credentials_struct * gf_user_credentials_register(GF_DownloadManager * dm, const char * server_name, const char * username, const char * password, Bool valid)
353 {
354 gf_user_credentials_struct * creds;
355 if (!dm)
356 return NULL;
357 assert( server_name );
358 creds = gf_find_user_credentials_for_site(dm, server_name);
359 /* If none found, we create one */
360 if (!creds) {
361 creds = (gf_user_credentials_struct*)gf_malloc(sizeof( gf_user_credentials_struct));
362 if (!creds)
363 return NULL;
364 gf_list_insert(dm->credentials, creds, 0);
365 }
366 creds->valid = valid;
367 strncpy(creds->username, username ? username : "", 49);
368 creds->username[49] = 0;
369 strcpy(creds->site, server_name);
370 if (username && password && valid)
371 gf_user_credentials_save_digest(dm, creds, password);
372 else {
373 if (GF_OK != gf_user_credentials_ask_password(dm, creds)) {
374 GF_LOG(GF_LOG_ERROR, GF_LOG_HTTP,
375 ("[HTTP] Failed to get password information.\n"));
376 gf_list_rem( dm->credentials, 0);
377 gf_free( creds );
378 creds = NULL;
379 }
380 }
381 return creds;
382 }
383
384 #ifdef GPAC_HAS_SSL
385
386 static Bool _ssl_is_initialized = GF_FALSE;
387
388 /*!
389 * initialize the SSL library once for all download managers
390 \param GF_FALSE if everyhing is OK, GF_TRUE otherwise
391 */
gf_ssl_init_lib()392 Bool gf_ssl_init_lib() {
393 if (_ssl_is_initialized)
394 return GF_FALSE;
395 GF_LOG(GF_LOG_DEBUG, GF_LOG_HTTP, ("[HTTPS] Initializing SSL library...\n"));
396 init_prng();
397 if (RAND_status() != 1) {
398 GF_LOG(GF_LOG_ERROR, GF_LOG_HTTP, ("[HTTPS] Error while initializing Random Number generator, failed to init SSL !\n"));
399 return GF_TRUE;
400 }
401
402 /* per https://www.openssl.org/docs/man1.1.0/ssl/OPENSSL_init_ssl.html
403 ** As of version 1.1.0 OpenSSL will automatically allocate all resources that it needs so no explicit initialisation is required.
404 ** Similarly it will also automatically deinitialise as required.
405 */
406 #if OPENSSL_VERSION_NUMBER < 0x10100000L
407 SSL_library_init();
408 SSL_load_error_strings();
409 SSLeay_add_all_algorithms();
410 SSLeay_add_ssl_algorithms();
411 #endif
412
413 _ssl_is_initialized = GF_TRUE;
414 GF_LOG(GF_LOG_DEBUG, GF_LOG_HTTP, ("[HTTPS] Initalization of SSL library complete.\n"));
415 return GF_FALSE;
416 }
417
ssl_init(GF_DownloadManager * dm,u32 mode)418 static int ssl_init(GF_DownloadManager *dm, u32 mode)
419 {
420 #if OPENSSL_VERSION_NUMBER > 0x00909000
421 const
422 #endif
423 SSL_METHOD *meth;
424
425 if (!dm) return 0;
426 gf_mx_p(dm->cache_mx);
427 /* The SSL has already been initialized. */
428 if (dm->ssl_ctx) {
429 gf_mx_v(dm->cache_mx);
430 return 1;
431 }
432 /* Init the PRNG. If that fails, bail out. */
433 if (gf_ssl_init_lib()) {
434 GF_LOG(GF_LOG_ERROR, GF_LOG_HTTP, ("[HTTPS] Failed to properly initialize SSL library\n"));
435 goto error;
436 }
437
438 switch (mode) {
439 #if OPENSSL_VERSION_NUMBER < 0x10100000L
440 case 0:
441 meth = SSLv23_client_method();
442 break;
443 #if 0 /*SSL v2 is no longer supported in OpenSSL 1.0*/
444 case 1:
445 meth = SSLv2_client_method();
446 break;
447 #endif
448 case 2:
449 meth = SSLv3_client_method();
450 break;
451 case 3:
452 meth = TLSv1_client_method();
453 break;
454 #else /* for openssl 1.1+ this is the preferred method */
455 case 0:
456 meth = TLS_client_method();
457 break;
458 #endif
459 default:
460 goto error;
461 }
462
463 dm->ssl_ctx = SSL_CTX_new(meth);
464 if (!dm->ssl_ctx) goto error;
465 SSL_CTX_set_default_verify_paths(dm->ssl_ctx);
466 SSL_CTX_load_verify_locations (dm->ssl_ctx, NULL, NULL);
467 /* SSL_VERIFY_NONE instructs OpenSSL not to abort SSL_connect if the
468 certificate is invalid. We verify the certificate separately in
469 ssl_check_certificate, which provides much better diagnostics
470 than examining the error stack after a failed SSL_connect. */
471 SSL_CTX_set_verify(dm->ssl_ctx, SSL_VERIFY_NONE, NULL);
472
473 /* Since fd_write unconditionally assumes partial writes (and handles them correctly),
474 allow them in OpenSSL. */
475 SSL_CTX_set_mode(dm->ssl_ctx, SSL_MODE_ENABLE_PARTIAL_WRITE);
476 gf_mx_v(dm->cache_mx);
477 return 1;
478 error:
479 if (dm->ssl_ctx) SSL_CTX_free(dm->ssl_ctx);
480 dm->ssl_ctx = NULL;
481 gf_mx_v(dm->cache_mx);
482 return 0;
483 }
484
485
gf_ssl_server_context_new(const char * cert,const char * key)486 void *gf_ssl_server_context_new(const char *cert, const char *key)
487 {
488 const SSL_METHOD *method;
489 SSL_CTX *ctx;
490
491 method = SSLv23_server_method();
492
493 ctx = SSL_CTX_new(method);
494 if (!ctx) {
495 GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("Unable to create SSL context\n"));
496 ERR_print_errors_fp(stderr);
497 return NULL;
498 }
499 SSL_CTX_set_ecdh_auto(ctx, 1);
500 if (SSL_CTX_use_certificate_file(ctx, cert, SSL_FILETYPE_PEM) <= 0) {
501 ERR_print_errors_fp(stderr);
502 SSL_CTX_free(ctx);
503 return NULL;
504 }
505 if (SSL_CTX_use_PrivateKey_file(ctx, key, SSL_FILETYPE_PEM) <= 0 ) {
506 ERR_print_errors_fp(stderr);
507 SSL_CTX_free(ctx);
508 return NULL;
509 }
510 return ctx;
511 }
512
gf_ssl_server_context_del(void * ssl_ctx)513 void gf_ssl_server_context_del(void *ssl_ctx)
514 {
515 SSL_CTX_free(ssl_ctx);
516 }
gf_ssl_new(void * ssl_ctx,GF_Socket * client_sock,GF_Err * e)517 void *gf_ssl_new(void *ssl_ctx, GF_Socket *client_sock, GF_Err *e)
518 {
519 SSL *ssl;
520 ssl = SSL_new(ssl_ctx);
521 if (!ssl) {
522 *e = GF_IO_ERR;
523 return NULL;
524 }
525 SSL_set_fd(ssl, gf_sk_get_handle(client_sock) );
526 if (SSL_accept(ssl) <= 0) {
527 ERR_print_errors_fp(stderr);
528 SSL_shutdown(ssl);
529 SSL_free(ssl);
530 *e = GF_AUTHENTICATION_FAILURE;
531 return NULL;
532 }
533 *e = GF_OK;
534 return ssl;
535 }
gf_ssl_del(void * ssl)536 void gf_ssl_del(void *ssl)
537 {
538 SSL_shutdown(ssl);
539 SSL_free(ssl);
540 }
541
gf_ssl_write(void * ssl_ctx,const u8 * buffer,u32 size)542 GF_Err gf_ssl_write(void *ssl_ctx, const u8 *buffer, u32 size)
543 {
544 u32 idx=0;
545 s32 nb_tls_blocks = size/16000;
546 while (nb_tls_blocks>=0) {
547 u32 len, to_write = 16000;
548 if (nb_tls_blocks==0)
549 to_write = size - idx*16000;
550
551 len = SSL_write(ssl_ctx, buffer + idx*16000, to_write);
552 nb_tls_blocks--;
553 idx++;
554
555 if (len != to_write) {
556 return GF_IP_NETWORK_FAILURE;
557 }
558 }
559 return GF_OK;
560 }
561
562
563 #endif /* GPAC_HAS_SSL */
564
565
gf_dm_is_local(GF_DownloadManager * dm,const char * url)566 static Bool gf_dm_is_local(GF_DownloadManager *dm, const char *url)
567 {
568 if (!strnicmp(url, "file://", 7)) return GF_TRUE;
569 if (!strstr(url, "://")) return GF_TRUE;
570 return GF_FALSE;
571 }
572
gf_dm_can_handle_url(GF_DownloadManager * dm,const char * url)573 static Bool gf_dm_can_handle_url(GF_DownloadManager *dm, const char *url)
574 {
575 if (!strnicmp(url, "http://", 7)) return GF_TRUE;
576 #ifdef GPAC_HAS_SSL
577 if (!strnicmp(url, "https://", 8)) return GF_TRUE;
578 #endif
579 return GF_FALSE;
580 }
581
582 /*!
583 * Finds an existing entry in the cache for a given URL
584 \param sess The session configured with the URL
585 \param NULL if none found, the DownloadedCacheEntry otherwise
586 */
gf_dm_find_cached_entry_by_url(GF_DownloadSession * sess)587 DownloadedCacheEntry gf_dm_find_cached_entry_by_url(GF_DownloadSession * sess)
588 {
589 u32 i, count;
590 assert( sess && sess->dm && sess->dm->cache_entries );
591 gf_mx_p( sess->dm->cache_mx );
592 count = gf_list_count(sess->dm->cache_entries);
593 for (i = 0 ; i < count; i++) {
594 const char * url;
595 DownloadedCacheEntry e = (DownloadedCacheEntry)gf_list_get(sess->dm->cache_entries, i);
596 assert(e);
597 url = gf_cache_get_url(e);
598 assert( url );
599 if (strcmp(url, sess->orig_url)) continue;
600 if (sess->needs_cache_reconfig==2)
601 continue;
602
603 if (! sess->is_range_continuation) {
604 if (sess->range_start != gf_cache_get_start_range(e)) continue;
605 if (sess->range_end != gf_cache_get_end_range(e)) continue;
606 }
607 /*OK that's ours*/
608 gf_mx_v( sess->dm->cache_mx );
609 return e;
610 }
611 gf_mx_v( sess->dm->cache_mx );
612 return NULL;
613 }
614
615 /**
616 * Creates a new cache entry
617 \param dm The download manager to create this entry
618 \param cache_directory The path to the directory containing cache files
619 \param url The full URL
620 \param start_range the start of the byte range request
621 \param end_range the end of the byte range request
622 \param mem_storage Boolean indicating if the cache data should be stored in memory
623 \param The DownloadedCacheEntry
624 */
625 DownloadedCacheEntry gf_cache_create_entry( GF_DownloadManager * dm, const char * cache_directory, const char * url, u64 start_range, u64 end_range, Bool mem_storage);
626
627 /*!
628 * Removes a session for a DownloadedCacheEntry
629 \param entry The entry
630 \param sess The session to remove
631 \param the number of sessions left in the cached entry, -1 if one of the parameters is wrong
632 */
633 s32 gf_cache_remove_session_from_cache_entry(DownloadedCacheEntry entry, GF_DownloadSession * sess);
634
635 Bool gf_cache_set_mime(const DownloadedCacheEntry entry, const char *mime);
636 Bool gf_cache_set_range(const DownloadedCacheEntry entry, u64 size, u64 start_range, u64 end_range);
637 Bool gf_cache_set_content(const DownloadedCacheEntry entry, char *data, u32 size, Bool copy);
638 Bool gf_cache_set_headers(const DownloadedCacheEntry entry, const char *headers);
639 Bool gf_cache_set_downtime(const DownloadedCacheEntry entry, u32 download_time_ms);
640
641
642 /**
643 * Removes a cache entry from cache and performs a cleanup if possible.
644 * If the cache entry is marked for deletion and has no sessions associated with it, it will be
645 * removed (so some modules using a streaming like cache will still work).
646 */
gf_dm_remove_cache_entry_from_session(GF_DownloadSession * sess)647 static void gf_dm_remove_cache_entry_from_session(GF_DownloadSession * sess) {
648 if (sess && sess->cache_entry) {
649 gf_cache_remove_session_from_cache_entry(sess->cache_entry, sess);
650 if (sess->dm
651 /*JLF - not sure what the rationale of this test is, and it prevents cleanup of cache entry
652 which then results to crash when restarting the session (entry->writeFilePtr i snot set back to NULL)*/
653 && gf_cache_entry_is_delete_files_when_deleted(sess->cache_entry)
654
655 && (0 == gf_cache_get_sessions_count_for_cache_entry(sess->cache_entry)))
656 {
657 u32 i, count;
658 gf_mx_p( sess->dm->cache_mx );
659 count = gf_list_count( sess->dm->cache_entries );
660 for (i = 0; i < count; i++) {
661 DownloadedCacheEntry ex = (DownloadedCacheEntry)gf_list_get(sess->dm->cache_entries, i);
662 if (ex == sess->cache_entry) {
663 gf_list_rem(sess->dm->cache_entries, i);
664 gf_cache_delete_entry( sess->cache_entry );
665 sess->cache_entry = NULL;
666 break;
667 }
668 }
669 gf_mx_v( sess->dm->cache_mx );
670 }
671 }
672 }
673
674 /*!
675 * Adds a session to a DownloadedCacheEntry.
676 * implemented in cache.c
677 \param entry The entry
678 \param sess The session to add
679 \param the number of sessions in the cached entry, -1 if one of the parameters is wrong
680 */
681 s32 gf_cache_add_session_to_cache_entry(DownloadedCacheEntry entry, GF_DownloadSession * sess);
682
683 static void gf_dm_sess_notify_state(GF_DownloadSession *sess, GF_NetIOStatus dnload_status, GF_Err error);
684
gf_dm_configure_cache(GF_DownloadSession * sess)685 static void gf_dm_configure_cache(GF_DownloadSession *sess)
686 {
687 DownloadedCacheEntry entry;
688 GF_LOG(GF_LOG_DEBUG, GF_LOG_CACHE, ("[Downloader] gf_dm_configure_cache(%p), cached=%s\n", sess, (sess->flags & GF_NETIO_SESSION_NOT_CACHED) ? "no" : "yes" ));
689 gf_dm_remove_cache_entry_from_session(sess);
690 if (sess->flags & GF_NETIO_SESSION_NOT_CACHED) {
691 sess->reused_cache_entry = GF_FALSE;
692 if (sess->cache_entry)
693 gf_cache_close_write_cache(sess->cache_entry, sess, GF_FALSE);
694 } else {
695 Bool found = GF_FALSE;
696 u32 i, count;
697 entry = gf_dm_find_cached_entry_by_url(sess);
698 if (!entry) {
699 if (sess->local_cache_only) {
700 sess->cache_entry = NULL;
701 sess->last_error = GF_URL_ERROR;
702 return;
703 }
704 if (sess->cache_entry) {
705 /* We found the existing session */
706 gf_cache_entry_set_delete_files_when_deleted(sess->cache_entry);
707 if (0 == gf_cache_get_sessions_count_for_cache_entry(sess->cache_entry)) {
708 gf_mx_p( sess->dm->cache_mx );
709 /* No session attached anymore... we can delete it */
710 gf_list_del_item(sess->dm->cache_entries, sess->cache_entry);
711 gf_mx_v( sess->dm->cache_mx );
712 gf_cache_delete_entry(sess->cache_entry);
713 }
714 sess->cache_entry = NULL;
715 }
716 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);
717 gf_mx_p( sess->dm->cache_mx );
718 gf_list_add(sess->dm->cache_entries, entry);
719 gf_mx_v( sess->dm->cache_mx );
720 sess->is_range_continuation = GF_FALSE;
721 }
722 assert( entry );
723 sess->cache_entry = entry;
724 sess->reused_cache_entry = gf_cache_is_in_progress(entry);
725 count = gf_list_count(sess->dm->sessions);
726 for (i=0; i<count; i++) {
727 GF_DownloadSession *a_sess = (GF_DownloadSession*)gf_list_get(sess->dm->sessions, i);
728 assert(a_sess);
729 if (a_sess==sess) continue;
730 if (a_sess->cache_entry==entry) {
731 found = GF_TRUE;
732 break;
733 }
734 }
735 if (!found) {
736 sess->reused_cache_entry = GF_FALSE;
737 gf_cache_close_write_cache(sess->cache_entry, sess, GF_FALSE);
738 }
739 gf_cache_add_session_to_cache_entry(sess->cache_entry, sess);
740 if (sess->needs_range)
741 gf_cache_set_range(sess->cache_entry, 0, sess->range_start, sess->range_end);
742 GF_LOG(GF_LOG_INFO, GF_LOG_HTTP, ("[CACHE] Cache setup to %p %s\n", sess, gf_cache_get_cache_filename(sess->cache_entry)));
743
744
745 if ( (sess->allow_direct_reuse || sess->dm->allow_offline_cache) && !gf_cache_check_if_cache_file_is_corrupted(sess->cache_entry)
746 ) {
747 sess->from_cache_only = GF_TRUE;
748 sess->connect_time = 0;
749 sess->status = GF_NETIO_CONNECTED;
750 GF_LOG(GF_LOG_WARNING, GF_LOG_HTTP, ("[HTTP] using existing cache entry\n"));
751 gf_dm_sess_notify_state(sess, GF_NETIO_CONNECTED, GF_OK);
752 }
753 }
754 }
755
gf_dm_delete_cached_file_entry(const GF_DownloadManager * dm,const char * url)756 void gf_dm_delete_cached_file_entry(const GF_DownloadManager * dm, const char * url)
757 {
758 GF_Err e;
759 u32 count, i;
760 char * realURL;
761 GF_URL_Info info;
762 if (!url || !dm)
763 return;
764 gf_mx_p( dm->cache_mx );
765 gf_dm_url_info_init(&info);
766 e = gf_dm_get_url_info(url, &info, NULL);
767 if (e != GF_OK) {
768 gf_mx_p( dm->cache_mx );
769 gf_dm_url_info_del(&info);
770 return;
771 }
772 realURL = gf_strdup(info.canonicalRepresentation);
773 gf_dm_url_info_del(&info);
774 assert( realURL );
775 count = gf_list_count(dm->cache_entries);
776 for (i = 0 ; i < count; i++) {
777 const char * e_url;
778 DownloadedCacheEntry cache_ent = (DownloadedCacheEntry)gf_list_get(dm->cache_entries, i);
779 assert(cache_ent);
780 e_url = gf_cache_get_url(cache_ent);
781 assert( e_url );
782 if (!strcmp(e_url, realURL)) {
783 /* We found the existing session */
784 gf_cache_entry_set_delete_files_when_deleted(cache_ent);
785 if (0 == gf_cache_get_sessions_count_for_cache_entry( cache_ent )) {
786 /* No session attached anymore... we can delete it */
787 gf_list_rem(dm->cache_entries, i);
788 gf_cache_delete_entry(cache_ent);
789 }
790 /* If deleted or not, we don't search further */
791 gf_mx_v( dm->cache_mx );
792 gf_free(realURL);
793 return;
794 }
795 }
796 /* If we are heren it means we did not found this URL in cache */
797 gf_mx_v( dm->cache_mx );
798 gf_free(realURL);
799 GF_LOG(GF_LOG_WARNING, GF_LOG_HTTP, ("[CACHE] Cannot find URL %s, cache file won't be deleted.\n", url));
800 }
801
802 GF_EXPORT
gf_dm_delete_cached_file_entry_session(const GF_DownloadSession * sess,const char * url)803 void gf_dm_delete_cached_file_entry_session(const GF_DownloadSession * sess, const char * url) {
804 if (sess && sess->dm && url) {
805 GF_LOG(GF_LOG_INFO, GF_LOG_HTTP, ("[CACHE] Requesting deletion for %s\n", url));
806 gf_dm_delete_cached_file_entry(sess->dm, url);
807 if (sess->local_cache_only && sess->dm->local_cache_url_provider_cbk)
808 sess->dm->local_cache_url_provider_cbk(sess->dm->lc_udta, (char *) url, GF_TRUE);
809 }
810 }
811
812
gf_dm_clear_headers(GF_DownloadSession * sess)813 static void gf_dm_clear_headers(GF_DownloadSession *sess)
814 {
815 while (gf_list_count(sess->headers)) {
816 GF_HTTPHeader *hdr = (GF_HTTPHeader*)gf_list_last(sess->headers);
817 gf_list_rem_last(sess->headers);
818 gf_free(hdr->name);
819 gf_free(hdr->value);
820 gf_free(hdr);
821 }
822 if (sess->mime_type) {
823 gf_free(sess->mime_type);
824 sess->mime_type = NULL;
825 }
826 }
827
828
gf_dm_disconnect(GF_DownloadSession * sess,Bool force_close)829 static void gf_dm_disconnect(GF_DownloadSession *sess, Bool force_close)
830 {
831 assert( sess );
832 if (sess->connection_close) force_close = GF_TRUE;
833 sess->connection_close = GF_FALSE;
834 sess->remaining_data_size = 0;
835
836 if (sess->status >= GF_NETIO_DISCONNECTED) {
837 if (force_close && sess->use_cache_file && sess->cache_entry) {
838 gf_cache_close_write_cache(sess->cache_entry, sess, GF_FALSE);
839 }
840 return;
841 }
842 GF_LOG(GF_LOG_DEBUG, GF_LOG_CORE, ("[Downloader] gf_dm_disconnect(%p)\n", sess ));
843
844 gf_mx_p(sess->mx);
845
846 if (!sess->server_mode) {
847 if (force_close || !(sess->flags & GF_NETIO_SESSION_PERSISTENT)) {
848 #ifdef GPAC_HAS_SSL
849 if (sess->ssl) {
850 SSL_shutdown(sess->ssl);
851 SSL_free(sess->ssl);
852 sess->ssl = NULL;
853 }
854 #endif
855 if (sess->sock) {
856 GF_Socket * sx = sess->sock;
857 sess->sock = NULL;
858 gf_sk_del(sx);
859 }
860 }
861 if (force_close && sess->use_cache_file) {
862 gf_cache_close_write_cache(sess->cache_entry, sess, GF_FALSE);
863 }
864 }
865 sess->status = GF_NETIO_DISCONNECTED;
866 if (sess->num_retry) sess->num_retry--;
867
868 gf_mx_v(sess->mx);
869 }
870
871 GF_EXPORT
gf_dm_sess_del(GF_DownloadSession * sess)872 void gf_dm_sess_del(GF_DownloadSession *sess)
873 {
874 if (!sess)
875 return;
876
877 GF_LOG(GF_LOG_DEBUG, GF_LOG_HTTP, ("[Downloader] %s session (%p) URL %s\n", sess->server_mode ? "Detach" : "Destroy", sess, sess->orig_url));
878 /*self-destruction, let the download manager destroy us*/
879 if ((sess->th || sess->ftask) && sess->in_callback) {
880 sess->destroy = GF_TRUE;
881 return;
882 }
883 gf_dm_disconnect(sess, GF_TRUE);
884 gf_dm_clear_headers(sess);
885
886 /*if threaded wait for thread exit*/
887 if (sess->th) {
888 while (!(sess->flags & GF_DOWNLOAD_SESSION_THREAD_DEAD))
889 gf_sleep(1);
890 gf_th_stop(sess->th);
891 gf_th_del(sess->th);
892 sess->th = NULL;
893 }
894
895 if (sess->dm) {
896 gf_mx_p(sess->dm->cache_mx);
897 gf_list_del_item(sess->dm->sessions, sess);
898 gf_mx_v(sess->dm->cache_mx);
899 }
900
901 gf_dm_remove_cache_entry_from_session(sess);
902 sess->cache_entry = NULL;
903 if (sess->orig_url) gf_free(sess->orig_url);
904 if (sess->orig_url_before_redirect) gf_free(sess->orig_url_before_redirect);
905 if (sess->server_name) gf_free(sess->server_name);
906 sess->server_name = NULL;
907 if (sess->remote_path) gf_free(sess->remote_path);
908 /* Credentials are stored into the sess->dm */
909 if (sess->creds) sess->creds = NULL;
910 if (sess->init_data) gf_free(sess->init_data);
911 if (sess->remaining_data) gf_free(sess->remaining_data);
912
913 sess->orig_url = sess->server_name = sess->remote_path;
914 sess->creds = NULL;
915 if (sess->sock && !sess->server_mode)
916 gf_sk_del(sess->sock);
917 gf_list_del(sess->headers);
918 gf_mx_del(sess->mx);
919 if (sess->ftask) {
920 sess->ftask->sess = NULL;
921 sess->ftask = NULL;
922 }
923 gf_free(sess);
924 }
925
926 void http_do_requests(GF_DownloadSession *sess);
927
gf_dm_sess_notify_state(GF_DownloadSession * sess,GF_NetIOStatus dnload_status,GF_Err error)928 static void gf_dm_sess_notify_state(GF_DownloadSession *sess, GF_NetIOStatus dnload_status, GF_Err error)
929 {
930 if (sess->user_proc) {
931 GF_NETIO_Parameter par;
932 sess->in_callback = GF_TRUE;
933 memset(&par, 0, sizeof(GF_NETIO_Parameter));
934 par.msg_type = dnload_status;
935 par.error = error;
936 par.sess = sess;
937 par.reply = 200;
938 sess->user_proc(sess->usr_cbk, &par);
939 sess->in_callback = GF_FALSE;
940 }
941 }
942
gf_dm_sess_user_io(GF_DownloadSession * sess,GF_NETIO_Parameter * par)943 static void gf_dm_sess_user_io(GF_DownloadSession *sess, GF_NETIO_Parameter *par)
944 {
945 if (sess->user_proc) {
946 sess->in_callback = GF_TRUE;
947 par->sess = sess;
948 sess->user_proc(sess->usr_cbk, par);
949 sess->in_callback = GF_FALSE;
950 }
951 }
952
953 #if 0 //unused
954 /*!
955 \brief is download manager thread dead?
956 *
957 *Indicates whether the thread has ended
958 \param sess the download session
959 */
960 Bool gf_dm_is_thread_dead(GF_DownloadSession *sess)
961 {
962 if (!sess) return GF_TRUE;
963 return (sess->flags & GF_DOWNLOAD_SESSION_THREAD_DEAD) ? GF_TRUE : GF_FALSE;
964 }
965 #endif
966
967 GF_EXPORT
gf_dm_sess_last_error(GF_DownloadSession * sess)968 GF_Err gf_dm_sess_last_error(GF_DownloadSession *sess)
969 {
970 if (!sess) return GF_BAD_PARAM;
971 return sess->last_error;
972 }
973
974 GF_EXPORT
gf_dm_url_info_init(GF_URL_Info * info)975 void gf_dm_url_info_init(GF_URL_Info * info)
976 {
977 memset(info, 0, sizeof(GF_URL_Info));
978 }
979
980 GF_EXPORT
gf_dm_url_info_del(GF_URL_Info * info)981 void gf_dm_url_info_del(GF_URL_Info * info) {
982 if (!info)
983 return;
984 if (info->canonicalRepresentation)
985 gf_free(info->canonicalRepresentation);
986 if (info->password)
987 gf_free(info->password);
988 if (info->userName)
989 gf_free(info->userName);
990 if (info->remotePath)
991 gf_free(info->remotePath);
992 if (info->server_name)
993 gf_free(info->server_name);
994 gf_dm_url_info_init(info);
995 }
996
997 /**
998 \param url The url to parse for protocol
999 \param info The info to fill
1000 \param Returns the offset in url of the protocol found -1 if not found
1001 */
gf_dm_parse_protocol(const char * url,GF_URL_Info * info)1002 static s32 gf_dm_parse_protocol(const char * url, GF_URL_Info * info) {
1003 assert(info);
1004 assert(url);
1005 if (!strnicmp(url, "http://", 7)) {
1006 info->port = 80;
1007 info->protocol = "http://";
1008 return 7;
1009 }
1010 else if (!strnicmp(url, "https://", 8)) {
1011 info->port = 443;
1012 #ifndef GPAC_HAS_SSL
1013 return -1;
1014 #endif
1015 info->protocol = "https://";
1016 return 8;
1017 }
1018 else if (!strnicmp(url, "ftp://", 6)) {
1019 info->port = 21;
1020 info->protocol = "ftp://";
1021 return -1;
1022 }
1023 return -1;
1024 }
1025
1026 GF_EXPORT
gf_dm_get_url_info(const char * url,GF_URL_Info * info,const char * baseURL)1027 GF_Err gf_dm_get_url_info(const char * url, GF_URL_Info * info, const char * baseURL) {
1028 char *tmp, *tmp_url, *current_pos, *urlConcatenateWithBaseURL, *ipv6;
1029 char * copyOfUrl;
1030 s32 proto_offset;
1031 gf_dm_url_info_del(info);
1032 urlConcatenateWithBaseURL = NULL;
1033 proto_offset = gf_dm_parse_protocol(url, info);
1034 if (proto_offset > 0) {
1035 url += proto_offset;
1036 } else {
1037 /*relative URL*/
1038 if (!strstr(url, "://")) {
1039 u32 i;
1040 info->protocol = "file://";
1041 if (baseURL) {
1042 urlConcatenateWithBaseURL = gf_url_concatenate(baseURL, url);
1043 /*relative file path*/
1044 if (!strstr(baseURL, "://")) {
1045 info->canonicalRepresentation = urlConcatenateWithBaseURL;
1046 return GF_OK;
1047 }
1048 proto_offset = gf_dm_parse_protocol(urlConcatenateWithBaseURL, info);
1049 } else {
1050 proto_offset = -1;
1051 }
1052
1053 if (proto_offset < 0) {
1054 tmp = urlConcatenateWithBaseURL;
1055 assert( ! info->remotePath );
1056 info->remotePath = gf_url_percent_encode(tmp);
1057 gf_free( urlConcatenateWithBaseURL );
1058 urlConcatenateWithBaseURL = NULL;
1059
1060 if (!info->remotePath) {
1061 GF_LOG(GF_LOG_WARNING, GF_LOG_HTTP, ("[Network] No supported protocol for url %s\n", url));
1062 gf_dm_url_info_del(info);
1063 return GF_BAD_PARAM;
1064 }
1065 for (i=0; i<strlen(info->remotePath); i++)
1066 if (info->remotePath[i]=='\\') info->remotePath[i]='/';
1067 info->canonicalRepresentation = (char*)gf_malloc(strlen(info->protocol) + strlen(info->remotePath) + 1);
1068 strcpy(info->canonicalRepresentation, info->protocol);
1069 strcat(info->canonicalRepresentation, info->remotePath);
1070
1071 return GF_OK;
1072 } else {
1073 /* We continue the parsing as usual */
1074 url = urlConcatenateWithBaseURL + proto_offset;
1075 }
1076 } else {
1077 GF_LOG(GF_LOG_WARNING, GF_LOG_HTTP, ("[Network] No supported protocol for url %s\n", url));
1078 gf_dm_url_info_del(info);
1079 return GF_BAD_PARAM;
1080 }
1081 }
1082 assert( proto_offset >= 0 );
1083 tmp = strchr(url, '/');
1084 assert( !info->remotePath );
1085 info->remotePath = gf_url_percent_encode(tmp ? tmp : "/");
1086 if (tmp) {
1087 tmp[0] = 0;
1088 copyOfUrl = gf_strdup(url);
1089 tmp[0] = '/';
1090 } else {
1091 copyOfUrl = gf_strdup(url);
1092 }
1093 tmp_url = copyOfUrl;
1094 current_pos = tmp_url;
1095 tmp = strrchr(tmp_url, '@');
1096 if (tmp) {
1097 current_pos = tmp + 1;
1098 assert( ! info->server_name );
1099 info->server_name = gf_strdup(current_pos);
1100 tmp[0] = 0;
1101 tmp = strchr(tmp_url, ':');
1102
1103 if (tmp) {
1104 tmp[0] = 0;
1105 info->password = gf_strdup(tmp+1);
1106 }
1107 info->userName = gf_strdup(tmp_url);
1108 } else {
1109 assert( ! info->server_name );
1110 info->server_name = gf_strdup(tmp_url);
1111 }
1112
1113 //scan for port number after IPv6 address ']' end char
1114 ipv6 = strrchr(current_pos, ']');
1115 tmp = strrchr(ipv6 ? ipv6 : current_pos, ':');
1116
1117 if (tmp) {
1118 info->port = atoi(tmp+1);
1119 tmp[0] = 0;
1120 if (info->server_name) {
1121 gf_free(info->server_name);
1122 }
1123 info->server_name = gf_strdup(current_pos);
1124 }
1125
1126 /* builds orig_url */
1127 /* We dont't want orig_url to contain user/passwords for security reasons or mismatch in cache hit */
1128 {
1129 char port[8];
1130 snprintf(port, sizeof(port)-1, ":%d", info->port);
1131 info->canonicalRepresentation = (char*)gf_malloc(strlen(info->protocol)+strlen(info->server_name)+1+strlen(port)+strlen(info->remotePath));
1132 strcpy(info->canonicalRepresentation, info->protocol);
1133 strcat(info->canonicalRepresentation, info->server_name);
1134 if (info->port!=80)
1135 strcat(info->canonicalRepresentation, port);
1136 strcat(info->canonicalRepresentation, info->remotePath);
1137 }
1138 gf_free(copyOfUrl);
1139 if (urlConcatenateWithBaseURL)
1140 gf_free(urlConcatenateWithBaseURL);
1141 return GF_OK;
1142 }
1143
1144 char *gf_cache_get_forced_headers(const DownloadedCacheEntry entry);
1145 u32 gf_cache_get_downtime(const DownloadedCacheEntry entry);
1146
gf_dm_sess_reload_cached_headers(GF_DownloadSession * sess)1147 static void gf_dm_sess_reload_cached_headers(GF_DownloadSession *sess)
1148 {
1149 char *hdrs;
1150
1151 if (!sess || !sess->local_cache_only) return;
1152
1153 hdrs = gf_cache_get_forced_headers(sess->cache_entry);
1154
1155 gf_dm_clear_headers(sess);
1156 while (hdrs) {
1157 char *sep2, *sepL = strstr(hdrs, "\r\n");
1158 if (sepL) sepL[0] = 0;
1159 sep2 = strchr(hdrs, ':');
1160 if (sep2) {
1161 GF_HTTPHeader *hdr;
1162 GF_SAFEALLOC(hdr, GF_HTTPHeader);
1163 if (!hdr) break;
1164 sep2[0]=0;
1165 hdr->name = gf_strdup(hdrs);
1166 sep2[0]=':';
1167 sep2++;
1168 while (sep2[0]==' ') sep2++;
1169 hdr->value = gf_strdup(sep2);
1170 gf_list_add(sess->headers, hdr);
1171 }
1172 if (!sepL) break;
1173 sepL[0] = '\r';
1174 hdrs = sepL + 2;
1175 }
1176 }
1177
1178 GF_EXPORT
gf_dm_sess_setup_from_url(GF_DownloadSession * sess,const char * url,Bool allow_direct_reuse)1179 GF_Err gf_dm_sess_setup_from_url(GF_DownloadSession *sess, const char *url, Bool allow_direct_reuse)
1180 {
1181 Bool socket_changed = GF_FALSE;
1182 GF_URL_Info info;
1183 char *sep_frag=NULL;
1184 if (!url) return GF_BAD_PARAM;
1185
1186 gf_dm_clear_headers(sess);
1187 sess->allow_direct_reuse = allow_direct_reuse;
1188 gf_dm_url_info_init(&info);
1189
1190 if (!sess->sock)
1191 socket_changed = GF_TRUE;
1192 else if (sess->status>GF_NETIO_DISCONNECTED)
1193 socket_changed = GF_TRUE;
1194
1195 assert(sess->status != GF_NETIO_WAIT_FOR_REPLY);
1196 assert(sess->status != GF_NETIO_DATA_EXCHANGE);
1197
1198 //strip fragment
1199 sep_frag = strchr(url, '#');
1200 if (sep_frag) sep_frag[0]=0;
1201 sess->last_error = gf_dm_get_url_info(url, &info, sess->orig_url);
1202 if (sess->last_error) {
1203 if (sep_frag) sep_frag[0]='#';
1204 return sess->last_error;
1205 }
1206
1207 if (!strstr(url, "://")) {
1208 char c, *sep;
1209 gf_dm_url_info_del(&info);
1210 info.port = sess->port;
1211 info.server_name = sess->server_name ? gf_strdup(sess->server_name) : NULL;
1212 info.remotePath = gf_strdup(url);
1213 sep = strstr(sess->orig_url_before_redirect, "://");
1214 assert(sep);
1215 c = sep[3];
1216 sep[3] = 0;
1217 info.protocol = gf_strdup(sess->orig_url_before_redirect);
1218 sep[3] = c;
1219 }
1220
1221 if (sess->port != info.port) {
1222 socket_changed = GF_TRUE;
1223 sess->port = info.port;
1224 }
1225
1226 if (sess->from_cache_only) {
1227 socket_changed = GF_TRUE;
1228 sess->from_cache_only = GF_FALSE;
1229 if (sess->cache_entry) {
1230 gf_dm_remove_cache_entry_from_session(sess);
1231 sess->cache_entry = NULL;
1232 }
1233 }
1234
1235 if (!strcmp("http://", info.protocol) || !strcmp("https://", info.protocol)) {
1236 if (sess->do_requests != http_do_requests) {
1237 sess->do_requests = http_do_requests;
1238 socket_changed = GF_TRUE;
1239 }
1240 if (!strcmp("https://", info.protocol)) {
1241 if (!(sess->flags & GF_DOWNLOAD_SESSION_USE_SSL)) {
1242 sess->flags |= GF_DOWNLOAD_SESSION_USE_SSL;
1243 socket_changed = GF_TRUE;
1244 }
1245 } else if (sess->flags & GF_DOWNLOAD_SESSION_USE_SSL) {
1246 sess->flags &= ~GF_DOWNLOAD_SESSION_USE_SSL;
1247 socket_changed = GF_TRUE;
1248 }
1249 } else {
1250 sess->do_requests = NULL;
1251 }
1252
1253 if (sess->server_name && info.server_name && !strcmp(sess->server_name, info.server_name)) {
1254 } else {
1255 socket_changed = GF_TRUE;
1256 if (sess->server_name) gf_free(sess->server_name);
1257 sess->server_name = info.server_name ? gf_strdup(info.server_name) : NULL;
1258 }
1259
1260 if (sess->orig_url) gf_free(sess->orig_url);
1261 sess->orig_url = gf_strdup(info.canonicalRepresentation);
1262
1263 if (!sess->orig_url_before_redirect)
1264 sess->orig_url_before_redirect = gf_strdup(url);
1265
1266 if (sess->remote_path) gf_free(sess->remote_path);
1267 sess->remote_path = gf_strdup(info.remotePath);
1268
1269 if (sess->status==GF_NETIO_STATE_ERROR)
1270 socket_changed = GF_TRUE;
1271
1272 if (!socket_changed && info.userName && !strcmp(info.userName, sess->creds->username)) {
1273 } else {
1274 sess->creds = NULL;
1275 if (info.userName ) {
1276 if (! sess->dm) {
1277 GF_LOG(GF_LOG_ERROR, GF_LOG_HTTP, ("[HTTP] Did not found any download manager, credentials not supported\n"));
1278 } else
1279 sess->creds = gf_user_credentials_register(sess->dm, sess->server_name, info.userName, info.password, info.userName && info.password);
1280 }
1281 }
1282 gf_dm_url_info_del(&info);
1283 if (sep_frag) sep_frag[0]='#';
1284
1285 if (sess->sock && !socket_changed) {
1286 sess->status = GF_NETIO_CONNECTED;
1287 sess->num_retry = SESSION_RETRY_COUNT;
1288 sess->needs_cache_reconfig = 1;
1289 } else {
1290 if (sess->sock) {
1291 gf_sk_del(sess->sock);
1292 sess->sock = NULL;
1293 }
1294 sess->status = GF_NETIO_SETUP;
1295 #ifdef GPAC_HAS_SSL
1296 if (sess->ssl) {
1297 SSL_shutdown(sess->ssl);
1298 SSL_free(sess->ssl);
1299 sess->ssl = NULL;
1300 }
1301 #endif
1302
1303 }
1304 sess->total_size = 0;
1305 sess->bytes_done = 0;
1306 assert(sess->remaining_data_size==0);
1307
1308 sess->local_cache_only = GF_FALSE;
1309 if (sess->dm && sess->dm->local_cache_url_provider_cbk) {
1310 Bool res = sess->dm->local_cache_url_provider_cbk(sess->dm->lc_udta, (char *)url, GF_FALSE);
1311 if (res == GF_TRUE) {
1312 sess->local_cache_only = GF_TRUE;
1313 gf_free(sess->orig_url);
1314 sess->orig_url = gf_strdup(url);
1315 sess->last_error = GF_OK;
1316 gf_dm_configure_cache(sess);
1317 sess->status = GF_NETIO_DATA_TRANSFERED;
1318 sess->total_size = gf_cache_get_content_length(sess->cache_entry);
1319 sess->bytes_done = sess->total_size;
1320 sess->total_time_since_req = gf_cache_get_downtime(sess->cache_entry);
1321 if (sess->total_time_since_req)
1322 sess->bytes_per_sec = (u32) ((1000 * (u64) sess->bytes_done) / sess->total_time_since_req);
1323 else
1324 sess->bytes_per_sec = 0;
1325 gf_dm_sess_reload_cached_headers(sess);
1326 }
1327 }
1328 return sess->last_error;
1329 }
1330
1331
gf_dm_session_do_task(GF_DownloadSession * sess)1332 Bool gf_dm_session_do_task(GF_DownloadSession *sess)
1333 {
1334 Bool do_run = GF_TRUE;
1335
1336 if (sess->destroy) {
1337 do_run = GF_FALSE;
1338 } else {
1339 gf_mx_p(sess->mx);
1340 if (sess->status >= GF_NETIO_DISCONNECTED) {
1341 do_run = GF_FALSE;
1342 } else {
1343 if (sess->status < GF_NETIO_CONNECTED) {
1344 gf_dm_connect(sess);
1345 } else {
1346 sess->do_requests(sess);
1347 }
1348 }
1349 gf_mx_v(sess->mx);
1350 }
1351 if (do_run) return GF_TRUE;
1352
1353 /*destroy all session but keep connection active*/
1354 gf_dm_disconnect(sess, GF_FALSE);
1355 sess->status = GF_NETIO_STATE_ERROR;
1356 sess->last_error = GF_OK;
1357 return GF_FALSE;
1358 }
1359
gf_dm_session_task(GF_FilterSession * fsess,void * callback,u32 * reschedule_ms)1360 Bool gf_dm_session_task(GF_FilterSession *fsess, void *callback, u32 *reschedule_ms)
1361 {
1362 GF_SessTask *task = callback;
1363 GF_DownloadSession *sess = task->sess;
1364 if (!sess) {
1365 gf_free(task);
1366 return GF_FALSE;
1367 }
1368 Bool ret = gf_dm_session_do_task(sess);
1369 if (ret) {
1370 *reschedule_ms = 1;
1371 return GF_TRUE;
1372 }
1373 assert(sess->ftask);
1374 gf_free(sess->ftask);
1375 sess->ftask = NULL;
1376 if (sess->destroy)
1377 gf_dm_sess_del(sess);
1378 return GF_FALSE;
1379 }
1380
gf_dm_session_thread(void * par)1381 static u32 gf_dm_session_thread(void *par)
1382 {
1383 GF_DownloadSession *sess = (GF_DownloadSession *)par;
1384 if (!sess) return 0;
1385
1386 GF_LOG(GF_LOG_DEBUG, GF_LOG_CORE, ("[Downloader] Entering thread ID %d\n", gf_th_id() ));
1387 sess->flags &= ~GF_DOWNLOAD_SESSION_THREAD_DEAD;
1388 while (!sess->destroy) {
1389 Bool ret = gf_dm_session_do_task(sess);
1390 if (!ret) break;
1391 gf_sleep(0);
1392 }
1393 sess->flags |= GF_DOWNLOAD_SESSION_THREAD_DEAD;
1394 if (sess->destroy)
1395 gf_dm_sess_del(sess);
1396 return 1;
1397 }
1398
gf_dm_sess_new_internal(GF_DownloadManager * dm,const char * url,u32 dl_flags,gf_dm_user_io user_io,void * usr_cbk,GF_Socket * server,GF_Err * e)1399 static GF_DownloadSession *gf_dm_sess_new_internal(GF_DownloadManager * dm, const char *url, u32 dl_flags,
1400 gf_dm_user_io user_io,
1401 void *usr_cbk,
1402 GF_Socket *server,
1403 GF_Err *e)
1404 {
1405 GF_DownloadSession *sess;
1406
1407 GF_SAFEALLOC(sess, GF_DownloadSession);
1408 if (!sess) {
1409 GF_LOG(GF_LOG_ERROR, GF_LOG_HTTP, ("%s:%d Cannot allocate session for URL %s: OUT OF MEMORY!\n", __FILE__, __LINE__, url));
1410 return NULL;
1411 }
1412 sess->headers = gf_list_new();
1413 sess->flags = dl_flags;
1414 if (sess->flags & GF_NETIO_SESSION_NOTIFY_DATA)
1415 sess->force_data_write_callback = GF_TRUE;
1416 sess->user_proc = user_io;
1417 sess->usr_cbk = usr_cbk;
1418 sess->creds = NULL;
1419
1420 if (!gf_opts_get_key("core", "head-timeout")) {
1421 sess->head_timeout = 5000;
1422 } else {
1423 sess->head_timeout = gf_opts_get_int("core", "head-timeout");
1424 }
1425
1426 sess->request_timeout = gf_opts_get_int("core", "req-timeout");
1427 if (!sess->request_timeout) sess->request_timeout = 20000;
1428
1429 sess->dm = dm;
1430 if (server) {
1431 sess->sock = server;
1432 sess->flags = GF_NETIO_SESSION_NOT_THREADED;
1433 sess->status = GF_NETIO_CONNECTED;
1434 sess->server_mode = GF_TRUE;
1435 sess->do_requests = http_do_requests;
1436 if (e) *e = GF_OK;
1437 return sess;
1438 }
1439
1440 if (!sess->head_timeout) sess->server_only_understand_get = GF_TRUE;
1441 if (dm)
1442 sess->disable_cache = dm->disable_cache;
1443
1444 if (! (dl_flags&GF_NETIO_SESSION_NOT_THREADED)) {
1445 sess->mx = gf_mx_new(url);
1446 if (!sess->mx) {
1447 gf_free(sess);
1448 return NULL;
1449 }
1450 }
1451
1452 *e = gf_dm_sess_setup_from_url(sess, url, GF_FALSE);
1453 if (*e) {
1454 GF_LOG(GF_LOG_WARNING, GF_LOG_HTTP, ("%s:%d gf_dm_sess_new_simple: error=%s at setup for '%s'\n", __FILE__, __LINE__, gf_error_to_string(*e), url));
1455 gf_dm_sess_del(sess);
1456 return NULL;
1457 }
1458 assert( sess );
1459 sess->num_retry = SESSION_RETRY_COUNT;
1460 /*threaded session must be started with gf_dm_sess_process*/
1461 return sess;
1462 }
1463
1464 GF_EXPORT
gf_dm_sess_new_server(GF_Socket * server,void * ssl_sock_ctx,gf_dm_user_io user_io,void * usr_cbk,GF_Err * e)1465 GF_DownloadSession *gf_dm_sess_new_server(GF_Socket *server,
1466 void *ssl_sock_ctx,
1467 gf_dm_user_io user_io,
1468 void *usr_cbk,
1469 GF_Err *e)
1470 {
1471 GF_DownloadSession *sess = gf_dm_sess_new_internal(NULL, NULL, 0, user_io, usr_cbk, server, e);
1472 #ifdef GPAC_HAS_SSL
1473 if (sess) sess->ssl = ssl_sock_ctx;
1474 #endif
1475 return sess;
1476 }
1477
1478
1479 GF_EXPORT
gf_dm_sess_new_simple(GF_DownloadManager * dm,const char * url,u32 dl_flags,gf_dm_user_io user_io,void * usr_cbk,GF_Err * e)1480 GF_DownloadSession *gf_dm_sess_new_simple(GF_DownloadManager * dm, const char *url, u32 dl_flags,
1481 gf_dm_user_io user_io,
1482 void *usr_cbk,
1483 GF_Err *e)
1484 {
1485 return gf_dm_sess_new_internal(dm, url, dl_flags, user_io, usr_cbk, NULL, e);
1486 }
1487 GF_EXPORT
gf_dm_sess_new(GF_DownloadManager * dm,const char * url,u32 dl_flags,gf_dm_user_io user_io,void * usr_cbk,GF_Err * e)1488 GF_DownloadSession *gf_dm_sess_new(GF_DownloadManager *dm, const char *url, u32 dl_flags,
1489 gf_dm_user_io user_io,
1490 void *usr_cbk,
1491 GF_Err *e)
1492 {
1493 GF_DownloadSession *sess;
1494 GF_LOG(GF_LOG_DEBUG, GF_LOG_HTTP, ("%s:%d gf_dm_sess_new(%s)\n", __FILE__, __LINE__, url));
1495 *e = GF_OK;
1496 if (gf_dm_is_local(dm, url)) return NULL;
1497
1498 if (!gf_dm_can_handle_url(dm, url)) {
1499 *e = GF_NOT_SUPPORTED;
1500 return NULL;
1501 }
1502 sess = gf_dm_sess_new_simple(dm, url, dl_flags, user_io, usr_cbk, e);
1503 if (sess && dm) {
1504 sess->dm = dm;
1505 gf_mx_p(dm->cache_mx);
1506 gf_list_add(dm->sessions, sess);
1507 gf_mx_v(dm->cache_mx);
1508 }
1509 return sess;
1510 }
1511
gf_dm_read_data(GF_DownloadSession * sess,char * data,u32 data_size,u32 * out_read)1512 static GF_Err gf_dm_read_data(GF_DownloadSession *sess, char *data, u32 data_size, u32 *out_read)
1513 {
1514 GF_Err e;
1515
1516 if (sess->dm && sess->dm->simulate_no_connection) {
1517 if (sess->sock) {
1518 sess->status = GF_NETIO_DISCONNECTED;
1519 }
1520 return GF_IP_NETWORK_FAILURE;
1521 }
1522
1523 if (!sess)
1524 return GF_BAD_PARAM;
1525
1526 gf_mx_p(sess->mx);
1527 if (!sess->sock) {
1528 sess->status = GF_NETIO_DISCONNECTED;
1529 gf_mx_v(sess->mx);
1530 return GF_IP_CONNECTION_CLOSED;
1531 }
1532
1533 #ifdef GPAC_HAS_SSL
1534 if (sess->ssl) {
1535 s32 size;
1536 e = gf_sk_receive(sess->sock, NULL, 0, NULL);
1537 if (e==GF_IP_NETWORK_EMPTY) {
1538 gf_mx_v(sess->mx);
1539 return e;
1540 }
1541 size = SSL_read(sess->ssl, data, data_size);
1542 if (size < 0)
1543 e = GF_IO_ERR;
1544 else if (!size)
1545 e = GF_IP_NETWORK_EMPTY;
1546 else {
1547 e = GF_OK;
1548 data[size] = 0;
1549 *out_read = size;
1550 }
1551 } else
1552 #endif
1553
1554 e = gf_sk_receive(sess->sock, data, data_size, out_read);
1555
1556 gf_mx_v(sess->mx);
1557 return e;
1558 }
1559
1560
1561 #ifdef GPAC_HAS_SSL
1562
1563 #define LWR(x) ('A' <= (x) && (x) <= 'Z' ? (x) - 32 : (x))
1564
rfc2818_match(const char * pattern,const char * string)1565 static Bool rfc2818_match(const char *pattern, const char *string)
1566 {
1567 char d;
1568 u32 i=0, k=0;
1569 while (1) {
1570 char c = LWR(pattern[i]);
1571 if (c == '\0') break;
1572
1573 if (c=='*') {
1574 /*remove *** patterns*/
1575 while (c == '*') {
1576 i++;
1577 c = LWR(pattern[i]);
1578 }
1579 /*look for same c character*/
1580 while (1) {
1581 d = LWR(string[k]);
1582 if (d == '\0') break;
1583 /*matched c character, check following substrings*/
1584 if ((d == c) && rfc2818_match (&pattern[i], &string[k]))
1585 return GF_TRUE;
1586 else if (d == '.')
1587 return GF_FALSE;
1588
1589 k++;
1590 }
1591 return (c == '\0') ? GF_TRUE : GF_FALSE;
1592 } else {
1593 if (c != LWR(string[k]))
1594 return GF_FALSE;
1595 }
1596 i++;
1597 k++;
1598 }
1599 return (string[k]=='\0') ? GF_TRUE : GF_FALSE;
1600 }
1601 #undef LWR
1602
1603 #endif
1604
gf_dm_connect(GF_DownloadSession * sess)1605 static void gf_dm_connect(GF_DownloadSession *sess)
1606 {
1607 GF_Err e;
1608 u16 proxy_port = 0;
1609 const char *proxy;
1610
1611 if (!sess->sock) {
1612 sess->num_retry = 40;
1613 sess->sock = gf_sk_new(GF_SOCK_TYPE_TCP);
1614 }
1615
1616 /*connect*/
1617 sess->status = GF_NETIO_SETUP;
1618 gf_dm_sess_notify_state(sess, sess->status, GF_OK);
1619
1620 /*PROXY setup*/
1621 if (sess->proxy_enabled!=2) {
1622 proxy = NULL;
1623 if (gf_opts_get_bool("core", "proxy-on")) {
1624 u32 i;
1625 Bool use_proxy=GF_TRUE;
1626 for (i=0; i<gf_list_count(sess->dm->skip_proxy_servers); i++) {
1627 char *skip = (char*)gf_list_get(sess->dm->skip_proxy_servers, i);
1628 if (!strcmp(skip, sess->server_name)) {
1629 use_proxy=GF_FALSE;
1630 break;
1631 }
1632 }
1633 if (use_proxy) {
1634 proxy_port = gf_opts_get_int("core", "proxy-port");
1635 if (!proxy_port) proxy_port = 80;
1636 proxy = gf_opts_get_key("core", "proxy-name");
1637 sess->proxy_enabled = 1;
1638 } else {
1639 proxy = NULL;
1640 }
1641 } else {
1642 proxy = NULL;
1643 sess->proxy_enabled = 0;
1644 }
1645 } else {
1646 proxy = NULL;
1647 }
1648
1649
1650 if (!proxy) {
1651 proxy = sess->server_name;
1652 proxy_port = sess->port;
1653 }
1654 GF_LOG(GF_LOG_INFO, GF_LOG_HTTP, ("[HTTP] Connecting to %s:%d\n", proxy, proxy_port));
1655
1656 if (sess->status == GF_NETIO_SETUP) {
1657 u64 now;
1658 if (sess->dm && sess->dm->simulate_no_connection) {
1659 sess->status = GF_NETIO_STATE_ERROR;
1660 sess->last_error = GF_IP_NETWORK_FAILURE;
1661 gf_dm_sess_notify_state(sess, sess->status, sess->last_error);
1662 return;
1663 }
1664
1665 now = gf_sys_clock_high_res();
1666 e = gf_sk_connect(sess->sock, (char *) proxy, proxy_port, NULL);
1667
1668 /*retry*/
1669 if ((e == GF_IP_SOCK_WOULD_BLOCK) && sess->num_retry) {
1670 sess->status = GF_NETIO_SETUP;
1671 sess->num_retry--;
1672 return;
1673 }
1674
1675 /*failed*/
1676 if (e) {
1677 if (!sess->cache_entry && sess->dm && sess->dm->allow_offline_cache) {
1678 gf_dm_configure_cache(sess);
1679 if (sess->from_cache_only) return;
1680 }
1681 sess->status = GF_NETIO_STATE_ERROR;
1682 sess->last_error = e;
1683 gf_dm_sess_notify_state(sess, sess->status, e);
1684 return;
1685 }
1686 if (sess->allow_direct_reuse) {
1687 gf_dm_configure_cache(sess);
1688 if (sess->from_cache_only) return;
1689 }
1690
1691 sess->connect_time = (u32) (gf_sys_clock_high_res() - now);
1692 sess->status = GF_NETIO_CONNECTED;
1693 GF_LOG(GF_LOG_INFO, GF_LOG_HTTP, ("[HTTP] Connected to %s:%d\n", proxy, proxy_port));
1694 gf_dm_sess_notify_state(sess, GF_NETIO_CONNECTED, GF_OK);
1695 // gf_sk_set_buffer_size(sess->sock, GF_TRUE, GF_DOWNLOAD_BUFFER_SIZE);
1696 // gf_sk_set_buffer_size(sess->sock, GF_FALSE, GF_DOWNLOAD_BUFFER_SIZE);
1697 }
1698
1699 #ifdef GPAC_HAS_SSL
1700 if (!sess->ssl && (sess->flags & GF_DOWNLOAD_SESSION_USE_SSL)) {
1701 u64 now = gf_sys_clock_high_res();
1702 if (sess->dm && !sess->dm->ssl_ctx)
1703 ssl_init(sess->dm, 0);
1704 /*socket is connected, configure SSL layer*/
1705 if (sess->dm && sess->dm->ssl_ctx) {
1706 int ret;
1707 X509 *cert;
1708 Bool success;
1709
1710 sess->ssl = SSL_new(sess->dm->ssl_ctx);
1711 SSL_set_fd(sess->ssl, gf_sk_get_handle(sess->sock));
1712 SSL_set_connect_state(sess->ssl);
1713 ret = SSL_connect(sess->ssl);
1714 if (ret<=0) {
1715 GF_LOG(GF_LOG_ERROR, GF_LOG_HTTP, ("[SSL] Cannot connect, error %d\n", ret));
1716 } else {
1717 GF_LOG(GF_LOG_DEBUG, GF_LOG_HTTP, ("[SSL] connected\n"));
1718 }
1719
1720 cert = SSL_get_peer_certificate(sess->ssl);
1721 /*if we have a cert, check it*/
1722 if (cert) {
1723 long vresult;
1724 SSL_set_verify_result(sess->ssl, 0);
1725 vresult = SSL_get_verify_result(sess->ssl);
1726
1727 if (vresult == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY) {
1728 GF_LOG(GF_LOG_WARNING, GF_LOG_HTTP, ("[SSL] Cannot locate issuer's certificate on the local system, will not attempt to validate\n"));
1729 SSL_set_verify_result(sess->ssl, 0);
1730 vresult = SSL_get_verify_result(sess->ssl);
1731 }
1732
1733 if (vresult == X509_V_OK) {
1734 char common_name[256];
1735 STACK_OF(GENERAL_NAME) *altnames;
1736 GF_List* valid_names;
1737 int i;
1738
1739 valid_names = gf_list_new();
1740
1741 common_name[0] = 0;
1742 X509_NAME_get_text_by_NID(X509_get_subject_name(cert), NID_commonName, common_name, sizeof (common_name));
1743 gf_list_add(valid_names, common_name);
1744
1745 altnames = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
1746 if (altnames) {
1747 for (i = 0; i < sk_GENERAL_NAME_num(altnames); ++i) {
1748 const GENERAL_NAME *altname = sk_GENERAL_NAME_value(altnames, i);
1749 if (altname->type == GEN_DNS)
1750 {
1751 #if OPENSSL_VERSION_NUMBER < 0x10100000L
1752 unsigned char *altname_str = ASN1_STRING_data(altname->d.ia5);
1753 #else
1754 unsigned char *altname_str = (unsigned char *)ASN1_STRING_get0_data(altname->d.ia5);
1755 #endif
1756 gf_list_add(valid_names, altname_str);
1757 }
1758 }
1759 }
1760
1761 success = GF_FALSE;
1762 for (i = 0; i < (int)gf_list_count(valid_names); ++i) {
1763 const char *valid_name = (const char*) gf_list_get(valid_names, i);
1764 if (rfc2818_match(valid_name, sess->server_name)) {
1765 success = GF_TRUE;
1766 GF_LOG(GF_LOG_INFO, GF_LOG_HTTP, ("[SSL] Hostname %s matches %s\n", sess->server_name, valid_name));
1767 break;
1768 }
1769 }
1770 if (!success) {
1771 if (sess->dm && sess->dm->allow_broken_certificate) {
1772 success = GF_TRUE;
1773 GF_LOG(GF_LOG_WARNING, GF_LOG_HTTP, ("[SSL] Mismatch in certificate names: expected %s\n", sess->server_name));
1774 } else {
1775 GF_LOG(GF_LOG_ERROR, GF_LOG_HTTP, ("[SSL] Mismatch in certificate names, try using -broken-cert: expected %s\n", sess->server_name));
1776 }
1777 #ifndef GPAC_DISABLE_LOG
1778 for (i = 0; i < (int)gf_list_count(valid_names); ++i) {
1779 const char *valid_name = (const char*) gf_list_get(valid_names, i);
1780 GF_LOG(success ? GF_LOG_WARNING : GF_LOG_ERROR, GF_LOG_HTTP, ("[SSL] Tried name: %s\n", valid_name));
1781 }
1782 #endif
1783 }
1784
1785 gf_list_del(valid_names);
1786 GENERAL_NAMES_free(altnames);
1787 } else {
1788 success = GF_FALSE;
1789 GF_LOG(GF_LOG_ERROR, GF_LOG_HTTP, ("[SSL] Error verifying certificate %x\n", vresult));
1790 }
1791
1792 X509_free(cert);
1793
1794 if (!success) {
1795 gf_dm_disconnect(sess, GF_TRUE);
1796 sess->status = GF_NETIO_STATE_ERROR;
1797 sess->last_error = GF_AUTHENTICATION_FAILURE;
1798 gf_dm_sess_notify_state(sess, sess->status, sess->last_error);
1799 }
1800 }
1801
1802 sess->ssl_setup_time = (u32) (gf_sys_clock_high_res() - now);
1803 }
1804 }
1805 #endif
1806
1807 /*this should be done when building HTTP GET request in case we have range directives*/
1808 gf_dm_configure_cache(sess);
1809
1810 }
1811
gf_dm_refresh_cache_entry(GF_DownloadSession * sess)1812 DownloadedCacheEntry gf_dm_refresh_cache_entry(GF_DownloadSession *sess)
1813 {
1814 Bool go;
1815 u32 timer = 0;
1816 u32 flags;
1817 if (!sess) return NULL;
1818 flags = sess->flags;
1819 sess->flags |= GF_NETIO_SESSION_NOT_CACHED;
1820 go = GF_TRUE;
1821 while (go) {
1822 switch (sess->status) {
1823 /*setup download*/
1824 case GF_NETIO_SETUP:
1825 gf_dm_connect(sess);
1826 break;
1827 case GF_NETIO_WAIT_FOR_REPLY:
1828 if (timer == 0)
1829 timer = gf_sys_clock();
1830 {
1831 u32 timer2 = gf_sys_clock();
1832 if (timer2 - timer > 5000) {
1833 GF_Err e;
1834 /* Since HEAD is not understood by this server, we use a GET instead */
1835 sess->http_read_type = GET;
1836 sess->flags |= GF_NETIO_SESSION_NOT_CACHED;
1837 gf_dm_disconnect(sess, GF_FALSE);
1838 sess->status = GF_NETIO_SETUP;
1839 sess->server_only_understand_get = GF_TRUE;
1840 GF_LOG(GF_LOG_INFO, GF_LOG_HTTP, ("gf_dm_refresh_cache_entry() : Timeout with HEAD, try with GET\n"));
1841 e = gf_dm_sess_setup_from_url(sess, sess->orig_url, GF_FALSE);
1842 if (e) {
1843 GF_LOG(GF_LOG_WARNING, GF_LOG_HTTP, ("gf_dm_refresh_cache_entry() : Error with GET %d\n", e));
1844 sess->status = GF_NETIO_STATE_ERROR;
1845 sess->last_error = e;
1846 gf_dm_sess_notify_state(sess, sess->status, e);
1847 } else {
1848 timer = 0;
1849 continue;
1850 }
1851 }
1852 }
1853 case GF_NETIO_CONNECTED:
1854 sess->do_requests(sess);
1855 break;
1856 case GF_NETIO_DATA_EXCHANGE:
1857 case GF_NETIO_DISCONNECTED:
1858 case GF_NETIO_STATE_ERROR:
1859 case GF_NETIO_DATA_TRANSFERED:
1860 go = GF_FALSE;
1861 break;
1862 default:
1863 break;
1864 }
1865 }
1866 sess->flags = flags;
1867 if (sess->status==GF_NETIO_STATE_ERROR) return NULL;
1868 return sess->cache_entry;
1869 }
1870
1871 GF_EXPORT
gf_dm_sess_mime_type(GF_DownloadSession * sess)1872 const char *gf_dm_sess_mime_type(GF_DownloadSession *sess)
1873 {
1874 DownloadedCacheEntry entry;
1875 if (sess->cache_entry) {
1876 const char * oldMimeIfAny = gf_cache_get_mime_type(sess->cache_entry);
1877 if (oldMimeIfAny)
1878 return oldMimeIfAny;
1879 }
1880 entry = gf_dm_refresh_cache_entry (sess);
1881 if (!entry)
1882 return sess->mime_type;
1883 assert( entry == sess->cache_entry && entry);
1884 return gf_cache_get_mime_type( sess->cache_entry );
1885 }
1886
1887 GF_EXPORT
gf_dm_sess_set_range(GF_DownloadSession * sess,u64 start_range,u64 end_range,Bool discontinue_cache)1888 GF_Err gf_dm_sess_set_range(GF_DownloadSession *sess, u64 start_range, u64 end_range, Bool discontinue_cache)
1889 {
1890 if (!sess) return GF_BAD_PARAM;
1891 if (sess->cache_entry) {
1892 if (!discontinue_cache) {
1893 if (gf_cache_get_end_range(sess->cache_entry) + 1 != start_range)
1894 discontinue_cache = GF_TRUE;
1895 }
1896 if (sess->sock) {
1897 if (sess->status != GF_NETIO_CONNECTED) {
1898 if (sess->status != GF_NETIO_DISCONNECTED) {
1899 return GF_BAD_PARAM;
1900 }
1901 }
1902 }
1903 if (!sess->local_cache_only) {
1904 sess->status = sess->sock ? GF_NETIO_CONNECTED : GF_NETIO_SETUP;
1905 sess->num_retry = SESSION_RETRY_COUNT;
1906
1907 if (!discontinue_cache) {
1908 gf_cache_set_end_range(sess->cache_entry, end_range);
1909 /*remember this in case we get disconnected*/
1910 sess->is_range_continuation = GF_TRUE;
1911 } else {
1912 sess->needs_cache_reconfig = 1;
1913 sess->reused_cache_entry = GF_FALSE;
1914 }
1915 }
1916 } else {
1917 if ((sess->status != GF_NETIO_SETUP) && (sess->status != GF_NETIO_CONNECTED))
1918 return GF_BAD_PARAM;
1919 }
1920 sess->range_start = start_range;
1921 sess->range_end = end_range;
1922 sess->needs_range = (start_range || end_range) ? GF_TRUE : GF_FALSE;
1923 return GF_OK;
1924 }
1925
1926 GF_EXPORT
gf_dm_sess_process(GF_DownloadSession * sess)1927 GF_Err gf_dm_sess_process(GF_DownloadSession *sess)
1928 {
1929 Bool go;
1930
1931 /*if session is threaded, start thread*/
1932 if (! (sess->flags & GF_NETIO_SESSION_NOT_THREADED)) {
1933 if (sess->dm->filter_session && !gf_opts_get_bool("core", "dm-threads")) {
1934 GF_SAFEALLOC(sess->ftask, GF_SessTask);
1935 if (!sess->ftask) return GF_OUT_OF_MEM;
1936 sess->ftask->sess = sess;
1937 gf_fs_post_user_task(sess->dm->filter_session, gf_dm_session_task, sess->ftask, "download");
1938 return GF_OK;
1939 }
1940 if (sess->th) {
1941 GF_LOG(GF_LOG_WARNING, GF_LOG_HTTP, ("[HTTP] Session already started - ignoring start\n"));
1942 return GF_OK;
1943 }
1944 sess->th = gf_th_new(sess->orig_url);
1945 if (!sess->th) return GF_OUT_OF_MEM;
1946 gf_th_run(sess->th, gf_dm_session_thread, sess);
1947 return GF_OK;
1948 }
1949
1950 if (sess->put_state==2) {
1951 if (sess->status==GF_NETIO_DATA_TRANSFERED)
1952 sess->status = GF_NETIO_WAIT_FOR_REPLY;
1953 }
1954
1955 /*otherwise do a synchronous download*/
1956 go = GF_TRUE;
1957 while (go) {
1958 switch (sess->status) {
1959 /*setup download*/
1960 case GF_NETIO_SETUP:
1961 gf_dm_connect(sess);
1962 break;
1963 case GF_NETIO_WAIT_FOR_REPLY:
1964 case GF_NETIO_CONNECTED:
1965 sess->do_requests(sess);
1966 break;
1967 case GF_NETIO_DATA_EXCHANGE:
1968 if (sess->put_state==2) {
1969 sess->status = GF_NETIO_DATA_TRANSFERED;
1970 go = GF_FALSE;
1971 break;
1972 }
1973 sess->do_requests(sess);
1974 break;
1975 case GF_NETIO_DATA_TRANSFERED:
1976 case GF_NETIO_DISCONNECTED:
1977 case GF_NETIO_STATE_ERROR:
1978 go = GF_FALSE;
1979 break;
1980
1981 case GF_NETIO_GET_METHOD:
1982 case GF_NETIO_GET_HEADER:
1983 case GF_NETIO_GET_CONTENT:
1984 case GF_NETIO_PARSE_HEADER:
1985 case GF_NETIO_PARSE_REPLY:
1986 break;
1987
1988 default:
1989 GF_LOG(GF_LOG_ERROR, GF_LOG_HTTP, ("[Downloader] Session in unknown state !! - aborting\n"));
1990 go = GF_FALSE;
1991 break;
1992 }
1993 }
1994 return sess->last_error;
1995 }
1996
1997 GF_EXPORT
gf_dm_sess_process_headers(GF_DownloadSession * sess)1998 GF_Err gf_dm_sess_process_headers(GF_DownloadSession *sess)
1999 {
2000 Bool go;
2001 go = GF_TRUE;
2002 while (go) {
2003 switch (sess->status) {
2004 /*setup download*/
2005 case GF_NETIO_SETUP:
2006 gf_dm_connect(sess);
2007 break;
2008 case GF_NETIO_WAIT_FOR_REPLY:
2009 case GF_NETIO_CONNECTED:
2010 sess->do_requests(sess);
2011
2012 if (sess->reused_cache_entry && sess->cache_entry && gf_cache_are_headers_processed(sess->cache_entry) ) {
2013 sess->status = GF_NETIO_DATA_EXCHANGE;
2014 }
2015 break;
2016 case GF_NETIO_DATA_EXCHANGE:
2017 case GF_NETIO_DATA_TRANSFERED:
2018 case GF_NETIO_DISCONNECTED:
2019 case GF_NETIO_STATE_ERROR:
2020 go = GF_FALSE;
2021 break;
2022 default:
2023 break;
2024 }
2025 }
2026 return sess->last_error;
2027 }
2028
gf_dm_needs_to_delete_cache(GF_DownloadManager * dm)2029 static Bool gf_dm_needs_to_delete_cache(GF_DownloadManager * dm)
2030 {
2031 if (!dm) return GF_FALSE;
2032 return dm->clean_cache;
2033 }
2034
2035 #ifdef BUGGY_gf_cache_cleanup_cache
2036 /*!
2037 * Cleans up the cache at start and stop.
2038 * Note that this method will perform any cleanup if
2039 * Configuration section [Downloader]/CleanCache is not set, meaning
2040 * that methods that create a "fake" GF_DownloadManager such as
2041 * gf_dm_wget() are not impacted and won't cleanup the cache
2042 *
2043 * FIXME: should be probably threaded to avoid too long start time
2044 \param dm The GF_DownloadManager
2045 */
gf_cache_cleanup_cache(GF_DownloadManager * dm)2046 static void gf_cache_cleanup_cache(GF_DownloadManager * dm) {
2047 if (gf_dm_needs_to_delete_cache(dm)) {
2048 gf_cache_delete_all_cached_files(dm->cache_directory);
2049 }
2050 }
2051 #endif
2052
2053 typedef struct
2054 {
2055 Bool check_size;
2056 u64 out_size;
2057 } cache_probe;
2058
2059
gf_dm_clean_cache(GF_DownloadManager * dm)2060 static void gf_dm_clean_cache(GF_DownloadManager *dm)
2061 {
2062 u64 out_size = gf_cache_get_size(dm->cache_directory);
2063 if (out_size >= dm->max_cache_size) {
2064 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));
2065 gf_cache_delete_all_cached_files(dm->cache_directory);
2066 }
2067 }
2068
2069 GF_EXPORT
gf_dm_new(GF_FilterSession * fsess)2070 GF_DownloadManager *gf_dm_new(GF_FilterSession *fsess)
2071 {
2072 const char *opt;
2073 const char * default_cache_dir;
2074 GF_DownloadManager *dm;
2075 GF_SAFEALLOC(dm, GF_DownloadManager);
2076 if (!dm) {
2077 GF_LOG(GF_LOG_ERROR, GF_LOG_HTTP, ("[Downloader] Failed to allocate downloader\n"));
2078 return NULL;
2079 }
2080 dm->sessions = gf_list_new();
2081 dm->cache_entries = gf_list_new();
2082 dm->credentials = gf_list_new();
2083 dm->skip_proxy_servers = gf_list_new();
2084 dm->partial_downloads = gf_list_new();
2085 dm->cache_mx = gf_mx_new("download_manager_cache_mx");
2086 dm->filter_session = fsess;
2087 default_cache_dir = NULL;
2088 gf_mx_p( dm->cache_mx );
2089
2090 opt = gf_opts_get_key("core", "cache");
2091
2092 retry_cache:
2093 if (!opt) {
2094 default_cache_dir = gf_get_default_cache_directory();
2095 opt = default_cache_dir;
2096 }
2097 if (opt[strlen(opt)-1] != GF_PATH_SEPARATOR) {
2098 dm->cache_directory = (char *) gf_malloc(sizeof(char)* (strlen(opt)+2));
2099 sprintf(dm->cache_directory, "%s%c", opt, GF_PATH_SEPARATOR);
2100 } else {
2101 dm->cache_directory = gf_strdup(opt);
2102 }
2103
2104 //check cache exists
2105 if (!default_cache_dir) {
2106 FILE *test;
2107 char szTemp[GF_MAX_PATH];
2108 strcpy(szTemp, dm->cache_directory);
2109 strcat(szTemp, "gpaccache.test");
2110 test = gf_fopen(szTemp, "wb");
2111 if (!test) {
2112 gf_mkdir(dm->cache_directory);
2113 test = gf_fopen(szTemp, "wb");
2114 if (!test) {
2115 GF_LOG(GF_LOG_WARNING, GF_LOG_HTTP, ("[Cache] Cannot write to %s directory, using system temp cache\n", dm->cache_directory ));
2116 gf_free(dm->cache_directory);
2117 dm->cache_directory = NULL;
2118 opt = NULL;
2119 goto retry_cache;
2120 }
2121 }
2122 if (test) {
2123 gf_fclose(test);
2124 gf_file_delete(szTemp);
2125 }
2126 }
2127
2128 /*use it in in BYTES per second*/
2129 dm->limit_data_rate = 1000 * gf_opts_get_int("core", "maxrate") / 8;
2130
2131 dm->read_buf_size = GF_DOWNLOAD_BUFFER_SIZE;
2132 //when rate is limited, use smaller smaller read size
2133 if (dm->limit_data_rate) {
2134 dm->read_buf_size = 1024;
2135 }
2136
2137 dm->disable_cache = gf_opts_get_bool("core", "no-cache");
2138
2139 dm->allow_offline_cache = gf_opts_get_bool("core", "offline-cache");
2140
2141 dm->clean_cache = GF_FALSE;
2142 dm->allow_broken_certificate = GF_FALSE;
2143 if ( gf_opts_get_bool("core", "clean-cache")) {
2144 dm->clean_cache = GF_TRUE;
2145 dm->max_cache_size=0;
2146 gf_dm_clean_cache(dm);
2147 } else {
2148 dm->max_cache_size = gf_opts_get_int("core", "cache-size");
2149 if (dm->max_cache_size) {
2150 gf_dm_clean_cache(dm);
2151 }
2152 }
2153 dm->allow_broken_certificate = gf_opts_get_bool("core", "broken-cert");
2154
2155 gf_mx_v( dm->cache_mx );
2156
2157 #ifdef GPAC_HAS_SSL
2158 dm->ssl_ctx = NULL;
2159 #endif
2160 /* TODO: Not ready for now, we should find a locking strategy between several GPAC instances...
2161 * gf_cache_cleanup_cache(dm);
2162 */
2163 return dm;
2164 }
2165
2166 GF_EXPORT
gf_dm_set_auth_callback(GF_DownloadManager * dm,Bool (* get_user_password)(void * usr_cbk,const char * site_url,char * usr_name,char * password),void * usr_cbk)2167 void gf_dm_set_auth_callback(GF_DownloadManager *dm,
2168 Bool (*get_user_password)(void *usr_cbk, const char *site_url, char *usr_name, char *password),
2169 void *usr_cbk)
2170 {
2171 if (dm) {
2172 dm->get_user_password = get_user_password;
2173 dm->usr_cbk = usr_cbk;
2174 }
2175 }
2176
2177 GF_EXPORT
gf_dm_del(GF_DownloadManager * dm)2178 void gf_dm_del(GF_DownloadManager *dm)
2179 {
2180 if (!dm)
2181 return;
2182 assert( dm->sessions);
2183 assert( dm->cache_mx );
2184 gf_mx_p( dm->cache_mx );
2185
2186 while (gf_list_count(dm->partial_downloads)) {
2187 GF_PartialDownload * entry = (GF_PartialDownload*)gf_list_get( dm->partial_downloads, 0);
2188 gf_list_rem( dm->partial_downloads, 0);
2189 assert( entry->filename );
2190 gf_file_delete( entry->filename );
2191 gf_free(entry->filename );
2192 entry->filename = NULL;
2193 entry->url = NULL;
2194 gf_free( entry );
2195 }
2196
2197 /*destroy all pending sessions*/
2198 while (gf_list_count(dm->sessions)) {
2199 GF_DownloadSession *sess = (GF_DownloadSession *) gf_list_get(dm->sessions, 0);
2200 gf_dm_sess_del(sess);
2201 }
2202 gf_list_del(dm->sessions);
2203 dm->sessions = NULL;
2204 assert( dm->skip_proxy_servers );
2205 while (gf_list_count(dm->skip_proxy_servers)) {
2206 char *serv = (char*)gf_list_get(dm->skip_proxy_servers, 0);
2207 gf_list_rem(dm->skip_proxy_servers, 0);
2208 gf_free(serv);
2209 }
2210 gf_list_del(dm->skip_proxy_servers);
2211 dm->skip_proxy_servers = NULL;
2212 assert( dm->credentials);
2213 while (gf_list_count(dm->credentials)) {
2214 gf_user_credentials_struct * cred = (gf_user_credentials_struct*)gf_list_get( dm->credentials, 0);
2215 gf_list_rem( dm->credentials, 0);
2216 gf_free( cred );
2217 }
2218 gf_list_del( dm->credentials);
2219 dm->credentials = NULL;
2220 assert( dm->cache_entries );
2221 {
2222 /* Deletes DownloadedCacheEntry and associated files if required */
2223 Bool delete_my_files = gf_dm_needs_to_delete_cache(dm);
2224 while (gf_list_count(dm->cache_entries)) {
2225 const DownloadedCacheEntry entry = (const DownloadedCacheEntry)gf_list_get( dm->cache_entries, 0);
2226 gf_list_rem( dm->cache_entries, 0);
2227 if (delete_my_files)
2228 gf_cache_entry_set_delete_files_when_deleted(entry);
2229 gf_cache_delete_entry(entry);
2230 }
2231 gf_list_del( dm->cache_entries );
2232 dm->cache_entries = NULL;
2233 }
2234
2235 gf_list_del( dm->partial_downloads );
2236 dm->partial_downloads = NULL;
2237 /* TODO: Not ready for now, we should find a locking strategy between several GPAC instances...
2238 * gf_cache_cleanup_cache(dm);
2239 */
2240 if (dm->cache_directory)
2241 gf_free(dm->cache_directory);
2242 dm->cache_directory = NULL;
2243
2244 #ifdef GPAC_HAS_SSL
2245 if (dm->ssl_ctx) SSL_CTX_free(dm->ssl_ctx);
2246 #endif
2247 /* Stored elsewhere, no need to free */
2248 gf_mx_v( dm->cache_mx );
2249 gf_mx_del( dm->cache_mx);
2250 dm->cache_mx = NULL;
2251 gf_free(dm);
2252 }
2253
2254 /*!
2255 * Skip ICY metadata from SHOUTCAST or ICECAST streams.
2256 * Data will be skipped and parsed and sent as a GF_NETIO_Parameter to the user_io,
2257 * so modules interrested by those streams may use the data
2258 \param sess The GF_DownloadSession
2259 \param data last data received
2260 \param nbBytes The number of bytes contained into data
2261 */
gf_icy_skip_data(GF_DownloadSession * sess,const char * data,u32 nbBytes)2262 static void gf_icy_skip_data(GF_DownloadSession * sess, const char * data, u32 nbBytes)
2263 {
2264 u32 icy_metaint;
2265 if (!sess || !data ) return;
2266
2267 icy_metaint = sess->icy_metaint;
2268 assert( icy_metaint > 0 );
2269 while (nbBytes) {
2270 if (sess->icy_bytes == icy_metaint) {
2271 sess->icy_count = 1 + 16* (u8) data[0];
2272 /*skip icy metadata*/
2273 if (sess->icy_count > nbBytes) {
2274 sess->icy_count -= nbBytes;
2275 nbBytes = 0;
2276 } else {
2277 if (sess->icy_count > 1) {
2278 GF_NETIO_Parameter par;
2279 char szData[4096];
2280 memset(szData, 0, 4096);
2281 memcpy(szData, data+1, sess->icy_count-1);
2282 szData[sess->icy_count] = 0;
2283
2284 par.error = GF_OK;
2285 par.msg_type = GF_NETIO_PARSE_HEADER;
2286 par.name = "icy-meta";
2287 par.value = szData;
2288 par.sess = sess;
2289 GF_LOG(GF_LOG_INFO, GF_LOG_HTTP, ("[ICY] Found metainfo in stream=%s, (every %d bytes)\n", szData, icy_metaint));
2290 gf_dm_sess_user_io(sess, &par);
2291 } else {
2292 GF_LOG(GF_LOG_DEBUG, GF_LOG_HTTP, ("[ICY] Empty metainfo in stream, (every %d bytes)\n", icy_metaint));
2293 }
2294 nbBytes -= sess->icy_count;
2295 data += sess->icy_count;
2296 sess->icy_count = 0;
2297 sess->icy_bytes = 0;
2298 }
2299 } else {
2300 GF_NETIO_Parameter par;
2301 u32 left = icy_metaint - sess->icy_bytes;
2302 if (left > nbBytes) {
2303 left = nbBytes;
2304 sess->icy_bytes += left;
2305 nbBytes = 0;
2306 } else {
2307 sess->icy_bytes = icy_metaint;
2308 nbBytes -= left;
2309 }
2310
2311 par.msg_type = GF_NETIO_DATA_EXCHANGE;
2312 par.data = data;
2313 par.size = left;
2314 gf_dm_sess_user_io(sess, &par);
2315
2316 data += left;
2317 }
2318 }
2319 }
2320
2321
gf_dm_get_chunk_data(GF_DownloadSession * sess,Bool first_chunk_in_payload,char * body_start,u32 * payload_size,u32 * header_size)2322 static char *gf_dm_get_chunk_data(GF_DownloadSession *sess, Bool first_chunk_in_payload, char *body_start, u32 *payload_size, u32 *header_size)
2323 {
2324 u32 size;
2325 s32 res;
2326 char *te_header, *sep;
2327
2328 if (!sess || !body_start) return NULL;
2329 if (!sess->chunked) return body_start;
2330
2331 if (sess->nb_left_in_chunk) {
2332 if (sess->nb_left_in_chunk > *payload_size) {
2333 sess->nb_left_in_chunk -= (*payload_size);
2334 GF_LOG(GF_LOG_DEBUG, GF_LOG_HTTP, ("[HTTP] Chunk encoding: still %d bytes to get\n", sess->nb_left_in_chunk));
2335 } else {
2336 *payload_size = sess->nb_left_in_chunk;
2337 sess->nb_left_in_chunk = 0;
2338 GF_LOG(GF_LOG_DEBUG, GF_LOG_HTTP, ("[HTTP] Chunk encoding: last bytes in chunk received\n"));
2339 }
2340 *header_size = 0;
2341 return body_start;
2342 }
2343
2344 if (*payload_size == 2) {
2345 *header_size = 0;
2346 }
2347 *header_size = 0;
2348 /*skip remaining CRLF from previous chunk if any*/
2349 if (*payload_size >= 2) {
2350 if ((body_start[0]=='\r') && (body_start[1]=='\n')) {
2351 body_start += 2;
2352 *header_size = 2;
2353 //chunk exactly ends our packet, reset session start time
2354 if (*payload_size == 2) {
2355 sess->chunk_run_time += gf_sys_clock_high_res() - sess->start_time;
2356 sess->start_time = 0;
2357 }
2358 }
2359 if (*payload_size <= 4) {
2360 *header_size = 0;
2361 return NULL;
2362 }
2363 te_header = strstr((char *) body_start, "\r\n");
2364 } else {
2365 //not enough bytes to read CRLF, don't bother parsing
2366 te_header = NULL;
2367 }
2368
2369 //start of a new chunk, update start time
2370 if (!sess->start_time) {
2371 sess->start_time = gf_sys_clock_high_res();
2372 //assume RTT is session reply time, and that chunk transfer started RTT/2 ago
2373 if (first_chunk_in_payload && sess->start_time > sess->reply_time/2)
2374 sess->start_time -= sess->reply_time/2;
2375 GF_LOG(GF_LOG_DEBUG, GF_LOG_HTTP, ("[HTTP] First byte in chunk received (%d bytes in packet), new start time %u ms\n", *payload_size, (u32) sess->start_time/1000));
2376 }
2377
2378 //cannot parse now, copy over the bytes
2379 if (!te_header) {
2380 *header_size = 0;
2381 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));
2382 return NULL;
2383 }
2384
2385 te_header[0] = 0;
2386 //assert(strlen(body_start));
2387 *header_size += (u32) (strlen(body_start)) + 2;
2388
2389 sep = strchr(body_start, ';');
2390 if (sep) sep[0] = 0;
2391 res = sscanf(body_start, "%x", &size);
2392 if (res<0) {
2393 te_header[0] = '\r';
2394 if (sep) sep[0] = ';';
2395 *header_size = 0;
2396 *payload_size = 0;
2397 GF_LOG(GF_LOG_ERROR, GF_LOG_HTTP, ("[HTTP] Chunk encoding: fail to read chunk size from buffer %s, aborting\n", body_start));
2398 return NULL;
2399 }
2400 if (sep) sep[0] = ';';
2401 *payload_size = size;
2402
2403 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()));
2404
2405 te_header[0] = '\r';
2406 if (!size)
2407 sess->last_chunk_found = GF_TRUE;
2408
2409 sess->current_chunk_size = size;
2410 sess->current_chunk_start = gf_sys_clock_high_res();
2411 return te_header+2;
2412 }
2413
2414
dm_sess_update_download_rate(GF_DownloadSession * sess,Bool always_check)2415 static void dm_sess_update_download_rate(GF_DownloadSession * sess, Bool always_check)
2416 {
2417 u64 runtime;
2418 if (!always_check && (sess->bytes_done==sess->total_size)) return;
2419
2420 /*update state*/
2421 runtime = sess->chunk_run_time;
2422 if (sess->start_time) {
2423 runtime += (gf_sys_clock_high_res() - sess->start_time);
2424 if (sess->active_time) {
2425 runtime = sess->active_time;
2426 }
2427 }
2428 if (!runtime) runtime=1;
2429
2430 sess->bytes_per_sec = (u32) ((1000000 * (u64) sess->bytes_done) / runtime);
2431
2432 if (sess->chunked) {
2433 GF_LOG(GF_LOG_DEBUG, GF_LOG_HTTP, ("[HTTP] bandwidth estimation: download time "LLD" us (chunk download time "LLD" us) - bytes %u - rate %u kbps\n", runtime, sess->chunk_run_time, sess->bytes_done, sess->bytes_per_sec*8/1000));
2434 } else {
2435 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*8/1000));
2436 }
2437 }
2438
2439
gf_dm_data_received(GF_DownloadSession * sess,u8 * payload,u32 payload_size,Bool store_in_init,u32 * rewrite_size,u8 * original_payload)2440 static GFINLINE void gf_dm_data_received(GF_DownloadSession *sess, u8 *payload, u32 payload_size, Bool store_in_init, u32 *rewrite_size, u8 *original_payload)
2441 {
2442 u32 nbBytes, remaining, hdr_size;
2443 u8 *data;
2444 Bool first_chunk_in_payload = GF_TRUE;
2445 Bool flush_chunk = GF_FALSE;
2446 GF_NETIO_Parameter par;
2447
2448 nbBytes = payload_size;
2449 hdr_size = 0;
2450 remaining = 0;
2451 if (!payload)
2452 return; //nothing to do
2453 if (sess->chunked) {
2454 data = (u8 *) gf_dm_get_chunk_data(sess, first_chunk_in_payload, (char *) payload, &nbBytes, &hdr_size);
2455 //first_chunk_in_payload = GF_FALSE;
2456 if (!hdr_size && !data) {
2457 /* keep the data and wait for the rest */
2458 sess->remaining_data_size = nbBytes;
2459 sess->remaining_data = (char *)gf_realloc(sess->remaining_data, nbBytes * sizeof(char));
2460 memcpy(sess->remaining_data, payload, nbBytes);
2461 payload_size = 0;
2462 payload = NULL;
2463 } else if (hdr_size + nbBytes > payload_size) {
2464 /* chunk header is processed but we will need several TCP frames to get the entire chunk*/
2465 remaining = nbBytes + hdr_size - payload_size;
2466 assert(payload_size >= hdr_size);
2467 nbBytes = payload_size - hdr_size;
2468 payload_size = 0;
2469 payload = NULL;
2470 } else {
2471 payload_size -= hdr_size + nbBytes;
2472 payload += hdr_size + nbBytes;
2473 flush_chunk = GF_TRUE;
2474 }
2475 /*chunk transfer is done*/
2476 if (sess->last_chunk_found) {
2477 sess->total_size = sess->bytes_done;
2478 }
2479 } else {
2480 data = payload;
2481 remaining = payload_size = 0;
2482 }
2483
2484 if (data && nbBytes && store_in_init) {
2485 sess->init_data = (char *) gf_realloc(sess->init_data , sizeof(char) * (sess->init_data_size + nbBytes) );
2486 memcpy(sess->init_data+sess->init_data_size, data, nbBytes);
2487 sess->init_data_size += nbBytes;
2488 }
2489
2490 //we have some new bytes received
2491 if (nbBytes && !sess->remaining_data_size) {
2492 sess->bytes_done += nbBytes;
2493 dm_sess_update_download_rate(sess, GF_TRUE);
2494
2495 GF_LOG(GF_LOG_DEBUG, GF_LOG_HTTP, ("[HTTP] url %s received %d new bytes (%d kbps)\n", gf_cache_get_url(sess->cache_entry), nbBytes, 8*sess->bytes_per_sec/1000));
2496 if (sess->total_size && (sess->bytes_done > sess->total_size)) {
2497 GF_LOG(GF_LOG_WARNING, GF_LOG_HTTP, ("[HTTP] url %s received more bytes than planned!! Got %d bytes vs %d content length\n", gf_cache_get_url(sess->cache_entry), sess->bytes_done , sess->total_size ));
2498 sess->bytes_done = sess->total_size;
2499 }
2500
2501 if (sess->icy_metaint > 0)
2502 gf_icy_skip_data(sess, (char *) data, nbBytes);
2503 else {
2504 if (sess->use_cache_file)
2505 gf_cache_write_to_cache( sess->cache_entry, sess, (char *) data, nbBytes);
2506
2507 par.msg_type = GF_NETIO_DATA_EXCHANGE;
2508 par.error = GF_OK;
2509 par.data = (char *) data;
2510 par.size = nbBytes;
2511 par.reply = flush_chunk;
2512 gf_dm_sess_user_io(sess, &par);
2513 }
2514 }
2515 //and we're done
2516 if (sess->total_size && (sess->bytes_done == sess->total_size)) {
2517 u64 run_time;
2518 if (sess->use_cache_file) {
2519 gf_cache_close_write_cache(sess->cache_entry, sess, GF_TRUE);
2520 GF_LOG(GF_LOG_DEBUG, GF_LOG_HTTP,
2521 ("[CACHE] url %s saved as %s\n", gf_cache_get_url(sess->cache_entry), gf_cache_get_cache_filename(sess->cache_entry)));
2522 }
2523
2524 gf_dm_disconnect(sess, GF_FALSE);
2525 par.msg_type = GF_NETIO_DATA_TRANSFERED;
2526 par.error = GF_OK;
2527
2528 gf_dm_sess_user_io(sess, &par);
2529 sess->total_time_since_req = (u32) (gf_sys_clock_high_res() - sess->request_start_time);
2530 run_time = gf_sys_clock_high_res() - sess->start_time;
2531 if (sess->in_time) {
2532 sess->active_time += gf_sys_clock_high_res() - sess->in_time;
2533 if (run_time > sess->active_time) {
2534 sess->total_time_since_req -= (u32) (run_time - sess->active_time);
2535 run_time = sess->active_time; // + (run_time - sess->active_time)/3;
2536 sess->bytes_per_sec = (u32) ((1000000 * (u64) sess->bytes_done) / run_time);
2537 }
2538 }
2539
2540 GF_LOG(GF_LOG_INFO, GF_LOG_HTTP, ("[HTTP] url %s (%d bytes) downloaded in "LLU" us (%d kbps) (%d us since request - got response in %d us - active time %d us)\n", gf_cache_get_url(sess->cache_entry), sess->bytes_done,
2541 run_time, 8*sess->bytes_per_sec/1000, sess->total_time_since_req, sess->reply_time, sess->active_time ? sess->active_time : sess->reply_time ));
2542
2543 if (sess->chunked && (payload_size==2))
2544 payload_size=0;
2545 }
2546
2547 if (rewrite_size && sess->chunked && data && original_payload) {
2548 //use memmove since regions overlap
2549 memmove(original_payload + *rewrite_size, data, nbBytes);
2550 *rewrite_size += nbBytes;
2551 }
2552
2553 if (!sess->nb_left_in_chunk && remaining) {
2554 sess->nb_left_in_chunk = remaining;
2555 } else if (payload_size) {
2556 gf_dm_data_received(sess, payload, payload_size, store_in_init, rewrite_size, original_payload);
2557 }
2558 }
2559
dm_exceeds_cap_rate(GF_DownloadManager * dm)2560 static Bool dm_exceeds_cap_rate(GF_DownloadManager * dm)
2561 {
2562 u32 cumul_rate = 0;
2563 u32 nb_sess = 0;
2564 u32 i, count = gf_list_count(dm->sessions);
2565
2566 //check if this fits with all other sessions
2567 for (i=0; i<count; i++) {
2568 GF_DownloadSession * sess = (GF_DownloadSession*)gf_list_get(dm->sessions, i);
2569
2570 //session not running done
2571 if (sess->status != GF_NETIO_DATA_EXCHANGE) continue;
2572
2573 dm_sess_update_download_rate(sess, GF_FALSE);
2574 cumul_rate += sess->bytes_per_sec;
2575 nb_sess ++;
2576 }
2577 if ( cumul_rate >= nb_sess * dm->limit_data_rate)
2578 return GF_TRUE;
2579
2580 return GF_FALSE;
2581 }
2582
2583
2584 GF_EXPORT
gf_dm_sess_fetch_data(GF_DownloadSession * sess,char * buffer,u32 buffer_size,u32 * read_size)2585 GF_Err gf_dm_sess_fetch_data(GF_DownloadSession *sess, char *buffer, u32 buffer_size, u32 *read_size)
2586 {
2587 u32 size;
2588 GF_Err e;
2589
2590 if (!buffer || !buffer_size) {
2591 if (sess->put_state) {
2592 sess->put_state = 2;
2593 sess->status = GF_NETIO_WAIT_FOR_REPLY;
2594 return GF_OK;
2595 }
2596 return GF_BAD_PARAM;
2597 }
2598 if (sess->th) return GF_BAD_PARAM;
2599 if (sess->status == GF_NETIO_DISCONNECTED) {
2600 if (!sess->init_data_size)
2601 return GF_EOS;
2602 }
2603 else if (sess->status > GF_NETIO_DATA_TRANSFERED) return GF_BAD_PARAM;
2604
2605 *read_size = 0;
2606 if (sess->status == GF_NETIO_DATA_TRANSFERED) {
2607 if (!sess->server_mode) return GF_EOS;
2608 if (!sess->init_data_size && sess->total_size && (sess->total_size==sess->bytes_done)) return GF_EOS;
2609 sess->status = GF_NETIO_DATA_EXCHANGE;
2610 }
2611
2612 sess->in_time = gf_sys_clock_high_res();
2613 if (sess->status == GF_NETIO_SETUP) {
2614 gf_dm_connect(sess);
2615 if (sess->last_error) return sess->last_error;
2616 e = GF_OK;
2617 } else if (sess->status < GF_NETIO_DATA_EXCHANGE) {
2618 sess->do_requests(sess);
2619 e = sess->last_error;
2620 }
2621 /*we're running but we had data previously*/
2622 else if (sess->init_data) {
2623 e = GF_OK;
2624 if (sess->init_data_size<=buffer_size) {
2625 memcpy(buffer, sess->init_data, sizeof(char)*sess->init_data_size);
2626 *read_size = sess->init_data_size;
2627 gf_free(sess->init_data);
2628 sess->init_data = NULL;
2629 if (sess->init_data_size==sess->total_size)
2630 e = GF_EOS;
2631 sess->init_data_size = 0;
2632 } else {
2633 memcpy(buffer, sess->init_data, sizeof(char)*buffer_size);
2634 *read_size = buffer_size;
2635 sess->init_data_size -= buffer_size;
2636 memmove(sess->init_data, sess->init_data+buffer_size, sizeof(char)*sess->init_data_size);
2637 e = GF_OK;
2638 }
2639 } else {
2640
2641 if (sess->dm && sess->dm->limit_data_rate && dm_exceeds_cap_rate(sess->dm)) {
2642 if (sess->idle_time) sess->active_time += sess->in_time - sess->idle_time;
2643 sess->idle_time = sess->in_time;
2644 return GF_IP_NETWORK_EMPTY;
2645 }
2646
2647 if (sess->remaining_data && sess->remaining_data_size) {
2648 if (sess->remaining_data_size >= buffer_size) {
2649 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));
2650 return GF_NON_COMPLIANT_BITSTREAM;
2651 }
2652 memcpy(buffer, sess->remaining_data, sess->remaining_data_size);
2653 }
2654
2655 e = gf_dm_read_data(sess, buffer + sess->remaining_data_size, buffer_size - sess->remaining_data_size, read_size);
2656 if (!e) {
2657 size = sess->remaining_data_size + (*read_size);
2658 sess->remaining_data_size = 0;
2659 *read_size = 0;
2660 gf_dm_data_received(sess, (u8 *) buffer, size, GF_FALSE, read_size, buffer);
2661 if (!sess->chunked)
2662 *read_size = size;
2663 }
2664 }
2665 sess->active_time += gf_sys_clock_high_res() - sess->in_time;
2666
2667 if (sess->server_mode && (sess->status == GF_NETIO_DATA_EXCHANGE)) {
2668 sess->status = GF_NETIO_DATA_TRANSFERED;
2669 }
2670
2671 return e;
2672 }
2673
2674 GF_EXPORT
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)2675 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)
2676 {
2677 if (!sess) return GF_BAD_PARAM;
2678 if (server) *server = sess->server_name;
2679 if (path) *path = sess->remote_path;
2680 if (total_size) {
2681 if (sess->total_size==SIZE_IN_STREAM) *total_size = 0;
2682 else *total_size = sess->total_size;
2683 }
2684 if (bytes_done) *bytes_done = sess->bytes_done;
2685 if (bytes_per_sec) *bytes_per_sec = sess->bytes_per_sec;
2686 if (net_status) *net_status = sess->status;
2687 if (sess->status == GF_NETIO_DISCONNECTED) return GF_EOS;
2688 else if (sess->status == GF_NETIO_STATE_ERROR) return GF_SERVICE_ERROR;
2689 return GF_OK;
2690 }
2691
2692 GF_EXPORT
gf_dm_sess_get_utc_start(GF_DownloadSession * sess)2693 u64 gf_dm_sess_get_utc_start(GF_DownloadSession * sess)
2694 {
2695 if (!sess) return 0;
2696 return sess->start_time_utc;
2697 }
2698
2699 GF_EXPORT
gf_dm_sess_get_cache_name(GF_DownloadSession * sess)2700 const char *gf_dm_sess_get_cache_name(GF_DownloadSession * sess)
2701 {
2702 if (!sess) return NULL;
2703 if (! sess->cache_entry || sess->needs_cache_reconfig) return NULL;
2704 if (!sess->use_cache_file) return NULL;
2705 return gf_cache_get_cache_filename(sess->cache_entry);
2706 }
2707
2708 #if 0 //unused
2709 /*!
2710 * Tells whether session can be cached on disk.
2711 * Typically, when request has no content length, it deserves being streamed an cannot be cached
2712 * (ICY or MPEG-streamed content
2713 \param sess The session
2714 \param True if a cache can be created
2715 */
2716 Bool gf_dm_sess_can_be_cached_on_disk(const GF_DownloadSession *sess)
2717 {
2718 if (!sess) return GF_FALSE;
2719 return gf_cache_get_content_length(sess->cache_entry) != 0;
2720 }
2721 #endif
2722
2723 GF_EXPORT
gf_dm_sess_abort(GF_DownloadSession * sess)2724 void gf_dm_sess_abort(GF_DownloadSession * sess)
2725 {
2726 if (sess) {
2727 gf_mx_p(sess->mx);
2728 gf_dm_disconnect(sess, GF_TRUE);
2729 sess->status = GF_NETIO_STATE_ERROR;
2730 gf_mx_v(sess->mx);
2731 }
2732 }
2733
2734 #if 0 //unused
2735 /*!
2736 \brief gets private data
2737 *
2738 *Gets private data associated with the session.
2739 \param sess the download session
2740 \return the private data
2741 \warning the private_data parameter is reserved for bandwidth statistics per service when used in the GPAC terminal.
2742 */
2743 void *gf_dm_sess_get_private(GF_DownloadSession * sess)
2744 {
2745 return sess ? sess->ext : NULL;
2746 }
2747
2748 /*!
2749 \brief sets private data
2750 *
2751 *associate private data with the session.
2752 \param sess the download session
2753 \param private_data the private data
2754 \warning the private_data parameter is reserved for bandwidth statistics per service when used in the GPAC terminal.
2755 */
2756 void gf_dm_sess_set_private(GF_DownloadSession * sess, void *private_data)
2757 {
2758 if (sess) sess->ext = private_data;
2759 }
2760 #endif
2761
2762 /*!
2763 * Sends the HTTP headers
2764 \param sess The GF_DownloadSession
2765 \param sHTTP buffer containing the request
2766 \param GF_OK if everything went fine, the error otherwise
2767 */
http_send_headers(GF_DownloadSession * sess,char * sHTTP)2768 static GF_Err http_send_headers(GF_DownloadSession *sess, char * sHTTP) {
2769 GF_Err e;
2770 GF_NETIO_Parameter par;
2771 Bool no_cache = GF_FALSE;
2772 char range_buf[1024];
2773 char pass_buf[1124];
2774 const char *user_agent;
2775 const char *url;
2776 const char *user_profile;
2777 const char *param_string;
2778 Bool has_accept, has_connection, has_range, has_agent, has_language, send_profile, has_mime;
2779 assert (sess->status == GF_NETIO_CONNECTED);
2780
2781 gf_dm_clear_headers(sess);
2782 sess->active_time = 0;
2783
2784 assert(sess->remaining_data_size == 0);
2785
2786 if (sess->needs_cache_reconfig) {
2787 gf_dm_configure_cache(sess);
2788 sess->needs_cache_reconfig = 0;
2789 }
2790 if (sess->from_cache_only) {
2791 sess->request_start_time = gf_sys_clock_high_res();
2792 sess->req_hdr_size = 0;
2793 sess->status = GF_NETIO_WAIT_FOR_REPLY;
2794 gf_dm_sess_notify_state(sess, GF_NETIO_WAIT_FOR_REPLY, GF_OK);
2795 return GF_OK;
2796 }
2797
2798 /*setup authentification*/
2799 strcpy(pass_buf, "");
2800 sess->creds = gf_find_user_credentials_for_site( sess->dm, sess->server_name );
2801 if (sess->creds && sess->creds->valid) {
2802 sprintf(pass_buf, "Authorization: Basic %s", sess->creds->digest);
2803 }
2804
2805 user_agent = gf_opts_get_key("core", "ua");
2806 if (!user_agent) user_agent = GF_DOWNLOAD_AGENT_NAME;
2807
2808 sess->put_state = 0;
2809
2810 par.error = GF_OK;
2811 par.msg_type = GF_NETIO_GET_METHOD;
2812 par.name = NULL;
2813 gf_dm_sess_user_io(sess, &par);
2814 if (!par.name || sess->server_only_understand_get) {
2815 par.name = "GET";
2816 }
2817
2818 if (par.name) {
2819 if (!strcmp(par.name, "GET")) sess->http_read_type = GET;
2820 else if (!strcmp(par.name, "HEAD")) sess->http_read_type = HEAD;
2821 else sess->http_read_type = OTHER;
2822
2823 if (!strcmp(par.name, "PUT") || !strcmp(par.name, "POST"))
2824 sess->put_state = 1;
2825 } else {
2826 sess->http_read_type = GET;
2827 }
2828
2829 url = (sess->proxy_enabled==1) ? sess->orig_url : sess->remote_path;
2830
2831 param_string = gf_opts_get_key("core", "query-string");
2832 if (param_string) {
2833 if (strchr(sess->remote_path, '?')) {
2834 sprintf(sHTTP, "%s %s&%s HTTP/1.0\r\nHost: %s\r\n" ,
2835 par.name ? par.name : "GET", url, param_string, sess->server_name);
2836 } else {
2837 sprintf(sHTTP, "%s %s?%s HTTP/1.0\r\nHost: %s\r\n" ,
2838 par.name ? par.name : "GET", url, param_string, sess->server_name);
2839 }
2840 } else {
2841 sprintf(sHTTP, "%s %s HTTP/1.1\r\nHost: %s\r\n" ,
2842 par.name ? par.name : "GET", url, sess->server_name);
2843 }
2844
2845 /*get all headers*/
2846 has_agent = has_accept = has_connection = has_range = has_language = has_mime = GF_FALSE;
2847 while (1) {
2848 par.msg_type = GF_NETIO_GET_HEADER;
2849 par.value = NULL;
2850 gf_dm_sess_user_io(sess, &par);
2851 if (!par.value) break;
2852 strcat(sHTTP, par.name);
2853 strcat(sHTTP, ": ");
2854 strcat(sHTTP, par.value);
2855 strcat(sHTTP, "\r\n");
2856 if (!strcmp(par.name, "Accept")) has_accept = GF_TRUE;
2857 else if (!strcmp(par.name, "Connection")) has_connection = GF_TRUE;
2858 else if (!strcmp(par.name, "Range")) has_range = GF_TRUE;
2859 else if (!strcmp(par.name, "User-Agent")) has_agent = GF_TRUE;
2860 else if (!strcmp(par.name, "Accept-Language")) has_language = GF_TRUE;
2861 else if (!strcmp(par.name, "Content-Type")) has_mime = GF_TRUE;
2862 else if (!stricmp(par.name, "Transfer-Encoding")) {
2863 if (!stricmp(par.value, "chunked"))
2864 sess->chunked = GF_TRUE;
2865 }
2866
2867 if (!par.msg_type) break;
2868 }
2869 if (!has_agent) {
2870 strcat(sHTTP, "User-Agent: ");
2871 strcat(sHTTP, user_agent);
2872 strcat(sHTTP, "\r\n");
2873 }
2874 /*no mime and POST/PUT, default to octet stream*/
2875 if (!has_mime && (sess->http_read_type==OTHER)) strcat(sHTTP, "Content-Type: application/octet-stream\r\n");
2876 if (!has_accept && (sess->http_read_type!=OTHER) ) strcat(sHTTP, "Accept: */*\r\n");
2877 if (sess->proxy_enabled==1) strcat(sHTTP, "Proxy-Connection: Keep-alive\r\n");
2878 else if (!has_connection) strcat(sHTTP, "Connection: Keep-Alive\r\n");
2879 if (!has_range && sess->needs_range) {
2880 if (!sess->range_end) sprintf(range_buf, "Range: bytes="LLD"-\r\n", sess->range_start);
2881 else sprintf(range_buf, "Range: bytes="LLD"-"LLD"\r\n", sess->range_start, sess->range_end);
2882 strcat(sHTTP, range_buf);
2883
2884 no_cache = GF_TRUE;
2885 }
2886 if (!has_language) {
2887 const char *opt = gf_opts_get_key("core", "lang");
2888 if (opt) {
2889 strcat(sHTTP, "Accept-Language: ");
2890 strcat(sHTTP, opt);
2891 strcat(sHTTP, "\r\n");
2892 }
2893 }
2894
2895
2896 if (strlen(pass_buf)) {
2897 strcat(sHTTP, pass_buf);
2898 strcat(sHTTP, "\r\n");
2899 }
2900
2901 par.msg_type = GF_NETIO_GET_CONTENT;
2902 par.data = NULL;
2903 par.size = 0;
2904
2905 /*check if we have personalization info*/
2906 send_profile = GF_FALSE;
2907 user_profile = gf_opts_get_key("core", "user-profileid");
2908
2909 if (user_profile) {
2910 strcat(sHTTP, "X-UserProfileID: ");
2911 strcat(sHTTP, user_profile);
2912 strcat(sHTTP, "\r\n");
2913 } else if ((sess->http_read_type == GET) || (sess->http_read_type == HEAD) ) {
2914 user_profile = gf_opts_get_key("core", "user-profile");
2915 if (user_profile && gf_file_exists(user_profile)) {
2916 FILE *profile = gf_fopen(user_profile, "rb");
2917 if (profile) {
2918 par.size = (u32) gf_fsize(profile);
2919 gf_fclose(profile);
2920 sprintf(range_buf, "Content-Length: %d\r\n", par.size);
2921 strcat(sHTTP, range_buf);
2922 strcat(sHTTP, "Content-Type: text/xml\r\n");
2923 send_profile = GF_TRUE;
2924 }
2925 }
2926 }
2927
2928
2929 if (!send_profile) {
2930 gf_dm_sess_user_io(sess, &par);
2931 if (par.data && par.size) {
2932 sprintf(range_buf, "Content-Length: %d\r\n", par.size);
2933 strcat(sHTTP, range_buf);
2934 } else {
2935 par.data = NULL;
2936 par.size = 0;
2937 }
2938 }
2939
2940 if (sess->http_read_type!=OTHER) {
2941 /*signal we support title streaming*/
2942 // if (!strcmp(sess->remote_path, "/")) strcat(sHTTP, "icy-metadata:1\r\n");
2943 /* This will force the server to respond with Icy-Metaint */
2944 strcat(sHTTP, "Icy-Metadata: 1\r\n");
2945
2946 /*cached headers are not appended in POST*/
2947 if (!no_cache && !sess->disable_cache && (GF_OK < gf_cache_append_http_headers( sess->cache_entry, sHTTP)) ) {
2948 GF_LOG(GF_LOG_WARNING, GF_LOG_HTTP, ("Cache Entry : %p, FAILED to append cache directives.", sess->cache_entry));
2949 }
2950 }
2951
2952 strcat(sHTTP, "\r\n");
2953
2954 if (send_profile || par.data) {
2955 u32 len = (u32) strlen(sHTTP);
2956 char *tmp_buf = (char*)gf_malloc(sizeof(char)*(len+par.size+1));
2957 strcpy(tmp_buf, sHTTP);
2958 if (par.data) {
2959 memcpy(tmp_buf+len, par.data, par.size);
2960 tmp_buf[len+par.size] = 0;
2961
2962 sess->put_state = 2;
2963 } else {
2964 FILE *profile;
2965 user_profile = gf_opts_get_key("core", "user-profile");
2966 assert (user_profile);
2967 profile = gf_fopen(user_profile, "rt");
2968 if (profile) {
2969 s32 read = (s32) gf_fread(tmp_buf+len, par.size, profile);
2970 if ((read<0) || (read< (s32) par.size)) {
2971 GF_LOG(GF_LOG_WARNING, GF_LOG_HTTP,
2972 ("[HTTP] Error while loading UserProfile, size=%d, should be %d.", read, par.size));
2973 for (; read < (s32) par.size; read++) {
2974 tmp_buf[len + read] = 0;
2975 }
2976 }
2977 gf_fclose(profile);
2978 } else {
2979 GF_LOG(GF_LOG_WARNING, GF_LOG_HTTP, ("[HTTP] Error while loading Profile file %s.", user_profile));
2980 }
2981 }
2982
2983 sess->request_start_time = gf_sys_clock_high_res();
2984 sess->req_hdr_size = len+par.size;
2985
2986 #ifdef GPAC_HAS_SSL
2987 if (sess->ssl) {
2988 e = gf_ssl_write(sess->ssl, tmp_buf, len+par.size);
2989 } else
2990 #endif
2991 e = gf_sk_send(sess->sock, tmp_buf, len+par.size);
2992
2993 GF_LOG(GF_LOG_INFO, GF_LOG_HTTP, ("[HTTP] Sending request at UTC "LLD" %s\n\n", gf_net_get_utc(), tmp_buf));
2994 gf_free(tmp_buf);
2995 } else {
2996 u32 len = (u32) strlen(sHTTP);
2997
2998 sess->request_start_time = gf_sys_clock_high_res();
2999 sess->req_hdr_size = len;
3000
3001 #ifdef GPAC_HAS_SSL
3002 if (sess->ssl) {
3003 e = gf_ssl_write(sess->ssl, sHTTP, len);
3004 } else
3005 #endif
3006 e = gf_sk_send(sess->sock, sHTTP, len);
3007
3008 #ifndef GPAC_DISABLE_LOG
3009 if (e) {
3010 GF_LOG(GF_LOG_ERROR, GF_LOG_HTTP, ("[HTTP] Error sending request %s\n", gf_error_to_string(e) ));
3011 } else {
3012 GF_LOG(GF_LOG_INFO, GF_LOG_HTTP, ("[HTTP] Sending request at UTC "LLU" %s\n\n", gf_net_get_utc(), sHTTP));
3013 }
3014 #endif
3015 }
3016
3017 if (e) {
3018 sess->status = GF_NETIO_STATE_ERROR;
3019 sess->last_error = e;
3020 gf_dm_sess_notify_state(sess, GF_NETIO_STATE_ERROR, e);
3021 return e;
3022 }
3023
3024 gf_dm_sess_notify_state(sess, GF_NETIO_WAIT_FOR_REPLY, GF_OK);
3025 if (sess->put_state==1) {
3026 sess->status = GF_NETIO_DATA_TRANSFERED;
3027 } else {
3028 sess->status = GF_NETIO_WAIT_FOR_REPLY;
3029 }
3030 return GF_OK;
3031 }
3032
3033
3034 /*!
3035 * Parse the remaining part of body
3036 \param sess The session
3037 \param sHTTP the data buffer
3038 \param The error code if any
3039 */
http_parse_remaining_body(GF_DownloadSession * sess,char * sHTTP)3040 static GF_Err http_parse_remaining_body(GF_DownloadSession * sess, char * sHTTP)
3041 {
3042 GF_Err e;
3043 u32 buf_size = sess->dm ? sess->dm->read_buf_size : GF_DOWNLOAD_BUFFER_SIZE;
3044
3045 while (1) {
3046 u32 remaining_data_size, size=0;
3047 if (sess->status>=GF_NETIO_DISCONNECTED)
3048 return GF_REMOTE_SERVICE_ERROR;
3049
3050 if (sess->dm && sess->dm->limit_data_rate && sess->bytes_per_sec) {
3051 if (dm_exceeds_cap_rate(sess->dm)) {
3052 gf_sleep(1);
3053 return GF_OK;
3054 }
3055 }
3056
3057 //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)
3058 if (sess->remaining_data && sess->remaining_data_size) {
3059 if (sess->remaining_data_size >= buf_size) {
3060 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));
3061 return GF_NON_COMPLIANT_BITSTREAM;
3062 }
3063 memcpy(sHTTP, sess->remaining_data, sess->remaining_data_size);
3064 }
3065 e = gf_dm_read_data(sess, sHTTP + sess->remaining_data_size, buf_size - sess->remaining_data_size, &size);
3066 if (e!= GF_IP_CONNECTION_CLOSED && (!size || e == GF_IP_NETWORK_EMPTY)) {
3067 if (e == GF_IP_CONNECTION_CLOSED || (!sess->total_size && !sess->chunked && (gf_sys_clock_high_res() - sess->start_time > 5000000))) {
3068 sess->total_size = sess->bytes_done;
3069 gf_dm_sess_notify_state(sess, GF_NETIO_DATA_TRANSFERED, GF_OK);
3070 assert(sess->server_name);
3071 GF_LOG(GF_LOG_ERROR, GF_LOG_HTTP, ("[HTTP] Disconnected from %s: %s\n", sess->server_name, gf_error_to_string(e)));
3072 gf_dm_disconnect(sess, (e == GF_IP_CONNECTION_CLOSED) ? GF_TRUE : GF_FALSE);
3073 }
3074 return GF_OK;
3075 }
3076
3077 if (e) {
3078 if (sess->sock && (e == GF_IP_CONNECTION_CLOSED)) {
3079 u32 len = gf_cache_get_content_length(sess->cache_entry);
3080 if (size > 0)
3081 gf_dm_data_received(sess, (u8 *) sHTTP, size, GF_FALSE, NULL, NULL);
3082 if ( ( (len == 0) && sess->use_cache_file)
3083 /*ivica patch*/
3084 || (size==0)
3085 ) {
3086 sess->total_size = sess->bytes_done;
3087 // HTTP 1.1 without content length...
3088 gf_dm_sess_notify_state(sess, GF_NETIO_DATA_TRANSFERED, GF_OK);
3089 assert(sess->server_name);
3090 GF_LOG(GF_LOG_ERROR, GF_LOG_HTTP, ("[HTTP] Disconnected from %s: %s\n", sess->server_name, gf_error_to_string(e)));
3091 if (sess->use_cache_file)
3092 gf_cache_set_content_length(sess->cache_entry, sess->bytes_done);
3093 e = GF_OK;
3094 }
3095 }
3096 gf_dm_disconnect(sess, GF_TRUE);
3097 sess->last_error = e;
3098 gf_dm_sess_notify_state(sess, sess->status, e);
3099 return e;
3100 }
3101
3102 remaining_data_size = sess->remaining_data_size;
3103 sess->remaining_data_size = 0;
3104
3105 sHTTP[size + remaining_data_size] = 0;
3106
3107 gf_dm_data_received(sess, (u8 *) sHTTP, size + remaining_data_size, GF_FALSE, NULL, NULL);
3108
3109 /*socket empty*/
3110 if (size < buf_size) {
3111 return GF_OK;
3112 }
3113 }
3114 return GF_OK;
3115 }
3116
notify_headers(GF_DownloadSession * sess,char * sHTTP,s32 bytesRead,s32 BodyStart)3117 static void notify_headers(GF_DownloadSession *sess, char * sHTTP, s32 bytesRead, s32 BodyStart)
3118 {
3119 GF_NETIO_Parameter par;
3120 u32 i, count;
3121
3122 count = gf_list_count(sess->headers);
3123 memset(&par, 0, sizeof(GF_NETIO_Parameter));
3124
3125 for (i=0; i<count; i++) {
3126 GF_HTTPHeader *hdrp = (GF_HTTPHeader*)gf_list_get(sess->headers, i);
3127 par.name = hdrp->name;
3128 par.value = hdrp->value;
3129
3130 par.error = GF_OK;
3131 par.msg_type = GF_NETIO_PARSE_HEADER;
3132 gf_dm_sess_user_io(sess, &par);
3133 }
3134
3135 if (sHTTP) {
3136 sHTTP[bytesRead]=0;
3137 par.error = GF_OK;
3138 par.data = sHTTP + BodyStart;
3139 par.size = (u32) strlen(par.data);
3140 par.msg_type = GF_NETIO_DATA_EXCHANGE;
3141 gf_dm_sess_user_io(sess, &par);
3142 }
3143 }
3144
3145 /*!
3146 * Waits for the response HEADERS, parse the information... and so on
3147 \param sess The session
3148 \param sHTTP the data buffer
3149 */
wait_for_header_and_parse(GF_DownloadSession * sess,char * sHTTP)3150 static GF_Err wait_for_header_and_parse(GF_DownloadSession *sess, char * sHTTP)
3151 {
3152 GF_NETIO_Parameter par;
3153 s32 bytesRead, BodyStart;
3154 u32 res, i, buf_size;
3155 s32 LinePos, Pos;
3156 u32 method=0;
3157 u32 rsp_code, ContentLength, first_byte, last_byte, total_size, range, no_range;
3158 Bool connection_closed = GF_FALSE;
3159 char buf[1025];
3160 char comp[400];
3161 GF_Err e;
3162 char * new_location;
3163 const char * mime_type;
3164
3165
3166 if (sess->server_mode) {
3167 assert( sess->status == GF_NETIO_CONNECTED );
3168 } else {
3169 assert( sess->status == GF_NETIO_WAIT_FOR_REPLY );
3170 if (!(sess->flags & GF_NETIO_SESSION_NOT_CACHED)) {
3171 sess->use_cache_file = sess->dm->disable_cache ? GF_FALSE : GF_TRUE;
3172 }
3173 }
3174 bytesRead = res = 0;
3175 new_location = NULL;
3176
3177 if (sess->from_cache_only) {
3178 sess->reply_time = (u32) (gf_sys_clock_high_res() - sess->request_start_time);
3179 sess->rsp_hdr_size = 0;
3180 sess->total_size = sess->bytes_done = gf_cache_get_content_length(sess->cache_entry);
3181
3182 memset(&par, 0, sizeof(GF_NETIO_Parameter));
3183 par.msg_type = GF_NETIO_DATA_TRANSFERED;
3184 par.error = GF_OK;
3185 gf_dm_sess_user_io(sess, &par);
3186 gf_dm_disconnect(sess, GF_FALSE);
3187 return GF_OK;
3188 }
3189
3190 buf_size = sess->dm ? sess->dm->read_buf_size : GF_DOWNLOAD_BUFFER_SIZE;
3191
3192 //always set start time to the time at last attempt reply parsing
3193 sess->start_time = gf_sys_clock_high_res();
3194 sess->start_time_utc = gf_net_get_utc();
3195 sess->chunked = GF_FALSE;
3196 sess->chunk_run_time = 0;
3197 sess->last_chunk_found = GF_FALSE;
3198 // gf_sk_reset(sess->sock);
3199 sHTTP[0] = 0;
3200
3201 while (1) {
3202 e = gf_dm_read_data(sess, sHTTP + bytesRead, buf_size - bytesRead, &res);
3203 switch (e) {
3204 case GF_IP_NETWORK_EMPTY:
3205 if (!bytesRead) {
3206 e = gf_sk_probe(sess->sock);
3207 if ((e==GF_IP_CONNECTION_CLOSED) || (gf_sys_clock_high_res() - sess->request_start_time > 1000 * sess->request_timeout)
3208 ) {
3209 sess->last_error = (e==GF_IP_CONNECTION_CLOSED) ? e : GF_IP_NETWORK_EMPTY;
3210 sess->status = GF_NETIO_STATE_ERROR;
3211 return GF_IP_NETWORK_EMPTY;
3212 }
3213 assert(res==0);
3214 return GF_OK;
3215 }
3216 continue;
3217 /*socket has been closed while configuring, retry (not sure if the server got the GET)*/
3218 case GF_IP_CONNECTION_CLOSED:
3219 if (sess->http_read_type == HEAD) {
3220 /* Some servers such as shoutcast directly close connection if HEAD or an unknown method is issued */
3221 sess->server_only_understand_get = GF_TRUE;
3222 }
3223 if (sess->server_mode) {
3224 sess->last_error = GF_IP_CONNECTION_CLOSED;
3225 sess->status = GF_NETIO_DISCONNECTED;
3226 GF_LOG(GF_LOG_INFO, GF_LOG_HTTP, ("[HTTP] Connection closed by client\n", sess->remote_path));
3227 return GF_IP_CONNECTION_CLOSED;
3228 }
3229 GF_LOG(GF_LOG_INFO, GF_LOG_HTTP, ("[HTTP] Connection closed by server when processing %s - retrying\n", sess->remote_path));
3230 gf_dm_disconnect(sess, GF_TRUE);
3231
3232 if (sess->num_retry)
3233 sess->status = GF_NETIO_SETUP;
3234 else {
3235 sess->last_error = e;
3236 sess->status = GF_NETIO_STATE_ERROR;
3237 }
3238 return e;
3239 case GF_OK:
3240 if (!res)
3241 return GF_OK;
3242 break;
3243 default:
3244 goto exit;
3245 }
3246 bytesRead += res;
3247
3248 /*locate body start*/
3249 BodyStart = gf_token_find(sHTTP, 0, bytesRead, "\r\n\r\n");
3250 if (BodyStart > 0) {
3251 BodyStart += 4;
3252 break;
3253 }
3254 BodyStart = gf_token_find(sHTTP, 0, bytesRead, "\n\n");
3255 if (BodyStart > 0) {
3256 BodyStart += 2;
3257 break;
3258 }
3259 }
3260 if (bytesRead < 0) {
3261 e = GF_REMOTE_SERVICE_ERROR;
3262 goto exit;
3263 }
3264 if (!BodyStart)
3265 BodyStart = bytesRead;
3266
3267 sHTTP[BodyStart-1] = 0;
3268 GF_LOG(GF_LOG_INFO, GF_LOG_HTTP, ("[HTTP] %s\n\n", sHTTP));
3269
3270 sess->reply_time = (u32) (gf_sys_clock_high_res() - sess->request_start_time);
3271 sess->rsp_hdr_size = BodyStart;
3272
3273 LinePos = gf_token_get_line(sHTTP, 0, bytesRead, buf, 1024);
3274 Pos = gf_token_get(buf, 0, " \t\r\n", comp, 400);
3275
3276 if (sess->server_mode) {
3277 if (!strcmp(comp, "GET")) method = GF_HTTP_GET;
3278 else if (!strcmp(comp, "HEAD")) method = GF_HTTP_HEAD;
3279 else if (!strcmp(comp, "OPTIONS")) method = GF_HTTP_OPTIONS;
3280 else if (!strcmp(comp, "PUT")) method = GF_HTTP_PUT;
3281 else if (!strcmp(comp, "POST")) method = GF_HTTP_POST;
3282 else if (!strcmp(comp, "DELETE"))
3283 method = GF_HTTP_DELETE;
3284 else if (!strcmp(comp, "CONNECT")) method = GF_HTTP_CONNECT;
3285 else if (!strcmp(comp, "TRACE")) method = GF_HTTP_TRACE;
3286 else method = 0;
3287
3288 Pos = gf_token_get(buf, Pos, " \t\r\n", comp, 400);
3289 if (sess->orig_url) gf_free(sess->orig_url);
3290 sess->orig_url = gf_strdup(comp);
3291 /*Pos = */gf_token_get(buf, Pos, " \t\r\n", comp, 400);
3292 if ((strncmp("HTTP", comp, 4) != 0)) {
3293 e = GF_REMOTE_SERVICE_ERROR;
3294 goto exit;
3295 }
3296 //flush potential body except for PUT/POST
3297 if ((method==GF_HTTP_PUT) || (method==GF_HTTP_POST))
3298 rsp_code = 200;
3299 else
3300 rsp_code = 300;
3301 } else {
3302
3303 if (!strncmp("ICY", comp, 3)) {
3304 sess->use_cache_file = GF_FALSE;
3305 /*be prepared not to receive any mime type from ShoutCast servers*/
3306 if (!gf_cache_get_mime_type(sess->cache_entry))
3307 gf_cache_set_mime_type(sess->cache_entry, "audio/mpeg");
3308 } else if ((strncmp("HTTP", comp, 4) != 0)) {
3309 e = GF_REMOTE_SERVICE_ERROR;
3310 goto exit;
3311 }
3312 Pos = gf_token_get(buf, Pos, " ", comp, 400);
3313 if (Pos <= 0) {
3314 e = GF_REMOTE_SERVICE_ERROR;
3315 goto exit;
3316 }
3317 rsp_code = (u32) atoi(comp);
3318 /*Pos = */gf_token_get(buf, Pos, " \r\n", comp, 400);
3319
3320 }
3321
3322 no_range = range = ContentLength = first_byte = last_byte = total_size = 0;
3323 /* parse headers*/
3324 while (1) {
3325 GF_HTTPHeader *hdrp;
3326 char *sep, *hdr_sep, *hdr, *hdr_val;
3327 if ( (s32) LinePos + 4 > BodyStart) break;
3328 LinePos = gf_token_get_line(sHTTP, LinePos , bytesRead, buf, 1024);
3329 if (LinePos < 0) break;
3330
3331 hdr_sep = NULL;
3332 hdr_val = NULL;
3333 hdr = buf;
3334 sep = strchr(buf, ':');
3335 if (sep) {
3336 sep[0]=0;
3337 hdr_val = sep+1;
3338 while (hdr_val[0]==' ') hdr_val++;
3339 hdr_sep = strrchr(hdr_val, '\r');
3340 if (hdr_sep) hdr_sep[0] = 0;
3341 }
3342
3343 GF_SAFEALLOC(hdrp, GF_HTTPHeader);
3344 if (hdrp) {
3345 hdrp->name = gf_strdup(hdr);
3346 hdrp->value = gf_strdup(hdr_val);
3347 gf_list_add(sess->headers, hdrp);
3348 }
3349
3350 if (sep) sep[0]=':';
3351 if (hdr_sep) hdr_sep[0] = '\r';
3352
3353 if (sess->server_mode) {
3354 if (!stricmp(hdrp->name, "Transfer-Encoding") && !stricmp(hdrp->value, "chunked"))
3355 sess->chunked = GF_TRUE;
3356 }
3357 }
3358
3359 if (!sess->server_mode) {
3360 Bool cache_no_store = GF_FALSE;
3361 //default pre-processing of headers - needs cleanup, not all of these have to be parsed before checking reply code
3362 for (i=0; i<gf_list_count(sess->headers); i++) {
3363 char *val;
3364 GF_HTTPHeader *hdrp = (GF_HTTPHeader*)gf_list_get(sess->headers, i);
3365
3366 if (!stricmp(hdrp->name, "Content-Length") ) {
3367 ContentLength = (u32) atoi(hdrp->value);
3368
3369 if (rsp_code<300)
3370 gf_cache_set_content_length(sess->cache_entry, ContentLength);
3371
3372 }
3373 else if (!stricmp(hdrp->name, "Content-Type")) {
3374 char *mime = gf_strdup(hdrp->value);
3375 while (1) {
3376 u32 len = (u32) strlen(mime);
3377 char c = len ? mime[len-1] : 0;
3378 if ((c=='\r') || (c=='\n')) {
3379 mime[len-1] = 0;
3380 } else {
3381 break;
3382 }
3383 }
3384 val = strchr(mime, ';');
3385 if (val) val[0] = 0;
3386
3387 strlwr(mime);
3388 if (rsp_code<300) {
3389 if (sess->cache_entry) {
3390 gf_cache_set_mime_type(sess->cache_entry, mime);
3391 } else {
3392 sess->mime_type = mime;
3393 mime = NULL;
3394 }
3395 }
3396 if (mime) gf_free(mime);
3397 }
3398 else if (!stricmp(hdrp->name, "Content-Range")) {
3399 if (!strncmp(hdrp->value, "bytes", 5)) {
3400 val = hdrp->value + 5;
3401 if (val[0] == ':') val += 1;
3402 while (val[0] == ' ') val += 1;
3403
3404 if (val[0] == '*') {
3405 sscanf(val, "*/%u", &total_size);
3406 } else {
3407 sscanf(val, "%u-%u/%u", &first_byte, &last_byte, &total_size);
3408 }
3409 }
3410 }
3411 else if (!stricmp(hdrp->name, "Accept-Ranges")) {
3412 if (strstr(hdrp->value, "none")) no_range = 1;
3413 }
3414 else if (!stricmp(hdrp->name, "Location"))
3415 new_location = gf_strdup(hdrp->value);
3416 else if (!strnicmp(hdrp->name, "ice", 3) || !strnicmp(hdrp->name, "icy", 3) ) {
3417 /* For HTTP icy servers, we disable cache */
3418 if (sess->icy_metaint == 0)
3419 sess->icy_metaint = -1;
3420 sess->use_cache_file = GF_FALSE;
3421 if (!stricmp(hdrp->name, "icy-metaint")) {
3422 sess->icy_metaint = atoi(hdrp->value);
3423 }
3424 }
3425 else if (!stricmp(hdrp->name, "Cache-Control")) {
3426 if (strstr(hdrp->value, "no-store")) {
3427 cache_no_store = GF_TRUE;
3428 }
3429 }
3430 else if (!stricmp(hdrp->name, "ETag")) {
3431 if (rsp_code<300)
3432 gf_cache_set_etag_on_server(sess->cache_entry, hdrp->value);
3433 }
3434 else if (!stricmp(hdrp->name, "Last-Modified")) {
3435 if (rsp_code<300)
3436 gf_cache_set_last_modified_on_server(sess->cache_entry, hdrp->value);
3437 }
3438 else if (!stricmp(hdrp->name, "Transfer-Encoding")) {
3439 if (!stricmp(hdrp->value, "chunked"))
3440 sess->chunked = GF_TRUE;
3441 }
3442 else if (!stricmp(hdrp->name, "X-UserProfileID") ) {
3443 gf_opts_set_key("core", "user-profileid", hdrp->value);
3444 }
3445 else if (!stricmp(hdrp->name, "Connection") ) {
3446 if (strstr(hdrp->value, "close"))
3447 connection_closed = GF_TRUE;
3448 }
3449
3450 if (sess->status==GF_NETIO_DISCONNECTED) return GF_OK;
3451 }
3452
3453 if (cache_no_store) {
3454 if (sess->cache_entry && !ContentLength && (rsp_code<300) ) {
3455 sess->use_cache_file = GF_FALSE;
3456 gf_cache_remove_session_from_cache_entry(sess->cache_entry, sess);
3457 sess->cache_entry = NULL;
3458 }
3459 }
3460
3461 if (no_range) first_byte = 0;
3462
3463
3464 gf_cache_set_headers_processed(sess->cache_entry);
3465 }
3466
3467 par.msg_type = GF_NETIO_PARSE_REPLY;
3468 par.error = GF_OK;
3469 par.reply = rsp_code;
3470 par.value = comp;
3471 /*
3472 * If response is correct, it means our credentials are correct
3473 */
3474 if (sess->creds && rsp_code != 304)
3475 sess->creds->valid = GF_TRUE;
3476
3477
3478 /*try to flush body */
3479 if (rsp_code>=300) {
3480 u32 start = gf_sys_clock();
3481 while (BodyStart + ContentLength > (u32) bytesRead) {
3482 e = gf_dm_read_data(sess, sHTTP + bytesRead, buf_size - bytesRead, &res);
3483 switch (e) {
3484 case GF_IP_NETWORK_EMPTY:
3485 break;
3486 case GF_OK:
3487 bytesRead += res;
3488 break;
3489 default:
3490 start=0;
3491 break;
3492 }
3493 if (gf_sys_clock()-start>100)
3494 break;
3495
3496 //does not fit in our buffer, too bad we'll kill the connection
3497 if (bytesRead == GF_DOWNLOAD_BUFFER_SIZE)
3498 break;
3499 }
3500
3501 if (BodyStart + ContentLength > (u32) bytesRead) {
3502 ContentLength = 0;
3503 //cannot flush, discard socket
3504 sess->connection_close = GF_TRUE;
3505 }
3506 }
3507 if (sess->server_mode) {
3508 if (ContentLength) {
3509 par.data = sHTTP + BodyStart;
3510 par.size = ContentLength;
3511 } else if (BodyStart < (s32) bytesRead) {
3512 if (sess->init_data) gf_free(sess->init_data);
3513 sess->init_data_size = 0;
3514 sess->init_data = NULL;
3515
3516 gf_dm_data_received(sess, (u8 *) sHTTP + BodyStart, bytesRead - BodyStart, GF_TRUE, NULL, NULL);
3517 }
3518 par.reply = method;
3519 gf_dm_sess_user_io(sess, &par);
3520 sess->status = GF_NETIO_DATA_TRANSFERED;
3521 return GF_OK;
3522 }
3523 //remember if we can keep the session alive after the transfer is done
3524 sess->connection_close = connection_closed;
3525
3526 switch (rsp_code) {
3527 case 200:
3528 case 201:
3529 case 202:
3530 case 206:
3531 gf_dm_sess_user_io(sess, &par);
3532 e = GF_OK;
3533 if (sess->proxy_enabled==2) {
3534 sess->proxy_enabled=0;
3535 if (sess->dm)
3536 gf_list_add(sess->dm->skip_proxy_servers, gf_strdup(sess->server_name));
3537 }
3538 break;
3539 /*redirection: extract the new location*/
3540 case 301:
3541 case 302:
3542 case 307:
3543 if (!new_location || !strlen(new_location) ) {
3544 gf_dm_sess_user_io(sess, &par);
3545 e = GF_URL_ERROR;
3546 goto exit;
3547 }
3548 while (
3549 (new_location[strlen(new_location)-1] == '\n')
3550 || (new_location[strlen(new_location)-1] == '\r') )
3551 new_location[strlen(new_location)-1] = 0;
3552
3553 /*reset and reconnect*/
3554 gf_dm_disconnect(sess, GF_TRUE);
3555 sess->status = GF_NETIO_SETUP;
3556 e = gf_dm_sess_setup_from_url(sess, new_location, GF_FALSE);
3557 if (e) {
3558 sess->status = GF_NETIO_STATE_ERROR;
3559 sess->last_error = e;
3560 gf_dm_sess_notify_state(sess, sess->status, e);
3561 }
3562 gf_free(new_location);
3563 return e;
3564 case 304:
3565 {
3566 sess->status = GF_NETIO_PARSE_REPLY;
3567 assert(sess->cache_entry);
3568 sess->total_size = gf_cache_get_cache_filesize(sess->cache_entry);
3569
3570 gf_dm_sess_notify_state(sess, GF_NETIO_PARSE_REPLY, GF_OK);
3571
3572 gf_dm_disconnect(sess, GF_FALSE);
3573 if (sess->user_proc) {
3574 /* For modules that do not use cache and have problems with GF_NETIO_DATA_TRANSFERED ... */
3575 const char * filename;
3576 FILE * f;
3577 filename = gf_cache_get_cache_filename(sess->cache_entry);
3578 GF_LOG(GF_LOG_INFO, GF_LOG_HTTP, ("[HTTP] Sending data to modules from %s...\n", filename));
3579 f = gf_fopen(filename, "rb");
3580 assert(filename);
3581 if (!f) {
3582 GF_LOG(GF_LOG_INFO, GF_LOG_HTTP, ("[HTTP] FAILED to open cache file %s for reading contents !\n", filename));
3583 /* Ooops, no cache, redowload everything ! */
3584 gf_dm_disconnect(sess, GF_FALSE);
3585 sess->status = GF_NETIO_SETUP;
3586 e = gf_dm_sess_setup_from_url(sess, sess->orig_url, GF_FALSE);
3587 sess->total_size = gf_cache_get_cache_filesize(sess->cache_entry);
3588 if (e) {
3589 sess->status = GF_NETIO_STATE_ERROR;
3590 sess->last_error = e;
3591 gf_dm_sess_notify_state(sess, sess->status, e);
3592 }
3593 return e;
3594 }
3595
3596 par.error = GF_OK;
3597 par.msg_type = GF_NETIO_PARSE_HEADER;
3598 par.name = "Content-Type";
3599 par.value = (char *) gf_cache_get_mime_type(sess->cache_entry);
3600 gf_dm_sess_user_io(sess, &par);
3601
3602 sess->status = GF_NETIO_DATA_EXCHANGE;
3603 if (! (sess->flags & GF_NETIO_SESSION_NOT_THREADED) || sess->force_data_write_callback) {
3604 char file_cache_buff[16544];
3605 s32 read = 0;
3606 total_size = gf_cache_get_cache_filesize(sess->cache_entry);
3607 do {
3608 read = (s32) gf_fread(file_cache_buff, 16384, f);
3609 if (read > 0) {
3610 sess->bytes_done += read;
3611 sess->total_size = total_size;
3612 sess->bytes_per_sec = 0xFFFFFFFF;
3613 par.size = read;
3614 par.msg_type = GF_NETIO_DATA_EXCHANGE;
3615 par.error = GF_EOS;
3616 par.reply = 2;
3617 par.data = file_cache_buff;
3618 gf_dm_sess_user_io(sess, &par);
3619 }
3620 } while ( read > 0);
3621 }
3622 gf_fclose(f);
3623 GF_LOG(GF_LOG_INFO, GF_LOG_HTTP, ("[HTTP] all data has been sent to modules from %s.\n", filename));
3624 }
3625 /* Cache file is the most recent */
3626 sess->status = GF_NETIO_DATA_TRANSFERED;
3627 par.error = GF_OK;
3628 gf_dm_sess_notify_state(sess, GF_NETIO_DATA_TRANSFERED, GF_OK);
3629 gf_dm_disconnect(sess, GF_FALSE);
3630 return GF_OK;
3631 }
3632 case 401:
3633 {
3634 /* Do we have a credentials struct ? */
3635 sess->creds = gf_user_credentials_register(sess->dm, sess->server_name, NULL, NULL, GF_FALSE);
3636 if (!sess->creds) {
3637 /* User credentials have not been filled properly, we have to abort */
3638 gf_dm_disconnect(sess, GF_TRUE);
3639 sess->status = GF_NETIO_STATE_ERROR;
3640 par.error = GF_AUTHENTICATION_FAILURE;
3641 par.msg_type = GF_NETIO_DISCONNECTED;
3642 gf_dm_sess_user_io(sess, &par);
3643 e = GF_AUTHENTICATION_FAILURE;
3644 sess->last_error = e;
3645 goto exit;
3646 }
3647 gf_dm_disconnect(sess, GF_FALSE);
3648 sess->status = GF_NETIO_SETUP;
3649 e = gf_dm_sess_setup_from_url(sess, sess->orig_url, GF_FALSE);
3650 if (e) {
3651 sess->status = GF_NETIO_STATE_ERROR;
3652 sess->last_error = e;
3653 gf_dm_sess_notify_state(sess, sess->status, e);
3654 }
3655 return e;
3656 }
3657 case 404:
3658 /* File not found */
3659 gf_dm_sess_user_io(sess, &par);
3660 if ((BodyStart < (s32) bytesRead)) {
3661 sHTTP[bytesRead] = 0;
3662 GF_LOG(GF_LOG_WARNING, GF_LOG_HTTP, ("[HTTP] Failure - body: %s\n", sHTTP + BodyStart));
3663 }
3664 notify_headers(sess, sHTTP, bytesRead, BodyStart);
3665 e = GF_URL_ERROR;
3666 goto exit;
3667 break;
3668 case 416:
3669 /* Range not accepted */
3670 gf_dm_sess_user_io(sess, &par);
3671
3672 notify_headers(sess, sHTTP, bytesRead, BodyStart);
3673 e = GF_SERVICE_ERROR;
3674 goto exit;
3675 break;
3676 case 400:
3677 case 501:
3678 /* Method not implemented ! */
3679 if (sess->http_read_type == HEAD) {
3680 /* Since HEAD is not understood by this server, we use a GET instead */
3681 sess->http_read_type = GET;
3682 sess->flags |= GF_NETIO_SESSION_NOT_CACHED;
3683 gf_dm_disconnect(sess, GF_FALSE);
3684 sess->status = GF_NETIO_SETUP;
3685 sess->server_only_understand_get = GF_TRUE;
3686 GF_LOG(GF_LOG_INFO, GF_LOG_HTTP, ("Method not supported, try with GET.\n"));
3687 e = gf_dm_sess_setup_from_url(sess, sess->orig_url, GF_FALSE);
3688 if (e) {
3689 sess->status = GF_NETIO_STATE_ERROR;
3690 sess->last_error = e;
3691 gf_dm_sess_notify_state(sess, sess->status, e);
3692 }
3693 return e;
3694 }
3695
3696 gf_dm_sess_user_io(sess, &par);
3697 notify_headers(sess, sHTTP, bytesRead, BodyStart);
3698 e = GF_REMOTE_SERVICE_ERROR;
3699 goto exit;
3700
3701 case 503:
3702 /*retry without proxy*/
3703 if (sess->proxy_enabled==1) {
3704 sess->proxy_enabled=2;
3705 gf_dm_disconnect(sess, GF_TRUE);
3706 sess->status = GF_NETIO_SETUP;
3707 return GF_OK;
3708 }
3709 case 204:
3710 gf_dm_sess_user_io(sess, &par);
3711 notify_headers(sess, sHTTP, bytesRead, BodyStart);
3712 e = GF_EOS;
3713 goto exit;
3714
3715 default:
3716 gf_dm_sess_user_io(sess, &par);
3717 notify_headers(sess, sHTTP, bytesRead, BodyStart);
3718 e = GF_REMOTE_SERVICE_ERROR;
3719 goto exit;
3720 }
3721
3722 notify_headers(sess, NULL, bytesRead, BodyStart);
3723
3724 if (sess->http_read_type != GET)
3725 sess->use_cache_file = GF_FALSE;
3726
3727 if (sess->http_read_type==HEAD) {
3728 gf_dm_disconnect(sess, GF_FALSE);
3729 gf_dm_sess_notify_state(sess, GF_NETIO_DATA_TRANSFERED, GF_OK);
3730 sess->http_read_type = GET;
3731 return GF_OK;
3732
3733
3734 }
3735
3736
3737 mime_type = gf_cache_get_mime_type(sess->cache_entry);
3738 if (!ContentLength && mime_type && ((strstr(mime_type, "ogg") || (!strcmp(mime_type, "audio/mpeg"))))) {
3739 if (0 == sess->icy_metaint)
3740 sess->icy_metaint = -1;
3741 sess->use_cache_file = GF_FALSE;
3742 }
3743
3744 #ifndef GPAC_DISABLE_LOG
3745 if (e) {
3746 GF_LOG(GF_LOG_ERROR, GF_LOG_HTTP, ("[HTTP] Error processing rely from %s: %s\n", sess->server_name, gf_error_to_string(e) ) );
3747 } else {
3748 GF_LOG(GF_LOG_DEBUG, GF_LOG_HTTP, ("[HTTP] Reply processed from %s\n", sess->server_name ) );
3749 }
3750 #endif
3751
3752 /*some servers may reply without content length, but we MUST have it*/
3753 if (e) goto exit;
3754 if (sess->icy_metaint != 0) {
3755 assert( ! sess->use_cache_file );
3756 GF_LOG(GF_LOG_INFO, GF_LOG_HTTP, ("[HTTP] ICY protocol detected\n"));
3757 if (mime_type && !stricmp(mime_type, "video/nsv")) {
3758 gf_cache_set_mime_type(sess->cache_entry, "audio/aac");
3759 }
3760 sess->icy_bytes = 0;
3761 sess->total_size = SIZE_IN_STREAM;
3762 sess->status = GF_NETIO_DATA_EXCHANGE;
3763 } else if (!ContentLength && !sess->chunked) {
3764 if (sess->http_read_type == GET) {
3765 sess->total_size = SIZE_IN_STREAM;
3766 sess->use_cache_file = GF_FALSE;
3767 sess->status = GF_NETIO_DATA_EXCHANGE;
3768 sess->bytes_done = 0;
3769 } else {
3770 gf_dm_sess_notify_state(sess, GF_NETIO_DATA_TRANSFERED, GF_OK);
3771 gf_dm_disconnect(sess, GF_FALSE);
3772 return GF_OK;
3773 }
3774 } else {
3775 sess->total_size = ContentLength;
3776 if (sess->use_cache_file && sess->http_read_type == GET ) {
3777 e = gf_cache_open_write_cache(sess->cache_entry, sess);
3778 if (e) {
3779 GF_LOG(GF_LOG_ERROR, GF_LOG_HTTP, ( "[CACHE] Failed to open cache, error=%d\n", e));
3780 goto exit;
3781 }
3782 }
3783 sess->status = GF_NETIO_DATA_EXCHANGE;
3784 sess->bytes_done = 0;
3785 }
3786
3787
3788 /* we may have existing data in this buffer ... */
3789 if (!e && (BodyStart < (s32) bytesRead)) {
3790 if (sess->init_data) gf_free(sess->init_data);
3791 sess->init_data_size = 0;
3792 sess->init_data = NULL;
3793
3794 gf_dm_data_received(sess, (u8 *) sHTTP + BodyStart, bytesRead - BodyStart, GF_TRUE, NULL, NULL);
3795 }
3796 exit:
3797 if (e) {
3798 if (e<0) {
3799 GF_LOG(GF_LOG_WARNING, GF_LOG_HTTP, ("[HTTP] Error parsing reply: %s for URL %s\nReply was:\n%s\n", gf_error_to_string(e), sess->orig_url, sHTTP ));
3800 } else {
3801 e = GF_OK;
3802 }
3803 gf_cache_entry_set_delete_files_when_deleted(sess->cache_entry);
3804 gf_dm_remove_cache_entry_from_session(sess);
3805 sess->cache_entry = NULL;
3806 gf_dm_disconnect(sess, GF_FALSE);
3807 if (connection_closed)
3808 sess->status = GF_NETIO_STATE_ERROR;
3809 else
3810 sess->status = GF_NETIO_DATA_TRANSFERED;
3811 sess->last_error = e;
3812 gf_dm_sess_notify_state(sess, sess->status, e);
3813 return e;
3814 }
3815 /*DO NOT call parse_body yet, as the final user may not be connected to our session*/
3816 return GF_OK;
3817 }
3818
3819 /**
3820 * Default performing behaviour
3821 \param sess The session
3822 */
http_do_requests(GF_DownloadSession * sess)3823 void http_do_requests(GF_DownloadSession *sess)
3824 {
3825 char sHTTP[GF_DOWNLOAD_BUFFER_SIZE+1];
3826
3827 if (sess->reused_cache_entry) {
3828 //main session is done downloading, notify - to do we should send progress events on this session also ...
3829 if (!gf_cache_is_in_progress(sess->cache_entry)) {
3830 GF_NETIO_Parameter par;
3831 gf_dm_disconnect(sess, GF_FALSE);
3832 sess->reused_cache_entry = GF_FALSE;
3833 memset(&par, 0, sizeof(GF_NETIO_Parameter));
3834 par.msg_type = GF_NETIO_DATA_TRANSFERED;
3835 par.error = GF_OK;
3836 gf_dm_sess_user_io(sess, &par);
3837 }
3838 return;
3839 }
3840
3841 switch (sess->status) {
3842 case GF_NETIO_CONNECTED:
3843 if (sess->server_mode) {
3844 wait_for_header_and_parse(sess, sHTTP);
3845 } else {
3846 http_send_headers(sess, sHTTP);
3847 }
3848 break;
3849 case GF_NETIO_WAIT_FOR_REPLY:
3850 if (sess->server_mode) {
3851 http_send_headers(sess, sHTTP);
3852 } else {
3853 wait_for_header_and_parse(sess, sHTTP);
3854 }
3855 break;
3856 case GF_NETIO_DATA_EXCHANGE:
3857 if (sess->server_mode) {
3858 sess->status = GF_NETIO_CONNECTED;
3859 break;
3860 }
3861 /*session has been reassigned, resend data retrieved in first GET reply to user but don't write to cache*/
3862 if (sess->reassigned) {
3863
3864 if (sess->icy_metaint > 0) {
3865 //we are reparsing init data, reset icy status
3866 sess->icy_bytes = 0;
3867 gf_icy_skip_data(sess, sess->init_data, sess->init_data_size);
3868 } else {
3869 GF_NETIO_Parameter par;
3870 par.msg_type = GF_NETIO_DATA_EXCHANGE;
3871 par.error = GF_OK;
3872 par.data = sess->init_data;
3873 par.size = sess->init_data_size;
3874 gf_dm_sess_user_io(sess, &par);
3875 }
3876 sess->reassigned = GF_FALSE;
3877 }
3878 http_parse_remaining_body(sess, sHTTP);
3879 break;
3880 default:
3881 break;
3882 }
3883 }
3884
3885
3886 /**
3887 * NET IO for MPD, we don't need this anymore since mime-type can be given by session
3888 */
wget_NetIO(void * cbk,GF_NETIO_Parameter * param)3889 static void wget_NetIO(void *cbk, GF_NETIO_Parameter *param)
3890 {
3891 FILE * f = (FILE*) cbk;
3892
3893 /*handle service message*/
3894 if (param->msg_type == GF_NETIO_DATA_EXCHANGE) {
3895 s32 written = (u32) gf_fwrite( param->data, param->size, f);
3896 if (written != param->size) {
3897 GF_LOG(GF_LOG_ERROR, GF_LOG_HTTP, ("Failed to write data on disk\n"));
3898 }
3899 }
3900 }
3901
3902 GF_EXPORT
gf_dm_wget(const char * url,const char * filename,u64 start_range,u64 end_range,char ** redirected_url)3903 GF_Err gf_dm_wget(const char *url, const char *filename, u64 start_range, u64 end_range, char **redirected_url)
3904 {
3905 GF_Err e;
3906 GF_DownloadManager * dm = gf_dm_new(NULL);
3907 if (!dm)
3908 return GF_OUT_OF_MEM;
3909 e = gf_dm_wget_with_cache(dm, url, filename, start_range, end_range, redirected_url);
3910 gf_dm_del(dm);
3911 return e;
3912 }
3913
3914 GF_EXPORT
gf_dm_wget_with_cache(GF_DownloadManager * dm,const char * url,const char * filename,u64 start_range,u64 end_range,char ** redirected_url)3915 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)
3916 {
3917 GF_Err e;
3918 FILE * f;
3919 GF_DownloadSession *dnload;
3920 if (!filename || !url || !dm)
3921 return GF_BAD_PARAM;
3922 f = gf_fopen(filename, "wb");
3923 if (!f) {
3924 GF_LOG(GF_LOG_ERROR, GF_LOG_HTTP, ("[WGET] Failed to open file %s for write.\n", filename));
3925 return GF_IO_ERR;
3926 }
3927 dnload = gf_dm_sess_new_simple(dm, (char *)url, GF_NETIO_SESSION_NOT_THREADED, &wget_NetIO, f, &e);
3928 if (!dnload) {
3929 return GF_BAD_PARAM;
3930 }
3931 dnload->use_cache_file = GF_FALSE;
3932 dnload->force_data_write_callback = GF_TRUE;
3933 if (end_range) {
3934 dnload->range_start = start_range;
3935 dnload->range_end = end_range;
3936 dnload->needs_range = GF_TRUE;
3937 }
3938 if (e == GF_OK) {
3939 e = gf_dm_sess_process(dnload);
3940 }
3941 e |= gf_cache_close_write_cache(dnload->cache_entry, dnload, (e == GF_OK) ? GF_TRUE : GF_FALSE);
3942 gf_fclose(f);
3943
3944 if (redirected_url) {
3945 if (dnload->orig_url_before_redirect) *redirected_url = gf_strdup(dnload->orig_url);
3946 }
3947 gf_dm_sess_del(dnload);
3948 return e;
3949 }
3950
3951 #if 0 //unused
3952
3953 /*
3954 \brief fetches remote file in memory
3955 *
3956 *Fetches remote file in memory.
3957 \param url the data to fetch
3958 \param out_data output data (allocated by function)
3959 \param out_size output data size
3960 \param out_mime if not NULL, pointer will contain the mime type (allocated by function)
3961 \return error code if any
3962 */
3963 GF_Err gf_dm_get_file_memory(const char *url, char **out_data, u32 *out_size, char **out_mime)
3964 {
3965 GF_Err e;
3966 FILE * f;
3967 char * f_fn = NULL;
3968 GF_DownloadSession *dnload;
3969 GF_DownloadManager *dm;
3970
3971 if (!url || !out_data || !out_size)
3972 return GF_BAD_PARAM;
3973 f = gf_file_temp(&f_fn);
3974 if (!f) {
3975 GF_LOG(GF_LOG_ERROR, GF_LOG_HTTP, ("[WGET] Failed to create temp file for write.\n"));
3976 return GF_IO_ERR;
3977 }
3978
3979 dm = gf_dm_new(NULL);
3980 if (!dm) {
3981 gf_fclose(f);
3982 gf_file_delete(f_fn);
3983 return GF_OUT_OF_MEM;
3984 }
3985
3986 dnload = gf_dm_sess_new_simple(dm, (char *)url, GF_NETIO_SESSION_NOT_THREADED, &wget_NetIO, f, &e);
3987 if (!dnload) {
3988 gf_dm_del(dm);
3989 gf_fclose(f);
3990 gf_file_delete(f_fn);
3991 return GF_BAD_PARAM;
3992 }
3993 dnload->use_cache_file = GF_FALSE;
3994 dnload->disable_cache = GF_TRUE;
3995 if (!e)
3996 e = gf_dm_sess_process(dnload);
3997
3998 if (!e)
3999 e = gf_cache_close_write_cache(dnload->cache_entry, dnload, e == GF_OK);
4000
4001 if (!e) {
4002 u32 size = (u32) gf_ftell(f);
4003 s32 read;
4004 *out_size = size;
4005 *out_data = (char*)gf_malloc(sizeof(char)* ( 1 + size));
4006 gf_fseek(f, 0, SEEK_SET);
4007 read = (s32) gf_fread(*out_data, size, f);
4008 if (read != size) {
4009 gf_free(*out_data);
4010 e = GF_IO_ERR;
4011 } else {
4012 (*out_data)[size] = 0;
4013 if (out_mime) {
4014 const char *mime = gf_dm_sess_mime_type(dnload);
4015 if (mime) *out_mime = gf_strdup(mime);
4016 }
4017 }
4018 }
4019 gf_fclose(f);
4020 gf_file_delete(f_fn);
4021 gf_free(f_fn);
4022 gf_dm_sess_del(dnload);
4023 gf_dm_del(dm);
4024 return e;
4025 }
4026 #endif
4027
4028 GF_EXPORT
gf_dm_sess_get_resource_name(GF_DownloadSession * dnload)4029 const char *gf_dm_sess_get_resource_name(GF_DownloadSession *dnload)
4030 {
4031 return dnload ? dnload->orig_url : NULL;
4032 }
4033
4034
4035 #if 0 //unused
4036 /*!
4037 \brief Get session original resource url
4038 *
4039 *Returns the original resource URL before any redirection associated with the session
4040 \param sess the download session
4041 \return the associated URL
4042 */
4043 const char *gf_dm_sess_get_original_resource_name(GF_DownloadSession *dnload)
4044 {
4045 if (dnload) return dnload->orig_url_before_redirect ? dnload->orig_url_before_redirect : dnload->orig_url;
4046 return NULL;
4047 }
4048
4049 /*!
4050 \brief fetch session status
4051 *
4052 *Fetch the session current status
4053 \param sess the download session
4054 \return the session status*/
4055 u32 gf_dm_sess_get_status(GF_DownloadSession *dnload)
4056 {
4057 return dnload ? dnload->status : GF_NETIO_STATE_ERROR;
4058 }
4059
4060
4061 /*!
4062 \brief Reset session
4063 *
4064 *Resets the session for new processing of the same url
4065 \param sess the download session
4066 \return error code if any
4067 */
4068 GF_Err gf_dm_sess_reset(GF_DownloadSession *sess)
4069 {
4070 if (!sess) return GF_BAD_PARAM;
4071 sess->status = GF_NETIO_SETUP;
4072 sess->needs_range = GF_FALSE;
4073 sess->range_start = sess->range_end = 0;
4074 sess->bytes_done = sess->bytes_per_sec = 0;
4075 if (sess->init_data) gf_free(sess->init_data);
4076 sess->init_data = NULL;
4077 sess->init_data_size = 0;
4078 sess->last_error = GF_OK;
4079 sess->total_size = 0;
4080 sess->start_time = 0;
4081 sess->start_time_utc = 0;
4082 sess->max_chunk_size = 0;
4083 sess->max_chunk_bytes_per_sec = 0;
4084 return GF_OK;
4085 }
4086
4087 /*!
4088 * Get a range of a cache entry file
4089 \param sess The session
4090 \param startOffset The first byte of the request to get
4091 \param endOffset The last byte of request to get
4092 \param The temporary name for the file created to have a range of the file
4093 */
4094 const char * gf_cache_get_cache_filename_range( const GF_DownloadSession * sess, u64 startOffset, u64 endOffset ) {
4095 u32 i, count;
4096 if (!sess || !sess->dm || endOffset < startOffset)
4097 return NULL;
4098 count = gf_list_count(sess->dm->partial_downloads);
4099 for (i = 0 ; i < count ; i++) {
4100 GF_PartialDownload * pd = (GF_PartialDownload*)gf_list_get(sess->dm->partial_downloads, i);
4101 assert( pd->filename && pd->url);
4102 if (!strcmp(pd->url, sess->orig_url) && pd->startOffset == startOffset && pd->endOffset == endOffset) {
4103 /* File already created, just return the file */
4104 return pd->filename;
4105 }
4106 }
4107 {
4108 /* Not found, we are gonna create the file */
4109 char * newFilename;
4110 GF_PartialDownload * partial;
4111 FILE * fw, *fr;
4112 u32 maxLen;
4113 const char * orig = gf_cache_get_cache_filename(sess->cache_entry);
4114 if (orig == NULL)
4115 return NULL;
4116 /* 22 if 1G + 1G + 2 dashes */
4117 maxLen = (u32) strlen(orig) + 22;
4118 newFilename = (char*)gf_malloc( maxLen );
4119 if (newFilename == NULL)
4120 return NULL;
4121 snprintf(newFilename, maxLen, "%s " LLU LLU, orig, startOffset, endOffset);
4122 fw = gf_fopen(newFilename, "wb");
4123 if (!fw) {
4124 GF_LOG(GF_LOG_ERROR, GF_LOG_HTTP, ("[CACHE] Cannot open partial cache file %s for write\n", newFilename));
4125 gf_free( newFilename );
4126 return NULL;
4127 }
4128 fr = gf_fopen(orig, "rb");
4129 if (!fr) {
4130 GF_LOG(GF_LOG_ERROR, GF_LOG_HTTP, ("[CACHE] Cannot open full cache file %s\n", orig));
4131 gf_free( newFilename );
4132 gf_fclose( fw );
4133 }
4134 /* Now, we copy ! */
4135 {
4136 char copyBuff[GF_DOWNLOAD_BUFFER_SIZE+1];
4137 s64 read, write, total;
4138 total = endOffset - startOffset;
4139 read = gf_fseek(fr, startOffset, SEEK_SET);
4140 if (read != startOffset) {
4141 GF_LOG(GF_LOG_ERROR, GF_LOG_HTTP, ("[CACHE] Cannot seek at right start offset in %s\n", orig));
4142 gf_fclose( fr );
4143 gf_fclose( fw );
4144 gf_free( newFilename );
4145 return NULL;
4146 }
4147 do {
4148 read = gf_fread(copyBuff, MIN(sizeof(copyBuff), (size_t) total), fr);
4149 if (read > 0) {
4150 total-= read;
4151 write = gf_fwrite(copyBuff, (size_t) read, fw);
4152 if (write != read) {
4153 /* Something bad happened */
4154 gf_fclose( fw );
4155 gf_fclose (fr );
4156 gf_free( newFilename );
4157 return NULL;
4158 }
4159 } else {
4160 if (read < 0) {
4161 gf_fclose( fw );
4162 gf_fclose( fr );
4163 gf_free( newFilename );
4164 return NULL;
4165 }
4166 }
4167 } while (total > 0);
4168 gf_fclose( fr );
4169 gf_fclose (fw);
4170 partial = (GF_PartialDownload*)gf_malloc( sizeof(GF_PartialDownload));
4171 if (partial == NULL) {
4172 gf_free(newFilename);
4173 return NULL;
4174 }
4175 partial->filename = newFilename;
4176 partial->url = sess->orig_url;
4177 partial->endOffset = endOffset;
4178 partial->startOffset = startOffset;
4179 gf_list_add(sess->dm->partial_downloads, partial);
4180 return newFilename;
4181 }
4182 }
4183 }
4184
4185 /*!
4186 * Reassigns session flags and callbacks. This is only possible if the session is not threaded.
4187 \param sess The session
4188 \param flags The new flags for the session - if flags is 0xFFFFFFFF, existing flags are not modified
4189 \param user_io The new callback function
4190 \param cbk The new user data to ba used in the callback function
4191 \param GF_OK or error
4192 */
4193 GF_Err gf_dm_sess_reassign(GF_DownloadSession *sess, u32 flags, gf_dm_user_io user_io, void *cbk)
4194 {
4195 /*shall only be called for non-threaded sessions!! */
4196 if (sess->th) return GF_BAD_PARAM;
4197
4198 if (flags == 0xFFFFFFFF) {
4199 sess->user_proc = user_io;
4200 sess->usr_cbk = cbk;
4201 return GF_OK;
4202 }
4203
4204 if (sess->flags & GF_DOWNLOAD_SESSION_USE_SSL) flags |= GF_DOWNLOAD_SESSION_USE_SSL;
4205 sess->flags = flags;
4206 if (sess->flags & GF_NETIO_SESSION_NOTIFY_DATA)
4207 sess->force_data_write_callback = GF_TRUE;
4208
4209 sess->user_proc = user_io;
4210 sess->usr_cbk = cbk;
4211 sess->reassigned = sess->init_data ? GF_TRUE : GF_FALSE;
4212 sess->num_retry = SESSION_RETRY_COUNT;
4213
4214 if (sess->status==GF_NETIO_DISCONNECTED)
4215 sess->status = GF_NETIO_SETUP;
4216
4217 /*threaded session shall be started with gf_dm_sess_process*/
4218 return GF_OK;
4219 }
4220 #endif
4221
4222
4223 GF_EXPORT
gf_dm_set_data_rate(GF_DownloadManager * dm,u32 rate_in_bits_per_sec)4224 void gf_dm_set_data_rate(GF_DownloadManager *dm, u32 rate_in_bits_per_sec)
4225 {
4226 if (rate_in_bits_per_sec == 0xFFFFFFFF) {
4227 dm->simulate_no_connection=GF_TRUE;
4228 } else {
4229 char opt[100];
4230 dm->simulate_no_connection=GF_FALSE;
4231 dm->limit_data_rate = rate_in_bits_per_sec/8;
4232
4233 sprintf(opt, "%d", rate_in_bits_per_sec);
4234 gf_opts_set_key("core", "maxrate", opt);
4235
4236 dm->read_buf_size = GF_DOWNLOAD_BUFFER_SIZE;
4237 //when rate is limited, use smaller smaller read size
4238 if (dm->limit_data_rate) dm->read_buf_size = 1024;
4239
4240 #ifdef GPAC_ENABLE_COVERAGE
4241 if (gf_sys_is_cov_mode()) {
4242 dm_exceeds_cap_rate(dm);
4243 }
4244 #endif
4245
4246 }
4247 }
4248
4249 GF_EXPORT
gf_dm_get_data_rate(GF_DownloadManager * dm)4250 u32 gf_dm_get_data_rate(GF_DownloadManager *dm)
4251 {
4252 return dm->limit_data_rate*8;
4253 }
4254
4255 GF_EXPORT
gf_dm_get_global_rate(GF_DownloadManager * dm)4256 u32 gf_dm_get_global_rate(GF_DownloadManager *dm)
4257 {
4258 u32 ret = 0;
4259 u32 i, count;
4260 if (!dm) return 0;
4261 gf_mx_p(dm->cache_mx);
4262 count = gf_list_count(dm->sessions);
4263
4264 for (i=0; i<count; i++) {
4265 GF_DownloadSession *sess = (GF_DownloadSession*)gf_list_get(dm->sessions, i);
4266 if (sess->total_size==sess->bytes_done) {
4267 if (gf_sys_clock_high_res() - sess->start_time>2000000) {
4268 continue;
4269 }
4270 }
4271 dm_sess_update_download_rate(sess, GF_FALSE);
4272 ret += sess->bytes_per_sec;
4273 }
4274 gf_mx_v(dm->cache_mx);
4275 return 8*ret;
4276 }
4277
4278 GF_EXPORT
gf_dm_sess_get_header(GF_DownloadSession * sess,const char * name)4279 const char *gf_dm_sess_get_header(GF_DownloadSession *sess, const char *name)
4280 {
4281 u32 i, count;
4282 if( !sess || !name) return NULL;
4283 count = gf_list_count(sess->headers);
4284 for (i=0; i<count; i++) {
4285 GF_HTTPHeader *header = (GF_HTTPHeader*)gf_list_get(sess->headers, i);
4286 if (!strcmp(header->name, name)) return header->value;
4287 }
4288 return NULL;
4289 }
4290
4291 GF_EXPORT
gf_dm_sess_enum_headers(GF_DownloadSession * sess,u32 * idx,const char ** hdr_name,const char ** hdr_val)4292 GF_Err gf_dm_sess_enum_headers(GF_DownloadSession *sess, u32 *idx, const char **hdr_name, const char **hdr_val)
4293 {
4294 GF_HTTPHeader *hdr;
4295 if( !sess || !idx || !hdr_name || !hdr_val) return GF_BAD_PARAM;
4296 hdr = gf_list_get(sess->headers, *idx);
4297 if (!hdr) return GF_EOS;
4298 (*idx) = (*idx) + 1;
4299 (*hdr_name) = hdr->name;
4300 (*hdr_val) = hdr->value;
4301 return GF_OK;
4302 }
4303
4304 GF_EXPORT
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)4305 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)
4306 {
4307 if (!sess) return GF_BAD_PARAM;
4308
4309 if (req_hdr_size) *req_hdr_size = sess->req_hdr_size;
4310 if (rsp_hdr_size) *rsp_hdr_size = sess->rsp_hdr_size;
4311 if (connect_time) *connect_time = sess->connect_time;
4312 if (reply_time) *reply_time = sess->reply_time;
4313 if (download_time) *download_time = sess->total_time_since_req;
4314 return GF_OK;
4315 }
4316
4317 GF_EXPORT
gf_dm_sess_force_memory_mode(GF_DownloadSession * sess)4318 void gf_dm_sess_force_memory_mode(GF_DownloadSession *sess)
4319 {
4320 if (sess) sess->flags |= GF_NETIO_SESSION_MEMORY_CACHE;
4321 }
4322
4323
4324 GF_EXPORT
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)4325 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)
4326 {
4327 if (!dm) return GF_BAD_PARAM;
4328 dm->local_cache_url_provider_cbk = local_cache_url_provider_cbk;
4329 dm->lc_udta = lc_udta;
4330 return GF_OK;
4331
4332 }
4333
4334 GF_EXPORT
gf_dm_add_cache_entry(GF_DownloadManager * dm,const char * szURL,u8 * data,u64 size,u64 start_range,u64 end_range,const char * mime,Bool clone_memory,u32 download_time_ms)4335 const DownloadedCacheEntry gf_dm_add_cache_entry(GF_DownloadManager *dm, const char *szURL, u8 *data, u64 size, u64 start_range, u64 end_range, const char *mime, Bool clone_memory, u32 download_time_ms)
4336 {
4337 u32 i, count;
4338 DownloadedCacheEntry the_entry = NULL;
4339
4340 gf_mx_p(dm->cache_mx );
4341 GF_LOG(GF_LOG_INFO, GF_LOG_CACHE, ("[HTTP] Pushing %s to cache\n", szURL));
4342 count = gf_list_count(dm->cache_entries);
4343 for (i = 0 ; i < count; i++) {
4344 const char * url;
4345 DownloadedCacheEntry e = (DownloadedCacheEntry)gf_list_get(dm->cache_entries, i);
4346 assert(e);
4347 url = gf_cache_get_url(e);
4348 assert( url );
4349 if (strcmp(url, szURL)) continue;
4350
4351 if (end_range) {
4352 if (start_range != gf_cache_get_start_range(e)) continue;
4353 if (end_range != gf_cache_get_end_range(e)) continue;
4354 }
4355 /*OK that's ours*/
4356 the_entry = e;
4357 break;
4358 }
4359 if (!the_entry) {
4360 the_entry = gf_cache_create_entry(dm, "", szURL, 0, 0, GF_TRUE);
4361 if (!the_entry) return NULL;
4362 gf_list_add(dm->cache_entries, the_entry);
4363 }
4364
4365 gf_cache_set_mime(the_entry, mime);
4366 gf_cache_set_range(the_entry, size, start_range, end_range);
4367 gf_cache_set_content(the_entry, data, (u32) size, clone_memory ? GF_TRUE : GF_FALSE);
4368 gf_cache_set_downtime(the_entry, download_time_ms);
4369
4370 gf_mx_v(dm->cache_mx );
4371 return the_entry;
4372 }
4373
4374 GF_EXPORT
gf_dm_force_headers(GF_DownloadManager * dm,const DownloadedCacheEntry entry,const char * headers)4375 GF_Err gf_dm_force_headers(GF_DownloadManager *dm, const DownloadedCacheEntry entry, const char *headers)
4376 {
4377 u32 i, count;
4378 Bool res;
4379 if (!entry) return GF_BAD_PARAM;
4380 gf_mx_p(dm->cache_mx);
4381 res = gf_cache_set_headers(entry, headers);
4382 count = gf_list_count(dm->sessions);
4383 for (i=0; i<count; i++) {
4384 GF_DownloadSession *sess = gf_list_get(dm->sessions, i);
4385 if (sess->cache_entry != entry) continue;
4386 gf_dm_sess_reload_cached_headers(sess);
4387 }
4388
4389 #ifdef GPAC_ENABLE_COVERAGE
4390 if (!count && gf_sys_is_cov_mode()) {
4391 gf_dm_sess_reload_cached_headers(NULL);
4392 gf_dm_refresh_cache_entry(NULL);
4393 gf_dm_session_thread(NULL);
4394 gf_user_credentials_save_digest(NULL, NULL, NULL);
4395 gf_user_credentials_ask_password(NULL, NULL);
4396 gf_user_credentials_register(NULL, NULL, NULL, NULL, GF_FALSE);
4397 gf_cache_are_headers_processed(NULL);
4398 gf_cache_get_start_range(NULL);
4399 gf_cache_get_end_range(NULL);
4400 gf_cache_get_content_length(NULL);
4401 gf_cache_set_end_range(NULL, 0);
4402 gf_cache_get_forced_headers(NULL);
4403 gf_cache_get_downtime(NULL);
4404 }
4405 #endif
4406
4407 gf_mx_v(dm->cache_mx);
4408 return res ? GF_OK : GF_BAD_PARAM;
4409 }
4410
4411 GF_EXPORT
gf_dm_sess_send(GF_DownloadSession * sess,u8 * data,u32 size)4412 GF_Err gf_dm_sess_send(GF_DownloadSession *sess, u8 *data, u32 size)
4413 {
4414 GF_Err e = GF_OK;
4415
4416 if (!data || !size) {
4417 if (sess->put_state) {
4418 sess->put_state = 2;
4419 sess->status = GF_NETIO_WAIT_FOR_REPLY;
4420 return GF_OK;
4421 }
4422 return GF_BAD_PARAM;
4423 }
4424
4425 #ifdef GPAC_HAS_SSL
4426 if (sess->ssl) {
4427 e = gf_ssl_write(sess->ssl, data, size);
4428 } else
4429 #endif
4430 e = gf_sk_send(sess->sock, data, size);
4431
4432 if (e==GF_IP_CONNECTION_CLOSED) {
4433 sess->status = GF_NETIO_STATE_ERROR;
4434 return e;
4435 }
4436 else if (e==GF_IP_SOCK_WOULD_BLOCK) {
4437 return gf_dm_sess_send(sess, data, size);
4438 }
4439 return e;
4440 }
4441
4442 #endif
4443