Open-source project
seanmonstar/httparse avatar
seanmonstar/httparse

httparse and the case for a push HTTP/1.x parser in Rust

A push parser for the HTTP 1.x protocol in Rust.

717 stars138 forksRustApache-2.0

At a glance

What is it?
httparse is a zero-copy HTTP/1.x parser for Rust that reads bytes you already own, reports partial parses instead of buffering, and runs without the standard library. It solves header parsing for people who need control over allocation and memory.
Who is it for?
httparse fits servers, proxies and embedded Rust targets that already own their socket buffers and want to parse HTTP/1.x without copying; it does not fit teams that need HTTP/2 support, a high level API, or automatic body decoding. Check three things before adopting it: that your Rust toolchain is at least 1.59 with edition 2021, that you can live with the push model and its is_partial loop, and that you are comfortable turning off the default std feature on no_std targets.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 115 days ago.
What is it written in?
Mainly Rust, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Pushing bytes at a parser instead of handing over a buffer

Most HTTP parsing code is pull style. You collect a whole request or response into a buffer, pass it to a parser, and the parser tells you what it found. httparse inverts that. It is described as a push parser for the HTTP 1.x protocol, and the usage example matches: you call a parse method with the bytes you have, and the parser reports back what it made of them.

That inversion exists to remove two costs from the hot path. The crate's own description says it "Avoids allocations. No copy." There is no owned string type in the example, and no request body is assembled anywhere in the shown code. The parser writes into a fixed array of header slots that you allocate yourself, on the stack, before the loop starts.

The audience is therefore specific. If you are writing a server, a proxy, a router or a device that speaks HTTP over a socket you already own, the bytes are already in your buffer and httparse lets you read them in place. If you are writing an application that fetches URLs with a full featured client that manages cookies, redirects, compression and connection pools for you, this crate is far below the level you need. It parses messages. It does not manage them for you.

is_partial and is_complete are the whole retry loop

The mechanism that makes a push parser usable is the pair of status flags on the parse result. The README example feeds a deliberately truncated request and asserts that the result is partial:

rust
let mut headers = [httparse::EMPTY_HEADER; 64];
let mut req = httparse::Request::new(&mut headers);

let buf = b"GET /index.html HTTP/1.1\r\nHost";
assert!(req.parse(buf)?.is_partial());

The next chunk completes the same request and the flag flips:

rust
let buf = b"GET /index.html HTTP/1.1\r\nHost: example.domain\r\n\r\n";
assert!(req.parse(buf)?.is_complete());

The comment in the example says it plainly: "a partial request, so we try again once we have more data". This is the contract you inherit. You read from the socket, hand the bytes to the parser, and when the status says partial you read more and call parse again with the fuller view of the request. The parser does not block, does not own a socket, and does not buffer for you.

The consequence is that a 64 element header array has to be sized by you before the first call, and a request with more headers than that is a case you have to decide about rather than one the crate resolves.

The header array is a fixed budget you choose up front

The first line of the usage example is the design decision in miniature: `let mut headers = [httparse::EMPTY_HEADER; 64];`. The caller provides the storage, the parser fills it in, and nothing is allocated per request. That is what makes the no copy claim plausible in practice, since the parsed header names and values borrow from the input bytes rather than being copied into owned buffers.

It also means the header count is a hard ceiling set before traffic starts. Sixty four is the number in the example, not a limit the crate enforces for you, so an application expecting a hundred headers simply sizes the array differently. The cost of that choice is that the array lives for the duration of the request, so its size feeds into your per connection memory budget, and a parser instance is bound to a single message at a time.

Because storage is caller owned, the crate fits naturally into a design where the connection loop reuses one buffer across many messages. It also means the crate does not suit code that wants a convenient owned value it can send to another thread or park in a queue.

Turning the std feature off for no_std targets

The README states that httparse "Works with `no_std`, simply disable the `std` Cargo feature." The manifest confirms the shape of that instruction, because the feature list makes std a default:

toml
[features]
default = ["std"]
std = []

The flag itself is empty, so disabling it does not remove a large dependency tree, it removes the assumption that the standard library is present. For a microcontroller or a custom kernel target, that is the difference between a crate that compiles and one that does not.

The same manifest sets `rust-version = "1.59"` and `edition = "2021"`. That floor is worth checking against your own toolchain, particularly in embedded workspaces where the pinned compiler tends to lag. The keywords list also names `no_std`, and the crate categories include `no-std`, which tells you the embedded use case is intended rather than accidental.

One thing the README does not do is show a complete no_std project, with a build script, a target triple or a linker configuration. The guidance is one sentence long, so a reader without embedded experience will find the surrounding scaffolding unspecified.

What the Cargo.toml reveals about testing and fuzzing

