net: webrtc/webrtchttp: Fix canceller usage

Commit 08b6251a added the check to ensure only one canceller at a time for net/webrtc.

In `whipsink` and since `whipwebrtcsink` picked up the same implementation, there exists a
bug around the use of canceller. `whipsink` calls `wait_async` while passing the canceller
as an argument. The path `send_offer -> do_post -> parse_endpoint_response` results in the
canceller being replaced in each subsequent call to `wait_async`. Since `wait_async` call
does not ensure one canceller, with the async call the use of canceller/abort was subtly
broken. Similarly, for `whepsrc`.

We really don't need to use `wait_async` inside `do_post` for any `await` calls. If the
root future viz. `do_post` with `wait_async` is aborted, the child futures will be taken
care of.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1290>
This commit is contained in:
Sanchayan Maity 2023-07-31 11:23:32 +05:30
parent d6616fed3f
commit 5b60ecbb18
4 changed files with 116 additions and 197 deletions

View file

@ -124,14 +124,12 @@ impl Signaller {
async fn do_post(&self, offer: gst_webrtc::WebRTCSessionDescription, webrtcbin: &gst::Element) { async fn do_post(&self, offer: gst_webrtc::WebRTCSessionDescription, webrtcbin: &gst::Element) {
let auth_token; let auth_token;
let endpoint; let endpoint;
let timeout;
{ {
let settings = self.settings.lock().unwrap(); let settings = self.settings.lock().unwrap();
endpoint = endpoint =
reqwest::Url::parse(settings.whip_endpoint.as_ref().unwrap().as_str()).unwrap(); reqwest::Url::parse(settings.whip_endpoint.as_ref().unwrap().as_str()).unwrap();
auth_token = settings.auth_token.clone(); auth_token = settings.auth_token.clone();
timeout = settings.timeout;
drop(settings); drop(settings);
} }
@ -174,33 +172,19 @@ impl Signaller {
); );
} }
let res = wait_async( let res = client
&self.canceller, .request(reqwest::Method::POST, endpoint.clone())
client .headers(headermap)
.request(reqwest::Method::POST, endpoint.clone()) .body(body)
.headers(headermap) .send()
.body(body) .await;
.send(),
timeout,
)
.await;
match res { match res {
Ok(r) => match r { Ok(resp) => {
Ok(resp) => { self.parse_endpoint_response(offer, resp, redirects, webrtcbin)
if let Err(e) = wait_async(
&self.canceller,
self.parse_endpoint_response(offer, resp, redirects, webrtcbin),
timeout,
)
.await .await
{ }
self.handle_future_error(e); Err(err) => self.raise_error(err.to_string()),
}
}
Err(err) => self.raise_error(err.to_string()),
},
Err(err) => self.handle_future_error(err),
} }
} }
@ -214,7 +198,6 @@ impl Signaller {
gst::debug!(CAT, imp: self, "Parsing endpoint response"); gst::debug!(CAT, imp: self, "Parsing endpoint response");
let endpoint; let endpoint;
let timeout;
let use_link_headers; let use_link_headers;
{ {
@ -222,7 +205,6 @@ impl Signaller {
endpoint = endpoint =
reqwest::Url::parse(settings.whip_endpoint.as_ref().unwrap().as_str()).unwrap(); reqwest::Url::parse(settings.whip_endpoint.as_ref().unwrap().as_str()).unwrap();
use_link_headers = settings.use_link_headers; use_link_headers = settings.use_link_headers;
timeout = settings.timeout;
drop(settings); drop(settings);
} }
@ -288,26 +270,21 @@ impl Signaller {
drop(state); drop(state);
} }
match wait_async(&self.canceller, resp.bytes(), timeout).await { match resp.bytes().await {
Ok(res) => match res { Ok(ans_bytes) => match gst_sdp::SDPMessage::parse_buffer(&ans_bytes) {
Ok(ans_bytes) => match gst_sdp::SDPMessage::parse_buffer(&ans_bytes) { Ok(ans_sdp) => {
Ok(ans_sdp) => { let answer = gst_webrtc::WebRTCSessionDescription::new(
let answer = gst_webrtc::WebRTCSessionDescription::new( gst_webrtc::WebRTCSDPType::Answer,
gst_webrtc::WebRTCSDPType::Answer, ans_sdp,
ans_sdp, );
); self.obj()
self.obj().emit_by_name::<()>( .emit_by_name::<()>("session-description", &[&"unique", &answer]);
"session-description", }
&[&"unique", &answer], Err(err) => {
); self.raise_error(format!("Could not parse answer SDP: {err}"));
} }
Err(err) => {
self.raise_error(format!("Could not parse answer SDP: {err}"));
}
},
Err(err) => self.raise_error(err.to_string()),
}, },
Err(err) => self.handle_future_error(err), Err(err) => self.raise_error(err.to_string()),
} }
} }
@ -347,12 +324,7 @@ impl Signaller {
redirect_url.as_str() redirect_url.as_str()
); );
if let Err(err) = self.do_post(offer, webrtcbin).await
wait_async(&self.canceller, self.do_post(offer, webrtcbin), timeout)
.await
{
self.handle_future_error(err);
}
} }
Err(e) => self.raise_error(e.to_string()), Err(e) => self.raise_error(e.to_string()),
} }
@ -362,16 +334,14 @@ impl Signaller {
} }
s => { s => {
match wait_async(&self.canceller, resp.bytes(), timeout).await { match resp.bytes().await {
Ok(r) => { Ok(r) => {
let res = r let res = r.escape_ascii().to_string();
.map(|x| x.escape_ascii().to_string())
.unwrap_or_else(|_| "(no further details)".to_string());
// FIXME: Check and handle 'Retry-After' header in case of server error // FIXME: Check and handle 'Retry-After' header in case of server error
self.raise_error(format!("Unexpected response: {} - {}", s.as_str(), res)); self.raise_error(format!("Unexpected response: {} - {}", s.as_str(), res));
} }
Err(err) => self.handle_future_error(err), Err(err) => self.raise_error(err.to_string()),
} }
} }
} }

