Skip to content

Commit 0c402d8

Browse files
authoredJan 28, 2025··
fix(client): Fix HTTP/2 websocket request (#165)
1 parent 5f055e3 commit 0c402d8

File tree

2 files changed

+83
-1
lines changed

2 files changed

+83
-1
lines changed
 

‎src/client/legacy/client.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -316,7 +316,7 @@ where
316316
} else {
317317
origin_form(req.uri_mut());
318318
}
319-
} else if req.method() == Method::CONNECT {
319+
} else if req.method() == Method::CONNECT && !pooled.is_http2() {
320320
authority_form(req.uri_mut());
321321
}
322322

‎tests/legacy_client.rs

+82
Original file line numberDiff line numberDiff line change
@@ -807,6 +807,88 @@ fn client_upgrade() {
807807
assert_eq!(vec, b"bar=foo");
808808
}
809809

810+
#[cfg(not(miri))]
811+
#[test]
812+
fn client_http2_upgrade() {
813+
use http::{Method, Response, Version};
814+
use hyper::service::service_fn;
815+
use tokio::io::{AsyncReadExt, AsyncWriteExt};
816+
use tokio::net::TcpListener;
817+
818+
let _ = pretty_env_logger::try_init();
819+
let rt = runtime();
820+
let server = rt
821+
.block_on(TcpListener::bind(SocketAddr::from(([127, 0, 0, 1], 0))))
822+
.unwrap();
823+
let addr = server.local_addr().unwrap();
824+
let mut connector = DebugConnector::new();
825+
connector.alpn_h2 = true;
826+
827+
let client = Client::builder(TokioExecutor::new()).build(connector);
828+
829+
rt.spawn(async move {
830+
let (stream, _) = server.accept().await.expect("accept");
831+
let stream = TokioIo::new(stream);
832+
let mut builder = hyper_util::server::conn::auto::Builder::new(TokioExecutor::new());
833+
// IMPORTANT: This is required to advertise our support for HTTP/2 websockets to the client.
834+
builder.http2().enable_connect_protocol();
835+
let _ = builder
836+
.serve_connection_with_upgrades(
837+
stream,
838+
service_fn(|req| async move {
839+
assert_eq!(req.headers().get("host"), None);
840+
assert_eq!(req.version(), Version::HTTP_2);
841+
assert_eq!(
842+
req.headers().get(http::header::SEC_WEBSOCKET_VERSION),
843+
Some(&http::header::HeaderValue::from_static("13"))
844+
);
845+
assert_eq!(
846+
req.extensions().get::<hyper::ext::Protocol>(),
847+
Some(&hyper::ext::Protocol::from_static("websocket"))
848+
);
849+
850+
let on_upgrade = hyper::upgrade::on(req);
851+
tokio::spawn(async move {
852+
let upgraded = on_upgrade.await.unwrap();
853+
let mut io = TokioIo::new(upgraded);
854+
855+
let mut vec = vec![];
856+
io.read_buf(&mut vec).await.unwrap();
857+
assert_eq!(vec, b"foo=bar");
858+
io.write_all(b"bar=foo").await.unwrap();
859+
});
860+
861+
Ok::<_, hyper::Error>(Response::new(Empty::<Bytes>::new()))
862+
}),
863+
)
864+
.await
865+
.expect("server");
866+
});
867+
868+
let req = Request::builder()
869+
.method(Method::CONNECT)
870+
.uri(&*format!("http://{}/up", addr))
871+
.header(http::header::SEC_WEBSOCKET_VERSION, "13")
872+
.version(Version::HTTP_2)
873+
.extension(hyper::ext::Protocol::from_static("websocket"))
874+
.body(Empty::<Bytes>::new())
875+
.unwrap();
876+
877+
let res = client.request(req);
878+
let res = rt.block_on(res).unwrap();
879+
880+
assert_eq!(res.status(), http::StatusCode::OK);
881+
assert_eq!(res.version(), Version::HTTP_2);
882+
883+
let upgraded = rt.block_on(hyper::upgrade::on(res)).expect("on_upgrade");
884+
let mut io = TokioIo::new(upgraded);
885+
886+
rt.block_on(io.write_all(b"foo=bar")).unwrap();
887+
let mut vec = vec![];
888+
rt.block_on(io.read_to_end(&mut vec)).unwrap();
889+
assert_eq!(vec, b"bar=foo");
890+
}
891+
810892
#[cfg(not(miri))]
811893
#[test]
812894
fn alpn_h2() {

0 commit comments

Comments
 (0)
Please sign in to comment.