mirror of
https://github.com/tokio-rs/axum.git
synced 2026-08-25 00:00:23 +02:00
Work around for http2 hang with Or (#199)
This is a nasty hack that works around https://github.com/hyperium/hyper/issues/2621. Fixes https://github.com/tokio-rs/axum/issues/191
This commit is contained in:
+38
-1
@@ -547,7 +547,11 @@ where
|
||||
|
||||
fn call(&mut self, request: Request<B>) -> Self::Future {
|
||||
let mut res = Response::new(crate::body::empty());
|
||||
res.extensions_mut().insert(FromEmptyRouter { request });
|
||||
|
||||
if request.extensions().get::<OrDepth>().is_some() {
|
||||
res.extensions_mut().insert(FromEmptyRouter { request });
|
||||
}
|
||||
|
||||
*res.status_mut() = self.status;
|
||||
EmptyRouterFuture {
|
||||
future: futures_util::future::ok(res),
|
||||
@@ -565,6 +569,39 @@ struct FromEmptyRouter<B> {
|
||||
request: Request<B>,
|
||||
}
|
||||
|
||||
/// We need to track whether we're inside an `Or` or not, and only if we then
|
||||
/// should we save the request into the response extensions.
|
||||
///
|
||||
/// This is to work around https://github.com/hyperium/hyper/issues/2621.
|
||||
///
|
||||
/// Since ours can be nested we have to track the depth to know when we're
|
||||
/// leaving the top most `Or`.
|
||||
///
|
||||
/// Hopefully when https://github.com/hyperium/hyper/issues/2621 is resolved we
|
||||
/// can remove this nasty hack.
|
||||
#[derive(Debug)]
|
||||
struct OrDepth(usize);
|
||||
|
||||
impl OrDepth {
|
||||
fn new() -> Self {
|
||||
Self(1)
|
||||
}
|
||||
|
||||
fn increment(&mut self) {
|
||||
self.0 += 1;
|
||||
}
|
||||
|
||||
fn decrement(&mut self) {
|
||||
self.0 -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<usize> for &mut OrDepth {
|
||||
fn eq(&self, other: &usize) -> bool {
|
||||
self.0 == *other
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct PathPattern(Arc<Inner>);
|
||||
|
||||
|
||||
+21
-3
@@ -1,6 +1,6 @@
|
||||
//! [`Or`] used to combine two services into one.
|
||||
|
||||
use super::{FromEmptyRouter, RoutingDsl};
|
||||
use super::{FromEmptyRouter, OrDepth, RoutingDsl};
|
||||
use crate::body::BoxBody;
|
||||
use futures_util::ready;
|
||||
use http::{Request, Response};
|
||||
@@ -46,7 +46,13 @@ where
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
|
||||
fn call(&mut self, req: Request<ReqBody>) -> Self::Future {
|
||||
fn call(&mut self, mut req: Request<ReqBody>) -> Self::Future {
|
||||
if let Some(count) = req.extensions_mut().get_mut::<OrDepth>() {
|
||||
count.increment();
|
||||
} else {
|
||||
req.extensions_mut().insert(OrDepth::new());
|
||||
}
|
||||
|
||||
ResponseFuture {
|
||||
state: State::FirstFuture {
|
||||
f: self.first.clone().oneshot(req),
|
||||
@@ -100,7 +106,7 @@ where
|
||||
StateProj::FirstFuture { f } => {
|
||||
let mut response = ready!(f.poll(cx)?);
|
||||
|
||||
let req = if let Some(ext) = response
|
||||
let mut req = if let Some(ext) = response
|
||||
.extensions_mut()
|
||||
.remove::<FromEmptyRouter<ReqBody>>()
|
||||
{
|
||||
@@ -109,6 +115,18 @@ where
|
||||
return Poll::Ready(Ok(response));
|
||||
};
|
||||
|
||||
let mut leaving_outermost_or = false;
|
||||
if let Some(depth) = req.extensions_mut().get_mut::<OrDepth>() {
|
||||
if depth == 1 {
|
||||
leaving_outermost_or = true;
|
||||
} else {
|
||||
depth.decrement();
|
||||
}
|
||||
}
|
||||
if leaving_outermost_or {
|
||||
req.extensions_mut().remove::<OrDepth>();
|
||||
}
|
||||
|
||||
let second = this.second.take().expect("future polled after completion");
|
||||
|
||||
State::SecondFuture {
|
||||
|
||||
Reference in New Issue
Block a user