LCOV - code coverage report
Current view: top level - utils - downloader.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 1604 2188 73.3 %
Date: 2021-04-29 23:48:07 Functions: 81 88 92.0 %

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

Generated by: LCOV version 1.13