10000 Send headers with Sec-WebSocket-* capitalization by qguv · Pull Request #71 · gobwas/ws · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Send headers with Sec-WebSocket-* capitalization #71

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

Merged
merged 8 commits into from
Apr 27, 2019
Merged

Send headers with Sec-WebSocket-* capitalization #71

merged 8 commits into from
Apr 27, 2019

Conversation

qguv
Copy link
Contributor
@qguv qguv commented Apr 5, 2019

According to the RFC, the Sec-WebSocket-* headers should have a capital S inside the word WebSocket. This is a shame, because most other headers are canonicalized differently.

Some websocket servers are particularly picky about the capitalization. The agar.io server (or maybe the Cloudflare servers it's behind) will reject connections with the canonical spelling, giving a 400 in response.

You can try it. This will succeed:

curl -v --http1.1 'https://live-arena-1cm5igj.agar.io/'
    -H 'Host: live-arena-1cm5igj.agar.io:443'
    -H 'Upgrade: websocket'
    -H 'Connection: Upgrade'
    -H 'Sec-WebSocket-Version: 13'
    -H 'Sec-WebSocket-Key: 1u4hDlp0kUjbZafgII9lLw=='

But this will fail, giving a 400 Bad Request:

curl -v --http1.1 'https://live-arena-1cm5igj.agar.io/'
    -H 'Host: live-arena-1cm5igj.agar.io:443'
    -H 'Upgrade: websocket'
    -H 'Connection: Upgrade'
    -H 'Sec-Websocket-Version: 13'
    -H 'Sec-Websocket-Key: 1u4hDlp0kUjbZafgII9lLw=='

This patch fixes this: ws will now accept any capitalization it receives, but use the RFC-correct capitalization when sending headers either as a response or as an upgrade request.

Thanks for this library! The zero-copy functionality is really nice for us.

...but don't be picky about which capitalization we receive
@cristaloleg
Copy link
Collaborator

WDYT to leave old and incorrect(😢) versions for a back compatibility and other incorrect clients?

@qguv
Copy link
Contributor Author
qguv commented Apr 5, 2019

@cristaloleg I don't know of any servers that require a lowercase S, but I do know of servers (like agar) that require an uppercase S. Sending the uppercase version from the RFC should allow for compatibility with the most websocket servers.

If anyone knows of a server that only accepts lowercase s headers, please comment!

@gobwas
Copy link
Owner
gobwas commented Apr 5, 2019

Hi @qguv. I think you are talking about a bug inside some particular server implementation. HTTP RFC says this:

Each header field consists of a case-insensitive field name followed by a colon (":"), optional leading whitespace, the field value, andoptional trailing whitespace.

Thus it is nevermind Sec-Websocket or Sec-WebSocket or sEc-WeBSocKET :)

@gobwas
Copy link
Owner
gobwas commented Apr 5, 2019

But, on the other hand, the "message headers registry" lists one that you suggested. 🤔

8000

@qguv
Copy link
Contributor Author
qguv commented Apr 8, 2019

@gobwas Yes exactly. If you look at the HTTP RFCs, it shouldn't matter. It's a shame that the WebSocket RFC breaks the convention on header normalization set in the HTTP RFC. But because I've seen servers in the wild that enforce the capital S and no servers in the wild that enforce lower-case S without accepting the capitalized version, I think it's a good idea to stay as close to the WebSocket RFC as possible.

@qguv
Copy link
Contributor Author
qguv commented Apr 11, 2019

Just pushed some updates. I fixed an issue that was breaking existing tests, and I added some new tests to catch issues with capitalization going forward.

@qguv
Copy link
Contributor Author
qguv commented Apr 12, 2019

I'd also like to point out that both Firefox and Chromium send headers with a capital S:

Firefox 66
GET / HTTP/1.1
Host: localhost:8888
User-Agent: Mozilla/5.0 (X11; Fedora; Linux x86_64; rv:66.0) Gecko/20100101 Firefox/66.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Sec-WebSocket-Version: 13
Origin: null
Sec-WebSocket-Extensions: permessage-deflate
Sec-WebSocket-Key: nMTnCo98JAkQQkkmozr3GA==
DNT: 1
Connection: keep-alive, Upgrade
Pragma: no-cache
Cache-Control: no-cache
Upgrade: websocket
Chromium 73
GET / HTTP/1.1
Host: localhost:8888
Connection: Upgrade
Pragma: no-cache
Cache-Control: no-cache
User-Agent: Mozilla/5.0 (X11; Fedora; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36
Upgrade: websocket
Origin: chrome-search://local-ntp
Sec-WebSocket-Version: 13
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Sec-WebSocket-Key: 2fym2MfB48lWtBYavpsdOg==
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits

You can test this by running a simple ncat server:

nc -lp 8888

and creating a new websocket in an about:blank tab in your browser of choice:

let ws = new WebSocket('ws://localhost:8888');

You should see the headers in nc stdout.

http.go Outdated
headerSecExtensions = textproto.CanonicalMIMEHeaderKey("Sec-Websocket-Extensions")
headerSecKey = textproto.CanonicalMIMEHeaderKey("Sec-Websocket-Key")
headerSecAccept = textproto.CanonicalMIMEHeaderKey("Sec-Websocket-Accept")
headerHost = "Host"
Copy link
Owner

Choose a reason for hiding this comment

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

Could you please add the same Canonical version for these headers for better symmetry?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done!

@gobwas
Copy link
Owner
gobwas commented Apr 13, 2019

Hi @qguv, commented few lines in the diff, other things looks good.

}
if test.err == nil {
test.res.Header.Set(headerSecAccept, string(makeAccept(test.nonce)))
test.res.Header[headerSecAccept] = []string{string(makeAccept(test.nonce))}
Copy link
Owner

Choose a reason for hiding this comment

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

Why these changes are needed?

Copy link
Contributor Author
@qguv qguv Apr 15, 2019

Choose a reason for hiding this comment

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

The (http.Header) Set() method calls (textproto.MIMEHeader) Set() which canonicalizes the header key before setting the header. This causes tests to fail because the expected response (forced to lowercase-s headers) does not match the actual response (which now contains uppercase-S headers). We can bypass canonicalization by setting the key directly in the map that the textproto.MIMEHeader is based on.

@gobwas gobwas merged commit 7338e26 into gobwas:master Apr 27, 2019
@gobwas
Copy link
Owner
gobwas commented Apr 27, 2019

Hi @qguv, sorry for the late reply!
Merged this PR finally. Thank you a lot 👍

@gobwas
Copy link
Owner
gobwas commented Apr 27, 2019

Also, I am about bump the patch version to 1.0.1. Does it looks fine for you guys @qguv @cristaloleg?

@qguv
Copy link
Contributor Author
qguv commented Apr 30, 2019

Thanks @gobwas! Yes, the merge looks good.

@qguv
Copy link
Contributor Author
qguv commented May 10, 2019

Hey @gobwas, are you still planning to tag a new release?

@gobwas
Copy link
Owner
gobwas commented May 15, 2019

Hi there! Yes, I am. But currently Im on vacation, sorry. Will do that at Sunday probably.

@gobwas
Copy link
Owner
gobwas commented May 19, 2019

@qguv done! 🎆

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
0