Support streaming bodies when using Rack::Events#2375
Merged
Conversation
When `Rack::Events` is used, it would use `EventedBodyProxy` to wrap the body. Unlike `BodyProxy`, `EventedBodyProxy` always implements `#each`. As per the spec, streaming bodies must both implement `#call` and not implement `#each`, and if both are present, the server must call `#each`, not `#call`. When a streaming body was wrapped by an `EventedBodyProxy`, the proxy will nonetheless `.respond_to?(:each)`. This would cause the server to call `#each` on the proxy. The proxy would then forward the call to `#each` to the body that it proxies, which does not implement `#each`, raising a `NoMethodError`. To fix this, `EventedBodyProxy` will only `.respond_to?(:each)` if the body it proxies responds to `#each`. Note that, while this fixes the `NoMethodError`, this means that the `on_send` handler will never be invoked for streaming bodies.
When using the `Rack::Events` middleware, call `on_send` on the middleware's handlers when `#call` is invoked, in the same way it is done when `#each` is invoked.
Rack::Events
jeremyevans
approved these changes
Sep 1, 2025
ioquatix
approved these changes
Sep 1, 2025
Member
|
Thanks for your contribution. As this is a bug fix, we could consider backporting. WDYT @jeremyevans? |
Contributor
|
No objections to backporting this fix to 3.2. |
ioquatix
pushed a commit
that referenced
this pull request
Sep 2, 2025
unflxw
added a commit
to appsignal/appsignal-ruby
that referenced
this pull request
Sep 2, 2025
A fix for the issue where `::Rack::Events` breaks streaming bodies was introduced in version 3.2.1. Define `Appsignal::Rack::Events`, our patched implementation, only when the Rack version present is below 3.2.1. Otherwise, alias it to `::Rack::Events`. Modify how the deprecation warning is silenced, doing so through `Appsignal::Rack::EventMiddleware`, not `Appsignal::Rack::Events`. Test the deprecation warning's behaviour more precisely by testing the behaviour of `Appsignal::Rack::EventHandler` via the middleware that it is initialised, rather than in isolation. See #1445, rack/rack#2373, rack/rack#2375 for context.
unflxw
added a commit
to appsignal/appsignal-ruby
that referenced
this pull request
Sep 3, 2025
A fix for the issue where `::Rack::Events` breaks streaming bodies was introduced in version 3.2.1. Define `Appsignal::Rack::Events`, our patched implementation, only when the Rack version present is below 3.2.1. Otherwise, alias it to `::Rack::Events`. Modify how the deprecation warning is silenced, doing so through `Appsignal::Rack::EventMiddleware`, not `Appsignal::Rack::Events`. Test the deprecation warning's behaviour more precisely by testing the behaviour of `Appsignal::Rack::EventHandler` via the middleware that it is initialised, rather than in isolation. See #1445, rack/rack#2373, rack/rack#2375 for context.
d1ceward
pushed a commit
to HappyRenting/rack
that referenced
this pull request
Oct 29, 2025
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #2373.
Fix streaming bodies when using
Rack::EventsWhen
Rack::Eventsis used, it would useEventedBodyProxyto wrapthe body. Unlike
BodyProxy,EventedBodyProxyalways implements#each.As per the spec, streaming bodies must both implement
#calland notimplement
#each, and if both are present, the server must call#each, not#call.When a streaming body was wrapped by an
EventedBodyProxy, the proxywill nonetheless
.respond_to?(:each). This would cause the server tocall
#eachon the proxy. The proxy would then forward the call to#eachto the body that it proxies, which does not implement#each,raising a
NoMethodError.To fix this,
EventedBodyProxywill only.respond_to?(:each)ifthe body it proxies responds to
#each.Note that, while this fixes the
NoMethodError, this means that theon_sendhandler will never be invoked for streaming bodies.Call
on_sendhandler for streaming bodiesWhen using the
Rack::Eventsmiddleware, callon_sendon themiddleware's handlers when
#callis invoked, in the same way itis done when
#eachis invoked.