blob: 4a97b41257f6dd891fa6fd2ff901b0e1d04f42c8 [file] [edit]
/*
* GPAC Multimedia Framework
*
* Authors: Jean Le Feuvre
* Copyright (c) Telecom ParisTech 2005-2025
* All rights reserved
*
* This file is part of GPAC / downloader sub-project
*
* GPAC is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* GPAC is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; see the file COPYING. If not, write to
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
#include "downloader.h"
#ifndef GPAC_DISABLE_NETWORK
#ifdef GPAC_HTTPMUX
//detach session from parent h2 / h3 session - the session mutex SHALL be grabbed before calling this
void hmux_detach_session(GF_HMUX_Session *hmux_sess, GF_DownloadSession *sess)
{
if (!hmux_sess || !sess) return;
gf_assert(sess->hmux_sess == hmux_sess);
gf_assert(sess->mx);
gf_list_del_item(hmux_sess->sessions, sess);
if (!gf_list_count(hmux_sess->sessions)) {
if (hmux_sess->close)
hmux_sess->close(hmux_sess);
#ifdef GPAC_HAS_SSL
if (sess->ssl) {
GF_LOG(GF_LOG_DEBUG, GF_LOG_HTTP, ("[Downloader] shut down SSL context\n"));
SSL_shutdown(sess->ssl);
SSL_free(sess->ssl);
sess->ssl = NULL;
}
#endif
dm_sess_sk_del(sess);
if (hmux_sess->destroy)
hmux_sess->destroy(hmux_sess);
gf_list_del(hmux_sess->sessions);
gf_mx_v(hmux_sess->mx);
gf_mx_del(sess->mx);
gf_free(hmux_sess);
} else {
GF_DownloadSession *asess = gf_list_get(hmux_sess->sessions, 0);
gf_assert(asess->hmux_sess == hmux_sess);
hmux_sess->net_sess = asess;
//swap async buf if any to new active session
if (sess->async_buf_alloc) {
gf_assert(!asess->async_buf);
asess->async_buf = sess->async_buf;
asess->async_buf_alloc = sess->async_buf_alloc;
asess->async_buf_size = sess->async_buf_size;
sess->async_buf_alloc = sess->async_buf_size = 0;
sess->async_buf = NULL;
}
sess->sock = NULL;
#ifdef GPAC_HAS_SSL
sess->ssl = NULL;
#endif
gf_mx_v(sess->mx);
}
if (sess->hmux_buf.data) {
gf_free(sess->hmux_buf.data);
memset(&sess->hmux_buf, 0, sizeof(hmux_reagg_buffer));
}
sess->hmux_sess = NULL;
sess->mx = NULL;
}
GF_DownloadSession *hmux_get_session(void *user_data, s64 stream_id, Bool can_reassign)
{
u32 i, nb_sess;
GF_DownloadSession *first_not_assigned = NULL;
GF_HMUX_Session *hmux_sess = (GF_HMUX_Session *)user_data;
nb_sess = gf_list_count(hmux_sess->sessions);
for (i=0;i<nb_sess; i++) {
GF_DownloadSession *s = gf_list_get(hmux_sess->sessions, i);
if (s->hmux_stream_id == stream_id)
return s;
if (s->server_mode && (s->hmux_stream_id<0) && !first_not_assigned) {
first_not_assigned = s;
}
}
if (can_reassign && first_not_assigned) {
first_not_assigned->hmux_stream_id = stream_id;
GF_LOG(GF_LOG_DEBUG, GF_LOG_HTTP, ("[%s] reassigning old server session to new stream %d\n", hmux_sess->net_sess->log_name, stream_id));
first_not_assigned->status = GF_NETIO_CONNECTED;
first_not_assigned->total_size = first_not_assigned->bytes_done = 0;
first_not_assigned->hmux_data_done = 0;
if (first_not_assigned->remote_path) gf_free(first_not_assigned->remote_path);
first_not_assigned->remote_path = NULL;
//reset internal reaggregation buffer
first_not_assigned->hmux_buf.size = 0;
gf_dm_sess_clear_headers(first_not_assigned);
return first_not_assigned;
}
return NULL;
}
void hmux_flush_internal_data(GF_DownloadSession *sess, Bool store_in_init)
{
gf_dm_data_received(sess, (u8 *) sess->hmux_buf.data, sess->hmux_buf.size, store_in_init, NULL, NULL);
sess->hmux_buf.size = 0;
}
void hmux_fetch_data(GF_DownloadSession *sess, u8 *obuffer, u32 size, u32 *nb_bytes)
{
u32 copy, nb_b_pck;
u8 *data;
*nb_bytes = 0;
if (!sess->hmux_buf.size) {
if (sess->hmux_is_eos) {
if (!sess->total_size) {
sess->total_size = sess->bytes_done;
gf_dm_data_received(sess, obuffer, 0, GF_FALSE, NULL, NULL);
}
if (sess->status==GF_NETIO_DATA_EXCHANGE)
sess->status = GF_NETIO_DATA_TRANSFERED;
}
return;
}
gf_assert(sess->hmux_buf.offset<=sess->hmux_buf.size);
nb_b_pck = sess->hmux_buf.size - sess->hmux_buf.offset;
if (nb_b_pck > size)
copy = size;
else
copy = nb_b_pck;
data = sess->hmux_buf.data + sess->hmux_buf.offset;
memcpy(obuffer, data, copy);
*nb_bytes = copy;
//signal we received data (for event triggering / user callbacks)
gf_dm_data_received(sess, (u8 *) data, copy, GF_FALSE, NULL, NULL);
if (copy < nb_b_pck) {
sess->hmux_buf.offset += copy;
} else {
sess->hmux_buf.size = sess->hmux_buf.offset = 0;
}
gf_assert(sess->hmux_buf.offset<=sess->hmux_buf.size);
}
GF_Err hmux_send_payload(GF_DownloadSession *sess, u8 *data, u32 size)
{
GF_Err e = GF_OK;
if (sess->hmux_send_data) {
e = gf_sk_probe(sess->sock);
if (e) return e;
return GF_SERVICE_ERROR;
}
if (sess->hmux_stream_id<0)
return GF_URL_REMOVED;
gf_mx_p(sess->mx);
GF_LOG(GF_LOG_DEBUG, GF_LOG_HTTP, ("[%s] Sending %d bytes on stream_id "LLD"\n", sess->log_name, size, sess->hmux_stream_id));
assert(sess->hmux_send_data==NULL);
sess->hmux_send_data = data;
sess->hmux_send_data_len = size;
if (sess->hmux_data_paused) {
e = sess->hmux_sess->resume(sess);
if (e) {
gf_mx_v(sess->mx);
return e;
}
}
//if no data, signal end of stream, otherwise regular send
if (!data || !size) {
if (sess->local_buf_len) {
gf_mx_v(sess->mx);
if (sess->hmux_is_eos) {
return GF_IP_NETWORK_EMPTY;
}
sess->hmux_is_eos = 1;
sess->hmux_sess->write(sess);
return GF_OK;
}
sess->hmux_is_eos = 1;
sess->hmux_sess->write(sess);
//stream_id is not yet 0 in case of PUT/PUSH, stream is closed once we get reply from server
} else {
sess->hmux_is_eos = 0;
//send the data
e = sess->hmux_sess->send_pending_data(sess);
if (e && (e!=GF_IP_NETWORK_EMPTY)) {
gf_mx_v(sess->mx);
return e;
}
}
gf_mx_v(sess->mx);
if (!data || !size) {
if (sess->put_state) {
//we are done sending
if (!sess->local_buf_len) {
sess->put_state = 2;
sess->status = GF_NETIO_WAIT_FOR_REPLY;
}
return GF_OK;
}
}
if ((sess->hmux_stream_id<0) && sess->hmux_send_data)
return GF_URL_REMOVED;
return GF_OK;
}
#endif //GPAC_HTTPMUX
void gf_dm_sess_close_hmux(GF_DownloadSession *sess)
{
#ifdef GPAC_HTTPMUX
if (sess->hmux_sess)
sess->hmux_sess->close_session(sess);
#endif
}
GF_DownloadSession *gf_dm_sess_new_subsession(GF_DownloadSession *sess, s64 stream_id, void *usr_cbk, GF_Err *e)
{
#ifdef GPAC_HTTPMUX
GF_DownloadSession *sub_sess;
if (!sess->hmux_sess) return NULL;
gf_mx_p(sess->mx);
sub_sess = gf_dm_sess_new_internal(NULL, NULL, 0, sess->user_proc, usr_cbk, sess->sock, GF_TRUE, e);
if (!sub_sess) {
gf_mx_v(sess->mx);
return NULL;
}
gf_list_add(sess->hmux_sess->sessions, sub_sess);
#ifdef GPAC_HAS_SSL
sub_sess->ssl = sess->ssl;
#endif
sub_sess->hmux_sess = sess->hmux_sess;
if (sub_sess->mx) gf_mx_del(sub_sess->mx);
sub_sess->mx = sess->hmux_sess->mx;
sub_sess->hmux_stream_id = stream_id;
sub_sess->status = GF_NETIO_CONNECTED;
sub_sess->hmux_sess->setup_session(sub_sess, GF_FALSE);
sub_sess->flags = sess->flags;
gf_mx_v(sess->mx);
return sub_sess;
#else
return NULL;
#endif //GPAC_HTTPMUX
}
#endif //GPAC_DISABLE_NETWORK