/
extension.rb
92 lines (74 loc) · 2.46 KB
/
extension.rb
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
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
require 'base64'
require 'digest/sha1'
module Cramp
module WebsocketExtension
WEBSOCKET_RECEIVE_CALLBACK = 'websocket.receive_callback'.freeze
def protocol_class
@env['HTTP_SEC_WEBSOCKET_VERSION'] ? Protocol10 : Protocol76
end
def websocket?
['WebSocket', 'websocket'].include?(@env['HTTP_UPGRADE'])
end
def secure_websocket?
if @env.has_key?('HTTP_X_FORWARDED_PROTO')
@env['HTTP_X_FORWARDED_PROTO'] == 'https'
else
@env['HTTP_ORIGIN'] =~ /^https:/i
end
end
def websocket_url
scheme = secure_websocket? ? 'wss:' : 'ws:'
@env['websocket.url'] = "#{ scheme }//#{ @env['HTTP_HOST'] }#{ @env['REQUEST_URI'] }"
end
class WebSocketHandler
def initialize(env, websocket_url, body = nil)
@env = env
@websocket_url = websocket_url
@body = body
end
end
class Protocol10 < WebSocketHandler
MAGIC_GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11".freeze
def handshake
digest = Base64.encode64(Digest::SHA1.digest("#{@env['HTTP_SEC_WEBSOCKET_KEY']}#{MAGIC_GUID}")).chomp
upgrade = "HTTP/1.1 101 Switching Protocols\r\n"
upgrade << "Upgrade: websocket\r\n"
upgrade << "Connection: Upgrade\r\n"
upgrade << "Sec-WebSocket-Accept: #{digest}\r\n\r\n"
upgrade
end
end
class Protocol76 < WebSocketHandler
def handshake
key1 = @env['HTTP_SEC_WEBSOCKET_KEY1']
value1 = number_from_key(key1) / spaces_in_key(key1)
key2 = @env['HTTP_SEC_WEBSOCKET_KEY2']
value2 = number_from_key(key2) / spaces_in_key(key2)
hash = Digest::MD5.digest(big_endian(value1) +
big_endian(value2) +
@body)
upgrade = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n"
upgrade << "Upgrade: WebSocket\r\n"
upgrade << "Connection: Upgrade\r\n"
upgrade << "Sec-WebSocket-Origin: #{@env['HTTP_ORIGIN']}\r\n"
upgrade << "Sec-WebSocket-Location: #{@websocket_url}\r\n\r\n"
upgrade << hash
upgrade
end
private
def number_from_key(key)
key.scan(/[0-9]/).join('').to_i(10)
end
def spaces_in_key(key)
key.scan(/ /).size
end
def big_endian(number)
string = ''
[24,16,8,0].each do |offset|
string << (number >> offset & 0xFF).chr
end
string
end
end
end
end