rakie 0.0.3 → 0.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/rakie.rb +13 -1
- data/lib/rakie/channel.rb +9 -3
- data/lib/rakie/event.rb +28 -9
- data/lib/rakie/http.rb +5 -0
- data/lib/rakie/http_proto.rb +197 -0
- data/lib/rakie/http_server.rb +127 -0
- data/lib/rakie/log.rb +96 -0
- data/lib/rakie/proto.rb +63 -0
- data/lib/rakie/tcp_channel.rb +19 -3
- data/lib/rakie/tcp_server_channel.rb +27 -11
- data/lib/rakie/version.rb +11 -0
- data/lib/rakie/websocket.rb +137 -0
- data/lib/rakie/websocket_proto.rb +286 -0
- data/lib/rakie/websocket_server.rb +83 -0
- metadata +10 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 754086391c9ec9e2eb623d8d6a1ead9ca9188d49fb0c391b26c4a9e36db88236
|
4
|
+
data.tar.gz: 6ac34e0868c8bc1748e7f572266b4223a91f676d9d7c11b22fa5a7e10586713c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 839acbd5155b745948a6144a37480b770e50f01ff4daf29146e39c8fb2e36cf0f434f10313577d3f7b1efcfd039317b036d9c87f96d6c57d09a25c9da6ba5e10
|
7
|
+
data.tar.gz: a844de019fa10103c96e7cc7ab4f7e78ffb2cd5a7c23b28be6483c35fbb7a5cfd1b51f4a1996fa053fa33e03d701af52682c0af306d92b3f2c2c98878aa7c4c6
|
data/lib/rakie.rb
CHANGED
@@ -1,12 +1,24 @@
|
|
1
1
|
|
2
2
|
module Rakie
|
3
|
-
|
3
|
+
NAME = "rakie"
|
4
|
+
MODE_DEV = 0
|
5
|
+
MODE_REL = 1
|
6
|
+
|
7
|
+
def self.current_mode
|
8
|
+
@current_mode ||= MODE_REL
|
9
|
+
end
|
4
10
|
end
|
5
11
|
|
6
12
|
require "rakie/channel"
|
7
13
|
require "rakie/event"
|
14
|
+
require "rakie/proto"
|
15
|
+
require "rakie/http_proto"
|
16
|
+
require "rakie/websocket_proto"
|
8
17
|
require "rakie/simple_server"
|
9
18
|
require "rakie/http_server"
|
19
|
+
require "rakie/websocket"
|
20
|
+
require "rakie/websocket_server"
|
10
21
|
require "rakie/tcp_channel"
|
11
22
|
require "rakie/tcp_server_channel"
|
12
23
|
require "rakie/log"
|
24
|
+
require "rakie/version"
|
data/lib/rakie/channel.rb
CHANGED
@@ -2,6 +2,8 @@ module Rakie
|
|
2
2
|
class Channel
|
3
3
|
attr_accessor :delegate
|
4
4
|
|
5
|
+
DEFAULT_BUFFER_SIZE = 512 * 1024
|
6
|
+
|
5
7
|
def initialize(io, delegate=nil)
|
6
8
|
@io = io
|
7
9
|
@read_buffer = String.new
|
@@ -14,7 +16,7 @@ module Rakie
|
|
14
16
|
def on_read(io)
|
15
17
|
begin
|
16
18
|
loop do
|
17
|
-
@read_buffer << io.read_nonblock(
|
19
|
+
@read_buffer << io.read_nonblock(DEFAULT_BUFFER_SIZE)
|
18
20
|
end
|
19
21
|
|
20
22
|
rescue IO::EAGAINWaitReadable
|
@@ -32,6 +34,7 @@ module Rakie
|
|
32
34
|
end
|
33
35
|
|
34
36
|
if @delegate != nil
|
37
|
+
Log.debug("Channel handle on_recv")
|
35
38
|
len = @delegate.on_recv(self, @read_buffer)
|
36
39
|
|
37
40
|
if len > @read_buffer.length
|
@@ -39,7 +42,6 @@ module Rakie
|
|
39
42
|
end
|
40
43
|
|
41
44
|
@read_buffer = @read_buffer[len .. -1]
|
42
|
-
Log.debug("Channel handle on_recv")
|
43
45
|
end
|
44
46
|
|
45
47
|
return Event::HANDLE_CONTINUED
|
@@ -90,10 +92,14 @@ module Rakie
|
|
90
92
|
return Event::HANDLE_FINISHED
|
91
93
|
end
|
92
94
|
|
93
|
-
def
|
95
|
+
def on_detach(io)
|
94
96
|
begin
|
95
97
|
io.close
|
96
98
|
|
99
|
+
if @delegate
|
100
|
+
@delegate.on_close(self)
|
101
|
+
end
|
102
|
+
|
97
103
|
rescue
|
98
104
|
Log.debug("Channel is already closed")
|
99
105
|
end
|
data/lib/rakie/event.rb
CHANGED
@@ -50,7 +50,7 @@ module Rakie
|
|
50
50
|
|
51
51
|
if handler != nil
|
52
52
|
Log.debug("Event close #{new_io}")
|
53
|
-
handler.
|
53
|
+
handler.on_detach(new_io)
|
54
54
|
end
|
55
55
|
|
56
56
|
@ios.delete(new_io)
|
@@ -105,11 +105,11 @@ module Rakie
|
|
105
105
|
result = handler.on_read(io)
|
106
106
|
|
107
107
|
if result == HANDLE_FINISHED
|
108
|
-
@ios[io]
|
108
|
+
@ios[io] &= ~READ_EVENT
|
109
109
|
Log.debug("Event remove read #{io}")
|
110
110
|
|
111
111
|
elsif result == HANDLE_FAILED
|
112
|
-
handler.
|
112
|
+
handler.on_detach(io)
|
113
113
|
Log.debug("Event close #{io}")
|
114
114
|
|
115
115
|
@ios.delete(io)
|
@@ -130,11 +130,11 @@ module Rakie
|
|
130
130
|
result = handler.on_write(io)
|
131
131
|
|
132
132
|
if result == HANDLE_FINISHED
|
133
|
-
@ios[io]
|
133
|
+
@ios[io] &= ~WRITE_EVENT
|
134
134
|
Log.debug("Event remove write #{io}")
|
135
135
|
|
136
136
|
elsif result == HANDLE_FAILED
|
137
|
-
handler.
|
137
|
+
handler.on_detach(io)
|
138
138
|
Log.debug("Event close #{io}")
|
139
139
|
|
140
140
|
@ios.delete(io)
|
@@ -168,19 +168,38 @@ module Rakie
|
|
168
168
|
end
|
169
169
|
|
170
170
|
def self.instance
|
171
|
-
@instance ||= Event.new
|
171
|
+
@instance ||= Array.new(self.concurrent) { |i| Event.new }
|
172
172
|
end
|
173
173
|
|
174
174
|
def self.push(io, listener, type)
|
175
|
-
|
175
|
+
i = io.fileno % self.concurrent
|
176
|
+
inst = self.instance[i]
|
177
|
+
inst.push(io, listener, type)
|
178
|
+
# instance.push(io, listener, type)
|
176
179
|
end
|
177
180
|
|
178
181
|
def self.delete(io)
|
179
|
-
self.
|
182
|
+
i = io.fileno % self.concurrent
|
183
|
+
inst = self.instance[i]
|
184
|
+
inst.delete(io)
|
185
|
+
# self.instance.delete(io)
|
180
186
|
end
|
181
187
|
|
182
188
|
def self.modify(io, listener, type)
|
183
|
-
|
189
|
+
i = io.fileno % self.concurrent
|
190
|
+
inst = self.instance[i]
|
191
|
+
inst.modify(io, listener, type)
|
192
|
+
# self.instance.modify(io, listener, type)
|
193
|
+
end
|
194
|
+
|
195
|
+
# @return [Integer]
|
196
|
+
def self.concurrent
|
197
|
+
@concurrent ||= 2
|
198
|
+
end
|
199
|
+
|
200
|
+
# @param [Integer] count
|
201
|
+
def self.concurrent=(count)
|
202
|
+
@concurrent = count
|
184
203
|
end
|
185
204
|
end
|
186
205
|
end
|
@@ -0,0 +1,197 @@
|
|
1
|
+
module Rakie
|
2
|
+
class HttpMIME
|
3
|
+
TEXT = "text/plain"
|
4
|
+
HTML = "text/html"
|
5
|
+
JSON = "application/json"
|
6
|
+
end
|
7
|
+
|
8
|
+
class HttpRequest
|
9
|
+
include Proto
|
10
|
+
|
11
|
+
attr_accessor :head, :headers, :content
|
12
|
+
|
13
|
+
PARSE_HEAD = PARSE_BEGIN
|
14
|
+
PARSE_HEADERS = 1
|
15
|
+
PARSE_CONTENT = 2
|
16
|
+
|
17
|
+
class Head
|
18
|
+
attr_accessor :method, :path, :version
|
19
|
+
|
20
|
+
def initialize
|
21
|
+
@method = 'HEAD'
|
22
|
+
@path = '/'
|
23
|
+
@version = 'HTTP/1.1'
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def initialize
|
28
|
+
@head = Head.new
|
29
|
+
@headers = {}
|
30
|
+
@content = ''
|
31
|
+
end
|
32
|
+
|
33
|
+
# @param [String] source
|
34
|
+
def parse_source_head(source)
|
35
|
+
offset = parse_offset
|
36
|
+
|
37
|
+
if eol_offset = source.index("\r\n")
|
38
|
+
head_s = source[0 .. eol_offset]
|
39
|
+
head_method, head_path, head_version = head_s.split(' ')
|
40
|
+
|
41
|
+
head.method = head_method
|
42
|
+
head.path = head_path
|
43
|
+
head.version = head_version
|
44
|
+
|
45
|
+
self.parse_state = PARSE_HEADERS
|
46
|
+
self.parse_offset = offset + 2
|
47
|
+
|
48
|
+
Log.debug("Rakie::HttpRequest parse source head ok")
|
49
|
+
return ParseStatus::CONTINUE
|
50
|
+
end
|
51
|
+
|
52
|
+
return ParseStatus::PENDING
|
53
|
+
end
|
54
|
+
|
55
|
+
def parse_source_header_item(header)
|
56
|
+
if semi_offset = header.index(':')
|
57
|
+
return [
|
58
|
+
header[0 .. (semi_offset - 1)].downcase,
|
59
|
+
header[(semi_offset + 1) .. -1].strip
|
60
|
+
]
|
61
|
+
end
|
62
|
+
|
63
|
+
return nil
|
64
|
+
end
|
65
|
+
|
66
|
+
# @param [String] source
|
67
|
+
def parse_source_headers(source)
|
68
|
+
offset = parse_offset
|
69
|
+
|
70
|
+
while eol_offset = source.index("\r\n", offset)
|
71
|
+
header = source[offset .. (eol_offset - 1)]
|
72
|
+
|
73
|
+
if header.length == 0
|
74
|
+
self.parse_state = PARSE_CONTENT
|
75
|
+
self.parse_offset = eol_offset + 2
|
76
|
+
|
77
|
+
Log.debug("Rakie::HttpRequest parse source header done")
|
78
|
+
return ParseStatus::CONTINUE
|
79
|
+
end
|
80
|
+
|
81
|
+
header_key, header_value = self.parse_source_header_item(header)
|
82
|
+
|
83
|
+
if header_key
|
84
|
+
headers[header_key] = header_value
|
85
|
+
Log.debug("Rakie::HttpRequest parse source #{header_key}, #{header_value} header ok")
|
86
|
+
end
|
87
|
+
|
88
|
+
offset = eol_offset + 2
|
89
|
+
end
|
90
|
+
|
91
|
+
self.parse_offset = offset
|
92
|
+
|
93
|
+
return ParseStatus::PENDING
|
94
|
+
end
|
95
|
+
|
96
|
+
# @param [String] source
|
97
|
+
def parse_source_content(source)
|
98
|
+
len = headers["content-length"]
|
99
|
+
offset = parse_offset
|
100
|
+
|
101
|
+
if len == nil
|
102
|
+
return ParseStatus::COMPLETE
|
103
|
+
end
|
104
|
+
|
105
|
+
len = len.to_i
|
106
|
+
|
107
|
+
if source.length >= len + offset
|
108
|
+
self.content = source[offset .. (offset + len - 1)]
|
109
|
+
self.parse_state = PARSE_HEAD
|
110
|
+
self.parse_offset = offset + len
|
111
|
+
|
112
|
+
Log.debug("Rakie::HttpRequest parse source content ok")
|
113
|
+
return ParseStatus::COMPLETE
|
114
|
+
end
|
115
|
+
|
116
|
+
return ParseStatus::PENDING
|
117
|
+
end
|
118
|
+
|
119
|
+
# @param [String] source
|
120
|
+
# @return [Integer]
|
121
|
+
def deserialize(source)
|
122
|
+
current_state = parse_state
|
123
|
+
|
124
|
+
case current_state
|
125
|
+
when PARSE_HEAD
|
126
|
+
return parse_source_head(source)
|
127
|
+
|
128
|
+
when PARSE_HEADERS
|
129
|
+
return parse_source_headers(source)
|
130
|
+
|
131
|
+
when PARSE_CONTENT
|
132
|
+
return parse_source_content(source)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
# @return [String]
|
137
|
+
def serialize
|
138
|
+
data = ""
|
139
|
+
|
140
|
+
data += "#{head.method} #{head.path} #{head.version}"
|
141
|
+
data += "\r\n"
|
142
|
+
|
143
|
+
headers_list = []
|
144
|
+
headers.each do |k, v|
|
145
|
+
headers_list << "#{k}: #{v}"
|
146
|
+
end
|
147
|
+
|
148
|
+
data += headers_list.join("\r\n")
|
149
|
+
data += "\r\n\r\n"
|
150
|
+
|
151
|
+
data += content
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
class HttpResponse
|
156
|
+
include Proto
|
157
|
+
|
158
|
+
PARSE_HEAD = 0
|
159
|
+
PARSE_HEADERS = 1
|
160
|
+
PARSE_CONTENT = 2
|
161
|
+
|
162
|
+
attr_accessor :head, :headers, :content
|
163
|
+
|
164
|
+
class Head
|
165
|
+
attr_accessor :version, :status, :message
|
166
|
+
|
167
|
+
def initialize
|
168
|
+
@version = 'HTTP/1.1'
|
169
|
+
@status = 200
|
170
|
+
@message = 'OK'
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
def initialize
|
175
|
+
@head = Head.new
|
176
|
+
@headers = {}
|
177
|
+
@content = ''
|
178
|
+
end
|
179
|
+
|
180
|
+
def serialize
|
181
|
+
data = ""
|
182
|
+
|
183
|
+
data += "#{head.version} #{head.status} #{head.message}"
|
184
|
+
data += "\r\n"
|
185
|
+
|
186
|
+
headers_list = []
|
187
|
+
headers.each do |k, v|
|
188
|
+
headers_list << "#{k}: #{v}"
|
189
|
+
end
|
190
|
+
|
191
|
+
data += headers_list.join("\r\n")
|
192
|
+
data += "\r\n\r\n"
|
193
|
+
|
194
|
+
data += content
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
@@ -0,0 +1,127 @@
|
|
1
|
+
module Rakie
|
2
|
+
class HttpServer
|
3
|
+
class Session
|
4
|
+
# @return [HttpRequest]
|
5
|
+
attr_accessor :request
|
6
|
+
|
7
|
+
# @type [Array<HttpResponse>]
|
8
|
+
attr_accessor :responses
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
@request = HttpRequest.new
|
12
|
+
@responses = []
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
attr_reader :channel, :opt
|
17
|
+
|
18
|
+
def initialize(delegate=nil)
|
19
|
+
@channel = TCPServerChannel.new('127.0.0.1', 10086, self)
|
20
|
+
|
21
|
+
# @type [Hash{Channel=>Session}]
|
22
|
+
@sessions = {}
|
23
|
+
@delegate = delegate
|
24
|
+
@opt = {}
|
25
|
+
end
|
26
|
+
|
27
|
+
def on_accept(channel)
|
28
|
+
channel.delegate = self
|
29
|
+
@sessions[channel] = Session.new
|
30
|
+
Log.debug("Rakie::HTTPServer accept client: #{channel}")
|
31
|
+
end
|
32
|
+
|
33
|
+
def on_recv(channel, data)
|
34
|
+
# Log.debug("Rakie::HTTPServer recv: #{data}")
|
35
|
+
session = @sessions[channel]
|
36
|
+
|
37
|
+
if session == nil
|
38
|
+
return 0
|
39
|
+
end
|
40
|
+
|
41
|
+
# @type [HttpRequest] request
|
42
|
+
request = session.request
|
43
|
+
|
44
|
+
if request.parse_status == ParseStatus::COMPLETE
|
45
|
+
request = HttpRequest.new
|
46
|
+
session.request = request
|
47
|
+
end
|
48
|
+
|
49
|
+
len = request.parse(data)
|
50
|
+
|
51
|
+
Log.debug("Rakie::HttpServer receive request: #{request.to_s}")
|
52
|
+
|
53
|
+
if request.parse_status == ParseStatus::COMPLETE
|
54
|
+
response = HttpResponse.new
|
55
|
+
|
56
|
+
if upgrade = request.headers["upgrade"]
|
57
|
+
if websocket_delegate = @opt[:websocket_delegate]
|
58
|
+
websocket_delegate.upgrade(request, response)
|
59
|
+
Log.debug("Rakie::HttpServer upgrade protocol")
|
60
|
+
end
|
61
|
+
|
62
|
+
elsif @delegate != nil
|
63
|
+
@delegate.handle(request, response)
|
64
|
+
|
65
|
+
else
|
66
|
+
response.headers["content-type"] = HttpMIME::HTML
|
67
|
+
response.content = "<html><body><h1>Rakie!</h1></body></html>"
|
68
|
+
end
|
69
|
+
|
70
|
+
if header_connection = request.headers["connection"]
|
71
|
+
response.headers["connection"] = header_connection
|
72
|
+
end
|
73
|
+
|
74
|
+
if response.content.length > 0
|
75
|
+
response.headers["content-length"] = response.content.length
|
76
|
+
end
|
77
|
+
|
78
|
+
response.headers["server"] = Rakie.full_version_s
|
79
|
+
session.responses << response
|
80
|
+
response_data = response.to_s
|
81
|
+
|
82
|
+
Log.debug("Rakie::HttpServer response: #{response_data}")
|
83
|
+
|
84
|
+
channel.write(response_data) # Response data
|
85
|
+
|
86
|
+
elsif request.parse_status == ParseStatus::ERROR
|
87
|
+
channel.close
|
88
|
+
@sessions.delete(channel)
|
89
|
+
|
90
|
+
Log.debug("Rakie::HttpServer: Illegal request")
|
91
|
+
return len
|
92
|
+
end
|
93
|
+
|
94
|
+
return len
|
95
|
+
end
|
96
|
+
|
97
|
+
def on_send(channel)
|
98
|
+
session = @sessions[channel]
|
99
|
+
# @type [HttpRequest]
|
100
|
+
last_response = session.responses.shift
|
101
|
+
|
102
|
+
if last_response
|
103
|
+
if connect_status = last_response.headers["connection"]
|
104
|
+
if connect_status.downcase == "close"
|
105
|
+
Log.debug("Rakie::HttpServer: send finish and close channel")
|
106
|
+
channel.close
|
107
|
+
@sessions.delete(channel)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
if upgrade = last_response.headers["upgrade"]
|
112
|
+
websocket_delegate = @opt[:websocket_delegate]
|
113
|
+
|
114
|
+
if websocket_delegate
|
115
|
+
websocket_delegate.on_accept(channel)
|
116
|
+
@sessions.delete(channel)
|
117
|
+
return
|
118
|
+
end
|
119
|
+
|
120
|
+
Log.debug("Rakie::HttpServer: no websocket delegate and close channel")
|
121
|
+
channel.close
|
122
|
+
@sessions.delete(channel)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
data/lib/rakie/log.rb
ADDED
@@ -0,0 +1,96 @@
|
|
1
|
+
module Rakie
|
2
|
+
class Log
|
3
|
+
attr_accessor :out, :level
|
4
|
+
|
5
|
+
@instance = nil
|
6
|
+
|
7
|
+
LEVEL_INFO = 0
|
8
|
+
LEVEL_ERROR = 1
|
9
|
+
LEVEL_DEBUG = 2
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
@level = Rakie.current_mode == Rakie::MODE_DEV ? LEVEL_DEBUG : LEVEL_ERROR
|
13
|
+
@out = STDOUT
|
14
|
+
end
|
15
|
+
|
16
|
+
def level_text(level=nil)
|
17
|
+
unless level
|
18
|
+
level = @level
|
19
|
+
end
|
20
|
+
|
21
|
+
case level
|
22
|
+
when LEVEL_INFO
|
23
|
+
return "INFO"
|
24
|
+
|
25
|
+
when LEVEL_ERROR
|
26
|
+
return "ERROR"
|
27
|
+
|
28
|
+
when LEVEL_DEBUG
|
29
|
+
return "DEBUG"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def level_info?
|
34
|
+
@level >= LEVEL_INFO
|
35
|
+
end
|
36
|
+
|
37
|
+
def level_error?
|
38
|
+
@level >= LEVEL_ERROR
|
39
|
+
end
|
40
|
+
|
41
|
+
def level_debug?
|
42
|
+
@level >= LEVEL_DEBUG
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.instance
|
46
|
+
@instance ||= Log.new
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.info(message, who=nil)
|
50
|
+
unless self.instance.level_info?
|
51
|
+
return
|
52
|
+
end
|
53
|
+
|
54
|
+
if who
|
55
|
+
message = "#{who}: #{message}"
|
56
|
+
end
|
57
|
+
|
58
|
+
level = self.instance.level_text(LEVEL_INFO)
|
59
|
+
self.instance.out.write("[#{Time.now.to_s}][#{level}] #{message}\n")
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.error(message, who=nil)
|
63
|
+
unless self.instance.level_error?
|
64
|
+
return
|
65
|
+
end
|
66
|
+
|
67
|
+
if who
|
68
|
+
message = "#{who}: #{message}"
|
69
|
+
end
|
70
|
+
|
71
|
+
level = self.instance.level_text(LEVEL_ERROR)
|
72
|
+
self.instance.out.write("[#{Time.now.to_s}][#{level}] #{message}\n")
|
73
|
+
end
|
74
|
+
|
75
|
+
def self.debug(message, who=nil)
|
76
|
+
unless self.instance.level_debug?
|
77
|
+
return
|
78
|
+
end
|
79
|
+
|
80
|
+
if who
|
81
|
+
message = "#{who}: #{message}"
|
82
|
+
end
|
83
|
+
|
84
|
+
level = self.instance.level_text(LEVEL_DEBUG)
|
85
|
+
self.instance.out.print("[#{Time.now.to_s}][#{level}] #{message}\n")
|
86
|
+
end
|
87
|
+
|
88
|
+
def self.level
|
89
|
+
self.instance.level
|
90
|
+
end
|
91
|
+
|
92
|
+
def self.level=(level)
|
93
|
+
self.instance.level = level
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
data/lib/rakie/proto.rb
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
module Rakie
|
2
|
+
class ParseStatus
|
3
|
+
ERROR = -1
|
4
|
+
CONTINUE = 0
|
5
|
+
PENDING = 1
|
6
|
+
COMPLETE = 2
|
7
|
+
end
|
8
|
+
|
9
|
+
module Proto
|
10
|
+
PARSE_BEGIN = 0
|
11
|
+
|
12
|
+
# @return [Integer]
|
13
|
+
def parse_status
|
14
|
+
@parse_status ||= ParseStatus::CONTINUE
|
15
|
+
end
|
16
|
+
|
17
|
+
# @return [Integer]
|
18
|
+
def parse_state
|
19
|
+
@parse_state ||= PARSE_BEGIN
|
20
|
+
end
|
21
|
+
|
22
|
+
# @param [Integer] state
|
23
|
+
def parse_state=(state)
|
24
|
+
@parse_state = state
|
25
|
+
# puts("Set state: #{@parse_state}")
|
26
|
+
end
|
27
|
+
|
28
|
+
# @return [Integer]
|
29
|
+
def parse_offset
|
30
|
+
@parse_offset ||= 0
|
31
|
+
end
|
32
|
+
|
33
|
+
# @param [Integer] offset
|
34
|
+
def parse_offset=(offset)
|
35
|
+
@parse_offset = offset
|
36
|
+
end
|
37
|
+
|
38
|
+
# @param [String] source
|
39
|
+
def parse(source)
|
40
|
+
status = ParseStatus::CONTINUE
|
41
|
+
|
42
|
+
while status == ParseStatus::CONTINUE
|
43
|
+
status = self.deserialize(source)
|
44
|
+
end
|
45
|
+
|
46
|
+
if status == ParseStatus::PENDING
|
47
|
+
status = ParseStatus::CONTINUE
|
48
|
+
end
|
49
|
+
|
50
|
+
offset = @parse_offset
|
51
|
+
@parse_status = status
|
52
|
+
@parse_offset = 0
|
53
|
+
|
54
|
+
return offset
|
55
|
+
end
|
56
|
+
|
57
|
+
# @param [Object] object
|
58
|
+
# @return [String]
|
59
|
+
def to_s
|
60
|
+
self.serialize
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
data/lib/rakie/tcp_channel.rb
CHANGED
@@ -1,8 +1,24 @@
|
|
1
1
|
module Rakie
|
2
2
|
class TCPChannel < Channel
|
3
|
-
|
4
|
-
|
5
|
-
|
3
|
+
LOCAL_HOST = '127.0.0.1'
|
4
|
+
|
5
|
+
# @param host [String]
|
6
|
+
# @param port [Integer]
|
7
|
+
# @param delegate [Object]
|
8
|
+
# @param socket [Socket]
|
9
|
+
# @overload initialize(host, port, delegate)
|
10
|
+
# @overload initialize(host, port)
|
11
|
+
# @overload initialize(host, port, delegate, socket)
|
12
|
+
def initialize(host=LOCAL_HOST, port=3001, delegate=nil, socket=nil)
|
13
|
+
if socket == nil
|
14
|
+
socket = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM)
|
15
|
+
socket.connect(Socket.pack_sockaddr_in(port, host))
|
16
|
+
end
|
17
|
+
|
18
|
+
@port = port
|
19
|
+
@host = host
|
20
|
+
|
21
|
+
super(socket, delegate)
|
6
22
|
end
|
7
23
|
end
|
8
24
|
end
|
@@ -1,25 +1,41 @@
|
|
1
1
|
module Rakie
|
2
|
-
class TCPServerChannel <
|
3
|
-
|
4
|
-
|
2
|
+
class TCPServerChannel < TCPChannel
|
3
|
+
# @param host [String]
|
4
|
+
# @param port [Integer]
|
5
|
+
# @param delegate [Object]
|
6
|
+
# @overload initialize(host, port, delegate)
|
7
|
+
# @overload initialize(host, port)
|
8
|
+
# @overload initialize(port)
|
9
|
+
def initialize(host=LOCAL_HOST, port=3001, delegate=nil)
|
10
|
+
socket = nil
|
5
11
|
|
6
12
|
if port == nil
|
7
|
-
port =
|
8
|
-
|
9
|
-
|
10
|
-
else
|
11
|
-
io = TCPServer.new(ip, port)
|
13
|
+
port = host
|
14
|
+
host = LOCAL_HOST
|
12
15
|
end
|
13
16
|
|
17
|
+
socket = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM)
|
18
|
+
socket.setsockopt(Socket::SOL_SOCKET, Socket::SO_REUSEADDR, 1)
|
19
|
+
socket.bind(Socket.pack_sockaddr_in(port, host))
|
20
|
+
socket.listen(255)
|
21
|
+
|
14
22
|
@clients = []
|
15
23
|
|
16
|
-
super(
|
24
|
+
super(host, port, delegate, socket)
|
17
25
|
end
|
18
26
|
|
27
|
+
# @param io [Socket]
|
19
28
|
def on_read(io)
|
20
29
|
begin
|
21
|
-
|
22
|
-
|
30
|
+
ret = io.accept_nonblock
|
31
|
+
# @type client_io [Socket]
|
32
|
+
client_io = ret[0]
|
33
|
+
# @type client_info [Addrinfo]
|
34
|
+
client_info = ret[1]
|
35
|
+
client_name_info = client_info.getnameinfo
|
36
|
+
client_host = client_name_info[0]
|
37
|
+
client_port = client_name_info[1]
|
38
|
+
channel = TCPChannel.new(client_host, client_port, nil, client_io)
|
23
39
|
|
24
40
|
if @delegate != nil
|
25
41
|
Log.debug("TCPServerChannel has delegate")
|
@@ -0,0 +1,137 @@
|
|
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::HTTPServer 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}")
|
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 len
|
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
|
+
|
113
|
+
Log.debug("Rakie::Websocket send: #{send_message}")
|
114
|
+
|
115
|
+
@channel.write(send_message) # Response data
|
116
|
+
end
|
117
|
+
|
118
|
+
def close
|
119
|
+
ws_message = WebsocketMessage.new
|
120
|
+
ws_message.fin = true
|
121
|
+
ws_message.op_code = WebsocketMessage::OP_CLOSE
|
122
|
+
|
123
|
+
if @client_side
|
124
|
+
ws_message.mask = true
|
125
|
+
ws_message.refresh_masking
|
126
|
+
end
|
127
|
+
|
128
|
+
ws_message.payload = "close"
|
129
|
+
|
130
|
+
send_message = ws_message.to_s
|
131
|
+
|
132
|
+
Log.debug("Rakie::Websocket send close: #{send_message}")
|
133
|
+
|
134
|
+
@channel.write(send_message) # Response data
|
135
|
+
end
|
136
|
+
end
|
137
|
+
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,83 @@
|
|
1
|
+
module Rakie
|
2
|
+
class WebsocketServer < Websocket
|
3
|
+
# @param [Rakie::HttpServer] http_server
|
4
|
+
def initialize(delegate=nil, http_server=nil)
|
5
|
+
@delegate = delegate
|
6
|
+
|
7
|
+
if http_server == nil
|
8
|
+
http_server = HttpServer.new('127.0.0.1', 10086, self)
|
9
|
+
end
|
10
|
+
|
11
|
+
http_server.opt[:websocket_delegate] = self
|
12
|
+
@channel = http_server.channel
|
13
|
+
|
14
|
+
# @type [Array<WebsocketClient>]
|
15
|
+
@clients = {}
|
16
|
+
end
|
17
|
+
|
18
|
+
def on_accept(channel)
|
19
|
+
ws_client = Websocket.new(@delegate, channel)
|
20
|
+
ws_client.client_side = false
|
21
|
+
|
22
|
+
channel.delegate = self
|
23
|
+
|
24
|
+
if @delegate
|
25
|
+
@delegate.on_connect(ws_client)
|
26
|
+
return
|
27
|
+
end
|
28
|
+
|
29
|
+
@clients[channel] = ws_client
|
30
|
+
Log.debug("Rakie::WebsocketServer accept client: #{ws_client}")
|
31
|
+
end
|
32
|
+
|
33
|
+
# @param [HttpRequest] request
|
34
|
+
# @param [HttpResponse] response
|
35
|
+
# @return bool
|
36
|
+
def upgrade(request, response)
|
37
|
+
if websocket_key = request.headers["sec-websocket-key"]
|
38
|
+
digest_key = Digest::SHA1.base64digest(websocket_key + '258EAFA5-E914-47DA-95CA-C5AB0DC85B11')
|
39
|
+
|
40
|
+
response.head.status = 101
|
41
|
+
response.head.message = 'Switching Protocols'
|
42
|
+
response.headers["connection"] = "upgrade"
|
43
|
+
response.headers["upgrade"] = "websocket"
|
44
|
+
response.headers["sec-websocket-accept"] = digest_key
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def on_recv(channel, data)
|
49
|
+
client = @clients[channel]
|
50
|
+
|
51
|
+
if client
|
52
|
+
client.on_recv(channel, data)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def on_send(channel)
|
57
|
+
client = @clients[channel]
|
58
|
+
|
59
|
+
if client
|
60
|
+
client.on_send(channel)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def on_close(channel)
|
65
|
+
client = @clients[channel]
|
66
|
+
|
67
|
+
if client
|
68
|
+
client.on_close(channel)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def send(message, is_binary=false); end
|
73
|
+
|
74
|
+
def close; end
|
75
|
+
|
76
|
+
def clients
|
77
|
+
@clients.values
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
require "digest"
|
83
|
+
require "base64"
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rakie
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jakit Liang
|
@@ -20,9 +20,18 @@ files:
|
|
20
20
|
- lib/rakie.rb
|
21
21
|
- lib/rakie/channel.rb
|
22
22
|
- lib/rakie/event.rb
|
23
|
+
- lib/rakie/http.rb
|
24
|
+
- lib/rakie/http_proto.rb
|
25
|
+
- lib/rakie/http_server.rb
|
26
|
+
- lib/rakie/log.rb
|
27
|
+
- lib/rakie/proto.rb
|
23
28
|
- lib/rakie/simple_server.rb
|
24
29
|
- lib/rakie/tcp_channel.rb
|
25
30
|
- lib/rakie/tcp_server_channel.rb
|
31
|
+
- lib/rakie/version.rb
|
32
|
+
- lib/rakie/websocket.rb
|
33
|
+
- lib/rakie/websocket_proto.rb
|
34
|
+
- lib/rakie/websocket_server.rb
|
26
35
|
- test/test_rakie.rb
|
27
36
|
homepage: https://github.com/Jakitto/rakie
|
28
37
|
licenses:
|