hanami/router

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 some puts debugging) perhaps these could also be lazy like string? The curious thing is however that this puts 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.
  • 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.