Skip to content

Commit d05e3b5

Browse files
committed
feat(http): set request authority and scheme for h1
1 parent 7645226 commit d05e3b5

16 files changed

Lines changed: 135 additions & 29 deletions

File tree

client/src/h1/proto/context.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ impl<const HEADER_LIMIT: usize> DerefMut for Context<'_, '_, HEADER_LIMIT> {
2929
}
3030

3131
impl<'c, 'd, const HEADER_LIMIT: usize> Context<'c, 'd, HEADER_LIMIT> {
32-
pub(crate) fn new(date: &'c DateTimeHandle<'d>) -> Self {
33-
Self(context::Context::new(date))
32+
pub(crate) fn new(date: &'c DateTimeHandle<'d>, is_tls: bool) -> Self {
33+
Self(context::Context::new(date, is_tls))
3434
}
3535
}

client/src/h1/proto/dispatcher.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,12 @@ where
6868
}
6969
}
7070

71+
let is_tls = req
72+
.uri()
73+
.scheme()
74+
.is_some_and(|scheme| scheme == "https" || scheme == "wss");
7175
// TODO: make const generic params configurable.
72-
let mut ctx = Context::<128>::new(&date);
76+
let mut ctx = Context::<128>::new(&date, is_tls);
7377

7478
// encode request head and return transfer encoding for request body
7579
let encoder = ctx.encode_head(&mut buf, req)?;

http/benches/h1_decode.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ impl DateTime for DT {
3737
fn decode(c: &mut Criterion) {
3838
let dt = DT::dummy_date_time();
3939

40-
let mut ctx = Context::<_, 8>::new(&dt);
40+
let mut ctx = Context::<_, 8>::new(&dt, false);
4141

4242
let req = b"\
4343
GET /HFQR/xitca-web HTTP/1.1\r\n\

http/src/h1/dispatcher.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ pub(crate) async fn run<
6363
config: HttpServiceConfig<HEADER_LIMIT, READ_BUF_LIMIT, WRITE_BUF_LIMIT>,
6464
service: &'a S,
6565
date: &'a D,
66+
is_tls: bool,
6667
) -> Result<(), Error<S::Error, BE>>
6768
where
6869
S: Service<ExtRequest<ReqB>, Response = Response<ResB>>,
@@ -77,7 +78,7 @@ where
7778
EitherBuf::Right(WriteBuf::<WRITE_BUF_LIMIT>::default())
7879
};
7980

