1use std::{cell::Cell, cell::RefCell, future::poll_fn, io, mem, rc::Rc};
2
3use ntex_h2::{self as h2, control::ExpectResult, frame::StreamId, server};
4use urly::{Authority, Scheme, Url};
5
6use crate::error::{Error, IntoFailure};
7use crate::http::body::{Body, BodySize, MessageBody, ResponseBody};
8use crate::http::config::DispatcherConfig;
9use crate::http::error::{DispatchError, H2Error, ResponseError};
10use crate::http::header::{self, HeaderMap, HeaderName, HeaderValue};
11use crate::http::helpers::is_valid_target;
12use crate::http::message::{CurrentIo, ResponseHead};
13use crate::http::{DateService, Method, Request, Response, StatusCode, Version};
14use crate::io::{Filter, Io, IoBoxed, IoRef, types};
15use crate::service::pipeline::{Pipeline, PipelineBinding, PipelineFactory};
16use crate::service::{Ctx, IntoServiceFactory, RequestState, Service, ServiceFactory};
17use crate::util::{Bytes, BytesMut, HashMap};
18
19use super::{DefaultControlService, payload::Payload, payload::PayloadSender};
20
21#[derive(derive_more::Debug)]
23#[debug("H2Service")]
24pub struct H2Service<F, Req: RequestState<Io<F>>, Err> {
25 sf: crate::http::HttpPipeline<Req::State, Err>,
26 ctl: crate::http::Ctl2Pipeline<Req::State>,
27 config: DispatcherConfig,
28}
29
30impl<F, Req, Err> H2Service<F, Req, Err>
31where
32 F: Filter,
33 Req: RequestState<Io<F>>,
34 Req::State: Clone,
35 Err: ResponseError + 'static,
36{
37 pub(crate) fn new<Sf>(sf: impl IntoServiceFactory<Sf, Req::State, Request>) -> Self
39 where
40 Sf: ServiceFactory<Req::State, Request, Error = Err> + 'static,
41 Sf::Res: Into<Response>,
42 Sf::InitError: IntoFailure,
43 {
44 H2Service {
45 sf: PipelineFactory::new(
46 sf.into_factory()
47 .map(Into::into)
48 .map_init_err(|e| DispatchError::Control(e.fail())),
49 ),
50 ctl: PipelineFactory::new(DefaultControlService),
51 config: DispatcherConfig::default(),
52 }
53 }
54}
55
56impl<F, Req, Err> H2Service<F, Req, Err>
57where
58 F: Filter,
59 Req: RequestState<Io<F>>,
60 Req::State: Clone,
61 Err: ResponseError + 'static,
62{
63 #[must_use]
64 pub fn control<I, Sf>(self, ctl: I) -> Self
66 where
67 I: IntoServiceFactory<Sf, Req::State, h2::Control<Error<H2Error>>>,
68 Sf: ServiceFactory<Req::State, h2::Control<Error<H2Error>>, Res = h2::ControlAck> + 'static,
69 Sf::Error: IntoFailure,
70 Sf::InitError: IntoFailure,
71 {
72 H2Service {
73 sf: self.sf,
74 ctl: PipelineFactory::new(
75 ctl.into_factory()
76 .map_err(|e| DispatchError::Service(e.fail()))
77 .map_init_err(|e| DispatchError::Service(e.fail())),
78 ),
79 config: self.config,
80 }
81 }
82}
83
84impl<St, F, Req, Err> Service<St, Req> for H2Service<F, Req, Err>
85where
86 F: Filter,
87 Req: RequestState<Io<F>>,
88 Req::State: Clone,
89 Err: ResponseError + 'static,
90{
91 type Res = ();
92 type Error = DispatchError;
93
94 async fn call(&self, req: Req, _: Ctx<'_, Self, St>) -> Result<Self::Res, Self::Error> {
95 let (st, io) = req.unpack();
96
97 let svc = self.sf.create(st.clone()).await?;
98 let ctl = self.ctl.create(st).await?;
99
100 let id = self.config.next_id();
101 let ioref = io.get_ref();
102 let (_guard, inflight) = self.config.insert_io(&ioref);
103 log::trace!(
104 "{}: New http2 connection {id}, peer address {:?}, inflight: {inflight}",
105 io.tag(),
106 io.query::<types::PeerAddr>().get()
107 );
108
109 handle(id, io.into(), svc, ctl).await
110 }
111
112 #[inline]
113 async fn ready(&self, _: Ctx<'_, Self, St>) -> Result<(), Self::Error> {
114 Ok(())
115 }
116
117 #[inline]
118 async fn shutdown(&self, _: Ctx<'_, Self, St>) {
119 let inflight = self.config.shutdown();
121 if inflight != 0 {
122 log::trace!("Shutting down service, in-flight connections: {inflight}");
123
124 self.config.wait_shutdown().await;
125 log::trace!("Shutting down is complected");
126 }
127 }
128}
129
130pub(in crate::http) async fn handle<Err>(
131 id: usize,
132 io: IoBoxed,
133 svc: Pipeline<Request, Response, Err>,
134 control: Pipeline<h2::Control<Error<H2Error>>, h2::ControlAck, DispatchError>,
135) -> Result<(), DispatchError>
136where
137 Err: ResponseError + 'static,
138{
139 let ioref = io.get_ref();
140 let control = Pipeline::new((), ControlService { inner: control });
141
142 let _ = server::handle_one(
143 io,
144 Pipeline::new((), PublishService::new(id, ioref, svc, control.bind())),
145 control,
146 )
147 .await;
148
149 Ok(())
150}
151
152struct ControlService {
154 inner: Pipeline<h2::Control<Error<H2Error>>, h2::ControlAck, DispatchError>,
155}
156
157impl Service<(), h2::Control<Error<H2Error>>> for ControlService {
158 type Res = h2::ControlAck;
159 type Error = DispatchError;
160
161 async fn ready(&self, _: Ctx<'_, Self, ()>) -> Result<(), Self::Error> {
162 self.inner.ready().await
163 }
164
165 async fn shutdown(&self, _: Ctx<'_, Self, ()>) {
166 self.inner.shutdown().await;
167 }
168
169 async fn call(
170 &self,
171 msg: h2::Control<Error<H2Error>>,
172 _: Ctx<'_, Self, ()>,
173 ) -> Result<Self::Res, Self::Error> {
174 let msg = match msg {
175 h2::Control::Disconnect(h2::control::Reason::Error(err))
176 if matches!(**err.get_ref(), H2Error::EmptyDataFrames) =>
177 {
178 h2::Control::Disconnect(h2::control::Reason::Error(
179 err.reason(h2::frame::Reason::ENHANCE_YOUR_CALM),
180 ))
181 }
182 msg => msg,
183 };
184 self.inner.call(msg).await
185 }
186}
187
188struct PublishService<Err> {
189 id: usize,
190 io: IoRef,
191 svc: Pipeline<Request, Response, Err>,
192 control: PipelineBinding<h2::Control<Error<H2Error>>, h2::ControlAck, DispatchError>,
193 streams: Rc<RefCell<HashMap<StreamId, StreamPayload>>>,
194 empty_data: Cell<u8>,
196}
197
198const MAX_EMPTY_DATA_FRAMES: u8 = 10;
201
202struct StreamPayload {
204 sender: PayloadSender,
205 complete: bool,
207}
208
209impl<Err> PublishService<Err>
210where
211 Err: ResponseError,
212{
213 fn new(
214 id: usize,
215 io: IoRef,
216 svc: Pipeline<Request, Response, Err>,
217 control: PipelineBinding<h2::Control<Error<H2Error>>, h2::ControlAck, DispatchError>,
218 ) -> Self {
219 Self {
220 id,
221 io,
222 svc,
223 control,
224 streams: Rc::new(RefCell::new(HashMap::default())),
225 empty_data: Cell::new(0),
226 }
227 }
228}
229
230impl<Err> Service<(), h2::Message> for PublishService<Err>
231where
232 Err: ResponseError + 'static,
233{
234 type Res = ();
235 type Error = Error<H2Error>;
236
237 async fn shutdown(&self, _: Ctx<'_, Self, ()>) {
238 self.svc.shutdown().await;
239 }
240
241 async fn call(&self, msg: h2::Message, _: Ctx<'_, Self, ()>) -> Result<Self::Res, Self::Error> {
242 let h2::Message { stream, kind } = msg;
243 let (io, pseudo, headers, eof, payload) = match kind {
244 h2::MessageKind::Headers {
245 pseudo,
246 headers,
247 eof,
248 } => {
249 let pl = if eof {
250 None
251 } else {
252 #[cfg(feature = "trace")]
253 log::debug!(
254 "{}: Creating local payload stream for {:?}",
255 self.io.tag(),
256 stream.id()
257 );
258 let (sender, payload) = Payload::create(stream.empty_capacity());
259 self.streams.borrow_mut().insert(
260 stream.id(),
261 StreamPayload {
262 sender,
263 complete: false,
264 },
265 );
266 Some(payload)
267 };
268 (self.io.clone(), pseudo, headers, eof, pl)
269 }
270 h2::MessageKind::Data(data, cap) => {
271 #[cfg(feature = "trace")]
272 log::debug!(
273 "{}: Got data chunk for {:?}: {:?}",
274 self.io.tag(),
275 stream.id(),
276 data.len()
277 );
278 let count = if data.is_empty() {
280 self.empty_data.get().saturating_add(1)
281 } else if self.empty_data.get() >= MAX_EMPTY_DATA_FRAMES {
282 MAX_EMPTY_DATA_FRAMES
283 } else {
284 0
285 };
286 self.empty_data.set(count);
287 if count >= MAX_EMPTY_DATA_FRAMES {
288 log::debug!(
289 "{}: Too many consecutive empty DATA frames, closing connection",
290 self.io.tag()
291 );
292 return Err(H2Error::EmptyDataFrames.into());
293 }
294 let mut streams = self.streams.borrow_mut();
295 if let Some(pl) = streams.get(&stream.id()) {
296 if pl.complete && pl.sender.is_dropped() {
297 streams.remove(&stream.id());
299 drop(streams);
300 stream.reset(h2::frame::Reason::NO_ERROR);
301 } else {
302 pl.sender.feed_data(data, cap);
303 }
304 } else {
305 log::error!(
306 "{}: Payload stream does not exists for {:?}",
307 self.io.tag(),
308 stream.id()
309 );
310 }
311 return Ok(());
312 }
313 h2::MessageKind::Eof(item) => {
314 log::debug!(
315 "{}: Got payload eof for {:?}: {item:?}",
316 self.io.tag(),
317 stream.id()
318 );
319 if self.empty_data.get() >= MAX_EMPTY_DATA_FRAMES {
320 return Err(H2Error::EmptyDataFrames.into());
321 }
322 if matches!(item, h2::StreamEof::Data(ref data, _) if !data.is_empty()) {
323 self.empty_data.set(0);
324 }
325 if let Some(StreamPayload { sender, .. }) =
326 self.streams.borrow_mut().remove(&stream.id())
327 {
328 match item {
329 h2::StreamEof::Data(data, cap) => {
330 sender.feed_eof(data, Some(cap));
331 }
332 h2::StreamEof::Trailers(hdrs) => {
333 sender.feed_trailers(hdrs);
334 }
335 h2::StreamEof::Error(err) => {
336 sender.set_error(err.into_error().into());
337 }
338 }
339 }
340 return Ok(());
341 }
342 h2::MessageKind::Disconnect(err) => {
343 log::debug!("{}: Connection is disconnected {err:?}", self.io.tag());
344 if let Some(pl) = self.streams.borrow_mut().remove(&stream.id()) {
345 pl.sender
346 .set_error(io::Error::new(io::ErrorKind::UnexpectedEof, err).into());
347 }
348 return Ok(());
349 }
350 };
351
352 let Some((method, uri)) = request_uri(&pseudo) else {
355 log::debug!(
356 "{}: Malformed request on {:?}: {pseudo:?}",
357 self.io.tag(),
358 stream.id()
359 );
360 self.streams.borrow_mut().remove(&stream.id());
361
362 let mut res = Response::new(StatusCode::BAD_REQUEST).drop_body();
363 let head = res.head_mut();
364 prepare_response(head, &mut BodySize::Empty);
365 let _ = stream.send_response(StatusCode::BAD_REQUEST, &head.headers, true);
366 if !eof {
367 stream.reset(h2::frame::Reason::NO_ERROR);
369 }
370 return Ok(());
371 };
372
373 let (pseudo, headers) = if !eof && expect_continue(&headers) {
376 let msg = h2::Control::expect(stream.clone(), pseudo, headers);
377 match self
378 .control
379 .call(msg)
380 .await
381 .map(h2::ControlAck::into_expect)
382 {
383 Ok(Some(ExpectResult::Continue(expect))) => {
384 if stream
385 .send_informational(StatusCode::CONTINUE, HeaderMap::new())
386 .is_err()
387 {
388 self.streams.borrow_mut().remove(&stream.id());
390 return Ok(());
391 }
392 let (_, pseudo, headers) = expect.into_parts();
393 (pseudo, headers)
394 }
395 Ok(Some(ExpectResult::Failed(_, status, headers))) => {
396 self.streams.borrow_mut().remove(&stream.id());
397
398 let mut res = Response::new(status).drop_body();
399 let head = res.head_mut();
400 h2::recycle_header_map(mem::replace(&mut head.headers, headers));
401 prepare_response(head, &mut BodySize::Empty);
402 let _ = stream.send_response(status, &head.headers, true);
403
404 stream.reset(h2::frame::Reason::NO_ERROR);
406 return Ok(());
407 }
408 result => {
409 log::error!(
410 "{}: Control service failed to handle expect for {:?}: {:?}",
411 self.io.tag(),
412 stream.id(),
413 result.map(|_| ())
414 );
415 self.streams.borrow_mut().remove(&stream.id());
416 stream.reset(h2::frame::Reason::INTERNAL_ERROR);
417 return Ok(());
418 }
419 }
420 } else {
421 (pseudo, headers)
422 };
423
424 log::trace!(
425 "{}: {:?} got request (eof: {eof}): {pseudo:#?}\nheaders: {headers:#?}",
426 self.io.tag(),
427 stream.id()
428 );
429 let mut req = if let Some(pl) = payload {
430 Request::with_payload(crate::http::Payload::H2(pl))
431 } else {
432 Request::new()
433 };
434
435 let is_head_req = method == Method::HEAD;
436 let head = req.head_mut();
437 head.uri = uri;
438 head.version = Version::HTTP_2;
439 head.method = method;
440 h2::recycle_header_map(mem::replace(&mut head.headers, headers));
442 head.io = CurrentIo::Ref(io);
443 head.id = self.id;
444
445 let result = self.svc.call(req).await;
446 let (mut res, mut body) = Response::from(result).into_parts();
447
448 let status = res.status();
451 if status.is_informational() {
452 log::error!(
453 "{}: Informational response {status} is not supported, sending 500",
454 self.io.tag()
455 );
456 res = Response::new(StatusCode::INTERNAL_SERVER_ERROR).drop_body();
457 body = ResponseBody::Other(Body::Empty);
458 }
459
460 let head = res.head_mut();
461 let mut size = body.size();
462 prepare_response(head, &mut size);
463
464 #[cfg(feature = "trace")]
465 log::debug!(
466 "{}: Received service response: {head:?} payload: {size:?}",
467 self.io.tag()
468 );
469
470 let hdrs = &head.headers;
472 let sent = async {
474 if size.is_eof() || is_head_req {
475 stream
476 .send_response(head.status, hdrs, true)
477 .map_err(|_| None)?;
478 return Ok(());
479 }
480 stream
481 .send_response(head.status, hdrs, false)
482 .map_err(|_| None)?;
483
484 let mut remaining = if let BodySize::Sized(len) = size {
486 Some(len)
487 } else {
488 None
489 };
490 loop {
491 match poll_fn(|cx| body.poll_next_chunk(cx)).await {
492 None => {
493 #[cfg(feature = "trace")]
494 log::debug!(
495 "{}: {:?} closing payload stream",
496 self.io.tag(),
497 stream.id()
498 );
499 return stream
500 .send_payload(Bytes::new(), true)
501 .await
502 .map_err(|_| None);
503 }
504 Some(Ok(chunk)) => {
505 #[cfg(feature = "trace")]
506 log::debug!(
507 "{}: {:?} sending data chunk {:?} bytes",
508 self.io.tag(),
509 stream.id(),
510 chunk.len()
511 );
512 if !chunk.is_empty() {
513 let eof = remaining.as_mut().is_some_and(|rem| {
514 *rem = rem.saturating_sub(chunk.len() as u64);
515 *rem == 0
516 });
517 stream.send_payload(chunk, eof).await.map_err(|_| None)?;
518 if eof {
519 return Ok(());
520 }
521 }
522 }
523 Some(Err(e)) => return Err(Some(e)),
524 }
525 }
526 }
527 .await;
528
529 match sent {
530 Ok(()) => (),
531 Err(Some(e)) => {
532 log::error!(
534 "{}: Response payload stream error for {:?}: {e:?}",
535 self.io.tag(),
536 stream.id()
537 );
538 stream.reset(h2::frame::Reason::INTERNAL_ERROR);
539 return Ok(());
540 }
541 Err(None) => {
542 self.streams.borrow_mut().remove(&stream.id());
544 return Ok(());
545 }
546 }
547
548 let id = stream.id();
550 let mut streams = self.streams.borrow_mut();
551 if let Some(pl) = streams.get_mut(&id) {
552 if pl.sender.is_dropped() {
553 streams.remove(&id);
554 drop(streams);
555 stream.reset(h2::frame::Reason::NO_ERROR);
556 } else {
557 pl.complete = true;
559 let streams = Rc::downgrade(&self.streams);
560 pl.sender.on_drop(move || {
561 if let Some(streams) = streams.upgrade()
562 && let Ok(mut streams) = streams.try_borrow_mut()
563 {
564 streams.remove(&id);
565 }
566 stream.reset(h2::frame::Reason::NO_ERROR);
567 });
568 }
569 }
570 Ok(())
571 }
572}
573
574#[allow(clippy::declare_interior_mutable_const)]
575const ZERO_CONTENT_LENGTH: HeaderValue = HeaderValue::from_static("0");
576#[allow(clippy::declare_interior_mutable_const)]
577const KEEP_ALIVE: HeaderName = HeaderName::from_static("keep-alive");
578#[allow(clippy::declare_interior_mutable_const)]
579const PROXY_CONNECTION: HeaderName = HeaderName::from_static("proxy-connection");
580
581fn request_uri(pseudo: &h2::frame::PseudoHeaders) -> Option<(Method, Url)> {
584 let method = pseudo.method.clone()?;
585 let authority = match pseudo.authority {
587 Some(ref authority) => {
588 if authority.is_empty() || authority.starts_with(':') || authority.contains('@') {
590 return None;
591 }
592 Some(Authority::new(authority.as_str()).ok()?)
593 }
594 None => None,
595 };
596 let path = pseudo.path.as_ref().map(crate::util::ByteString::as_str);
597 if path.is_some_and(|p| !is_valid_target(p.as_bytes())) {
599 return None;
600 }
601
602 let uri = match (authority, path) {
603 (Some(authority), None) if method == Method::CONNECT => {
605 Url::parse(authority.as_str()).ok()?
606 }
607 (_, Some("*")) if method == Method::OPTIONS => Url::from_static("*"),
608 (Some(authority), Some(path)) if path.starts_with('/') => {
609 let scheme = Scheme::new(pseudo.scheme.as_ref()?.as_str()).ok()?;
610 Url::try_from(concat(&[scheme.as_str(), "://", authority.as_str(), path])).ok()?
611 }
612 (None, Some(path)) if path.starts_with('/') => {
613 let uri = if path.starts_with("//") {
614 Url::try_from(format!("/.{path}")).ok()?
616 } else {
617 Url::try_from(pseudo.path.as_ref()?).ok()?
619 };
620 if uri.authority().is_some() {
621 return None;
622 }
623 uri
624 }
625 _ => return None,
626 };
627 Some((method, uri))
628}
629
630fn concat(pieces: &[&str]) -> Bytes {
633 const INLINE: usize = 23;
634
635 let len = pieces.iter().map(|p| p.len()).sum::<usize>();
636 if len <= INLINE {
637 let mut buf = [0u8; INLINE];
638 let mut pos = 0;
639 for p in pieces {
640 buf[pos..pos + p.len()].copy_from_slice(p.as_bytes());
641 pos += p.len();
642 }
643 Bytes::copy_from_slice(&buf[..len])
644 } else {
645 let mut buf = BytesMut::with_capacity(len);
646 for p in pieces {
647 buf.extend_from_slice(p.as_bytes());
648 }
649 buf.freeze()
650 }
651}
652
653fn expect_continue(headers: &HeaderMap) -> bool {
655 headers.get_all(header::EXPECT).any(|value| {
656 value
657 .as_bytes()
658 .split(|&b| b == b',')
659 .any(|e| e.trim_ascii().eq_ignore_ascii_case(b"100-continue"))
660 })
661}
662
663fn prepare_response(head: &mut ResponseHead, size: &mut BodySize) {
664 if head.status == StatusCode::NO_CONTENT || head.status == StatusCode::NOT_MODIFIED {
667 *size = BodySize::None;
668 }
669 match size {
670 BodySize::None | BodySize::Stream => head.headers.remove(header::CONTENT_LENGTH),
671 BodySize::Empty => head
672 .headers
673 .insert(header::CONTENT_LENGTH, ZERO_CONTENT_LENGTH),
674 BodySize::Sized(len) => {
675 head.headers
676 .insert(header::CONTENT_LENGTH, HeaderValue::from(*len));
677 }
678 }
679
680 head.headers.remove(header::CONNECTION);
682 head.headers.remove(header::TRANSFER_ENCODING);
683 head.headers.remove(header::UPGRADE);
684
685 head.headers.remove(KEEP_ALIVE);
688 head.headers.remove(PROXY_CONNECTION);
689
690 if !head.headers.contains_key(header::DATE) {
692 head.headers
693 .insert(header::DATE, DateService::header_value());
694 }
695}
696
697#[cfg(test)]
698mod tests {
699 use super::*;
700
701 #[test]
702 fn test_request_uri() {
703 let mut pseudo = h2::frame::PseudoHeaders::default();
704 assert!(request_uri(&pseudo).is_none());
705
706 pseudo.method = Some(Method::CONNECT);
708 pseudo.authority = Some("example.com:443".into());
709 let (method, uri) = request_uri(&pseudo).unwrap();
710 assert_eq!(method, Method::CONNECT);
711 assert_eq!(uri.authority().unwrap(), "example.com:443");
712
713 for authority in ["bad authority", "[email protected]:443", ":443"] {
714 pseudo.authority = Some(authority.into());
715 assert!(request_uri(&pseudo).is_none(), "{authority}");
716 }
717
718 for authority in ["a.io:1", "very-long-host-name.example.com:8443"] {
720 pseudo.authority = Some(authority.into());
721 let (_, uri) = request_uri(&pseudo).unwrap();
722 assert_eq!(uri.authority().unwrap(), authority);
723 assert_eq!(uri.to_string(), format!("//{authority}"));
724 }
725
726 pseudo.method = Some(Method::GET);
728 pseudo.authority = Some("example.com".into());
729 pseudo.path = Some("/path".into());
730 assert!(request_uri(&pseudo).is_none());
731 pseudo.scheme = Some("https".into());
732 let (_, uri) = request_uri(&pseudo).unwrap();
733 assert_eq!(uri.to_string(), "https://example.com/path");
734 for authority in ["u:[email protected]", ""] {
736 pseudo.authority = Some(authority.into());
737 assert!(request_uri(&pseudo).is_none(), "{authority}");
738 }
739 pseudo.authority = Some("example.com".into());
740
741 for path in ["/123", "/1234", "/api/v1/users/1?fields=name,email"] {
743 pseudo.path = Some(path.into());
744 let (_, uri) = request_uri(&pseudo).unwrap();
745 assert_eq!(uri.to_string(), format!("https://example.com{path}"));
746 }
747 pseudo.scheme = Some("HTTPS".into());
749 pseudo.authority = Some("Example.COM".into());
750 pseudo.path = Some("/a/./b/../c".into());
751 let (_, uri) = request_uri(&pseudo).unwrap();
752 assert_eq!(uri.to_string(), "https://example.com/a/c");
753 pseudo.path = Some("/a/./b/../c/long-enough-for-the-heap".into());
754 let (_, uri) = request_uri(&pseudo).unwrap();
755 assert_eq!(
756 uri.to_string(),
757 "https://example.com/a/c/long-enough-for-the-heap"
758 );
759 pseudo.authority = Some("example.com".into());
760 pseudo.path = Some("/path".into());
761
762 pseudo.scheme = Some("ht/tp".into());
763 assert!(request_uri(&pseudo).is_none());
764 pseudo.scheme = Some("https".into());
765 pseudo.authority = Some("example.com/x?".into());
766 assert!(request_uri(&pseudo).is_none());
767 pseudo.authority = Some("example.com".into());
768 pseudo.path = Some("x".into());
769 assert!(request_uri(&pseudo).is_none());
770
771 pseudo.authority = None;
772 pseudo.path = Some("/path".into());
773 let (_, uri) = request_uri(&pseudo).unwrap();
774 assert_eq!(uri.path(), "/path");
775 pseudo.path = Some("//example.com/path".into());
776 let (_, uri) = request_uri(&pseudo).unwrap();
777 assert_eq!(uri.path(), "//example.com/path");
778 assert!(uri.authority().is_none());
779 pseudo.path = Some("/a b".into());
780 assert!(request_uri(&pseudo).is_none());
781 pseudo.path = Some("/a\x7f".into());
782 assert!(request_uri(&pseudo).is_none());
783 for path in ["/a#f", "/a%zz", "/a\"", "/a%00"] {
784 pseudo.path = Some(path.into());
785 assert!(request_uri(&pseudo).is_none(), "{path}");
786 }
787 pseudo.path = Some("/a%3b/%2e%2e/b?%00".into());
788 let (_, uri) = request_uri(&pseudo).unwrap();
789 assert_eq!(uri.path_and_query(), "/b?%00");
790
791 pseudo.path = Some("*".into());
792 assert!(request_uri(&pseudo).is_none());
793 pseudo.method = Some(Method::OPTIONS);
794 let (_, uri) = request_uri(&pseudo).unwrap();
795 assert_eq!(uri.path(), "*");
796 }
797
798 #[test]
799 fn test_request_uri_authority() {
800 for (method, path) in [
801 (Method::GET, Some("/path")),
802 (Method::CONNECT, None),
803 (Method::OPTIONS, Some("*")),
804 ] {
805 let mut pseudo = h2::frame::PseudoHeaders {
806 method: Some(method),
807 scheme: path.map(|_| "https".into()),
808 path: path.map(Into::into),
809 ..Default::default()
810 };
811 for authority in [
812 "",
813 ":",
814 ":443",
815 "@example.com",
816 "u:[email protected]:443",
817 "example.com@",
818 "[email protected]",
819 "bad authority",
820 "exa\tmple.com",
821 "m\u{fc}nchen.de",
822 "example.com/path",
823 "example.com?query",
824 "example.com#fragment",
825 "example.com%zz",
826 "example.com:abc",
827 "example.com:+443",
828 "example.com:65536",
829 "127.0.0.256",
830 "::1",
831 "[::1",
832 "[::g]",
833 "[::1]:65536",
834 ] {
835 pseudo.authority = Some(authority.into());
836 assert!(request_uri(&pseudo).is_none(), "{pseudo:?}");
837 }
838 for (authority, normalized) in [
839 ("example.com", "example.com"),
840 ("Example.COM:00080", "example.com:80"),
841 ("example.com:", "example.com"),
842 ("example.com:0", "example.com:0"),
843 ("example.com:65535", "example.com:65535"),
844 ("%45xample.com:443", "example.com:443"),
845 ("127.0.0.1:8443", "127.0.0.1:8443"),
846 ("[::1]", "[::1]"),
847 ("[0:0:0:0:0:0:0:1]:00443", "[::1]:443"),
848 ] {
849 pseudo.authority = Some(authority.into());
850 let (_, uri) = request_uri(&pseudo).unwrap();
851 let expected = match path {
852 None => format!("//{normalized}"),
853 Some("*") => "*".to_owned(),
854 _ => format!("https://{normalized}/path"),
855 };
856 assert_eq!(uri.as_str(), expected, "{pseudo:?}");
857 }
858 }
859 }
860
861 #[test]
862 fn test_concat() {
863 assert_eq!(concat(&[]), "");
864 assert_eq!(concat(&["//", "a.io"]), "//a.io");
865 let inline = "x".repeat(23);
866 assert_eq!(concat(&[&inline[..20], &inline[20..]]), inline.as_str());
867 let heap = "y".repeat(24);
868 assert_eq!(concat(&[&heap[..1], "", &heap[1..]]), heap.as_str());
869 }
870}