The manifest and the repository layout say more about how this crate is worked on than the short README does. There is a `fuzz/` directory at the top level, alongside `tests/` and `benches/`, so malformed input is treated as a first class concern rather than an afterthought for a crate that reads bytes off a network.

Benchmarks are configured rather than improvised. The manifest pulls in `criterion` and `rand` as dev dependencies, declares a bench named `parse` with `harness = false`, and turns on `lto`, `codegen-units = 1` and `opt-level = 3` for the bench profile. That is a deliberate measurement setup for a crate whose main claim is speed.

The lint configuration is the smallest but most telling detail. Under `[lints.rust]`, the crate sets `unexpected_cfgs` to warn and registers a check for `httparse_disable_simd`. In other words the crate contains a conditional compilation path that can be turned off, and the lint exists so that turning it on is an explicit, visible choice rather than an unknown cfg that silently never matches. The presence of a disable switch suggests the default path uses some vectorized routine, though the README does not describe the difference in behaviour or speed between the two settings.

What httparse deliberately leaves to somebody else

The scope is narrow on purpose, and the limits are visible in the dependency list. httparse declares no dependencies at all in its `[package]` section. It does not read a socket, does not manage a connection, does not decode a chunked body, and does not handle compression. There is no TLS story here either, since encrypted bytes would need to be decrypted and unwrapped before they reach a parser that only understands HTTP/1.x syntax.

The version boundary is equally clear. The crate is scoped to the HTTP 1.x protocol, so an HTTP/2 or HTTP/3 connection has no use for it, and a service that speaks both needs a different parser for the newer protocol. Nothing in the README or the manifest mentions HTTP/2, HTTP/3, trailers or upgrade handling, so treat each of those as unsupported rather than as undocumented extras.

There is a second limitation that is more about ergonomics than features. The API is a stateful push parser, which means the caller owns the retry loop, the buffer lifetime and the header budget. There is no convenience function that takes a complete request and returns an owned struct, and the README example is the entire usage story it offers. If your team wants a high level request type, this crate is the wrong layer.

Two licences and a gap between the last release and the last push

httparse is offered under a dual licence. The manifest records `license = "MIT OR Apache-2.0"`, and the README lists both, with LICENSE-APACHE and LICENSE-MIT files in the repository root. The contribution note says that unless stated otherwise, contributions are dual licensed under the same terms, with no additional conditions. The repository is not archived, and the last push was on 2026-06-30, so the source tree has been touched more recently than the most recent published release, which was v1.10.1 on 2025-03-03. Earlier tags in the release list include v1.10.0 on 2025-02-04 and v1.9.5 on 2024-09-30.

So there are two clocks to track. The published crate on crates.io is at 1.10.1, while the repository has moved on since that tag. Nothing in the README tells you whether the current master contains changes you would need, or whether the tag is the sensible thing to depend on, so pinning to the released version is the predictable choice for most projects.

Licensing itself is simple to reason about. MIT is permissive and Apache-2.0 includes an explicit patent grant and requires notice retention. Choosing between them is a matter of what your organisation already uses. This is not legal advice, and it is worth confirming with whoever owns licence policy on your side.

Editorial conclusion

httparse fits servers, proxies and embedded Rust targets that already own their socket buffers and want to parse HTTP/1.x without copying; it does not fit teams that need HTTP/2 support, a high level API, or automatic body decoding. Check three things before adopting it: that your Rust toolchain is at least 1.59 with edition 2021, that you can live with the push model and its is_partial loop, and that you are comfortable turning off the default std feature on no_std targets.

Frequently asked questions

Does httparse support HTTP/2 or HTTP/3?

No. The crate is scoped to the HTTP 1.x protocol, and neither the README nor the Cargo.toml mentions HTTP/2 or HTTP/3 anywhere. For those protocols you need a different parser.

How do I use httparse in a no_std project?

The README says it works with no_std and instructs you to disable the std Cargo feature. The manifest lists std in the default feature set, so that feature has to be turned off explicitly for a no_std build.

Is httparse actively maintained?

The repository is not archived and the last push was on 2026-06-30, while the most recent published release is v1.10.1 from 2025-03-03. If you depend on the crate, pin to a released version rather than tracking master.

Does httparse allocate memory while parsing a request?

The crate describes itself as avoiding allocations and copies, and the usage example backs that up by letting the caller provide the header array and passing the raw bytes straight into parse. You allocate the storage up front rather than per request.

Official sources

  1. License: Apache-2.0
  2. Project website
  3. README
  4. Releases
  5. seanmonstar/httparse on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/seanmonstar-httparse.svg)](https://hysenlabs.com/projects/seanmonstar-httparse)

Was this useful?