2019-06-11 18:38:07 +00:00
|
|
|
require "http"
|
|
|
|
|
2017-09-10 11:41:07 +00:00
|
|
|
module Kemal
|
2017-10-06 11:53:53 +00:00
|
|
|
# Takes 2 parameters: *path* and a *handler* to specify
|
2017-09-10 11:41:07 +00:00
|
|
|
# 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
|
|
|
|
|
2019-06-11 18:38:07 +00:00
|
|
|
def error(code : Int16, message : String)
|
|
|
|
err = WebsocketError.new(code, message)
|
|
|
|
raise err
|
|
|
|
end
|
|
|
|
|
2017-09-10 11:41:07 +00:00
|
|
|
def call(context : HTTP::Server::Context)
|
2019-06-11 18:38:07 +00:00
|
|
|
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
|
|
|
|
if error.is_a?(WebsocketError)
|
|
|
|
# TODO
|
|
|
|
context.response.status_code = 500
|
|
|
|
code = error.code.to_i16
|
|
|
|
message = error.status_message
|
|
|
|
else
|
|
|
|
context.response.status_code = error.code
|
|
|
|
code = 1011_i16
|
|
|
|
message = "Exception"
|
|
|
|
end
|
|
|
|
|
|
|
|
raw = uninitialized UInt8[2]
|
|
|
|
IO::ByteFormat::BigEndian.encode(code, raw.to_slice)
|
|
|
|
socket.not_nil!.close(String.new(raw.to_slice) + message)
|
|
|
|
|
|
|
|
raise error unless error.is_a?(WebsocketError)
|
|
|
|
end
|
|
|
|
else
|
|
|
|
raise UpgradeRequired.new
|
|
|
|
end
|
2017-09-10 11:41:07 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|