Skip to main content

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}