View file

@ -35,6 +35,12 @@ where
let (abort_handle, abort_registration) = future::AbortHandle::new_pair(); let (abort_handle, abort_registration) = future::AbortHandle::new_pair();
{ {
let mut canceller_guard = canceller.lock().unwrap(); let mut canceller_guard = canceller.lock().unwrap();
if canceller_guard.is_some() {
return Err(WaitError::FutureError(gst::error_msg!(
gst::ResourceError::Failed,
["Old Canceller should not exist"]
)));
}
canceller_guard.replace(abort_handle); canceller_guard.replace(abort_handle);
drop(canceller_guard); drop(canceller_guard);
} }

View file

@ -626,7 +626,6 @@ impl WhepSrc {
redirects: u8, redirects: u8,
) { ) {
let endpoint; let endpoint;
let timeout;
let use_link_headers; let use_link_headers;
{ {
@ -634,7 +633,6 @@ impl WhepSrc {
endpoint = endpoint =
reqwest::Url::parse(settings.whep_endpoint.as_ref().unwrap().as_str()).unwrap(); reqwest::Url::parse(settings.whep_endpoint.as_ref().unwrap().as_str()).unwrap();
use_link_headers = settings.use_link_headers; use_link_headers = settings.use_link_headers;
timeout = settings.timeout;
drop(settings); drop(settings);
} }
@ -692,29 +690,26 @@ impl WhepSrc {
} }
}; };
match wait_async(&self.canceller, resp.bytes(), timeout).await { match resp.bytes().await {
Ok(res) => match res { Ok(ans_bytes) => {
Ok(ans_bytes) => { let mut state = self.state.lock().unwrap();
let mut state = self.state.lock().unwrap(); *state = match *state {
*state = match *state { State::Post { redirects: _r } => State::Running {
State::Post { redirects: _r } => State::Running { whep_resource: url.to_string(),
whep_resource: url.to_string(), },
}, _ => {
_ => { self.raise_error(
self.raise_error( gst::ResourceError::Failed,
gst::ResourceError::Failed, "Expected to be in POST state".to_string(),
"Expected to be in POST state".to_string(), );
); return;
return; }
} };
}; drop(state);
drop(state);
self.sdp_message_parse(ans_bytes) self.sdp_message_parse(ans_bytes)
} }
Err(err) => self.raise_error(gst::ResourceError::Failed, err.to_string()), Err(err) => self.raise_error(gst::ResourceError::Failed, err.to_string()),
},
Err(err) => self.handle_future_error(err),
} }
} }
@ -753,11 +748,7 @@ impl WhepSrc {
redirect_url.as_str() redirect_url.as_str()
); );
if let Err(err) = self.do_post(sess_desc).await
wait_async(&self.canceller, self.do_post(sess_desc), timeout).await
{
self.handle_future_error(err);
}
} }
Err(e) => self.raise_error(gst::ResourceError::Failed, e.to_string()), Err(e) => self.raise_error(gst::ResourceError::Failed, e.to_string()),
} }
@ -770,11 +761,9 @@ impl WhepSrc {
} }
s => { s => {
match wait_async(&self.canceller, resp.bytes(), timeout).await { match resp.bytes().await {
Ok(r) => { Ok(r) => {
let res = r let res = r.escape_ascii().to_string();
.map(|x| x.escape_ascii().to_string())
.unwrap_or_else(|_| "(no further details)".to_string());
// FIXME: Check and handle 'Retry-After' header in case of server error // FIXME: Check and handle 'Retry-After' header in case of server error
self.raise_error( self.raise_error(
@ -782,7 +771,7 @@ impl WhepSrc {
format!("Unexpected response: {} - {}", s.as_str(), res), format!("Unexpected response: {} - {}", s.as_str(), res),
); );
} }
Err(err) => self.handle_future_error(err), Err(err) => self.raise_error(gst::ResourceError::Failed, err.to_string()),
} }
} }
} }
@ -944,14 +933,12 @@ impl WhepSrc {
async fn do_post(&self, offer: WebRTCSessionDescription) { async fn do_post(&self, offer: WebRTCSessionDescription) {
let auth_token; let auth_token;
let endpoint; let endpoint;
let timeout;
{ {
let settings = self.settings.lock().unwrap(); let settings = self.settings.lock().unwrap();
endpoint = endpoint =
reqwest::Url::parse(settings.whep_endpoint.as_ref().unwrap().as_str()).unwrap(); reqwest::Url::parse(settings.whep_endpoint.as_ref().unwrap().as_str()).unwrap();
auth_token = settings.auth_token.clone(); auth_token = settings.auth_token.clone();
timeout = settings.timeout;
drop(settings); drop(settings);
} }
@ -982,51 +969,37 @@ impl WhepSrc {
endpoint.as_str() endpoint.as_str()
); );
let res = wait_async( let resp = self
&self.canceller, .client
self.client .request(reqwest::Method::POST, endpoint.clone())
.request(reqwest::Method::POST, endpoint.clone()) .headers(headermap)
.headers(headermap) .body(body)
.body(body) .send()
.send(), .await;
timeout,
)
.await;
match res { match resp {
Ok(resp) => match resp { Ok(r) => {
Ok(r) => { #[allow(unused_mut)]
#[allow(unused_mut)] let mut redirects;
let mut redirects;
{ {
let state = self.state.lock().unwrap(); let state = self.state.lock().unwrap();
redirects = match *state { redirects = match *state {
State::Post { redirects } => redirects, State::Post { redirects } => redirects,
_ => { _ => {
self.raise_error( self.raise_error(
gst::ResourceError::Failed, gst::ResourceError::Failed,
"Trying to do POST in unexpected state".to_string(), "Trying to do POST in unexpected state".to_string(),
); );
return; return;
} }
}; };
drop(state); drop(state);
}
if let Err(e) = wait_async(
&self.canceller,
self.parse_endpoint_response(offer, r, redirects),
timeout,
)
.await
{
self.handle_future_error(e);
}
} }
Err(err) => self.raise_error(gst::ResourceError::Failed, err.to_string()),
}, self.parse_endpoint_response(offer, r, redirects).await
Err(err) => self.handle_future_error(err), }
Err(err) => self.raise_error(gst::ResourceError::Failed, err.to_string()),
} }
} }

