#![forbid(unsafe_code)]
mod b64;
mod body;
pub mod h2c;
mod parse;
pub mod write;
#[cfg(feature = "websocket")]
mod sha1;
#[cfg(feature = "websocket")]
pub mod ws;
pub use b64::{base64_decode, base64_encode, base64url_decode};
pub use body::{BodyDecoder, BodyEvent};
pub use parse::{has_token, parse_request, parse_response, parse_trailers, RequestParser};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Version {
Http10,
Http11,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct Header<'b> {
pub name: &'b [u8],
pub value: &'b [u8],
}
pub const EMPTY_HEADER: Header<'static> = Header { name: b"", value: b"" };
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Framing {
None,
Length(u64),
Chunked,
Close,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum TargetForm {
Origin,
Absolute,
Authority,
Asterisk,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Status<T> {
Complete(T),
Partial,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Request<'b, 'h> {
pub method: &'b [u8],
pub target: &'b [u8],
pub form: TargetForm,
pub version: Version,
pub headers: &'h [Header<'b>],
pub framing: Framing,
pub keep_alive: bool,
pub expect_continue: bool,
pub upgrade: Option<&'b [u8]>,
pub host: Option<&'b [u8]>,
pub head_len: usize,
}
impl<'b> Request<'b, '_> {
pub fn header(&self, name: &[u8]) -> Option<&'b [u8]> {
self.headers.iter().find(|h| h.name.eq_ignore_ascii_case(name)).map(|h| h.value)
}
pub fn wants_continue(&self) -> bool {
self.expect_continue && self.framing != Framing::None
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Response<'b, 'h> {
pub version: Version,
pub status: u16,
pub reason: &'b [u8],
pub headers: &'h [Header<'b>],
pub framing: Framing,
pub keep_alive: bool,
pub upgraded: bool,
pub head_len: usize,
}
impl<'b> Response<'b, '_> {
pub fn header(&self, name: &[u8]) -> Option<&'b [u8]> {
self.headers.iter().find(|h| h.name.eq_ignore_ascii_case(name)).map(|h| h.value)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Limits {
pub max_request_line: usize,
pub max_head: usize,
pub max_headers: usize,
pub max_header_line: usize,
pub max_body: u64,
pub max_chunk_line: usize,
pub max_trailers: usize,
}
impl Default for Limits {
fn default() -> Self {
Limits {
max_request_line: 8 * 1024,
max_head: 16 * 1024,
max_headers: 100,
max_header_line: 8 * 1024,
max_body: 10 * 1024 * 1024,
max_chunk_line: 1024,
max_trailers: 8 * 1024,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Error {
InvalidMethod,
InvalidTarget,
InvalidVersion,
VersionNotSupported,
RequestLineTooLong,
HeadTooLarge,
TooManyHeaders,
HeaderLineTooLong,
InvalidHeaderName,
InvalidHeaderValue,
WhitespaceBeforeColon,
ObsFold,
BareCr,
BareLf,
MissingHost,
DuplicateHost,
InvalidHost,
TeWithContentLength,
DuplicateContentLength,
InvalidContentLength,
InvalidTransferEncoding,
ChunkedNotFinal,
UnsupportedTransferCoding,
TransferEncodingHttp10,
BodyTooLarge,
ExpectationFailed,
DuplicateUpgrade,
InvalidChunkSize,
ChunkSizeOverflow,
InvalidChunkExtension,
ChunkLineTooLong,
MissingChunkCrlf,
TrailersTooLarge,
ForbiddenTrailer,
UnexpectedEof,
InvalidStatus,
InvalidReason,
FramingHeader,
}
impl Error {
pub fn status(self) -> u16 {
use Error::*;
match self {
RequestLineTooLong => 414,
HeadTooLarge | TooManyHeaders | HeaderLineTooLong => 431,
VersionNotSupported => 505,
UnsupportedTransferCoding => 501,
BodyTooLarge => 413,
ExpectationFailed => 417,
_ => 400,
}
}
}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Debug::fmt(self, f)
}
}
impl std::error::Error for Error {}