Recent Releases of https://github.com/ncr/rack-proxy
https://github.com/ncr/rack-proxy - v2.0.1 — RequestBodyStream#read accepts an optional length (WebMock fix)
Fixed
- The bounded request body stream accepts
IO#read's optionallength
argument. Net::HTTP instrumentation layers that read the whole request body
at once (WebMock's adapter, for example) raisedArgumentErroragainst
2.0.0, failing the test suite of any app that proxies a request body. (#145)
- Ruby
Published by ncr 9 days ago
https://github.com/ncr/rack-proxy - v2.0.0 — HTTP framing and response hardening
Security hardening for request/response framing, resource limits, and Rack compatibility. This major release includes changes to non-streaming retries and response-hook types.
Breaking changes
- Non-streaming requests no longer automatically retry transport failures.
This prevents replay with consumed request input; applications that require
retries must implement them with a fresh body and an explicit retry policy. - Rack 3 response hooks receive arrays for repeated header values, including
Set-Cookie, and non-streaming statuses are integers. Update hooks that
assumed newline-separated headers or string statuses. Rack 2 retains its
newline-separated header representation. - Invalid or ambiguous HTTP framing is now rejected, incomplete responses fail,
and uploads without a known length use fresh chunked framing. Backends must
accept properly framed HTTP/1.1 requests and send valid, complete responses.
Security
- Reframe decoded request bodies without a
CONTENT_LENGTHusing chunked
encoding instead of sending body bytes afterContent-Length: 0. Bound
known-length input streams so excess bytes cannot become another backend
request; malformed lengths and prematurely ended uploads return400. - Reject ambiguous backend framing (
Transfer-EncodingwithContent-Length,
conflicting or invalid lengths, and unsupported transfer codings) with502. - Strip response headers named by the backend's
Connectionfields as well as
the standard hop-by-hop headers. - Enforce
max_response_lengthbefore buffering each chunk in non-streaming
mode, including rejecting declared oversized responses before reading them. - Detect premature EOF in fixed-length responses: return
502before sending
headers, or raise while streaming so the server aborts the incomplete transfer. - Disable Net::HTTP transport retries in non-streaming mode as well as streaming
mode, preventing replay with an already-consumed request body.
Fixed
- Return integer statuses in non-streaming mode and preserve multiple response
header values as arrays on Rack 3 (includingSet-Cookie). Rack 2 keeps its
newline-separated representation. - Remove entity headers forbidden by Rack on 1xx/204/304 responses. HEAD and
304 representation sizes no longer incorrectly trigger the response body cap. - Replace a remaining live-host test with a local fixture and simulate failed
DNS resolution without sending external DNS queries. Add offline adversarial
framing, size-limit, retry, andRack::Lintregression checks.
See the upgrade guide before upgrading.
Install with gem "rack-proxy", "~> 2.0".
Full changelog: https://github.com/ncr/rack-proxy/compare/v1.0.2...v2.0.0
- Ruby
Published by ncr 21 days ago
https://github.com/ncr/rack-proxy - v1.0.2 — dev-dependency security bump (json), housekeeping
Housekeeping — no library behavior changes. No action is needed by users:
the shipped gem does not depend on json and was never affected by the
advisory below.
Security
- Development dependency
jsonbumped 2.21.1 → 2.21.2 to clear
CVE-2026-71847 / GHSA-9hj4-r449-hfvc (JSON::ResumableParser#partial_value
dereferences a freed input buffer on truncated duplicate-key streams).
jsonreaches this repo only transitively (standard→rubocop→json)
and lives solely in the developmentGemfile.lock; the bump keeps the CI
bundler-auditgate green. (#143)
Changed
- Release workflow: the laggy rubygems full-index await was replaced with a
versions-API check, so a successful publish no longer fails the run. (#142)
Full diff: https://github.com/ncr/rack-proxy/compare/v1.0.1...v1.0.2
- Ruby
Published by ncr 24 days ago
https://github.com/ncr/rack-proxy - v1.0.1 — docs modernization + housekeeping
Docs and housekeeping — no library behavior changes.
Changed
- README modernized: badges, quick start, a "How it works" pipeline overview,
grouped options, and tightened recipes. - Removed the internal modernization roadmap document, completed by 1.0.0.
- Development dependencies refreshed (rake 13.4.2, test-unit 3.7.8,
rack-test 2.2.0) and CI bumped toactions/checkout@v7. (#136–#139)
Full diff: https://github.com/ncr/rack-proxy/compare/v1.0.0...v1.0.1
- Ruby
Published by ncr 2 months ago
https://github.com/ncr/rack-proxy - v1.0.0 — the modernization + security-hardening release
The 2026 modernization + security-hardening release. From 1.0.0 on this
project follows SemVer strictly: breaking changes only in majors.
Breaking changes
Read this list before upgrading from 0.8.x; everything else below is additive
or a compatible fix. See the README's "Upgrading" section for migration steps.
- Host-derived (dynamic) backends are refused by default. With no
:backendand noenv["rack.backend"], requests now get502unless you
passallow_dynamic_backend: true. A bareRack::Proxy.newis no longer an
open proxy. Combine the opt-in with abackend_allowed?allowlist. net_http_hackedis gone — the file,require "net_http_hacked", and
thebegin_request_hacked/end_request_hackedmethods (see Removed).- The bundled examples left the gem —
require "rack_proxy_examples/..."
raisesLoadError; copy the snippets fromexamples/instead (see Removed). - Backend failures no longer raise.
OpenSSL::SSL::SSLError,EOFError,
timeouts, resets, and malformed backend responses now become a502triplet
instead of an exception —rescue-based error handling aroundproxy.call
must inspect the status instead. - Hop-by-hop request headers are no longer forwarded (
Connection,TE,
Transfer-Encoding,Proxy-Authorization,Upgrade, anything named by
Connection). - gzip backend bodies are forwarded verbatim in
streaming: falsemode
(previously they were transparently inflated);rewrite_responsehooks that
read body text must inflate it themselves. - Supported runtimes: Ruby >= 3.0 and Rack >= 2.0, < 4 (was Ruby >= 2.6,
rack unpinned). - Smaller wire/API changes: body-less POST/PUT sends
Content-Length: 0on
the streaming path; the streaming body is thread-affine (iterate it on the
thread that called the app, as mainstream servers do);HttpStreamingResponse
raisesIOErroron use-after-close; early termination closes the backend
connection instead of draining it.
Security
- Refuse Host-derived backends unless
allow_dynamic_backend: trueis set
(see Breaking changes) — closes the default open-proxy/SSRF pivot. - Strip hop-by-hop headers from the forwarded request (Connection, TE,
Transfer-Encoding, Proxy-Authorization, …, plus any header named by the inbound
Connectionheader), closing a Content-Length/Transfer-Encoding request-smuggling
surface. Hop-by-hop headers were previously stripped only from the response. - Add
backend_allowed?(backend)— an overridable per-request allowlist hook,
consulted for every request (static backends included). A refused backend
responds502. - Add
:max_response_lengthto cap the backend response size (bounds memory
against a hostile/huge backend). Enforced incrementally while streaming. - Add
:ca_file/:cert_storeso private-CA backends can be verified under the
defaultVERIFY_PEERinstead of disabling verification. - Add
:open_timeout/:write_timeoutto bound connect and per-write stalls
(previously only:read_timeoutwas configurable).
Added
:min_version/:max_versionTLS options (mapping toNet::HTTP#min_version=
/#max_version=).:ssl_versionstill works but is deprecated (it pins an
exact protocol and forbids TLS 1.3).HttpStreamingResponse#closeso Rack servers release the backend connection on
early termination (HEAD, 304, client disconnect) instead of leaking it until GC.- Opt-in request hardening:
strip_credentials: truedrops the client's
Cookie/Authorizationheaders from the forwarded request, and
replace_x_forwarded_for: trueforwards only this hop'sREMOTE_ADDR
instead of appending to the client-suppliedX-Forwarded-Forchain. - Project scaffolding:
SECURITY.md,CHANGELOG.md,CONTRIBUTING.md,
CLAUDE.md/AGENTS.md, GitHub Actions CI (Ruby 3.1–3.4 × Rack 2/3),
Dependabot, issue/PR templates, SimpleCov with a ratcheted coverage floor
(COVERAGE=1), and Standard (standardrb) +bundler-auditenforced by a
CI lint job.
Changed
- The streaming path no longer monkey-patches
net/http.
Rack::HttpStreamingResponsenow runs the public block form of
Net::HTTP#requestinside a Fiber (pausing at the response head, resuming
per body chunk), replacing the 2010-era patch of privatenet/httpinternals
innet_http_hacked.rb. Behavioral notes: the streaming session sets
max_retries = 0so a transport error can never silently replay the request
mid-stream; early termination (client abort, HEAD, 204/304) now closes
the backend connection immediately instead of draining the remaining body;
and a body-less POST/PUT/PATCH on the streaming path now carries
Content-Length: 0(matching the non-streaming path and plainNet::HTTP—
the old patched path sent noContent-Lengthat all). - Backend and construction failures now map to status codes instead of raising a
500:400(malformed request URI),501(unknown HTTP method),502
(broadened backend-error set incl.ECONNRESET,EPIPE, read/write timeouts,
EOFError,OpenSSL::SSL::SSLError, protocol errors, and malformed backend
responses —Net::HTTPBadResponse/Net::HTTPHeaderSyntaxError). - gzip-encoded backend responses are forwarded verbatim (Content-Encoding and
Content-Length preserved) instead of being transparently inflated. - Skip all
1xxinterim responses (including103 Early Hints) on the streaming
path, so a backend's103is no longer mistaken for the final response. rackdependency constrained to>= 2.0, < 4; the library nowrequires rack
itself.- Test suite is fully offline (local WEBrick server); the previous live-host
tests are gated behindLIVE=1.
Deprecated
:ssl_version— use:min_version/:max_version.
Removed
-
lib/net_http_hacked.rb— the 2010-era monkey-patch of privateNet::HTTP
internals. The library stopped using it when streaming moved to the public
Net::HTTPAPI (see Changed);require "net_http_hacked"and the
begin_request_hacked/end_request_hackedmethods are gone. If external
code still depends on them, vendor the file from a 0.8.x release — and plan
to migrate; it breaks under modern net/http refactors. -
The bundled examples moved from the gem load path
(lib/rack_proxy_examples/) toexamples/in the repository.
require "rack_proxy_examples/..."no longer works — copy the example class
into your app instead (they were never safe to require blindly: each one
installs itself into the Rails middleware stack when Rails is booted).
Upgrading from 0.8.x: see the README upgrading guide.
Full diff: https://github.com/ncr/rack-proxy/compare/v0.8.3...v1.0.0
- Ruby
Published by ncr 2 months ago