Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add option to follow 301 and 302 redirection #93

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 51 additions & 3 deletions src/client/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -497,14 +497,15 @@ fn keepalive_interval(session: &SessionHeader) -> std::time::Duration {
/// Options which must be known right as a session is created.
///
/// Decisions which can be deferred are in [`SetupOptions`] or [`PlayOptions`] instead.
#[derive(Default)]
#[derive(Default, Clone)]
pub struct SessionOptions {
creds: Option<Credentials>,
user_agent: Option<Box<str>>,
session_group: Option<Arc<SessionGroup>>,
teardown: TeardownPolicy,
unassigned_channel_data: UnassignedChannelDataPolicy,
session_id: SessionIdPolicy,
max_redirect: u8,
}

/// Policy for handling data received on unassigned RTSP interleaved channels.
Expand Down Expand Up @@ -713,6 +714,11 @@ impl SessionOptions {
self.session_id = policy;
self
}

pub fn max_redirect(mut self, max_redirect: u8) -> Self {
self.max_redirect = max_redirect;
self
}
}

/// Per-stream options decided for `SETUP` time, for future expansion.
Expand Down Expand Up @@ -1352,6 +1358,33 @@ impl RtspConnection {
}),
};
continue;
} else if resp.status() == rtsp_types::StatusCode::Found
|| resp.status() == rtsp_types::StatusCode::MovedPermanently
{
let location = match resp.header(&rtsp_types::headers::LOCATION) {
None => bail!(ErrorInt::RtspResponseError {
conn_ctx: *self.inner.ctx(),
msg_ctx,
method: req.method().clone(),
cseq,
status: resp.status(),
description: "Redirect without Location header".into(),
}),
Some(h) => h,
};
let location = location.as_str();
let location = match location.parse() {
Ok(l) => l,
Err(e) => bail!(ErrorInt::RtspResponseError {
conn_ctx: *self.inner.ctx(),
msg_ctx,
method: req.method().clone(),
cseq,
status: resp.status(),
description: format!("Can't parse Location header: {e}"),
}),
};
bail!(ErrorInt::RtspRedirection(location));
} else if !resp.status().is_success() {
bail!(ErrorInt::RtspResponseError {
conn_ctx: *self.inner.ctx(),
Expand Down Expand Up @@ -1487,8 +1520,23 @@ impl Session<Described> {
///
/// Expects to be called from a tokio runtime.
pub async fn describe(url: Url, options: SessionOptions) -> Result<Self, Error> {
let conn = RtspConnection::connect(&url).await?;
Self::describe_with_conn(conn, options, url).await
let mut url = url;
let mut redirect = 0u8;
loop {
let conn = RtspConnection::connect(&url).await?;
let result = Self::describe_with_conn(conn, options.clone(), url).await;
if let Err(Error(e)) = &result {
if let ErrorInt::RtspRedirection(ref location) = e.as_ref() {
if options.max_redirect > redirect {
redirect += 1;
url = location.clone();
continue;
}
bail!(ErrorInt::RtspTooManyRedirects);
}
}
return result;
}
}

async fn describe_with_conn(
Expand Down
7 changes: 7 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use std::{fmt::Display, sync::Arc};
use crate::{ConnectionContext, PacketContext, RtspMessageContext, StreamContext, WallTime};
use bytes::Bytes;
use thiserror::Error;
use url::Url;

/// An opaque `std::error::Error + Send + Sync + 'static` implementation.
///
Expand Down Expand Up @@ -49,6 +50,12 @@ pub(crate) enum ErrorInt {
#[error("Invalid argument: {0}")]
InvalidArgument(String),

#[error("Redirect to: {0}")]
RtspRedirection(Url),

#[error("Too many redirects")]
RtspTooManyRedirects,

/// Unparseable or unexpected RTSP message.
#[error("RTSP framing error: {description}\n\nconn: {conn_ctx}\nmsg: {msg_ctx}")]
RtspFramingError {
Expand Down
Loading