| /* | |
| * 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 | |