Skip to content

Conversation

@unflxw
Copy link
Contributor

@unflxw unflxw commented Aug 28, 2025

The Rack::Events middleware is not compatible with streaming bodies. It wraps the body in a Rack::Events::EventedBodyProxy, which unlike a Rack::BodyProxy, always responds to #each, even if the body it is proxying does not respond to #each.

According to the Rack spec, when a body responds to both #each and #call, the former is preferred, ultimately causing a NoMethodError on the original body.

Deprecate the use of Rack::Events for our event-based Rack instrumentation. Implement a modified Appsignal::Rack::Events which wraps the body in a Rack::BodyProxy, ensuring it does not respond to #each when the body it's proxying does not respond to #each.

Since Appsignal::Rack::Events' modifications break the on_send event, we do not recommend its direct usage. Instead, we provide a convenience Appsignal::Rack::EventMiddleware wrapper, which uses our modified events middleware with our event handler.

A deprecation warning is emitted when Appsignal::Rack::EventMiddleware is used with Rack::Events.

Fixes #1442.

@unflxw unflxw requested a review from tombruijn August 28, 2025 09:08
@unflxw unflxw self-assigned this Aug 28, 2025
@unflxw unflxw added the bug Confirmed and unconfirmed bugs reported by us and customers. label Aug 28, 2025
@backlog-helper
Copy link

backlog-helper bot commented Aug 28, 2025

✔️ All good!

New issue guide | Backlog management | Rules | Feedback

@unflxw unflxw force-pushed the fix-streaming-responses-rack-middleware branch 3 times, most recently from d9ca3f1 to ff845a6 Compare August 28, 2025 12:00
Copy link
Member

@tombruijn tombruijn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Took me a while to understand all the changes and why they were necessary. Thanks for the explanations!

@unflxw unflxw force-pushed the fix-streaming-responses-rack-middleware branch from ff845a6 to a6fe0f4 Compare August 28, 2025 12:24
The `Rack::Events` middleware is not compatible with streaming bodies.
It wraps the body in a `Rack::Events::EventedBodyProxy`, which unlike
a `Rack::BodyProxy`, always responds to `#each`, even if the body it
is proxying does not respond to `#each`.

According to the Rack spec, when a body responds to both `#each` and
`#call`, the former is preferred, ultimately causing a `NoMethodError`
on the original body.

Deprecate the use of `Rack::Events` for our event-based Rack
instrumentation. Implement a modified `Appsignal::Rack::Events` which
wraps the body in a `Rack::BodyProxy`, ensuring it does not respond to
`#each` when the body it's proxying does not respond to `#each`.

Since `Appsignal::Rack::Events`' modifications break the `on_send`
event, we do not recommend its direct usage. Instead, we provide a
convenience `Appsignal::Rack::EventMiddleware` wrapper, which uses
our modified events middleware with our event handler.

A deprecation warning is emitted when `Appsignal::Rack::EventMiddleware`
is used with `Rack::Events`.
@unflxw unflxw force-pushed the fix-streaming-responses-rack-middleware branch from a6fe0f4 to c94315e Compare August 29, 2025 10:05
@unflxw unflxw merged commit 2ef98de into main Aug 29, 2025
187 checks passed
unflxw added a commit 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 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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Confirmed and unconfirmed bugs reported by us and customers.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Incompatible with Async::Cable?

2 participants