ntex/http/h1/mod.rs
1//! HTTP/1 protocol services, codecs, payload decoding, and lifecycle control.
2//!
3//! [`H1Service`] runs an HTTP/1-only server service. [`Codec`] provides
4//! low-level request decoding and response encoding, while [`control`] exposes
5//! connection, request, expectation, upgrade, and disconnect events.
6use std::rc::Rc;
7
8mod codec;
9pub(crate) mod decoder;
10mod default;
11mod dispatcher;
12pub(crate) mod encoder;
13mod payload;
14mod service;
15mod timer;
16
17/// Connection lifecycle messages and acknowledgements.
18pub mod control;
19
20pub use self::codec::Codec;
21pub use self::control::{Control, ControlAck};
22pub use self::decoder::{PayloadDecoder, PayloadItem, PayloadType};
23pub use self::default::DefaultControlService;
24pub use self::payload::Payload;
25pub use self::service::H1Service;
26
27pub(super) use self::service::handle_io;
28use crate::util::Bytes;
29
30/// A message passed to an HTTP/1 request or response encoder.
31///
32/// Encoding a message starts with [`Message::Item`]. If the head declares a
33/// body, zero or more [`Message::Chunk(Some(_))`](Message::Chunk) values follow,
34/// and [`Message::Chunk(None)`](Message::Chunk) completes the body. The final
35/// `None` writes the chunked terminator when required and verifies that a
36/// fixed-length body supplied all declared bytes.
37///
38/// A new [`Message::Item`] must not be encoded until the preceding body has
39/// completed. Body chunks should be non-empty; use `Message::Chunk(None)` to
40/// complete the body explicitly.
41#[derive(Debug)]
42pub enum Message<T> {
43 /// A complete request or response head, including the body-size metadata
44 /// required by the concrete codec.
45 Item(T),
46 /// Body bytes, or `None` to complete the current body.
47 Chunk(Option<Bytes>),
48}
49
50impl<T> From<T> for Message<T> {
51 fn from(item: T) -> Self {
52 Message::Item(item)
53 }
54}
55
56/// Payload state reported by the HTTP client codec.
57///
58/// Unlike [`PayloadType`], which contains the decoder for an incoming HTTP/1
59/// payload, this enum only reports whether the client codec has no payload, a
60/// message body, or an upgraded connection stream.
61#[derive(Debug, Clone, Copy, PartialEq, Eq)]
62pub enum MessageType {
63 /// The message has no payload.
64 None,
65 /// The message has a body decoded according to its HTTP framing.
66 ///
67 /// This includes fixed-length, chunked, and connection-close-delimited
68 /// bodies.
69 Payload,
70 /// The response switched protocols, so subsequent bytes belong to the
71 /// upgraded connection rather than an HTTP message body.
72 Stream,
73}
74
75#[derive(thiserror::Error, Clone, Debug)]
76/// Errors that can occur while dispatching HTTP/1 requests.
77///
78/// The default [`ResponseError`](super::ResponseError) implementation maps
79/// header-count and message-head size failures to
80/// `431 Request Header Fields Too Large`, other decoding failures to
81/// `400 Bad Request`, request and payload timeouts to `408 Request Timeout`,
82/// and response encoding or body-stream failures to
83/// `500 Internal Server Error`.
84pub enum ProtocolError {
85 /// HTTP request parsing failed.
86 #[error("Parse error: {0}")]
87 Decode(#[from] super::error::DecodeError),
88
89 /// HTTP response encoding failed.
90 #[error("Encode error: {0}")]
91 Encode(#[from] super::error::EncodeError),
92
93 /// The request head did not complete within the configured timeout.
94 #[error("Request did not complete within the specified timeout")]
95 SlowRequestTimeout,
96
97 /// The request payload did not complete within the configured timeout.
98 #[error("Payload did not complete within the specified timeout")]
99 SlowPayloadTimeout,
100
101 /// The response body stream returned an error.
102 #[error("Response body processing error: {0}")]
103 ResponsePayload(Rc<dyn std::error::Error>),
104}
105
106impl super::ResponseError for ProtocolError {
107 fn error_response(&self) -> super::Response {
108 match self {
109 ProtocolError::Decode(
110 super::error::DecodeError::MaxHeaders | super::error::DecodeError::TooLarge(_),
111 ) => super::Response::RequestHeaderFieldsTooLarge().into(),
112 ProtocolError::Decode(super::error::DecodeError::StartLineTooLong(_)) => {
113 super::Response::UriTooLong().into()
114 }
115 ProtocolError::Decode(super::error::DecodeError::UnsupportedTransferCoding) => {
116 super::Response::NotImplemented().into()
117 }
118 ProtocolError::Decode(_) => super::Response::BadRequest().into(),
119 ProtocolError::SlowRequestTimeout | ProtocolError::SlowPayloadTimeout => {
120 super::Response::RequestTimeout().into()
121 }
122 ProtocolError::Encode(_) | ProtocolError::ResponsePayload(_) => {
123 super::Response::InternalServerError().into()
124 }
125 }
126 }
127}