http.rb: a chainable Ruby HTTP client built on llhttp
HTTP (The Gem! a.k.a. http.rb) - a fast Ruby HTTP client with a chainable API, streaming support, and timeouts
At a glance
- What is it?
- http.rb implements HTTP in Ruby and delegates parsing to the llhttp native extension. It is aimed at Ruby developers who want a chainable request API, streaming bodies and per-request timeouts without Net::HTTP boilerplate.
- Who is it for?
- Adopt http.rb if you want a chainable Ruby client with streaming bodies, base URIs and per-request timeouts, and you are on Ruby 3.2 through 4.0. Do not adopt it if you need a thread-safe shared persistent session out of the box, since the README states the persistent session is not thread-safe and points to the connection_pool gem instead.
- Can I use it commercially?
- Yes. MIT 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 36 days ago.
- What is it written in?
- Mainly Ruby, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 30, 2026, and from our analysis. They are not legal advice.
Editorial analysis
What http.rb replaces, and for whom
The README is blunt about the target: http.rb "isn't just yet another wrapper around Net::HTTP. It implements the HTTP protocol natively and outsources the parsing to native extensions." That single sentence defines the audience. If your Ruby code currently reaches for Net::HTTP and you find yourself writing the same header assembly, redirect handling and timeout wiring in every file, http.rb is the replacement the project is pitching. The chainable surface is explicitly compared to Python's Requests library, which tells you the intended feel: build a request by chaining methods, then execute it.
The second audience is narrower. Because parsing runs through llhttp, a native extension, the gem ships compiled code. That matters for anyone deploying to platforms where building native gems is awkward or slow, and it matters for teams that audit their dependency tree for C extensions. The README lists the payoff as performance, but it does not publish numbers, so treat the speed argument as a design claim rather than a measured result. What the README does document concretely is the feature set: persistent connections and fine-grained timeouts, plus a maturity claim that the project places among the most mature Ruby HTTP clients.
How the request pipeline is put together
The architecture visible in the README has three layers. At the bottom sits the llhttp parser, a native extension that handles wire-format parsing. Above it, HTTP itself is implemented in Ruby rather than C, which is the trade-off the project chose: Ruby code for protocol logic, C for the parsing hot path. On top sits the chainable API, and the README names the concrete return type. Methods like .headers, .timeout and .auth return an HTTP::Session, and that session creates a fresh HTTP::Client for every request.
That last detail is the most useful thing in the whole document, because it explains the threading story. A configured session is safe to share across threads precisely because it does not hold a connection; each call gets its own client. Persistent connections break that model. HTTP.persistent returns a session that pools one HTTP::Client per origin, and the README states plainly that the session itself is not thread-safe. So the data flow differs depending on which entry point you pick: stateless chaining allocates per request, persistent chaining reuses a pooled client per origin and therefore needs external synchronization.
Responses come back as HTTP::Response objects, with .body returning an HTTP::Response::Body. The README shows that body supports readpartial, which returns successive chunks and finally nil. That is the streaming path: bind the body to a local variable and loop until the stream ends. Redirects are handled inside the session, and the README notes that cross-origin redirects cause the session to open a separate persistent connection for each origin in the chain.
Installing the http gem and making a first request
The README gives two install paths. In a Bundler-managed application, add the gem to your Gemfile, then run bundle:
gem "http"$ bundleOutside Bundler, install it directly and require it in your program:
$ gem install httprequire "http"The first real call is a one-liner. HTTP.get returns an HTTP::Response, and calling to_s on it gives you the body:
HTTP.get("https://github.com").to_sOmit to_s and you get the response object with status and headers instead. If you want to consume a large body in pieces, grab the body and call readpartial repeatedly until it returns nil:
body = HTTP.get("https://github.com").body
body.readpartial
body.readpartialFor anything beyond a single call, set a base URI so the scheme and host are not repeated. The README states that relative paths resolve per RFC 3986, and shows combining this with persistent to reuse the connection across several requests:
HTTP.base_uri("https://api.example.com/v1").persistent do |http|
http.get("users")
http.get("posts")
endThread safety is conditional, not a property of the gem
This is the limitation worth reading twice. It is easy to skim the thread-safety example in the README, see ten threads sharing a session, and conclude that http.rb is thread-safe. It is thread-safe for that specific shape. The README says configured sessions are safe to share because chainable configuration returns an HTTP::Session that creates a fresh HTTP::Client per request. Nothing is shared except configuration.
The moment you want connection reuse across threads, the guarantee disappears. HTTP.persistent returns a session that pools one HTTP::Client per origin, and the README states the session itself is not thread-safe. The recommended workaround is the connection_pool gem, wrapping a persistent session in a pool and checking out a connection with pool.with. That is a real extra dependency and a real extra concept to manage, and it is the point at which http.rb stops being a drop-in convenience.
There is a second boundary. The README lists supported Ruby versions as 3.2, 3.3, 3.4 and 4.0, and says support is only provided for those. It acknowledges the library may work on other versions but will not be supported, and it offers a maintainer path for anyone who wants another implementation covered. On an older runtime, that is a hard stop rather than a soft warning.
http.rb against Faraday and HTTParty
The related searches around this project cluster on Faraday and HTTParty, and the difference is architectural rather than cosmetic. Faraday is built around an adapter and middleware stack: you compose request and response processing through a pipeline, and the underlying HTTP transport is pluggable. That makes Faraday the natural pick when you need to insert retries, logging, authentication or instrumentation as reusable middleware shared across many clients. You pay for it with indirection, since the call path runs through the stack.
HTTParty takes the opposite approach, mixing request methods into a class so that a model-like object can declare its base URI and perform calls. It is a convenient fit for wrapping a single API in a class, and it leans on Net::HTTP underneath. http.rb sits between the two: no middleware pipeline, no class-level DSL, just method chaining over an HTTP implementation written in Ruby with llhttp doing the parsing. If you want to swap transports, http.rb is the wrong tool. If you want a small chainable client with streaming and timeouts and no adapter layer to reason about, it is the right one.
Upgrade cost, release cadence and the MIT licence
The repository carries an UPGRADING.md, and the README points to it as a detailed migration guide between major versions. That file is the first thing to read before moving an application across a major boundary, because the README gives no compatibility promises of its own. The release history shows v6.0.3, v6.0.2 and v6.0.1 all published within roughly a month of each other in early 2026, which is the pattern of patch releases settling a major line rather than a long-stable series. The last push to the repository was on 2026-08-25.
On licensing, the gem is MIT and LICENSE.txt sits at the repository root alongside the gemspec. MIT is permissive, and the practical implication for most teams is that the obligation is limited to retaining the copyright notice and licence text. The README also carries a copyright line covering 2011 to 2026 and naming four maintainers. None of this is legal advice, and if your organisation has a policy review step for new dependencies, the file to hand over is LICENSE.txt.
The upgrade surface is not just the gem. Because parsing runs through the llhttp native extension, a version bump can mean a recompile on deploy targets that build gems from source. That is a deployment cost, not an API cost, and it is worth checking before a bulk dependency update.
Editorial conclusion
Adopt http.rb if you want a chainable Ruby client with streaming bodies, base URIs and per-request timeouts, and you are on Ruby 3.2 through 4.0. Do not adopt it if you need a thread-safe shared persistent session out of the box, since the README states the persistent session is not thread-safe and points to the connection_pool gem instead. Before upgrading an existing app, read UPGRADING.md for the major-version migration steps, and confirm your Gemfile resolves to v6.0.3.
Frequently asked questions
How do I install the http.rb gem?
Add gem "http" to your Gemfile and run bundle, or install it directly with gem install http. Then require "http" inside your Ruby program to pull it in as a dependency.
Is http.rb thread-safe?
Configured sessions are safe to share across threads because chainable configuration returns an HTTP::Session that creates a fresh HTTP::Client per request. A persistent session is not thread-safe, and the README points to the connection_pool gem for thread-safe persistent connections.
Which Ruby versions does http.rb support?
The README lists Ruby 3.2, 3.3, 3.4 and 4.0 as the tested versions. It notes the library may work on other versions but that support is only provided for the listed ones.
How do I stream a response body with http.rb?
Call HTTP.get and take the .body, which returns an HTTP::Response::Body. Call readpartial on that object repeatedly until it returns nil.
Official sources
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.
[](https://hysenlabs.com/projects/httprb-http)