Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle ActionDispatch::Response::RackBody as body #22

Merged
merged 16 commits into from
Sep 20, 2024

Conversation

skatkov
Copy link
Contributor

@skatkov skatkov commented Jun 26, 2024

With our new rails 7.1 project, idempotent requests don't seem to be working. They are not even saved.

As it turns out, Idempo middleware receives an instance of ActionDispatch::Response::RackBody as a body. And since it's not Array, we're not saving that body.

I'm not sure exactly why another project based on rails 7.0 keeps working. Because rails started returning ActionDispatch::Response::RackBody pre 7.0 version. I assume, that this could be due to the way how middleware is being inserted (how deep in a stack).

Here is what we do in our application.rb:

config.middleware.insert_after ActionDispatch::Executor, Idempo, backend: Idempo::ActiveRecordBackend.new

And here is an output of rails middleware:

use Rack::Sendfile
use ActionDispatch::Static
use ActionDispatch::Executor
use Idempo
use ActionDispatch::ServerTiming
use ActiveSupport::Cache::Strategy::LocalCache::Middleware
use Rack::Runtime
use Rack::MethodOverride
use ActionDispatch::RequestId
use ActionDispatch::RemoteIp
use Rails::Rack::Logger
use ActionDispatch::ShowExceptions
use WebConsole::Middleware
use ActionDispatch::DebugExceptions
use ActionDispatch::ActionableExceptions
use ActionDispatch::Reloader
use ActionDispatch::Callbacks
use ActiveRecord::Migration::CheckPending
use ActionDispatch::Cookies
use ActionDispatch::Session::CookieStore
use ActionDispatch::Flash
use ActionDispatch::ContentSecurityPolicy::Middleware
use ActionDispatch::PermissionsPolicy::Middleware
use Rack::Head
use Rack::ConditionalGet
use Rack::ETag
use Rack::TempfileReaper
use ActionDispatch::Static
run Banksvc::Application.routes

But that is just a wild guess.

As a fix, I suggest trying converting body to array — this proved effective in our project.

@skatkov skatkov force-pushed the handle-rack-body-object branch from 6590400 to d8cc3ff Compare June 26, 2024 12:50
lib/idempo.rb Outdated Show resolved Hide resolved
@franzliedke
Copy link
Collaborator

Whew, I got quite scared when I read the gem is not working for you with Rails 7.1. In our project (also Rails 7.1), it does.

We insert the middleware directly in the base controller (a barely-known Rails feature), can you try that out to get closer to the root cause? (You hinted middleware order may play a role.)

class MyRailsController
  use Idempo, backend: Idempo::ActiveRecordBackend.new, other: options
end

@julik
Copy link
Owner

julik commented Sep 18, 2024

I am inclined to merge this with a few changes (only relying on to_ary if we are, in fact, under Rack 3 or greater). to_ary will materialize the entire response into memory and the body can then be handled as an array of strings, in the +- usual manner, but there have been cases with various libraries implementing to_ary on Rack response bodies incorrectly. @franzliedke you have any objections to this?

@franzliedke
Copy link
Collaborator

No big objections, haven't peeked into the insides of Rack here. Is there any way we can add some regression tests around this?

@skatkov
Copy link
Contributor Author

skatkov commented Sep 19, 2024

@julik > only relying on to_ary if we are, in fact, under Rack 3 or greater

I'll make a change.

@skatkov skatkov marked this pull request as draft September 19, 2024 07:58
@skatkov skatkov force-pushed the handle-rack-body-object branch from e77725f to aed365d Compare September 19, 2024 14:17
@skatkov skatkov force-pushed the handle-rack-body-object branch 2 times, most recently from 449bdc8 to 94d9221 Compare September 19, 2024 14:33
@skatkov skatkov force-pushed the handle-rack-body-object branch from 94d9221 to c8bbc89 Compare September 19, 2024 14:34
@skatkov
Copy link
Contributor Author

skatkov commented Sep 19, 2024

@julik Did couple of adjustments.

  • Body will be converted to array only for rack 3.
  • Ensured that we keep testing rack v2 as well

@skatkov skatkov requested a review from julik September 19, 2024 15:26
@skatkov skatkov marked this pull request as ready for review September 19, 2024 15:26
Copy link
Owner

@julik julik left a comment

Choose a reason for hiding this comment

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

Few tweaks still needed. Also maybe a good idea to note around rack_response_body.close that this will not be applied to Arrays resulting from to_ary

lib/idempo.rb Outdated Show resolved Hide resolved
lib/idempo.rb Outdated Show resolved Hide resolved
lib/idempo.rb Outdated Show resolved Hide resolved
lib/idempo.rb Outdated Show resolved Hide resolved
@skatkov skatkov requested a review from julik September 20, 2024 09:58
@skatkov
Copy link
Contributor Author

skatkov commented Sep 20, 2024

@julik thanks for such a thorough review! I have implemented requested changes.

Copy link
Owner

@julik julik left a comment

Choose a reason for hiding this comment

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

Awesome. One little optional bit and we're good to go

lib/idempo.rb Outdated
# In some cases `body` could respond to to_ary. In this case, we don't need to call .close on body afterwards.
#
# @see https://github.com/rack/rack/blob/main/SPEC.rdoc#the-body-
body = body.try(:to_ary) if rack_v3? && body.respond_to?(:to_ary)
Copy link
Owner

Choose a reason for hiding this comment

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

nit: since you already do respond_to? you don't need try() you can call to_ary` directly

@julik julik merged commit 7454463 into julik:main Sep 20, 2024
5 checks passed
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.

3 participants