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: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5222>
This commit is contained in:
Daniel Morin 2023-08-22 09:21:17 -04:00 committed by GStreamer Marge Bot
parent f1ad885c9b
commit 13c5406747
3 changed files with 187 additions and 41 deletions

View file

@ -2997,6 +2997,9 @@ UTC times will be converted to nanoseconds since 1900.</doc>
<member name="ok" value="0" c:identifier="GST_RTSP_OK" glib:nick="ok"> <member name="ok" value="0" c:identifier="GST_RTSP_OK" glib:nick="ok">
<doc xml:space="preserve" filename="../subprojects/gst-plugins-base/gst-libs/gst/rtsp/gstrtspdefs.h">no error</doc> <doc xml:space="preserve" filename="../subprojects/gst-plugins-base/gst-libs/gst/rtsp/gstrtspdefs.h">no error</doc>
</member> </member>
<member name="ok_redirect" value="1" c:identifier="GST_RTSP_OK_REDIRECT" version="1.24" glib:nick="ok-redirect">
<doc xml:space="preserve" filename="../subprojects/gst-plugins-base/gst-libs/gst/rtsp/gstrtspdefs.h">RTSP request is successful, but was redirected.</doc>
</member>
<member name="error" value="-1" c:identifier="GST_RTSP_ERROR" glib:nick="error"> <member name="error" value="-1" c:identifier="GST_RTSP_ERROR" glib:nick="error">
<doc xml:space="preserve" filename="../subprojects/gst-plugins-base/gst-libs/gst/rtsp/gstrtspdefs.h">some unspecified error occurred</doc> <doc xml:space="preserve" filename="../subprojects/gst-plugins-base/gst-libs/gst/rtsp/gstrtspdefs.h">some unspecified error occurred</doc>
</member> </member>
@ -3094,6 +3097,12 @@ UTC times will be converted to nanoseconds since 1900.</doc>
</member> </member>
<member name="use_proxy" value="305" c:identifier="GST_RTSP_STS_USE_PROXY" glib:nick="use-proxy"> <member name="use_proxy" value="305" c:identifier="GST_RTSP_STS_USE_PROXY" glib:nick="use-proxy">
</member> </member>
<member name="redirect_temporarily" value="307" c:identifier="GST_RTSP_STS_REDIRECT_TEMPORARILY" version="1.24" glib:nick="redirect-temporarily">
<doc xml:space="preserve" filename="../subprojects/gst-plugins-base/gst-libs/gst/rtsp/gstrtspdefs.h">RTSP request is temporarily redirected</doc>
</member>
<member name="redirect_permanently" value="308" c:identifier="GST_RTSP_STS_REDIRECT_PERMANENTLY" version="1.24" glib:nick="redirect-permanently">
<doc xml:space="preserve" filename="../subprojects/gst-plugins-base/gst-libs/gst/rtsp/gstrtspdefs.h">RTSP request is permanently redirected</doc>
</member>
<member name="bad_request" value="400" c:identifier="GST_RTSP_STS_BAD_REQUEST" glib:nick="bad-request"> <member name="bad_request" value="400" c:identifier="GST_RTSP_STS_BAD_REQUEST" glib:nick="bad-request">
</member> </member>
<member name="unauthorized" value="401" c:identifier="GST_RTSP_STS_UNAUTHORIZED" glib:nick="unauthorized"> <member name="unauthorized" value="401" c:identifier="GST_RTSP_STS_UNAUTHORIZED" glib:nick="unauthorized">

View file

