Class endpoints causing problem with cookies in Hanami container app
createdbypete opened this issue · 3 comments
This may or may not be problem with the Hanami Router but since I have isolated this part as the trigger for the problem when testing I am opening the issue here but if could turn out to be an issue within Hanami Controller.
The Problem
While developing a Hanami application (in the container architecture) I experienced a strange issue in production where the contents of a cookie would only be set once until ruby server was restarted.
What this means is the first request after the server was started would get the cookie set/contents updated, but any subsequent requests to update or set that cookie (from any client) would do nothing. Restarting the server only repeated the same behaviour over again.
After much tinkering the cause of this appears to be related to specifying the route endpoint as a class instead of a string.
get '/', to: 'homepage#index' # This works
get '/', to: ->(env) { Web::Controllers::Homepage::Index.new.call(env) } # This also appears to work
get '/', to: Web::Controllers::Homepage::Index # This does not work
get '/', to: Web::Controllers::Homepage::Index.new # This does not work
An important factor when testing this is --no-code-reloading
.
I've created a repo to help others to experience the issue createdbypete/hanami_cookie_issue.
Observations
While investigating this issue I noticed a couple of things that may or may not be significant.
- Endpoint loading differences:
- The string endpoint, being lazy, is initialized when the route is called so a
puts
in the#initialize
method only shows up when the endpoint is used. This all seems correct. - Class endpoints appear to call
#initialize
when the application loaded (again noticed with someputs
debugging) perhaps these could also be lazy like string? The curious thing is however that thisputs
value is output twice when the app loads. - Instance endpoints (of course) are initialized when the app loads but this also appears to happen twice according to the
puts
output. I'm not sure why this would be called twice.
- The string endpoint, being lazy, is initialized when the route is called so a
- When debugging the cookie being set I was checking out the headers before and after setting the cookie (Hanami::Action::CookieJar) with:
def set_cookie(key, value)
puts "Before: #{@_headers}"
::Rack::Utils.set_cookie_header!(@_headers, key, value)
puts "After: #{@_headers}"
end
Running in my test repo (above) the output is as follows:
$ bundle exec hanami server --no-code-reloading
[2016-10-09 15:27:29] INFO WEBrick 1.3.1
[2016-10-09 15:27:29] INFO ruby 2.3.1 (2016-04-26) [x86_64-darwin16]
[2016-10-09 15:27:29] INFO WEBrick::HTTPServer#start: pid=41491 port=2300
Before: {"Location"=>"/", "Content-Type"=>"text/html; charset=utf-8"}
After: {"Location"=>"/", "Content-Type"=>"text/html; charset=utf-8", "Set-Cookie"=>"foo=2016-10-09+15%3A27%3A31+%2B1100; HttpOnly"}
127.0.0.1 - - [09/Oct/2016:15:27:31 +1100] "GET HTTP/1.1" 302 - 0.0221
127.0.0.1 - - [09/Oct/2016:15:27:31 +1100] "GET HTTP/1.1" 200 - 0.0123
Before: {"Location"=>"/", "Content-Type"=>"text/html; charset=utf-8", "Set-Cookie"=>"foo=2016-10-09+15%3A27%3A31+%2B1100; HttpOnly"}
After: {"Location"=>"/", "Content-Type"=>"text/html; charset=utf-8", "Set-Cookie"=>"foo=2016-10-09+15%3A27%3A31+%2B1100; HttpOnly\nfoo=2016-10-09+15%3A27%3A38+%2B1100; HttpOnly"}
127.0.0.1 - - [09/Oct/2016:15:27:38 +1100] "GET HTTP/1.1" 302 - 0.0047
127.0.0.1 - - [09/Oct/2016:15:27:38 +1100] "GET HTTP/1.1" 200 - 0.0048
Overall it's all expected output until the last After: where the cookie is repeated in the Set-Cookie
value. It has the first value set and then following it the value we actually want to set:
"Set-Cookie"=>"foo=2016-10-09+15%3A27%3A31+%2B1100; HttpOnly\nfoo=2016-10-09+15%3A27%3A38+%2B1100; HttpOnly"
Perhaps the most interesting part is when opening another browser and trying to set the cookie we see the following output:
127.0.0.1 - - [09/Oct/2016:15:27:59 +1100] "GET HTTP/1.1" 200 - 0.0054
Before: {"Location"=>"/", "Content-Type"=>"text/html; charset=utf-8", "Set-Cookie"=>"foo=2016-10-09+15%3A27%3A31+%2B1100; HttpOnly\nfoo=2016-10-09+15%3A27%3A38+%2B1100; HttpOnly"}
After: {"Location"=>"/", "Content-Type"=>"text/html; charset=utf-8", "Set-Cookie"=>"foo=2016-10-09+15%3A27%3A31+%2B1100; HttpOnly\nfoo=2016-10-09+15%3A27%3A38+%2B1100; HttpOnly\nfoo=2016-10-09+15%3A28%3A05+%2B1100; HttpOnly"}
127.0.0.1 - - [09/Oct/2016:15:28:05 +1100] "GET HTTP/1.1" 302 - 0.0038
127.0.0.1 - - [09/Oct/2016:15:28:05 +1100] "GET HTTP/1.1" 200 - 0.0075
As you can see the foo
cookie now appears 3 times under the Set-Cookie
key. Each request adds one more reference.
@createdbypete Hi! Why do you need to map actions as instances instead of using the default syntax? I'm not saying it shouldn't be possible, I'm just genuinely curious. 😉
Can you please write a fix for this problem?
Hey @jodosha originally it was while playing around with adding action dependencies to the initializer, obviously I could provide a default value but that wasn't what I was trying out.
I did try and find a way to fix this issue but I have to admit I got a bit lost and frustrated when I was trying to work out which component was the cause so could focus my efforts. This is why I dumped everything I had found here and reached out in case someone else has a deeper understanding of these areas.
I'm closing this because, it's not high priority and because we'll discuss after 1.0 how to improve Dependency Injection in Hanami.