Skip to content

Support streaming bodies when using Rack::Events#2375

Merged
ioquatix merged 2 commits intorack:mainfrom
unflxw:rake-events-streaming-bodies
Sep 1, 2025
Merged

Support streaming bodies when using Rack::Events#2375
ioquatix merged 2 commits intorack:mainfrom
unflxw:rake-events-streaming-bodies

Conversation

@unflxw
Copy link
Copy Markdown
Contributor

@unflxw unflxw commented Sep 1, 2025

Fixes #2373.


Fix streaming bodies when using Rack::Events

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.

Call on_send handler 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.

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.
@unflxw unflxw changed the title Rake events streaming bodies Support streaming bodies when using Rack::Events Sep 1, 2025
@ioquatix ioquatix merged commit b7e068e into rack:main Sep 1, 2025
17 checks passed
@ioquatix
Copy link
Copy Markdown
Member

ioquatix commented Sep 1, 2025

Thanks for your contribution.

As this is a bug fix, we could consider backporting. WDYT @jeremyevans?

@jeremyevans
Copy link
Copy Markdown
Contributor

No objections to backporting this fix to 3.2.

@ioquatix ioquatix added this to the v3.2.1 milestone 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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Rack::Events breaks streaming bodies

3 participants