rakie 0.0.2 → 0.0.8

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,11 @@
1
+ module Rakie
2
+ VERSION = [0, 0, 8]
3
+
4
+ def self.version_s
5
+ VERSION.join('.')
6
+ end
7
+
8
+ def self.full_version_s
9
+ "#{NAME} v#{self.version_s}"
10
+ end
11
+ end
@@ -0,0 +1,138 @@
1
+ module Rakie
2
+ class Websocket
3
+ attr_accessor :delegate, :client_side
4
+ attr_reader :channel
5
+
6
+ # @param [Rakie::TCPChannel] channel
7
+ def initialize(delegate=nil, channel=nil)
8
+ @delegate = delegate
9
+
10
+ if channel == nil
11
+ channel = TCPChannel.new('127.0.0.1', 10086, self)
12
+ end
13
+
14
+ @channel = channel
15
+
16
+ # @type [WebsocketMessage]
17
+ @recv_message = WebsocketMessage.new
18
+
19
+ # @type [Array<WebsocketMessage>]
20
+ @send_messages = []
21
+ @client_side = true
22
+ end
23
+
24
+ def on_recv(channel, data)
25
+ Log.debug("Rakie::Websocket recv: #{data}")
26
+
27
+ # @type [WebsocketMessage] request
28
+ message = @recv_message
29
+
30
+ if message.parse_status == ParseStatus::COMPLETE
31
+ message = WebsocketMessage.new
32
+ @recv_message = message
33
+ end
34
+
35
+ len = message.parse(data)
36
+
37
+ Log.debug("Rakie::Websocket receive message: #{message.to_s} parse with #{len}")
38
+
39
+ if message.parse_status == ParseStatus::COMPLETE
40
+ response = WebsocketMessage.new
41
+
42
+ if message.op_code == WebsocketMessage::OP_PING
43
+ response.fin = true
44
+ response.op_code = WebsocketMessage::OP_PONG
45
+ response.payload = "pong"
46
+
47
+ elsif message.op_code == WebsocketMessage::OP_PONG
48
+ response.fin = true
49
+ response.op_code = WebsocketMessage::OP_PING
50
+ response.payload = "ping"
51
+
52
+ elsif @delegate
53
+ @delegate.on_message(self, message.payload)
54
+ return len
55
+
56
+ else
57
+ response.fin = true
58
+ response.op_code = WebsocketMessage::OP_TEXT
59
+ response.payload = "Rakie!"
60
+ end
61
+
62
+ response_data = response.to_s
63
+
64
+ Log.debug("Rakie::Websocket response: #{response_data}")
65
+
66
+ channel.write(response_data) # Response data
67
+
68
+ elsif message.parse_status == ParseStatus::ERROR
69
+ channel.close
70
+
71
+ Log.debug("Rakie::Websocket: Illegal message")
72
+ return 0
73
+ end
74
+
75
+ return len
76
+ end
77
+
78
+ def on_send(channel)
79
+ # @type [WebsocketMessage]
80
+ last_message = @send_messages.shift
81
+
82
+ if last_message
83
+ if last_message.op_code == WebsocketMessage::OP_CLOSE
84
+ Log.debug("Rakie::Websocket: send finish and close channel")
85
+ channel.close
86
+ end
87
+ end
88
+ end
89
+
90
+ def on_close(channel)
91
+ if @delegate
92
+ @delegate.on_disconnect(self)
93
+ end
94
+ end
95
+
96
+ def send(message, is_binary=false)
97
+ ws_message = WebsocketMessage.new
98
+ ws_message.fin = true
99
+ ws_message.op_code = WebsocketMessage::OP_TEXT
100
+ ws_message.payload = message
101
+
102
+ if is_binary
103
+ ws_message.op_code = WebsocketMessage::OP_BIN
104
+ end
105
+
106
+ if @client_side
107
+ ws_message.mask = true
108
+ ws_message.refresh_masking
109
+ end
110
+
111
+ send_message = ws_message.to_s
112
+ @send_messages << ws_message
113
+
114
+ Log.debug("Rakie::Websocket send: #{send_message}")
115
+
116
+ @channel.write(send_message) # Response data
117
+ end
118
+
119
+ def close
120
+ ws_message = WebsocketMessage.new
121
+ ws_message.fin = true
122
+ ws_message.op_code = WebsocketMessage::OP_CLOSE
123
+
124
+ if @client_side
125
+ ws_message.mask = true
126
+ ws_message.refresh_masking
127
+ end
128
+
129
+ ws_message.payload = "close"
130
+
131
+ send_message = ws_message.to_s
132
+
133
+ Log.debug("Rakie::Websocket send close: #{send_message}")
134
+
135
+ @channel.write(send_message) # Response data
136
+ end
137
+ end
138
+ end
@@ -0,0 +1,286 @@
1
+ module Rakie
2
+ class WebsocketBasicMessage
3
+ include Proto
4
+
5
+ FLAG_FIN = 1 << 7
6
+
7
+ OP_CONTINUE = 0x0
8
+ OP_TEXT = 0x1
9
+ OP_BIN = 0x2
10
+ OP_CLOSE = 0x8
11
+ OP_PING = 0x9
12
+ OP_PONG = 0xA
13
+
14
+ FLAG_MASK = 1 << 7
15
+
16
+ PARSE_OPERATION = PARSE_BEGIN
17
+ PARSE_LEN = 1
18
+ PARSE_EXT_LEN = 2
19
+ PARSE_MASKING = 3
20
+ PARSE_PAYLOAD = 4
21
+
22
+ attr_accessor :op_code, :mask, :length, :payload
23
+ attr_writer :fin
24
+
25
+ def initialize
26
+ @fin = false
27
+ @op_code = 0x0
28
+ @mask = false
29
+ @masking = []
30
+ @length = 0
31
+ @long_ext = false
32
+ @payload = ''
33
+ end
34
+
35
+ def fin?
36
+ @fin
37
+ end
38
+
39
+ # @param [String] source
40
+ def deserialize(source)
41
+ current_state = parse_state
42
+
43
+ case current_state
44
+ when PARSE_OPERATION
45
+ return parse_source_operation(source)
46
+
47
+ when PARSE_LEN
48
+ return parse_source_len(source)
49
+
50
+ when PARSE_EXT_LEN
51
+ return parse_source_ext_len(source)
52
+
53
+ when PARSE_MASKING
54
+ return parse_source_masking(source)
55
+
56
+ when PARSE_PAYLOAD
57
+ return parse_source_payload(source)
58
+ end
59
+ end
60
+
61
+ def serialize
62
+ data = ''
63
+
64
+ data += pack_source_operation
65
+ data += pack_source_len
66
+
67
+ if @mask
68
+ data += pack_source_masking
69
+ data += pack_source_masked_payload
70
+
71
+ return data
72
+ end
73
+
74
+ data += @payload
75
+
76
+ return data
77
+ end
78
+ end
79
+
80
+ class WebsocketMessage < WebsocketBasicMessage
81
+ # @param [String] source
82
+ def parse_source_operation(source)
83
+ offset = parse_offset
84
+
85
+ if source.length >= 1 + offset
86
+ byte = source[offset]
87
+ data = byte.unpack('C')[0]
88
+
89
+ if data & FLAG_FIN > 0
90
+ @fin = true
91
+ end
92
+
93
+ i = 3
94
+ code = 0x0
95
+
96
+ while i >= 0
97
+ code |= data & (1 << i)
98
+ i -= 1
99
+ end
100
+
101
+ @op_code = code
102
+
103
+ self.parse_state = PARSE_LEN
104
+ self.parse_offset = offset + 1
105
+
106
+ Log.debug("Rakie::WebsocketMessage parse source operation ok")
107
+ return ParseStatus::CONTINUE
108
+ end
109
+
110
+ return ParseStatus::PENDING
111
+ end
112
+
113
+ # @param [String] source
114
+ def parse_source_len(source)
115
+ offset = parse_offset
116
+
117
+ if source.length >= 1 + offset
118
+ byte = source[offset]
119
+ data = byte.unpack('C')[0]
120
+
121
+ if data & FLAG_MASK > 0
122
+ @mask = true
123
+ end
124
+
125
+ len = data & ~FLAG_MASK
126
+
127
+ Log.debug("Rakie::WebsocketMessage len: #{len}")
128
+
129
+ if len <= 125
130
+ @length = len
131
+
132
+ if @mask
133
+ self.parse_state = PARSE_MASKING
134
+ self.parse_offset = offset + 1
135
+
136
+ Log.debug("Rakie::WebsocketMessage parse source len ok")
137
+ return ParseStatus::CONTINUE
138
+ end
139
+
140
+ self.parse_state = PARSE_PAYLOAD
141
+ self.parse_offset = offset + 1
142
+
143
+ Log.debug("Rakie::WebsocketMessage parse source len ok")
144
+ return ParseStatus::CONTINUE
145
+ end
146
+
147
+ if len == 127
148
+ @long_ext = true
149
+ end
150
+
151
+ self.parse_state = PARSE_EXT_LEN
152
+ self.parse_offset = offset + 1
153
+
154
+ Log.debug("Rakie::WebsocketMessage parse source len ok")
155
+ return ParseStatus::CONTINUE
156
+ end
157
+
158
+ return ParseStatus::PENDING
159
+ end
160
+
161
+ # @param [String] source
162
+ def parse_source_ext_len(source)
163
+ offset = parse_offset
164
+ ext_len_size = 2
165
+ byte_format = 'S'
166
+
167
+ if @long_ext
168
+ ext_len_size = 8
169
+ byte_format = 'Q'
170
+ end
171
+
172
+ if source.length >= ext_len_size + offset
173
+ bytes = source[offset .. (offset + ext_len_size - 1)]
174
+ @length = bytes.unpack(byte_format + '>')[0]
175
+
176
+ if @mask
177
+ self.parse_state = PARSE_MASKING
178
+ self.parse_offset = offset + ext_len_size
179
+
180
+ Log.debug("Rakie::WebsocketMessage parse source ext len ok")
181
+ return ParseStatus::CONTINUE
182
+ end
183
+
184
+ self.parse_state = PARSE_PAYLOAD
185
+ self.parse_offset = offset + ext_len_size
186
+
187
+ Log.debug("Rakie::WebsocketMessage parse source ext len ok")
188
+ return ParseStatus::CONTINUE
189
+ end
190
+
191
+ return ParseStatus::PENDING
192
+ end
193
+
194
+ # @param [String] source
195
+ def parse_source_masking(source)
196
+ offset = parse_offset
197
+
198
+ if source.length >= 4 + offset
199
+ bytes = source[offset .. (offset + 4 - 1)]
200
+ @masking = bytes.unpack('C*')
201
+
202
+ self.parse_state = PARSE_PAYLOAD
203
+ self.parse_offset = offset + 4
204
+
205
+ Log.debug("Rakie::WebsocketMessage parse source masking ok")
206
+ return ParseStatus::CONTINUE
207
+ end
208
+
209
+ return ParseStatus::PENDING
210
+ end
211
+
212
+ # @param [String] source
213
+ def parse_source_payload(source)
214
+ offset = parse_offset
215
+
216
+ if source.length >= @length + offset
217
+ bytes = source[offset .. (offset + @length - 1)]
218
+
219
+ if @mask
220
+ bytes_list = bytes.unpack('C*')
221
+ masking = @masking
222
+ bytes_list_unmasked = bytes_list.map.with_index { |b, i| b ^ masking[i % 4] }
223
+
224
+ @payload = bytes_list_unmasked.pack('C*')
225
+
226
+ self.parse_state = PARSE_OPERATION
227
+ self.parse_offset = offset + @length
228
+
229
+ Log.debug("Rakie::WebsocketMessage parse source masked payload ok")
230
+ return ParseStatus::COMPLETE
231
+ end
232
+
233
+ @payload = bytes
234
+
235
+ self.parse_state = PARSE_OPERATION
236
+ self.parse_offset = offset + @length
237
+
238
+ Log.debug("Rakie::WebsocketMessage parse source payload ok")
239
+ return ParseStatus::COMPLETE
240
+ end
241
+
242
+ return ParseStatus::PENDING
243
+ end
244
+
245
+ def pack_source_operation
246
+ fin_bit = @fin ? 1 : 0
247
+ return [(fin_bit << 7) + @op_code].pack('C')
248
+ end
249
+
250
+ def pack_source_len
251
+ mask_bit = @mask ? 1 : 0
252
+
253
+ if @payload.length < 126
254
+ return [(mask_bit << 7) + @payload.length].pack('C')
255
+
256
+ elsif @payload.length < 65536
257
+ return [(mask_bit << 7) + 126, @payload.length].pack('CS>')
258
+ end
259
+
260
+ return [(mask_bit << 7) + 127, @payload.length].pack('CQ>')
261
+ end
262
+
263
+ def pack_source_masking
264
+ return @masking.pack('C*')
265
+ end
266
+
267
+ def pack_source_masked_payload
268
+ masking = @masking
269
+
270
+ if masking.empty?
271
+ return ''
272
+ end
273
+
274
+ bytes_list = @payload.unpack('C*')
275
+ bytes_list_masked = bytes_list.map.with_index { |b, i| b ^ masking[i % 4] }
276
+
277
+ return bytes_list_masked.pack('C*')
278
+ end
279
+
280
+ def refresh_masking
281
+ masking = []
282
+ 4.times { masking << rand(1 .. 255) }
283
+ @masking = masking
284
+ end
285
+ end
286
+ end
@@ -0,0 +1,89 @@
1
+ module Rakie
2
+ class WebsocketServer < Websocket
3
+ # @param [Rakie::HttpServer] http_server
4
+ def initialize(host: '127.0.0.1', port: 10086, delegate: nil, http_server: nil)
5
+ @delegate = delegate
6
+
7
+ if http_server == nil
8
+ http_server = HttpServer.new(host: host, port: port)
9
+ end
10
+
11
+ @host = http_server.host
12
+ @port = http_server.port
13
+
14
+ http_server.opt[:websocket_delegate] = self
15
+ @channel = http_server.channel
16
+
17
+ # @type [Array<WebsocketClient>]
18
+ @clients = {}
19
+ end
20
+
21
+ def on_accept(channel)
22
+ ws_client = Websocket.new(@delegate, channel)
23
+ ws_client.client_side = false
24
+
25
+ channel.delegate = self
26
+
27
+ if @delegate
28
+ @delegate.on_connect(ws_client)
29
+ end
30
+
31
+ @clients[channel] = ws_client
32
+ Log.debug("Rakie::WebsocketServer accept client: #{ws_client}")
33
+ end
34
+
35
+ # @param [HttpRequest] request
36
+ # @param [HttpResponse] response
37
+ # @return bool
38
+ def upgrade(request, response)
39
+ if websocket_key = request.headers["sec-websocket-key"]
40
+ digest_key = Digest::SHA1.base64digest(websocket_key + '258EAFA5-E914-47DA-95CA-C5AB0DC85B11')
41
+
42
+ response.head.status = 101
43
+ response.head.message = 'Switching Protocols'
44
+ response.headers["connection"] = "upgrade"
45
+ response.headers["upgrade"] = "websocket"
46
+ response.headers["sec-websocket-accept"] = digest_key
47
+ end
48
+ end
49
+
50
+ def on_recv(channel, data)
51
+ client = @clients[channel]
52
+
53
+ if client
54
+ return client.on_recv(channel, data)
55
+ end
56
+
57
+ return data.length
58
+ end
59
+
60
+ def on_send(channel)
61
+ client = @clients[channel]
62
+
63
+ if client
64
+ client.on_send(channel)
65
+ end
66
+ end
67
+
68
+ def on_close(channel)
69
+ client = @clients[channel]
70
+
71
+ if client
72
+ client.on_close(channel)
73
+ end
74
+
75
+ @clients.delete(channel)
76
+ end
77
+
78
+ def send(message, is_binary=false); end
79
+
80
+ def close; end
81
+
82
+ def clients
83
+ @clients.values
84
+ end
85
+ end
86
+ end
87
+
88
+ require "digest"
89
+ require "base64"