M => +11 -1
@@ 1,11 1,15 @@
use super::Exhausted;
/// Headers builder used by [`RequestBuilder`] and [`ResponseBuilder`].
pub(crate) struct HeadersBuilder<'a> {
/// Buffer to write headers to.
pub(crate) buffer: &'a mut [u8],
/// The offset in the buffer to write additional headers to.
pub(crate) index: usize,
}
impl<'a> HeadersBuilder<'a> {
/// Add a single header.
pub fn add_header(mut self, header: &str, value: &str) -> Result<Self, Exhausted> {
let size = header.len() + 2 + value.len() + 2;
if self.buffer.len() - self.index < size + 2 {
@@ 29,6 33,7 @@ impl<'a> HeadersBuilder<'a> {
Ok(self)
}
/// Finish constructing the headers.
#[inline]
pub fn finish(self) -> (&'a [u8], &'a mut [u8]) {
self.buffer[self.index..][..2].copy_from_slice(b"\r\n");
@@ 37,12 42,14 @@ impl<'a> HeadersBuilder<'a> {
}
}
/// Headers parser used by [`RequestBuilder`] and [`ResponseBuilder`].
#[derive(Debug)]
pub(crate) struct HeadersParser<'a, 'b> {
headers: &'b [&'a str],
}
impl<'a, 'b> HeadersParser<'a, 'b> {
/// Parse a list of headers.
pub fn parse(mut data: &'a [u8], storage: &'b mut [&'a str]) -> Result<(Self, &'a [u8]), InvalidHeader> {
'l: for index in 0..storage.len() {
if data.len() < 2 {
@@ 85,9 92,12 @@ impl<'a, 'b> HeadersParser<'a, 'b> {
}
}
/// Errors that may occur during parsing.
pub enum InvalidHeader {
/// Data is missing.
Truncated,
Exhausted,
/// A header contains invalid UTF-8.
InvalidUTF8,
/// A header is missing a value.
NoValue,
}
M src/lib.rs => src/lib.rs +1 -0
@@ 9,6 9,7 @@ use core::fmt;
pub use request::{Method, RequestBuilder, RequestParser, InvalidRequest};
pub use response::{Status, ResponseBuilder, ResponseParser, InvalidResponse};
+/// An error that is returned if the buffer is too small.
pub struct Exhausted;
impl fmt::Debug for Exhausted {
M src/request.rs => src/request.rs +10 -2
@@ 1,5 1,6 @@
use super::{Exhausted, header::{HeadersBuilder, HeadersParser, InvalidHeader}};
+/// All methods that may be used in requests.
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum Method {
Get,
@@ 13,9 14,11 @@ pub enum Method {
Patch,
}
+/// Utility for creating HTTP requests.
pub struct RequestBuilder<'a>(HeadersBuilder<'a>);
impl<'a> RequestBuilder<'a> {
+ /// Begin creating a new HTTP request.
pub fn new(buffer: &'a mut [u8], path: &str, method: Method) -> Result<Self, Exhausted> {
let s = match method {
Method::Get => "GET",
@@ 52,17 55,21 @@ impl<'a> RequestBuilder<'a> {
Ok(Self(HeadersBuilder { buffer, index: size }))
}
+ /// Append a single header.
#[inline]
pub fn add_header(self, header: &str, value: &str) -> Result<Self, Exhausted> {
self.0.add_header(header, value).map(Self)
}
+ /// Construct the final request, returning the slice with the headers and the remainder
+ /// of the buffer.
#[inline]
pub fn finish(self) -> (&'a [u8], &'a mut [u8]) {
self.0.finish()
}
}
+/// Utility for parsing HTTP requests.
#[derive(Debug)]
pub struct RequestParser<'a, 'b> {
pub method: Method,
@@ 71,6 78,7 @@ pub struct RequestParser<'a, 'b> {
}
impl<'a, 'b> RequestParser<'a, 'b> {
+ /// Parse a HTTP request, returning the method, path, headers and any additional data if successful.
pub fn parse(data: &'a [u8], storage: &'b mut [&'a str]) -> Result<(Self, &'a [u8]), InvalidRequest<'a>> {
for (i, w) in data.windows(2).enumerate() {
if w == b"\r\n" {
@@ 113,12 121,14 @@ impl<'a, 'b> RequestParser<'a, 'b> {
Err(InvalidRequest::Truncated)
}
+ /// Get the value of the header with the given name.
#[inline]
pub fn header(&self, header: &str) -> Option<&'a str> {
self.headers.get(header)
}
}
+/// Errors that may occur while parsing a request.
#[derive(Debug)]
pub enum InvalidRequest<'a> {
InvalidMethod(&'a [u8]),
@@ 126,7 136,6 @@ pub enum InvalidRequest<'a> {
UnsupportedVersion(&'a [u8]),
TrailingGarbage(&'a [u8]),
Truncated,
- Exhausted,
InvalidUTF8,
NoValue,
}
@@ 135,7 144,6 @@ impl From<InvalidHeader> for InvalidRequest<'_> {
fn from(h: InvalidHeader) -> Self {
match h {
InvalidHeader::Truncated => Self::Truncated,
- InvalidHeader::Exhausted => Self::Exhausted,
InvalidHeader::InvalidUTF8 => Self::InvalidUTF8,
InvalidHeader::NoValue => Self::NoValue,
}
M src/response.rs => src/response.rs +10 -2
@@ 1,5 1,6 @@
use super::{Exhausted, header::{HeadersBuilder, HeadersParser, InvalidHeader}};
+/// All status codes that responses can return.
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum Status {
// 1xx
@@ 72,9 73,11 @@ pub enum Status {
NetworkAuthenticationRequired,
}
+/// Utility for creating HTTP responses.
pub struct ResponseBuilder<'a>(HeadersBuilder<'a>);
impl<'a> ResponseBuilder<'a> {
+ /// Create a new HTTP response by writing into the given buffer.
pub fn new(buffer: &'a mut [u8], status: Status) -> Result<Self, Exhausted> {
let s = match status {
// 1xx
@@ 164,17 167,21 @@ impl<'a> ResponseBuilder<'a> {
Ok(Self(HeadersBuilder { buffer, index: size }))
}
+ /// Append a single header.
#[inline]
pub fn add_header(self, header: &str, value: &str) -> Result<Self, Exhausted> {
self.0.add_header(header, value).map(Self)
}
+ /// Finish constructing the HTTP response, returning the slice with the headers and
+ /// the remaining buffer slice.
#[inline]
pub fn finish(self) -> (&'a [u8], &'a mut [u8]) {
self.0.finish()
}
}
+/// Utility for parsing HTTP responses.
#[derive(Debug)]
pub struct ResponseParser<'a, 'b> {
pub status: Status,
@@ 182,6 189,7 @@ pub struct ResponseParser<'a, 'b> {
}
impl<'a, 'b> ResponseParser<'a, 'b> {
+ /// Parse a HTTP response, returning the status and any additional data if successful.
pub fn parse(data: &'a [u8], storage: &'b mut [&'a str]) -> Result<(Self, &'a [u8]), InvalidResponse<'a>> {
for (i, w) in data.windows(2).enumerate() {
if w == b"\r\n" {
@@ 276,18 284,19 @@ impl<'a, 'b> ResponseParser<'a, 'b> {
Err(InvalidResponse::Truncated)
}
+ /// Get the value of the header with the given name.
#[inline]
pub fn header(&self, header: &str) -> Option<&'a str> {
self.headers.get(header)
}
}
+/// Errors that may occur while parsing a response.
#[derive(Debug)]
pub enum InvalidResponse<'a> {
InvalidStatus(&'a [u8]),
UnsupportedVersion(&'a [u8]),
Truncated,
- Exhausted,
InvalidUTF8,
NoValue,
}
@@ 296,7 305,6 @@ impl From<InvalidHeader> for InvalidResponse<'_> {
fn from(h: InvalidHeader) -> Self {
match h {
InvalidHeader::Truncated => Self::Truncated,
- InvalidHeader::Exhausted => Self::Exhausted,
InvalidHeader::InvalidUTF8 => Self::InvalidUTF8,
InvalidHeader::NoValue => Self::NoValue,
}