@ -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 */ /* transfer full */
static GCancellable * static GCancellable *
get_cancellable (GstRTSPConnection * conn) get_cancellable (GstRTSPConnection * conn)
@ -892,7 +912,7 @@ add_extra_headers (GstRTSPMessage * msg, GArray * headers)
} }
static GstRTSPResult static GstRTSPResult
setup_tunneling (GstRTSPConnection * conn, gint64 timeout, gchar * uri, setup_tunneling (GstRTSPConnection * conn, gint64 timeout, gchar ** req_uri,
GstRTSPMessage * response) GstRTSPMessage * response)
{ {
gint i; gint i;
@ -909,6 +929,10 @@ setup_tunneling (GstRTSPConnection * conn, gint64 timeout, gchar * uri,
gchar *request_uri = NULL; gchar *request_uri = NULL;
gchar *host = NULL; gchar *host = NULL;
GCancellable *cancellable; GCancellable *cancellable;
gchar *uri;
g_return_val_if_fail (req_uri != NULL, GST_RTSP_EINVAL);
uri = *req_uri;
url = conn->url; url = conn->url;
@ -952,10 +976,35 @@ setup_tunneling (GstRTSPConnection * conn, gint64 timeout, gchar * uri,
read_failed); read_failed);
conn->manual_http = old_http; conn->manual_http = old_http;
if (response->type != GST_RTSP_MESSAGE_HTTP_RESPONSE || if (response->type != GST_RTSP_MESSAGE_HTTP_RESPONSE)
response->type_data.response.code != GST_RTSP_STS_OK)
goto wrong_result; 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 && if (!conn->ignore_x_server_reply &&
gst_rtsp_message_get_header (response, GST_RTSP_HDR_X_SERVER_IP_ADDRESS, gst_rtsp_message_get_header (response, GST_RTSP_HDR_X_SERVER_IP_ADDRESS,
&value, 0) == GST_RTSP_OK) { &value, 0) == GST_RTSP_OK) {
@ -1102,15 +1151,17 @@ GstRTSPResult
gst_rtsp_connection_connect_with_response_usec (GstRTSPConnection * conn, gst_rtsp_connection_connect_with_response_usec (GstRTSPConnection * conn,
gint64 timeout, GstRTSPMessage * response) gint64 timeout, GstRTSPMessage * response)
{ {
GstRTSPResult res; GstRTSPResult res = GST_RTSP_OK;
GSocketConnection *connection; GSocketConnection *connection;
GSocket *socket; GSocket *socket;
GError *error = NULL; GError *error = NULL;
gchar *connection_uri, *request_uri, *remote_ip; gchar *connection_uri, *request_uri, *remote_ip, *query = NULL, *path = NULL;
GstClockTime to; GstClockTime to;
guint16 url_port; guint16 url_port;
GstRTSPUrl *url; GstRTSPUrl *url;
GCancellable *cancellable; 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 != NULL, GST_RTSP_EINVAL);
g_return_val_if_fail (conn->url != NULL, GST_RTSP_EINVAL); g_return_val_if_fail (conn->url != NULL, GST_RTSP_EINVAL);
@ -1130,6 +1181,7 @@ gst_rtsp_connection_connect_with_response_usec (GstRTSPConnection * conn,
connection_uri = gst_rtsp_url_get_request_uri (url); connection_uri = gst_rtsp_url_get_request_uri (url);
} }
while (res == GST_RTSP_OK) {
cancellable = get_cancellable (conn); cancellable = get_cancellable (conn);
if (conn->proxy_host) { if (conn->proxy_host) {
@ -1137,12 +1189,26 @@ gst_rtsp_connection_connect_with_response_usec (GstRTSPConnection * conn,
conn->proxy_host, conn->proxy_port, cancellable, &error); conn->proxy_host, conn->proxy_port, cancellable, &error);
request_uri = g_strdup (connection_uri); request_uri = g_strdup (connection_uri);
} else { } else {
if (uri != NULL) {
url_port = gst_uri_get_port (uri);
}
connection = g_socket_client_connect_to_uri (conn->client, connection = g_socket_client_connect_to_uri (conn->client,
connection_uri, url_port, cancellable, &error); connection_uri, url_port, cancellable, &error);
/* use the relative component of the uri for non-proxy connections */ 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, request_uri = g_strdup_printf ("%s%s%s", url->abspath,
url->query ? "?" : "", url->query ? url->query : ""); 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);
@ -1170,14 +1236,55 @@ gst_rtsp_connection_connect_with_response_usec (GstRTSPConnection * conn,
conn->control_stream = NULL; conn->control_stream = NULL;
if (conn->tunneled) { if (conn->tunneled) {
res = setup_tunneling (conn, timeout, request_uri, response); res = setup_tunneling (conn, timeout, &request_uri, response);
if (res != GST_RTSP_OK) 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; 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 (connection_uri);
g_free (request_uri); g_free (request_uri);
return GST_RTSP_OK; return res;
/* ERRORS */ /* ERRORS */
connect_failed: connect_failed:

View file

@ -65,6 +65,7 @@ G_STMT_START { \
/** /**
* GstRTSPResult: * GstRTSPResult:
* @GST_RTSP_OK: no error * @GST_RTSP_OK: no error
* @GST_RTSP_OK_REDIRECT: no error, but redirected
* @GST_RTSP_ERROR: some unspecified error occurred * @GST_RTSP_ERROR: some unspecified error occurred
* @GST_RTSP_EINVAL: invalid arguments were provided to a function * @GST_RTSP_EINVAL: invalid arguments were provided to a function
* @GST_RTSP_EINTR: an operation was canceled * @GST_RTSP_EINTR: an operation was canceled
@ -87,6 +88,16 @@ G_STMT_START { \
*/ */
typedef enum { typedef enum {
GST_RTSP_OK = 0, GST_RTSP_OK = 0,
/**
* GST_RTSP_OK_REDIRECT:
*
* RTSP request is successful, but was redirected.
*
* Since: 1.24
*/
GST_RTSP_OK_REDIRECT = 1,
/* errors */ /* errors */
GST_RTSP_ERROR = -1, GST_RTSP_ERROR = -1,
GST_RTSP_EINVAL = -2, GST_RTSP_EINVAL = -2,
@ -365,6 +376,25 @@ typedef enum {
GST_RTSP_STS_SEE_OTHER = 303, GST_RTSP_STS_SEE_OTHER = 303,
GST_RTSP_STS_NOT_MODIFIED = 304, GST_RTSP_STS_NOT_MODIFIED = 304,
GST_RTSP_STS_USE_PROXY = 305, 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_BAD_REQUEST = 400,
GST_RTSP_STS_UNAUTHORIZED = 401, GST_RTSP_STS_UNAUTHORIZED = 401,
GST_RTSP_STS_PAYMENT_REQUIRED = 402, GST_RTSP_STS_PAYMENT_REQUIRED = 402,