Coverage Report

Created: 2026-01-17 06:48

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