Skip to content

Commit 45aa624

Browse files
authored
fix(client): Do not strip path and scheme components from URIs for HTTP/2 Extended CONNECT requests (#3242)
1 parent aac6760 commit 45aa624

File tree

3 files changed

+105
-33
lines changed

3 files changed

+105
-33
lines changed

src/client/client.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ use crate::common::{
1616
exec::BoxSendFuture, lazy as hyper_lazy, sync_wrapper::SyncWrapper, task, Future, Lazy, Pin,
1717
Poll,
1818
};
19+
#[cfg(feature = "http2")]
20+
use crate::ext::Protocol;
1921
use crate::rt::Executor;
2022

2123
use super::conn;
@@ -278,7 +280,13 @@ where
278280
origin_form(req.uri_mut());
279281
}
280282
} else if req.method() == Method::CONNECT {
283+
#[cfg(not(feature = "http2"))]
281284
authority_form(req.uri_mut());
285+
286+
#[cfg(feature = "http2")]
287+
if req.extensions().get::<Protocol>().is_none() {
288+
authority_form(req.uri_mut());
289+
}
282290
}
283291

284292
let mut res = match pooled.send_request_retryable(req).await {

tests/integration.rs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,48 @@ t! {
305305
;
306306
}
307307

308+
t! {
309+
h2_connect_authority_form,
310+
client:
311+
request:
312+
method: "CONNECT",
313+
// http2 should strip scheme and path from URI (authority-form)
314+
uri: "/connect_normal",
315+
;
316+
response:
317+
;
318+
server:
319+
request:
320+
method: "CONNECT",
321+
// path should be stripped
322+
uri: "",
323+
;
324+
response:
325+
;
326+
}
327+
328+
t! {
329+
h2_only;
330+
h2_extended_connect_full_uri,
331+
client:
332+
request:
333+
method: "CONNECT",
334+
// http2 should not strip scheme and path from URI for extended CONNECT requests
335+
uri: "/connect_extended",
336+
protocol: "the-bread-protocol",
337+
;
338+
response:
339+
;
340+
server:
341+
request:
342+
method: "CONNECT",
343+
// path should not be stripped
344+
uri: "/connect_extended",
345+
;
346+
response:
347+
;
348+
}
349+
308350
t! {
309351
get_2,
310352
client:

tests/support/mod.rs

Lines changed: 55 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,16 @@ use hyper::{Body, Client, Request, Response, Server, Version};
1313
pub use futures_util::{
1414
future, FutureExt as _, StreamExt as _, TryFutureExt as _, TryStreamExt as _,
1515
};
16-
pub use hyper::{HeaderMap, StatusCode};
16+
pub use hyper::{ext::Protocol, http::Extensions, HeaderMap, StatusCode};
1717
pub use std::net::SocketAddr;
1818

1919
#[allow(unused_macros)]
2020
macro_rules! t {
2121
(
22+
@impl
2223
$name:ident,
23-
parallel: $range:expr
24+
parallel: $range:expr,
25+
$(h2_only: $_h2_only:expr)?
2426
) => (
2527
#[test]
2628
fn $name() {
@@ -75,6 +77,7 @@ macro_rules! t {
7577
}
7678
);
7779
(
80+
@impl
7881
$name:ident,
7982
client: $(
8083
request: $(
@@ -91,7 +94,8 @@ macro_rules! t {
9194
response: $(
9295
$s_res_prop:ident: $s_res_val:tt,
9396
)*;
94-
)*
97+
)*,
98+
h2_only: $h2_only:expr
9599
) => (
96100
#[test]
97101
fn $name() {
@@ -116,15 +120,17 @@ macro_rules! t {
116120
}
117121
),)*];
118122

119-
__run_test(__TestConfig {
120-
client_version: 1,
121-
client_msgs: c.clone(),
122-
server_version: 1,
123-
server_msgs: s.clone(),
124-
parallel: false,
125-
connections: 1,
126-
proxy: false,
127-
});
123+
if !$h2_only {
124+
__run_test(__TestConfig {
125+
client_version: 1,
126+
client_msgs: c.clone(),
127+
server_version: 1,
128+
server_msgs: s.clone(),
129+
parallel: false,
130+
connections: 1,
131+
proxy: false,
132+
});
133+
}
128134

129135
__run_test(__TestConfig {
130136
client_version: 2,
@@ -136,15 +142,17 @@ macro_rules! t {
136142
proxy: false,
137143
});
138144

139-
__run_test(__TestConfig {
140-
client_version: 1,
141-
client_msgs: c.clone(),
142-
server_version: 1,
143-
server_msgs: s.clone(),
144-
parallel: false,
145-
connections: 1,
146-
proxy: true,
147-
});
145+
if !$h2_only {
146+
__run_test(__TestConfig {
147+
client_version: 1,
148+
client_msgs: c.clone(),
149+
server_version: 1,
150+
server_msgs: s.clone(),
151+
parallel: false,
152+
connections: 1,
153+
proxy: true,
154+
});
155+
}
148156

149157
__run_test(__TestConfig {
150158
client_version: 2,
@@ -157,6 +165,12 @@ macro_rules! t {
157165
});
158166
}
159167
);
168+
(h2_only; $($t:tt)*) => {
169+
t!(@impl $($t)*, h2_only: true);
170+
};
171+
($($t:tt)*) => {
172+
t!(@impl $($t)*, h2_only: false);
173+
};
160174
}
161175

162176
macro_rules! __internal_map_prop {
@@ -245,6 +259,7 @@ pub struct __CReq {
245259
pub uri: &'static str,
246260
pub headers: HeaderMap,
247261
pub body: Vec<u8>,
262+
pub protocol: Option<&'static str>,
248263
}
249264

250265
impl Default for __CReq {
@@ -254,6 +269,7 @@ impl Default for __CReq {
254269
uri: "/",
255270
headers: HeaderMap::new(),
256271
body: Vec::new(),
272+
protocol: None,
257273
}
258274
}
259275
}
@@ -371,6 +387,7 @@ async fn async_test(cfg: __TestConfig) {
371387

372388
let server = hyper::Server::bind(&SocketAddr::from(([127, 0, 0, 1], 0)))
373389
.http2_only(cfg.server_version == 2)
390+
.http2_enable_connect_protocol()
374391
.serve(new_service);
375392

376393
let mut addr = server.local_addr();
@@ -398,6 +415,9 @@ async fn async_test(cfg: __TestConfig) {
398415
//.headers(creq.headers)
399416
.body(creq.body.into())
400417
.expect("Request::build");
418+
if let Some(protocol) = creq.protocol {
419+
req.extensions_mut().insert(Protocol::from_static(protocol));
420+
}
401421
*req.headers_mut() = creq.headers;
402422
let cstatus = cres.status;
403423
let cheaders = cres.headers;
@@ -458,18 +478,20 @@ fn naive_proxy(cfg: ProxyConfig) -> (SocketAddr, impl Future<Output = ()>) {
458478
let max_connections = cfg.connections;
459479
let counter = AtomicUsize::new(0);
460480

461-
let srv = Server::bind(&([127, 0, 0, 1], 0).into()).serve(make_service_fn(move |_| {
462-
let prev = counter.fetch_add(1, Ordering::Relaxed);
463-
assert!(max_connections > prev, "proxy max connections");
464-
let client = client.clone();
465-
future::ok::<_, hyper::Error>(service_fn(move |mut req| {
466-
let uri = format!("http://{}{}", dst_addr, req.uri().path())
467-
.parse()
468-
.expect("proxy new uri parse");
469-
*req.uri_mut() = uri;
470-
client.request(req)
471-
}))
472-
}));
481+
let srv = Server::bind(&([127, 0, 0, 1], 0).into())
482+
.http2_enable_connect_protocol()
483+
.serve(make_service_fn(move |_| {
484+
let prev = counter.fetch_add(1, Ordering::Relaxed);
485+
assert!(max_connections > prev, "proxy max connections");
486+
let client = client.clone();
487+
future::ok::<_, hyper::Error>(service_fn(move |mut req| {
488+
let uri = format!("http://{}{}", dst_addr, req.uri().path())
489+
.parse()
490+
.expect("proxy new uri parse");
491+
*req.uri_mut() = uri;
492+
client.request(req)
493+
}))
494+
}));
473495
let proxy_addr = srv.local_addr();
474496
(proxy_addr, srv.map(|res| res.expect("proxy error")))
475497
}

0 commit comments

Comments
 (0)