80-
Dispatcher::new(io, addr, timer, config, service, date, write_buf)
81+
Dispatcher::new(io, addr, timer, config, service, date, write_buf, is_tls)
8182
.run()
8283
.await
8384
}
@@ -166,6 +167,7 @@ where
166167
W: H1BufWrite,
167168
D: DateTime,
168169
{
170+
#[allow(clippy::too_many_arguments)]
169171
fn new<const WRITE_BUF_LIMIT: usize>(
170172
io: &'a mut St,
171173
addr: SocketAddr,
@@ -174,11 +176,12 @@ where
174176
service: &'a S,
175177
date: &'a D,
176178
write_buf: W,
179+
is_tls: bool,
177180
) -> Self {
178181
Self {
179182
io: BufferedIo::new(io, write_buf),
180183
timer: Timer::new(timer, config.keep_alive_timeout, config.request_head_timeout),
181-
ctx: Context::with_addr(addr, date),
184+
ctx: Context::with_addr(addr, date, is_tls),
182185
service,
183186
_phantom: PhantomData,
184187
}

http/src/h1/dispatcher_uring.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,11 +118,12 @@ where
118118
config: HttpServiceConfig<H_LIMIT, R_LIMIT, W_LIMIT>,
119119
service: &'a S,
120120
date: &'a D,
121+
is_tls: bool,
121122
) -> Self {
122123
Self {
123124
io: Rc::new(io),
124125
timer: Timer::new(timer, config.keep_alive_timeout, config.request_head_timeout),
125-
ctx: Context::<_, H_LIMIT>::with_addr(addr, date),
126+
ctx: Context::<_, H_LIMIT>::with_addr(addr, date, is_tls),
126127
service,
127128
read_buf: BufOwned::new(),
128129
write_buf: BufOwned::new(),

http/src/h1/proto/context.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ pub struct Context<'a, D, const HEADER_LIMIT: usize> {
1111
// http extensions reused by next request.
1212
exts: Extensions,
1313
date: &'a D,
14+
pub(crate) is_tls: bool,
1415
}
1516

1617
// A set of state for current request that are used after request's ownership is passed
@@ -49,21 +50,22 @@ impl<'a, D, const HEADER_LIMIT: usize> Context<'a, D, HEADER_LIMIT> {
4950
///
5051
/// [DateTime]: crate::date::DateTime
5152
#[inline]
52-
pub fn new(date: &'a D) -> Self {
53-
Self::with_addr(crate::unspecified_socket_addr(), date)
53+
pub fn new(date: &'a D, is_tls: bool) -> Self {
54+
Self::with_addr(crate::unspecified_socket_addr(), date, is_tls)
5455
}
5556

5657
/// Context is constructed with [SocketAddr] and reference of certain type that impl [DateTime] trait.
5758
///
5859
/// [DateTime]: crate::date::DateTime
5960
#[inline]
60-
pub fn with_addr(addr: SocketAddr, date: &'a D) -> Self {
61+
pub fn with_addr(addr: SocketAddr, date: &'a D, is_tls: bool) -> Self {
6162
Self {
6263
addr,
6364
state: ContextState::new(),
6465
header: None,
6566
exts: Extensions::new(),
6667
date,
68+
is_tls,
6769
}
6870
}
6971

http/src/h1/proto/decode.rs

Lines changed: 52 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use core::mem::MaybeUninit;
22

3+
use http::uri::{Authority, Scheme};
34
use httparse::Status;
45

56
use crate::{
@@ -71,7 +72,7 @@ impl<D, const MAX_HEADERS: usize> Context<'_, D, MAX_HEADERS> {
7172
// split the headers from buffer.
7273
let slice = buf.split_to(len).freeze();
7374

74-
let uri = Uri::from_maybe_shared(slice.slice(path_head..path_head + path_len))?;
75+
let mut uri = Uri::from_maybe_shared(slice.slice(path_head..path_head + path_len))?.into_parts();
7576

7677
// pop a cached headermap or construct a new one.
7778
let mut headers = self.take_headers();
@@ -87,6 +88,25 @@ impl<D, const MAX_HEADERS: usize> Context<'_, D, MAX_HEADERS> {
8788

8889
let extensions = self.take_extensions();
8990

91+
// Try to set authority from host header if not present in request path
92+
if uri.authority.is_none() {
93+
// @TODO if it's a tls connection we could set the sni server name as authority instead
94+
if let Some(host) = headers.get(http::header::HOST) {
95+
uri.authority = Some(Authority::try_from(host.as_bytes())?);
96+
}
97+
}
98+
99+
// If authority is set, this will set the correct scheme depending on the tls acceptor used in the service.
100+
if uri.authority.is_some() && uri.scheme.is_none() {
101+
uri.scheme = if self.is_tls {
102+
Some(Scheme::HTTPS)
103+
} else {
104+
Some(Scheme::HTTP)
105+
};
106+
}
107+
108+
let uri = Uri::from_parts(uri)?;
109+
90110
*req.method_mut() = method;
91111
*req.version_mut() = version;
92112
*req.uri_mut() = uri;
@@ -173,7 +193,7 @@ mod test {
173193

174194
#[test]
175195
fn connection_multiple_value() {
176-
let mut ctx = Context::<_, 4>::new(&());
196+
let mut ctx = Context::<_, 4>::new(&(), false);
177197

178198
let head = b"\
179199
GET / HTTP/1.1\r\n\
@@ -211,7 +231,7 @@ mod test {
211231

212232
#[test]
213233
fn transfer_encoding() {
214-
let mut ctx = Context::<_, 4>::new(&());
234+
let mut ctx = Context::<_, 4>::new(&(), false);
215235

216236
let head = b"\
217237
GET / HTTP/1.1\r\n\
@@ -311,4 +331,33 @@ mod test {
311331
"transfer coding is not decoded to chunked"
312332
);
313333
}
334+
335+
#[test]
336+
fn test_host_with_scheme() {
337+
let mut ctx = Context::<_, 4>::new(&(), true);
338+
339+
let head = b"\
340+
GET / HTTP/1.1\r\n\
341+
Host: example.com\r\n\
342+
\r\n\
343+
";
344+
let mut buf = BytesMut::from(&head[..]);
345+
346+
let (req, _) = ctx.decode_head::<128>(&mut buf).unwrap().unwrap();
347+
348+
assert_eq!(req.uri().scheme(), Some(&Scheme::HTTPS));
349+
assert_eq!(req.uri().authority(), Some(&Authority::from_static("example.com")));
350+
assert_eq!(req.headers().get(http::header::HOST).unwrap(), "example.com");
351+
352+
let head = b"\
353+
GET / HTTP/1.1\r\n\
354+
\r\n\
355+
";
356+
let mut buf = BytesMut::from(&head[..]);
357+
358+
let (req, _) = ctx.decode_head::<128>(&mut buf).unwrap().unwrap();
359+
360+
assert_eq!(req.uri().scheme(), None);
361+
assert_eq!(req.uri().authority(), None);
362+
}
314363
}

http/src/h1/proto/encode.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,7 @@ mod test {
257257

258258
#[test]
259259
fn append_header() {
260-
let mut ctx = Context::<_, 64>::new(&SystemTimeDateTimeHandler);
260+
let mut ctx = Context::<_, 64>::new(&SystemTimeDateTimeHandler, false);
261261

262262
let mut res = Response::new(BoxBody::new(Once::new(Bytes::new())));
263263

@@ -287,7 +287,7 @@ mod test {
287287

288288
#[test]
289289
fn multi_set_cookie() {
290-
let mut ctx = Context::<_, 64>::new(&SystemTimeDateTimeHandler);
290+
let mut ctx = Context::<_, 64>::new(&SystemTimeDateTimeHandler, false);
291291

292292
let mut res = Response::new(BoxBody::new(Once::new(Bytes::new())));
293293

http/src/h1/proto/error.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,12 @@ impl From<http::uri::InvalidUri> for ProtoError {
4040
}
4141
}
4242

43+
impl From<http::uri::InvalidUriParts> for ProtoError {
44+
fn from(_: http::uri::InvalidUriParts) -> Self {
45+
Self::Uri
46+
}
47+
}
48+
4349
impl From<http::status::InvalidStatusCode> for ProtoError {
4450
fn from(_: http::status::InvalidStatusCode) -> Self {
4551
Self::Status

http/src/h1/service.rs

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use crate::{
99
error::{HttpServiceError, TimeoutError},
1010
http::{Request, RequestExt, Response},
1111
service::HttpService,
12+
tls::IsTls,
1213
util::timer::Timeout,
1314
};
1415

@@ -21,7 +22,7 @@ impl<St, S, B, BE, A, const HEADER_LIMIT: usize, const READ_BUF_LIMIT: usize, co
2122
Service<(St, SocketAddr)> for H1Service<St, S, A, HEADER_LIMIT, READ_BUF_LIMIT, WRITE_BUF_LIMIT>
2223
where
2324
S: Service<Request<RequestExt<RequestBody>>, Response = Response<B>>,
24-
A: Service<St>,
25+
A: Service<St> + IsTls,
2526
St: AsyncIo,
2627
A::Response: AsyncIo,
2728
B: Stream<Item = Result<Bytes, BE>>,
@@ -41,9 +42,17 @@ where
4142
.await
4243
.map_err(|_| HttpServiceError::Timeout(TimeoutError::TlsAccept))??;
4344

44-
super::dispatcher::run(&mut io, addr, timer, self.config, &self.service, self.date.get())
45-
.await
46-
.map_err(Into::into)
45+
super::dispatcher::run(
46+
&mut io,
47+
addr,
48+
timer,
49+
self.config,
50+
&self.service,
51+
self.date.get(),
52+
self.tls_acceptor.is_tls(),
53+
)
54+
.await
55+
.map_err(Into::into)
4756
}
4857
}
4958

@@ -94,7 +103,7 @@ impl<S, B, BE, A, const HEADER_LIMIT: usize, const READ_BUF_LIMIT: usize, const
94103
Service<(TcpStream, SocketAddr)> for H1UringService<S, A, HEADER_LIMIT, READ_BUF_LIMIT, WRITE_BUF_LIMIT>
95104
where
96105
S: Service<Request<RequestExt<RequestBody>>, Response = Response<B>>,
97-
A: Service<TcpStream>,
106+
A: Service<TcpStream> + IsTls,
98107
A::Response: AsyncBufRead + AsyncBufWrite + 'static,
99108
B: Stream<Item = Result<Bytes, BE>>,
100109
HttpServiceError<S::Error, BE>: From<A::Error>,
@@ -113,10 +122,18 @@ where
113122
.await
114123
.map_err(|_| HttpServiceError::Timeout(TimeoutError::TlsAccept))??;
115124

116-
super::dispatcher_uring::Dispatcher::new(io, addr, timer, self.config, &self.service, self.date.get())
117-
.run()
118-
.await
119-
.map_err(Into::into)
125+
super::dispatcher_uring::Dispatcher::new(
126+
io,
127+
addr,
128+
timer,
129+
self.config,
130+
&self.service,
131+
self.date.get(),
132+
self.tls_acceptor.is_tls(),
133+
)
134+
.run()
135+
.await
136+
.map_err(Into::into)
120137
}
121138
}
122139

0 commit comments

Comments
 (0)