View file

@ -565,14 +565,12 @@ impl WhipSink {
async fn do_post(&self, offer: gst_webrtc::WebRTCSessionDescription) { async fn do_post(&self, offer: gst_webrtc::WebRTCSessionDescription) {
let auth_token; let auth_token;
let endpoint; let endpoint;
let timeout;
{ {
let settings = self.settings.lock().unwrap(); let settings = self.settings.lock().unwrap();
endpoint = endpoint =
reqwest::Url::parse(settings.whip_endpoint.as_ref().unwrap().as_str()).unwrap(); reqwest::Url::parse(settings.whip_endpoint.as_ref().unwrap().as_str()).unwrap();
auth_token = settings.auth_token.clone(); auth_token = settings.auth_token.clone();
timeout = settings.timeout;
drop(settings); drop(settings);
} }
@ -618,33 +616,16 @@ impl WhipSink {
); );
} }
let res = wait_async( let res = client
&self.canceller, .request(reqwest::Method::POST, endpoint.clone())
client .headers(headermap)
.request(reqwest::Method::POST, endpoint.clone()) .body(body)
.headers(headermap) .send()
.body(body) .await;
.send(),
timeout,
)
.await;
match res { match res {
Ok(r) => match r { Ok(resp) => self.parse_endpoint_response(offer, resp, redirects).await,
Ok(resp) => { Err(err) => self.raise_error(gst::ResourceError::Failed, err.to_string()),
if let Err(e) = wait_async(
&self.canceller,
self.parse_endpoint_response(offer, resp, redirects),
timeout,
)
.await
{
self.handle_future_error(e);
}
}
Err(err) => self.raise_error(gst::ResourceError::Failed, err.to_string()),
},
Err(err) => self.handle_future_error(err),
} }
} }
@ -655,7 +636,6 @@ impl WhipSink {
redirects: u8, redirects: u8,
) { ) {
let endpoint; let endpoint;
let timeout;
let use_link_headers; let use_link_headers;
{ {
@ -663,7 +643,6 @@ impl WhipSink {
endpoint = endpoint =
reqwest::Url::parse(settings.whip_endpoint.as_ref().unwrap().as_str()).unwrap(); reqwest::Url::parse(settings.whip_endpoint.as_ref().unwrap().as_str()).unwrap();
use_link_headers = settings.use_link_headers; use_link_headers = settings.use_link_headers;
timeout = settings.timeout;
drop(settings); drop(settings);
} }
@ -737,29 +716,26 @@ impl WhipSink {
drop(state); drop(state);
} }
match wait_async(&self.canceller, resp.bytes(), timeout).await { match resp.bytes().await {
Ok(res) => match res { Ok(ans_bytes) => match sdp_message::SDPMessage::parse_buffer(&ans_bytes) {
Ok(ans_bytes) => match sdp_message::SDPMessage::parse_buffer(&ans_bytes) { Ok(ans_sdp) => {
Ok(ans_sdp) => { let answer = gst_webrtc::WebRTCSessionDescription::new(
let answer = gst_webrtc::WebRTCSessionDescription::new( gst_webrtc::WebRTCSDPType::Answer,
gst_webrtc::WebRTCSDPType::Answer, ans_sdp,
ans_sdp, );
); self.webrtcbin.emit_by_name::<()>(
self.webrtcbin.emit_by_name::<()>( "set-remote-description",
"set-remote-description", &[&answer, &None::<gst::Promise>],
&[&answer, &None::<gst::Promise>], );
); }
} Err(err) => {
Err(err) => { self.raise_error(
self.raise_error( gst::ResourceError::Failed,
gst::ResourceError::Failed, format!("Could not parse answer SDP: {err}"),
format!("Could not parse answer SDP: {err}"), );
); }
}
},
Err(err) => self.raise_error(gst::ResourceError::Failed, err.to_string()),
}, },
Err(err) => self.handle_future_error(err), Err(err) => self.raise_error(gst::ResourceError::Failed, err.to_string()),
} }
} }
@ -798,11 +774,7 @@ impl WhipSink {
redirect_url.as_str() redirect_url.as_str()
); );
if let Err(err) = self.do_post(offer).await
wait_async(&self.canceller, self.do_post(offer), timeout).await
{
self.handle_future_error(err);
}
} }
Err(e) => self.raise_error(gst::ResourceError::Failed, e.to_string()), Err(e) => self.raise_error(gst::ResourceError::Failed, e.to_string()),
} }
@ -815,11 +787,9 @@ impl WhipSink {
} }
s => { s => {
match wait_async(&self.canceller, resp.bytes(), timeout).await { match resp.bytes().await {
Ok(r) => { Ok(r) => {
let res = r let res = r.escape_ascii().to_string();
.map(|x| x.escape_ascii().to_string())
.unwrap_or_else(|_| "(no further details)".to_string());
// FIXME: Check and handle 'Retry-After' header in case of server error // FIXME: Check and handle 'Retry-After' header in case of server error
self.raise_error( self.raise_error(
@ -827,7 +797,7 @@ impl WhipSink {
format!("Unexpected response: {} - {}", s.as_str(), res), format!("Unexpected response: {} - {}", s.as_str(), res),
); );
} }
Err(err) => self.handle_future_error(err), Err(err) => self.raise_error(gst::ResourceError::Failed, err.to_string()),
} }
} }
} }