Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
WebSocket: should compare 'Upgrade' header value with case insensitive (
#4617)

RFC 6445, section 4.2.1 says clearly: "An Upgrade header field containing
the value "websocket", treated as an ASCII case-insensitive value".

https://tools.ietf.org/html/rfc6455#section-4.2.1

And now, we have `String#compare(other, case_insensitive: true)`.
  • Loading branch information
makenowjust authored and Martin Verzilli committed Jul 2, 2017
1 parent cb0ee67 commit dcf8a03
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 1 deletion.
47 changes: 47 additions & 0 deletions spec/std/http/server/handlers/websocket_handler_spec.cr
Expand Up @@ -18,6 +18,28 @@ describe HTTP::WebSocketHandler do
io.to_s.should eq("HTTP/1.1 200 OK\r\nContent-Length: 5\r\n\r\nHello")
end

it "returns not found if the request Upgrade is invalid" do
io = IO::Memory.new

headers = HTTP::Headers{
"Upgrade" => "WS",
"Connection" => "Upgrade",
"Sec-WebSocket-Key" => "dGhlIHNhbXBsZSBub25jZQ==",
}
request = HTTP::Request.new("GET", "/", headers: headers)
response = HTTP::Server::Response.new(io)
context = HTTP::Server::Context.new(request, response)

invoked = false
handler = HTTP::WebSocketHandler.new { invoked = true }
handler.next = HTTP::Handler::Proc.new &.response.print("Hello")
handler.call context

response.close

io.to_s.should eq("HTTP/1.1 200 OK\r\nContent-Length: 5\r\n\r\nHello")
end

{% for connection in ["Upgrade", "keep-alive, Upgrade"] %}
it "gives upgrade response for websocket upgrade request with '{{connection.id}}' request" do
io = IO::Memory.new
Expand All @@ -44,4 +66,29 @@ describe HTTP::WebSocketHandler do
io.to_s.should eq("HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-Websocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n\r\n")
end
{% end %}

it "gives upgrade response for case-insensitive 'WebSocket' upgrade request" do
io = IO::Memory.new
headers = HTTP::Headers{
"Upgrade" => "WebSocket",
"Connection" => "Upgrade",
"Sec-WebSocket-Key" => "dGhlIHNhbXBsZSBub25jZQ==",
}
request = HTTP::Request.new("GET", "/", headers: headers)
response = HTTP::Server::Response.new(io)
context = HTTP::Server::Context.new(request, response)

handler = HTTP::WebSocketHandler.new { }
handler.next = HTTP::Handler::Proc.new &.response.print("Hello")

begin
handler.call context
rescue IO::Error
# Raises because the IO::Memory is empty
end

response.close

io.to_s.should eq("HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-Websocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n\r\n")
end
end
9 changes: 8 additions & 1 deletion src/http/server/handlers/websocket_handler.cr
Expand Up @@ -14,7 +14,7 @@ class HTTP::WebSocketHandler
end

def call(context)
if context.request.headers["Upgrade"]? == "websocket" && context.request.headers.includes_word?("Connection", "Upgrade")
if websocket_upgrade_request? context.request
key = context.request.headers["Sec-Websocket-Key"]

accept_code =
Expand All @@ -39,4 +39,11 @@ class HTTP::WebSocketHandler
call_next(context)
end
end

private def websocket_upgrade_request?(request)
return false unless upgrade = request.headers["Upgrade"]?
return false unless upgrade.compare("websocket", case_insensitive: true) == 0

request.headers.includes_word?("Connection", "Upgrade")
end
end

0 comments on commit dcf8a03

Please sign in to comment.