From 40bb2772139ae821c476a0169430b590b1e3c763 Mon Sep 17 00:00:00 2001 From: TSUYUSATO Kitsune Date: Sat, 24 Jun 2017 11:23:30 +0900 Subject: [PATCH] WebSocket: should compare 'Upgrade' header value with case insensitive 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)`. --- .../server/handlers/websocket_handler_spec.cr | 47 +++++++++++++++++++ src/http/server/handlers/websocket_handler.cr | 2 +- 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/spec/std/http/server/handlers/websocket_handler_spec.cr b/spec/std/http/server/handlers/websocket_handler_spec.cr index ca560105708d..db226502ce36 100644 --- a/spec/std/http/server/handlers/websocket_handler_spec.cr +++ b/spec/std/http/server/handlers/websocket_handler_spec.cr @@ -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 @@ -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 diff --git a/src/http/server/handlers/websocket_handler.cr b/src/http/server/handlers/websocket_handler.cr index 3f4a1cb01e26..d66f607da899 100644 --- a/src/http/server/handlers/websocket_handler.cr +++ b/src/http/server/handlers/websocket_handler.cr @@ -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 context.request.headers["Upgrade"]?.try(&.compare("websocket", case_insensitive: true)) == 0 && context.request.headers.includes_word?("Connection", "Upgrade") key = context.request.headers["Sec-Websocket-Key"] accept_code =