-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
/
websocket_handler.cr
61 lines (50 loc) · 1.72 KB
/
websocket_handler.cr
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
require "base64"
require "http/web_socket"
# A handler which adds websocket functionality to an `HTTP::Server`.
#
# When a request can be upgraded, the associated `HTTP::Websocket` and
# `HTTP::Server::Context` will be yielded to the block. For example:
#
# ```
# ws_handler = HTTP::WebSocketHandler.new do |ws, ctx|
# ws.on_ping { ws.pong ctx.request.path }
# end
# server = HTTP::Server.new [ws_handler]
# ```
class HTTP::WebSocketHandler
include HTTP::Handler
def initialize(&@proc : WebSocket, Server::Context ->)
end
def call(context) : Nil
unless websocket_upgrade_request? context.request
return call_next context
end
response = context.response
version = context.request.headers["Sec-WebSocket-Version"]?
unless version == WebSocket::Protocol::VERSION
response.status = :upgrade_required
response.headers["Sec-WebSocket-Version"] = WebSocket::Protocol::VERSION
return
end
key = context.request.headers["Sec-WebSocket-Key"]?
unless key
response.respond_with_status(:bad_request)
return
end
accept_code = WebSocket::Protocol.key_challenge(key)
response.status = :switching_protocols
response.headers["Upgrade"] = "websocket"
response.headers["Connection"] = "Upgrade"
response.headers["Sec-WebSocket-Accept"] = accept_code
response.upgrade do |io|
ws_session = WebSocket.new(io, sync_close: false)
@proc.call(ws_session, context)
ws_session.run
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