From 13c54067476ba307521c0aebd5acb3e596d777f3 Mon Sep 17 00:00:00 2001 From: Daniel Morin Date: Tue, 22 Aug 2023 09:21:17 -0400 Subject: [PATCH] rtspconnection: support redirect when using tunnel - Support HTTP redirect codes (301,302,307,308) on response to GET. "Location" field is extracted and used for following GET and POST. - Notify caller a redirect took place using return value - log source and destination url on redirect Part-of: --- girs/GstRtsp-1.0.gir | 9 + .../gst-libs/gst/rtsp/gstrtspconnection.c | 189 ++++++++++++++---- .../gst-libs/gst/rtsp/gstrtspdefs.h | 30 +++ 3 files changed, 187 insertions(+), 41 deletions(-) diff --git a/girs/GstRtsp-1.0.gir b/girs/GstRtsp-1.0.gir index daa63237a2..9bfcb58f3b 100644 --- a/girs/GstRtsp-1.0.gir +++ b/girs/GstRtsp-1.0.gir @@ -2997,6 +2997,9 @@ UTC times will be converted to nanoseconds since 1900. no error + + RTSP request is successful, but was redirected. + some unspecified error occurred @@ -3094,6 +3097,12 @@ UTC times will be converted to nanoseconds since 1900. + + RTSP request is temporarily redirected + + + RTSP request is permanently redirected + diff --git a/subprojects/gst-plugins-base/gst-libs/gst/rtsp/gstrtspconnection.c b/subprojects/gst-plugins-base/gst-libs/gst/rtsp/gstrtspconnection.c index bd5c5d750a..a822d956b9 100644 --- a/subprojects/gst-plugins-base/gst-libs/gst/rtsp/gstrtspconnection.c +++ b/subprojects/gst-plugins-base/gst-libs/gst/rtsp/gstrtspconnection.c @@ -362,6 +362,26 @@ socket_client_event (GSocketClient * client, GSocketClientEvent event, } } +static void +stream0_reset (GstRTSPConnection * conn) +{ + if (conn->stream0) { + g_object_unref (conn->stream0); + conn->stream0 = NULL; + conn->socket0 = NULL; + } + conn->input_stream = NULL; + conn->output_stream = NULL; + g_free (conn->remote_ip); + + conn->remote_ip = NULL; + conn->read_socket = NULL; + conn->write_socket = NULL; + conn->read_socket_used = FALSE; + conn->write_socket_used = FALSE; + conn->control_stream = NULL; +} + /* transfer full */ static GCancellable * get_cancellable (GstRTSPConnection * conn) @@ -892,7 +912,7 @@ add_extra_headers (GstRTSPMessage * msg, GArray * headers) } static GstRTSPResult -setup_tunneling (GstRTSPConnection * conn, gint64 timeout, gchar * uri, +setup_tunneling (GstRTSPConnection * conn, gint64 timeout, gchar ** req_uri, GstRTSPMessage * response) { gint i; @@ -909,6 +929,10 @@ setup_tunneling (GstRTSPConnection * conn, gint64 timeout, gchar * uri, gchar *request_uri = NULL; gchar *host = NULL; GCancellable *cancellable; + gchar *uri; + + g_return_val_if_fail (req_uri != NULL, GST_RTSP_EINVAL); + uri = *req_uri; url = conn->url; @@ -952,10 +976,35 @@ setup_tunneling (GstRTSPConnection * conn, gint64 timeout, gchar * uri, read_failed); conn->manual_http = old_http; - if (response->type != GST_RTSP_MESSAGE_HTTP_RESPONSE || - response->type_data.response.code != GST_RTSP_STS_OK) + if (response->type != GST_RTSP_MESSAGE_HTTP_RESPONSE) goto wrong_result; + switch (response->type_data.response.code) { + case GST_RTSP_STS_OK: + break; + case GST_RTSP_STS_MOVED_PERMANENTLY: + case GST_RTSP_STS_MOVE_TEMPORARILY: + case GST_RTSP_STS_REDIRECT_TEMPORARILY: + case GST_RTSP_STS_REDIRECT_PERMANENTLY: + { + gchar *location_val = NULL; + gst_rtsp_message_get_header (response, GST_RTSP_HDR_LOCATION, + &location_val, 0); + + if (location_val != NULL) { + GST_TRACE ("redirect (%d) to %s", + response->type_data.response.code, location_val); + g_free (uri); + uri = g_strdup (location_val); + *req_uri = uri; + res = GST_RTSP_OK_REDIRECT; + goto exit; + } + } + default: + goto wrong_result; + } + if (!conn->ignore_x_server_reply && gst_rtsp_message_get_header (response, GST_RTSP_HDR_X_SERVER_IP_ADDRESS, &value, 0) == GST_RTSP_OK) { @@ -1102,15 +1151,17 @@ GstRTSPResult gst_rtsp_connection_connect_with_response_usec (GstRTSPConnection * conn, gint64 timeout, GstRTSPMessage * response) { - GstRTSPResult res; + GstRTSPResult res = GST_RTSP_OK; GSocketConnection *connection; GSocket *socket; GError *error = NULL; - gchar *connection_uri, *request_uri, *remote_ip; + gchar *connection_uri, *request_uri, *remote_ip, *query = NULL, *path = NULL; GstClockTime to; guint16 url_port; GstRTSPUrl *url; GCancellable *cancellable; + guint redirect_cnt = 0; + GstUri *uri = NULL; g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL); g_return_val_if_fail (conn->url != NULL, GST_RTSP_EINVAL); @@ -1130,54 +1181,110 @@ gst_rtsp_connection_connect_with_response_usec (GstRTSPConnection * conn, connection_uri = gst_rtsp_url_get_request_uri (url); } - cancellable = get_cancellable (conn); + while (res == GST_RTSP_OK) { + cancellable = get_cancellable (conn); - if (conn->proxy_host) { - connection = g_socket_client_connect_to_host (conn->client, - conn->proxy_host, conn->proxy_port, cancellable, &error); - request_uri = g_strdup (connection_uri); - } else { - connection = g_socket_client_connect_to_uri (conn->client, - connection_uri, url_port, cancellable, &error); + if (conn->proxy_host) { + connection = g_socket_client_connect_to_host (conn->client, + conn->proxy_host, conn->proxy_port, cancellable, &error); + request_uri = g_strdup (connection_uri); + } else { + if (uri != NULL) { + url_port = gst_uri_get_port (uri); + } + connection = g_socket_client_connect_to_uri (conn->client, + connection_uri, url_port, cancellable, &error); - /* use the relative component of the uri for non-proxy connections */ - request_uri = g_strdup_printf ("%s%s%s", url->abspath, - url->query ? "?" : "", url->query ? url->query : ""); - } + if (uri == NULL) { + /* Use the relative component of the uri for non-proxy connections. + * Note: request_uri is not a complete URI, it only contain path + + * query.*/ + request_uri = g_strdup_printf ("%s%s%s", url->abspath, + url->query ? "?" : "", url->query ? url->query : ""); + } else { + path = gst_uri_get_path (uri); + query = gst_uri_get_query_string (uri); + request_uri = g_strdup_printf ("%s%s%s", + path, query ? "?" : "", query ? query : ""); + } + g_free (path); + g_free (query); + } - g_clear_object (&cancellable); + g_clear_object (&cancellable); - if (connection == NULL) - goto connect_failed; + if (connection == NULL) + goto connect_failed; - /* get remote address */ - socket = g_socket_connection_get_socket (connection); + /* get remote address */ + socket = g_socket_connection_get_socket (connection); - if (!collect_addresses (socket, &remote_ip, NULL, TRUE, &error)) - goto remote_address_failed; + if (!collect_addresses (socket, &remote_ip, NULL, TRUE, &error)) + goto remote_address_failed; - g_free (conn->remote_ip); - conn->remote_ip = remote_ip; - conn->stream0 = G_IO_STREAM (connection); - conn->socket0 = socket; - /* this is our read socket */ - conn->read_socket = conn->socket0; - conn->write_socket = conn->socket0; - conn->read_socket_used = FALSE; - conn->write_socket_used = FALSE; - conn->input_stream = g_io_stream_get_input_stream (conn->stream0); - conn->output_stream = g_io_stream_get_output_stream (conn->stream0); - conn->control_stream = NULL; + g_free (conn->remote_ip); + conn->remote_ip = remote_ip; + conn->stream0 = G_IO_STREAM (connection); + conn->socket0 = socket; + /* this is our read socket */ + conn->read_socket = conn->socket0; + conn->write_socket = conn->socket0; + conn->read_socket_used = FALSE; + conn->write_socket_used = FALSE; + conn->input_stream = g_io_stream_get_input_stream (conn->stream0); + conn->output_stream = g_io_stream_get_output_stream (conn->stream0); + conn->control_stream = NULL; - if (conn->tunneled) { - res = setup_tunneling (conn, timeout, request_uri, response); - if (res != GST_RTSP_OK) - goto tunneling_failed; + if (conn->tunneled) { + res = setup_tunneling (conn, timeout, &request_uri, response); + if (res != GST_RTSP_OK) { + if (res == GST_RTSP_OK_REDIRECT) { + if (conn->proxy_host) { + GST_TRACE ("redirect behind proxy is not supported"); + res = GST_RTSP_ERROR; + goto tunneling_failed; + } + + GST_LOG ("redirect from %s to %s.", connection_uri, request_uri); + + stream0_reset (conn); + connection_uri = request_uri; + gst_uri_unref (uri); + + uri = gst_uri_from_string (connection_uri); + if (uri == NULL) { + GST_TRACE ("failed to parse redirect uri"); + res = GST_RTSP_ERROR; + goto tunneling_failed; + } + + conn->url->abspath = gst_uri_get_path (uri); + conn->url->host = g_strdup (gst_uri_get_host (uri)); + conn->url->port = gst_uri_get_port (uri); + conn->url->query = gst_uri_get_query_string (uri); + res = GST_RTSP_OK; + + /* at most allow 5 redirect */ + if (redirect_cnt++ > 4) { + GST_TRACE ("redirect max reached"); + res = GST_RTSP_ERROR; + goto tunneling_failed; + } + } else { + goto tunneling_failed; + } + } else { + /* Caller must be informed */ + res = GST_RTSP_OK_REDIRECT; + } + } else { + break; + } } g_free (connection_uri); g_free (request_uri); - return GST_RTSP_OK; + return res; /* ERRORS */ connect_failed: diff --git a/subprojects/gst-plugins-base/gst-libs/gst/rtsp/gstrtspdefs.h b/subprojects/gst-plugins-base/gst-libs/gst/rtsp/gstrtspdefs.h index 8a028c381b..83adb74c51 100644 --- a/subprojects/gst-plugins-base/gst-libs/gst/rtsp/gstrtspdefs.h +++ b/subprojects/gst-plugins-base/gst-libs/gst/rtsp/gstrtspdefs.h @@ -65,6 +65,7 @@ G_STMT_START { \ /** * GstRTSPResult: * @GST_RTSP_OK: no error + * @GST_RTSP_OK_REDIRECT: no error, but redirected * @GST_RTSP_ERROR: some unspecified error occurred * @GST_RTSP_EINVAL: invalid arguments were provided to a function * @GST_RTSP_EINTR: an operation was canceled @@ -87,6 +88,16 @@ G_STMT_START { \ */ typedef enum { GST_RTSP_OK = 0, + + /** + * GST_RTSP_OK_REDIRECT: + * + * RTSP request is successful, but was redirected. + * + * Since: 1.24 + */ + GST_RTSP_OK_REDIRECT = 1, + /* errors */ GST_RTSP_ERROR = -1, GST_RTSP_EINVAL = -2, @@ -365,6 +376,25 @@ typedef enum { GST_RTSP_STS_SEE_OTHER = 303, GST_RTSP_STS_NOT_MODIFIED = 304, GST_RTSP_STS_USE_PROXY = 305, + + /** + * GST_RTSP_STS_REDIRECT_TEMPORARILY: + * + * RTSP request is temporarily redirected + * + * Since: 1.24 + */ + GST_RTSP_STS_REDIRECT_TEMPORARILY = 307, + + /** + * GST_RTSP_STS_REDIRECT_PERMANENTLY: + * + * RTSP request is permanently redirected + * + * Since: 1.24 + */ + GST_RTSP_STS_REDIRECT_PERMANENTLY = 308, + GST_RTSP_STS_BAD_REQUEST = 400, GST_RTSP_STS_UNAUTHORIZED = 401, GST_RTSP_STS_PAYMENT_REQUIRED = 402,