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

Bring Rack::Mock cookie parsing into SPEC (fixes #1629) #1630

Closed
wants to merge 2 commits into from

Conversation

mpalmer
Copy link
Contributor

@mpalmer mpalmer commented Mar 30, 2020

#has_key? isn't required to be supported by the object in the
headers element of the response coming back out of the app.
This replaces that call with a transmogrification into a
HeaderHash so we can use an index as normal. The odd
hash[] || "" construction is necessary because HeaderHash#fetch
doesn't respect header name case insensitivity.

@mpalmer
Copy link
Contributor Author

mpalmer commented Mar 30, 2020

I'm inclined to think that the circleci error is more that someone hasn't finished configuring that job, rather than the code being broken.

@mpalmer mpalmer force-pushed the spec-cookies branch 2 times, most recently from 59b7dad to d445f04 Compare March 30, 2020 11:11
@ioquatix
Copy link
Member

@tenderlove can you please disable CircleCI it's irrelevant now.

@ioquatix
Copy link
Member

HeaderHash#fetch in theory is an internal API.

I am happy for you to include in this PR any changes you think make sense regarding that interface in terms of making the main change more straight forward.

Maybe HeaderHash#each_value(header_key) which yields items from the array or tries to split it into an array would make sense. I'm open to ideas.

lib/rack/mock.rb Outdated
set_cookie_header.split("\n").each do |cookie|
hashed_headers = Rack::Utils::HeaderHash[original_headers]
if hashed_headers.has_key?("Set-Cookie")
values = (v = hashed_headers["Set-Cookie"]).respond_to?(:to_ary) ? v.to_ary : v.split("\n")
Copy link
Member

Choose a reason for hiding this comment

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

I see, the behaviour here is a bit... messed up.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, HeaderHash is a bit... under-specified, compared to the reality of headers and their oddities, as I notice has been discussed at some length. I am in violent agreement that "considering HTTP headers as general metadata is generally a mistake, in my experience. There are too many things which have specific semantic behaviour which requires specific things to handle."

I'm a bit down on HeaderHash overall, after discovering it doesn't handle [["set-cookie", "foo=bar"], ["Set-Cookie", "baz=wombat"]] correctly (though that's something for another PR). Looking at its history, the joining of arrays by newlines has always been on a bit of an "as-needed" basis, and I'm loathe to change the behaviour in #fetch, because it appears to be used heavily and garbage collection, etc.

I've just pushed a new version of the PR, with an extra commit to make HeaderHash#fetch case-insensitive. I also tried a different approach to handling "could be a string, could be an array of strings, who knows?" in the cookie parsing. It's at least differently ugly this time. Let me know which approach you prefer; I'm equally unhappy with both of them.

It just makes good sense in general, but it specifically makes the
proximate fix for rack#1629 a lot cleaner.
`#has_key?` isn't required to be supported by the object in the `headers`
element of the response coming back out of the app.  This replaces that call
with a transmogrification into a `HeaderHash` so we can use an index as
normal.  The odd `hash[] || ""` construction is necessary because
`HeaderHash#fetch` doesn't respect header name case insensitivity.
@lukaso
Copy link

lukaso commented May 5, 2020

If you want to work around the case insensitivity error seen here plus a strange base64 encoded error is saw in a cookie, add this to your capybara configuration file:

module Rack
  module Utils
    # Makes fetch case insensitive
    class HeaderHash
      def fetch(*args)
        patch_bug(super(args.first))
      rescue KeyError
        patch_bug(super(@names[args.first.downcase], *args[1..-1]))
      end

      private

      # this cookie from cloudflare with base64 encoded value that ends in
      # '=', kills cookie handling
      # Not patch received for this part yet.
      # '__cf_bm=somebase64encodedstringwithequalsatthened='
      def patch_bug(string)
        string.gsub(/=; /, '= ; ')
      end
    end
  end
end

The fix to the second error is to be added to this line:

https://github.com/rack/rack/blob/master/lib/rack/mock.rb#L260

Where attribute_value.strip but attribute_value will be nil.

@tenderlove
Copy link
Member

I've been trying to figure out how to remove Circle for the past 20 min. I am going to stop now. I'll try again later. If anyone has ideas how to remove it I'm all ears. I couldn't find any settings in the Circle UI, and I don't see any integrations in GitHub settings so I'm kind of at a loss right now.

@ioquatix
Copy link
Member

ioquatix commented May 8, 2020

@tenderlove that's weird!? I took a look and can't see anything either. Maybe a webhook? Or some other integration?

@olleolleolle
Copy link
Contributor

@tenderlove Hope this helps:

Settings "Branches" - enter the master branch settings.

There will be named checkboxes there.

@tenderlove
Copy link
Member

@olleolleolle is that on the circle ci side? Because I don't see that on the GitHub side. Here is the GitHub side:

Branches 2020-05-21 17-10-18

CircleCI seems to think that rack/rack is untested:

Org settings - rack - CircleCI 2020-05-21 17-07-15

There's nothing in Integrations:

Apps and integrations 2020-05-21 17-11-29

Nothing in Webhooks:

Webhooks 2020-05-21 17-11-52

Nothing in the org level web hooks:

Webhooks 2020-05-21 17-12-41

I have no idea why this is happening.

@olleolleolle
Copy link
Contributor

olleolleolle commented May 22, 2020

The name of the heading is "Branch Protection Rules".

The master branch has a button Edit which opens a list of "required CI jobs that must pass" - extremely unintuitive. @tenderlove I understand that you missed it. Thanks for the thorough look.

@tenderlove
Copy link
Member

@olleolleolle I actually clicked that edit button once and didn't see anything relevant so I didn't post a screenshot. Here it is though:

Branch protection rule 2020-05-22 08-53-38

FWIW, newer pull requests aren't running the CircleCI build afaict. For example this one.

Copy link
Member

@tenderlove tenderlove left a comment

Choose a reason for hiding this comment

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

For some reason when I try to comment inline on the code I just get errors from GitHub, so I will write in long form here.

I think Hash#fetch can take a block, so we need to support that too.

I noticed this implementation rescues a KeyError rather than checking key?. Do we expect fetch to typically succeed? I'd rather not rescue exceptions as part of our logic, but I could understand for performance (though I'm not sure HeaderHash#fetch is a hotspot).

Finally I think we can use Array#drop to eliminate range allocation.

jeremyevans added a commit to jeremyevans/rack that referenced this pull request Jul 14, 2020
Rack::MockResponse inherits from Rack::Response, which already
uses a HeaderHash for the headers.  The original_headers were
only used for cookie parsing, which for some reason was happening
before the call to super in initialize (so the headers weren't
available yet). There seems to be no reason why the cookie parsing
can't happen after the call to super, in which case we can use the
headers directly.

Co-Authored-By: Matt Palmer <mpalmer@hezmatt.org>

Fixes rack#1629
Fixes rack#1630
jeremyevans added a commit to jeremyevans/rack that referenced this pull request Jul 14, 2020
Rack::MockResponse inherits from Rack::Response, which already
uses a HeaderHash for the headers.  The original_headers were
only used for cookie parsing, which for some reason was happening
before the call to super in initialize (so the headers weren't
available yet). There seems to be no reason why the cookie parsing
can't happen after the call to super, in which case we can use the
headers directly.

Fixes rack#1629
Fixes rack#1630

Co-authored-by: Matt Palmer <mpalmer@hezmatt.org>
jeremyevans added a commit that referenced this pull request Dec 1, 2020
Rack::MockResponse inherits from Rack::Response, which already
uses a HeaderHash for the headers.  The original_headers were
only used for cookie parsing, which for some reason was happening
before the call to super in initialize (so the headers weren't
available yet). There seems to be no reason why the cookie parsing
can't happen after the call to super, in which case we can use the
headers directly.

Fixes #1629
Fixes #1630

Co-authored-by: Matt Palmer <mpalmer@hezmatt.org>
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.

None yet

5 participants