http_wire
A Rust library to serialize and parse HTTP/1.x requests and responses to/from their wire format (raw bytes).
Note: This crate only supports HTTP/1.0 and HTTP/1.1. HTTP/2 is not supported due to its binary framing, HPACK header compression, and multiplexed nature which make single request/response serialization impractical.
Usage
Add to your Cargo.toml:
[dependencies]
http_wire = "0.2"
Encoding (Serialization)
Use the WireEncode trait to serialize HTTP requests and responses to their wire format bytes.
Encoding Requests
use http_wire::WireEncode;
use http::Request;
use http_body_util::Empty;
use bytes::Bytes;
#[tokio::main]
async fn main() {
let request = Request::builder()
.method("GET")
.uri("/api/users")
.header("Host", "example.com")
.body(Empty::<Bytes>::new())
.unwrap();
let bytes = request.encode().await.unwrap();
}
Encoding Requests with Body
use http_wire::WireEncode;
use http::Request;
use http_body_util::Full;
use bytes::Bytes;
#[tokio::main]
async fn main() {
let body = r#"{"name":"John"}"#;
let request = Request::builder()
.method("POST")
.uri("/api/users")
.header("Host", "example.com")
.header("Content-Type", "application/json")
.body(Full::new(Bytes::from(body)))
.unwrap();
let bytes = request.encode().await.unwrap();
}
Encoding Responses
use http_wire::WireEncode;
use http::Response;
use http_body_util::Full;
use bytes::Bytes;
#[tokio::main]
async fn main() {
let response = Response::builder()
.status(200)
.header("Content-Type", "text/plain")
.body(Full::new(Bytes::from("Hello World")))
.unwrap();
let bytes = response.encode().await.unwrap();
}
Decoding (Parsing)
Use the WireDecode trait to parse raw HTTP bytes and determine message boundaries.
Parsing Request Length
Use RequestLength to determine the total length of an HTTP request, including its body, in a byte buffer:
use http_wire::WireDecode;
use http_wire::request::RequestLength;
fn main() {
let raw = b"GET /api/users HTTP/1.1\r\nHost: example.com\r\n\r\n";
if let Some(length) = RequestLength::decode(raw) {
println!("Request is {} bytes", length);
let request_bytes = &raw[..length];
} else {
println!("Incomplete request");
}
}
Parsing Response Status and Length
Use ResponseStatusCode to get both the HTTP status code and total length of a response:
use http_wire::WireDecode;
use http_wire::response::ResponseStatusCode;
use http::StatusCode;
fn main() {
let raw = b"HTTP/1.1 200 OK\r\nContent-Length: 5\r\n\r\nhello";
if let Some((status, length)) = ResponseStatusCode::decode(raw) {
println!("Status: {}, Length: {} bytes", status, length);
assert_eq!(status, StatusCode::OK);
let response_bytes = &raw[..length];
} else {
println!("Incomplete response");
}
}
Handling Chunked Transfer Encoding
Both decoders fully support chunked transfer encoding:
use http_wire::WireDecode;
use http_wire::response::ResponseStatusCode;
fn main() {
let raw = b"HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n5\r\nhello\r\n0\r\n\r\n";
if let Some((status, length)) = ResponseStatusCode::decode(raw) {
println!("Chunked response complete: {} bytes", length);
}
}
Stream Parsing Example
Use decoders to handle streaming data:
use http_wire::WireDecode;
use http_wire::request::RequestLength;
fn parse_stream(buffer: &[u8]) -> Option<(&[u8], &[u8])> {
if let Some(length) = RequestLength::decode(buffer) {
let (request, remaining) = buffer.split_at(length);
Some((request, remaining))
} else {
None
}
}
Error Handling
use http_wire::{WireEncode, WireError};
#[tokio::main]
async fn main() -> Result<(), WireError> {
let request = http::Request::builder()
.uri("/")
.body(http_body_util::Empty::<bytes::Bytes>::new())
.unwrap();
let bytes = request.encode().await?;
println!("Serialized {} bytes", bytes.len());
Ok(())
}
WireError has three variants:
Connection - HTTP connection error (handshake or send failed)
Sync - Internal synchronization error
UnsupportedVersion - HTTP version not supported (only HTTP/1.0 and HTTP/1.1 are supported)
Features
- Full support for chunked transfer encoding
- Handles requests and responses with or without bodies
- Case-insensitive header parsing
- Zero-copy parsing for determining message boundaries
- HTTP/1.0 and HTTP/1.1 support
License
MIT OR Apache-2.0