http.rb Documentation

repository·main·Indexed 25 days ago

https://github.com/httprb/http

A high-performance Ruby HTTP client library that implements the HTTP protocol natively using the llhttp native extension for parsing. It supports persistent connections, response body streaming, Ruby pattern matching for responses, and a chainable session API. The library includes built-in features for RFC 7234 compliant caching and request/response logging, as well as flexible configuration via HTTP::Options.

Tokens
8.2K
Snippets
43
Records
67
Agent score
84%

What's inside http.rb

  1. Replace build_request with HTTP::Request::Builder in v6

    main

    The build_request method has been removed from Client, Session, and the top-level HTTP module in v6.

    Action: Use HTTP::Request::Builder to construct requests.

    # v6
    options = HTTP::Options.new(headers: {"Accept" => "application/json"}, json: {name: "test"})
    builder = HTTP::Request::Builder.new(options)
    request = builder.build(:post, "https://example.com")
  2. Thread safety and sessions

    main

    Chainable configuration methods (like .headers, .timeout, .auth) return an HTTP::Session. These sessions are safe to share across threads because they create a fresh HTTP::Client for every request.

    # Build a session once, use it from any thread
    session = HTTP.headers("Accept" => "application/json")
                  .timeout(10)
                  .auth("Bearer token")
    
    threads = 10.times.map do
      Thread.new { session.get("https://example.com/api/data") }
    end
    threads.each(&:join)
  3. Use HTTP.persistent with connection pooling in v6

    main

    In v6, HTTP.persistent returns an HTTP::Session that pools one persistent HTTP::Client per origin. This enables automatic support for cross-origin redirects (which previously raised StateError in v5) because each origin now gets its own connection.

    Chaining on a persistent session now correctly shares the parent's connection pool. Use session.close to shut down all pooled connections.

    # v6
    session = HTTP.persistent("https://api.example.com")
    session.is_a?(HTTP::Session) # => true
    # Cross-origin redirects work — each origin gets its own connection
    session.close # shuts down all pooled connections
    
    # Chaining works correctly and shares the parent's connection pool
    HTTP.persistent("https://api.example.com").headers("X-Token" => "abc").get("/users")
  4. Handle HTTP::URI immutability and changes in v6

    main

    In v6, HTTP::URI objects are effectively immutable. Setter methods like scheme=, host=, port=, path=, query=, etc., have been removed.

    Action:

    • To modify a URI, construct a new one using HTTP::URI.parse with the full string.
    • Use URI.encode_www_form from the Ruby stdlib instead of uri.query_values= or HTTP::URI.form_encode.
    • Use HTTP.get(..., params: { ... }) for easy query parameter handling.
    • Use HTTP::URI.parse instead of HTTP::URI.new(addressable_uri).
  5. Configure timeouts in v6

    main

    HTTP.rb v6 introduces several changes to timeout behavior:

    1. No implicit defaults: Omitted per-operation timeouts (write and connect) no longer default to 0.25s. They now have no timeout limit. If you require the old behavior, set all three explicitly.
    2. Stricter parsing: HTTP.timeout rejects unknown keys and mixed short/long forms (e.g., mixing read and read_timeout).
    3. Combined timeouts: You can now combine global timeouts with specific per-operation timeouts.

    Action: If you relied on the 0.25s default, set read, write, and connect explicitly.

  6. Migrate to keyword arguments for public API methods in v6

    main

    Methods across the public API (including all HTTP verb methods, request, follow, retriable, URI.new, Request.new, Response.new, Options.new, Client.new, and Session.new) now require keyword arguments. Passing an explicit Hash as a positional argument is no longer supported and will raise an ArgumentError.

    Action: If you have an options hash, use the double-splat (**) operator to pass it as keyword arguments.

    # v5 — both work
    HTTP.get("https://example.com", body: "data")
    HTTP.get("https://example.com", {body: "data"})
    
    # v6 — keywords only
    HTTP.get("https://example.com", body: "data")
    
    opts = {body: "data", headers: {"Content-Type" => "text/plain"}}
    HTTP.get("https://example.com", **opts) # note the double-splat
  7. Migrate from HTTP::Client to HTTP::Session in v6

    main

    In HTTP.rb v6, all chainable configuration methods (such as .headers, .timeout, .cookies, .auth, .follow, .via, .use, .encoding, .nodelay, .basic_auth, and .accept) now return a thread-safe HTTP::Session instead of an HTTP::Client.

    Session creates a fresh Client for each request, making it safe to share across threads. The HTTP verb methods (.get, .post, etc.) and .default_options behave the same way.

    Action: Update any code that performs type checks like is_a?(HTTP::Client) on the return value of chainable methods to check for HTTP::Session instead.

    # v5
    client = HTTP.headers("Accept" => "application/json")
    client.is_a?(HTTP::Client) # => true
    
    # v6
    session = HTTP.headers("Accept" => "application/json")
    session.is_a?(HTTP::Session) # => true
    session.get("https://example.com") # works the same
  8. Use persistent connections

    main

    To reuse connections for multiple requests to the same origin, use HTTP.persistent. When combined with base_uri, it allows efficient sequential requests.

    Note: The HTTP::Session returned by HTTP.persistent is not thread-safe. For thread-safe persistent connections, use the connection_pool gem.

    # Sequential persistent requests
    HTTP.base_uri("https://api.example.com/v1").persistent do |http|
      http.get("users")
      http.get("posts")
    end
    
    # Thread-safe persistent connections using connection_pool gem
    pool = ConnectionPool.new(size: 5) { HTTP.persistent("https://example.com") }
    pool.with { |http| http.get("/path") }
  9. Use status.code for Integer methods on Response::Status in v6

    main

    In v6, Response::Status is no longer a Delegator subclass of Integer. While it supports Comparable and Forwardable (allowing ==, <=>, to_i, and predicates like .ok?), it no longer delegates all Integer methods.

    Action: Replace direct Integer method calls on status objects with status.code.<method>.

    status = response.status
    
    # Still works in v6
    status.to_i          # => 200
    status == 200        # => true
    status.ok?           # => true
    status.code          # => 200
    
    # v5 only — breaks in v6
    # Use status.code instead:
    status.code.even? 
    status.code.between?(200, 299)
    status.code + 1
  10. Access headers on Request and Response in v6

    main

    In v6, Request and Response no longer include Headers::Mixin. You can no longer use bracket access ([] or []=) directly on the request or response objects.

    Action: Access headers via the .headers method.

    # v6
    response = HTTP.get("https://example.com")
    response.headers["Content-Type"]    # => "text/html"
    
    request = HTTP.request(:get, "https://example.com")
    request.headers["Authorization"] = "Bearer token"
  11. Install http.rb

    main

    You can install http.rb using Bundler or by installing the gem directly.

    To use with Bundler, add this to your Gemfile:

    gem "http"

    Then run bundle.

    To install via command line:

    gem install http

    In your Ruby code, require the library:

    require "http"
    gem "http"
    # then run
    $ bundle
    
    # or
    $ gem install http
    
    # in ruby
    require "http"