kemal/src/kemal/websocket.cr

66 lines
1.9 KiB
Crystal

require "http"
module Kemal
# Takes 2 parameters: *path* and a *handler* to specify
# what action to be done if the route is matched.
class WebSocket < HTTP::WebSocketHandler
getter proc
def initialize(@path : String, &@proc : HTTP::WebSocket, HTTP::Server::Context -> Void)
end
def error(code : Int16, message : String)
err = WebsocketError.new(code, message)
raise err
end
def call(context : HTTP::Server::Context)
if websocket_upgrade_request?(context.request)
response = context.response
version = context.request.headers["Sec-WebSocket-Version"]?
unless version == ::HTTP::WebSocket::Protocol::VERSION
response.headers["Sec-WebSocket-Version"] = ::HTTP::WebSocket::Protocol::VERSION
raise UpgradeRequired.new
end
key = context.request.headers["Sec-WebSocket-Key"]?
raise BadRequest.new("Sec-WebSocket-Key header is missing") unless key
accept_code = ::HTTP::WebSocket::Protocol.key_challenge(key)
response.status_code = 101
response.headers["Upgrade"] = "websocket"
response.headers["Connection"] = "Upgrade"
response.headers["Sec-WebSocket-Accept"] = accept_code
response.upgrade do |io|
socket = ::HTTP::WebSocket.new(io)
@proc.call(socket, context)
socket.run
rescue error : Exception
# TODO: check if 500 is what we're supposed to give
context.response.status_code = 500
if error.is_a?(WebsocketError)
code = error.code.to_i32
message = error.status_message
else
code = 1011_i32
message = "Exception"
end
socket.not_nil!.close(
HTTP::WebSocket::CloseCode.new(code),
message: message
)
raise error unless error.is_a?(WebsocketError)
end
else
raise UpgradeRequired.new
end
end
end
end