rakie 0.0.2 → 0.0.8
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/rakie.rb +15 -1
- data/lib/rakie/channel.rb +57 -12
- data/lib/rakie/event.rb +70 -35
- data/lib/rakie/http.rb +5 -0
- data/lib/rakie/http_proto.rb +197 -0
- data/lib/rakie/http_server.rb +134 -0
- data/lib/rakie/log.rb +96 -0
- data/lib/rakie/proto.rb +63 -0
- data/lib/rakie/simple_server.rb +7 -11
- data/lib/rakie/tcp_channel.rb +19 -3
- data/lib/rakie/tcp_server_channel.rb +32 -16
- data/lib/rakie/version.rb +11 -0
- data/lib/rakie/websocket.rb +138 -0
- data/lib/rakie/websocket_proto.rb +286 -0
- data/lib/rakie/websocket_server.rb +89 -0
- data/test/test_rakie.rb +8 -3
- metadata +13 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 81b73eacd5a896ae3c13192543965d353a0b2dcd1e1ae021664e91793d130d77
|
4
|
+
data.tar.gz: bf30ef52373d98df118064c99aaaf7465e6ac8f684d0d49bea01f7d9c6f1a5ff
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8a2152503ca81745b8ed0cc467826cd8efd4d8006b8760ed102500f80c3bc88bf946196d03bc3242f87e7db512152147781384be05d891d51fa4aca4928428d2
|
7
|
+
data.tar.gz: 29934b5f3ecf636d047c04b00a83e45ddf54a581476f65b23cfe106f231c9cf399536d42f5b2b194ed96c747c41639999be7009e4521481ae989727b4902e5eb
|
data/lib/rakie.rb
CHANGED
@@ -1,10 +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"
|
18
|
+
require "rakie/http_server"
|
19
|
+
require "rakie/websocket"
|
20
|
+
require "rakie/websocket_server"
|
9
21
|
require "rakie/tcp_channel"
|
10
22
|
require "rakie/tcp_server_channel"
|
23
|
+
require "rakie/log"
|
24
|
+
require "rakie/version"
|
data/lib/rakie/channel.rb
CHANGED
@@ -2,10 +2,13 @@ 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
|
8
10
|
@write_buffer = String.new
|
11
|
+
@write_task = []
|
9
12
|
@delegate = delegate
|
10
13
|
Event.push(io, self, Event::READ_EVENT)
|
11
14
|
end
|
@@ -13,25 +16,28 @@ module Rakie
|
|
13
16
|
def on_read(io)
|
14
17
|
begin
|
15
18
|
loop do
|
16
|
-
@read_buffer << io.read_nonblock(
|
19
|
+
@read_buffer << io.read_nonblock(DEFAULT_BUFFER_SIZE)
|
17
20
|
end
|
18
21
|
|
19
22
|
rescue IO::EAGAINWaitReadable
|
20
|
-
|
23
|
+
Log.debug("Channel read pending")
|
21
24
|
|
22
|
-
rescue
|
25
|
+
rescue IO::EWOULDBLOCKWaitReadable
|
26
|
+
Log.debug("Channel read pending")
|
27
|
+
|
28
|
+
rescue Exception => e
|
23
29
|
# Process the last message on exception
|
24
30
|
if @delegate != nil
|
25
31
|
@delegate.on_recv(self, @read_buffer)
|
26
32
|
@read_buffer = String.new # Reset buffer
|
27
33
|
end
|
28
34
|
|
29
|
-
|
35
|
+
Log.debug("Channel error #{io}: #{e}")
|
30
36
|
return Event::HANDLE_FAILED
|
31
37
|
end
|
32
38
|
|
33
|
-
puts("Channel has delegate?: #{@delegate}")
|
34
39
|
if @delegate != nil
|
40
|
+
Log.debug("Channel handle on_recv")
|
35
41
|
len = @delegate.on_recv(self, @read_buffer)
|
36
42
|
|
37
43
|
if len > @read_buffer.length
|
@@ -39,40 +45,77 @@ module Rakie
|
|
39
45
|
end
|
40
46
|
|
41
47
|
@read_buffer = @read_buffer[len .. -1]
|
42
|
-
puts("Channel handle on_recv")
|
43
48
|
end
|
44
49
|
|
45
50
|
return Event::HANDLE_CONTINUED
|
46
51
|
end
|
47
52
|
|
53
|
+
def handle_write(len)
|
54
|
+
task = @write_task[0]
|
55
|
+
|
56
|
+
while len > 0
|
57
|
+
if len < task
|
58
|
+
@write_task[0] = task - len
|
59
|
+
end
|
60
|
+
|
61
|
+
len -= task
|
62
|
+
@write_task.shift
|
63
|
+
|
64
|
+
if @delegate != nil
|
65
|
+
@delegate.on_send(self)
|
66
|
+
Log.debug("Channel handle on_send")
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
48
71
|
def on_write(io)
|
72
|
+
len = 0
|
73
|
+
offset = 0
|
74
|
+
|
49
75
|
begin
|
50
76
|
while @write_buffer.length > 0
|
51
77
|
len = io.write_nonblock(@write_buffer)
|
78
|
+
offset += len
|
52
79
|
@write_buffer = @write_buffer[len .. -1]
|
53
80
|
end
|
54
81
|
|
55
|
-
|
82
|
+
Log.debug("Channel write finished")
|
56
83
|
|
57
84
|
rescue IO::EAGAINWaitWritable
|
58
|
-
|
85
|
+
self.handle_write(offset)
|
86
|
+
|
87
|
+
Log.debug("Channel write pending")
|
88
|
+
return Event::HANDLE_CONTINUED
|
89
|
+
|
90
|
+
rescue IO::EWOULDBLOCKWaitWritable
|
91
|
+
self.handle_write(offset)
|
92
|
+
|
93
|
+
Log.debug("Channel write pending")
|
59
94
|
return Event::HANDLE_CONTINUED
|
60
95
|
|
61
96
|
rescue
|
62
|
-
|
97
|
+
Log.debug("Channel close #{io}")
|
63
98
|
return Event::HANDLE_FAILED
|
64
99
|
end
|
65
100
|
|
101
|
+
self.handle_write(offset)
|
102
|
+
|
66
103
|
return Event::HANDLE_FINISHED
|
67
104
|
end
|
68
105
|
|
69
|
-
def
|
106
|
+
def on_detach(io)
|
70
107
|
begin
|
71
108
|
io.close
|
72
109
|
|
110
|
+
if @delegate
|
111
|
+
@delegate.on_close(self)
|
112
|
+
end
|
113
|
+
|
73
114
|
rescue
|
74
|
-
|
115
|
+
Log.debug("Channel is already closed")
|
75
116
|
end
|
117
|
+
|
118
|
+
Log.debug("Channel close ok")
|
76
119
|
end
|
77
120
|
|
78
121
|
def read(size)
|
@@ -96,7 +139,9 @@ module Rakie
|
|
96
139
|
end
|
97
140
|
|
98
141
|
@write_buffer << data
|
99
|
-
|
142
|
+
@write_task << data.length
|
143
|
+
|
144
|
+
Event.modify(@io, self, (Event::READ_EVENT | Event::WRITE_EVENT))
|
100
145
|
|
101
146
|
return 0
|
102
147
|
end
|
data/lib/rakie/event.rb
CHANGED
@@ -9,6 +9,10 @@ module Rakie
|
|
9
9
|
HANDLE_CONTINUED = 0
|
10
10
|
HANDLE_FINISHED = 1
|
11
11
|
|
12
|
+
OPERATION_ADD = 0
|
13
|
+
OPERATION_MODIFY = 1
|
14
|
+
OPERATION_DELETE = 2
|
15
|
+
|
12
16
|
def initialize
|
13
17
|
@wait_ios = []
|
14
18
|
@lock = Mutex.new
|
@@ -24,36 +28,39 @@ module Rakie
|
|
24
28
|
|
25
29
|
def process_signal(io)
|
26
30
|
signal = io.read(1)
|
27
|
-
puts("Event handling #{signal}")
|
28
31
|
|
29
|
-
if signal == '
|
30
|
-
|
32
|
+
if signal == 'q'
|
33
|
+
return 1
|
34
|
+
end
|
35
|
+
|
36
|
+
operation, new_io, new_handler, new_event = @wait_ios.shift
|
37
|
+
if new_io.closed?
|
38
|
+
return 0
|
39
|
+
end
|
40
|
+
|
41
|
+
Log.debug("Event handling #{signal} with #{new_io.fileno} to #{new_event}")
|
42
|
+
|
43
|
+
if operation == OPERATION_ADD
|
31
44
|
@ios[new_io] = new_event
|
32
45
|
@handlers[new_io] = new_handler
|
33
|
-
|
46
|
+
Log.debug("Event add all #{new_io.fileno} to #{new_event}")
|
34
47
|
|
35
|
-
elsif
|
36
|
-
new_io, = @wait_ios.shift
|
48
|
+
elsif operation == OPERATION_DELETE
|
37
49
|
handler = @handlers[new_io]
|
38
50
|
|
39
51
|
if handler != nil
|
40
|
-
|
41
|
-
|
52
|
+
Log.debug("Event close #{new_io}")
|
53
|
+
handler.on_detach(new_io)
|
42
54
|
end
|
43
55
|
|
44
56
|
@ios.delete(new_io)
|
45
57
|
@handlers.delete(new_io)
|
58
|
+
Log.debug("Event remove all #{new_io}")
|
46
59
|
|
47
|
-
|
48
|
-
|
49
|
-
elsif signal == 'm'
|
50
|
-
new_io, new_handler, new_event = @wait_ios.shift
|
60
|
+
elsif operation == OPERATION_MODIFY
|
51
61
|
@ios[new_io] = new_event
|
52
62
|
@handlers[new_io] = new_handler
|
53
|
-
|
54
|
-
|
55
|
-
elsif signal == 'q'
|
56
|
-
return 1
|
63
|
+
Log.debug("Event modify all #{new_io.fileno} to #{new_event}")
|
57
64
|
end
|
58
65
|
|
59
66
|
return 0
|
@@ -61,9 +68,18 @@ module Rakie
|
|
61
68
|
|
62
69
|
def run_loop
|
63
70
|
loop do
|
71
|
+
# p @ios
|
72
|
+
# begin
|
73
|
+
# read_ios = @ios.select {|k, v| v & READ_EVENT > 0}
|
74
|
+
# write_ios = @ios.select {|k, v| v & WRITE_EVENT > 0}
|
75
|
+
# rescue Exception => e
|
76
|
+
# p @ios
|
77
|
+
# end
|
78
|
+
|
64
79
|
read_ios = @ios.select {|k, v| v & READ_EVENT > 0}
|
65
80
|
write_ios = @ios.select {|k, v| v & WRITE_EVENT > 0}
|
66
81
|
|
82
|
+
# Log.debug("Event selecting ...")
|
67
83
|
read_ready, write_ready = IO.select(read_ios.keys, write_ios.keys, [], 5)
|
68
84
|
|
69
85
|
if read_ready != nil
|
@@ -89,16 +105,16 @@ module Rakie
|
|
89
105
|
result = handler.on_read(io)
|
90
106
|
|
91
107
|
if result == HANDLE_FINISHED
|
92
|
-
@ios[io]
|
93
|
-
|
108
|
+
@ios[io] &= ~READ_EVENT
|
109
|
+
Log.debug("Event remove read #{io}")
|
94
110
|
|
95
111
|
elsif result == HANDLE_FAILED
|
96
|
-
handler.
|
97
|
-
|
112
|
+
handler.on_detach(io)
|
113
|
+
Log.debug("Event close #{io}")
|
98
114
|
|
99
115
|
@ios.delete(io)
|
100
116
|
@handlers.delete(io)
|
101
|
-
|
117
|
+
Log.debug("Event remove all #{io}")
|
102
118
|
end
|
103
119
|
end
|
104
120
|
end
|
@@ -114,16 +130,16 @@ module Rakie
|
|
114
130
|
result = handler.on_write(io)
|
115
131
|
|
116
132
|
if result == HANDLE_FINISHED
|
117
|
-
@ios[io]
|
118
|
-
|
133
|
+
@ios[io] &= ~WRITE_EVENT
|
134
|
+
Log.debug("Event remove write #{io}")
|
119
135
|
|
120
136
|
elsif result == HANDLE_FAILED
|
121
|
-
handler.
|
122
|
-
|
137
|
+
handler.on_detach(io)
|
138
|
+
Log.debug("Event close #{io}")
|
123
139
|
|
124
140
|
@ios.delete(io)
|
125
141
|
@handlers.delete(io)
|
126
|
-
|
142
|
+
Log.debug("Event remove all #{io}")
|
127
143
|
end
|
128
144
|
end
|
129
145
|
end
|
@@ -132,39 +148,58 @@ module Rakie
|
|
132
148
|
|
133
149
|
def push(io, handler, event)
|
134
150
|
@lock.lock
|
135
|
-
@wait_ios.push([io, handler, event])
|
151
|
+
@wait_ios.push([OPERATION_ADD, io, handler, event])
|
136
152
|
@signal_out.write('a')
|
137
153
|
@lock.unlock
|
138
154
|
end
|
139
155
|
|
140
156
|
def delete(io)
|
141
157
|
@lock.lock
|
142
|
-
@wait_ios.push([io, nil, nil])
|
143
|
-
@signal_out.write('
|
158
|
+
@wait_ios.push([OPERATION_DELETE, io, nil, nil])
|
159
|
+
@signal_out.write('a')
|
144
160
|
@lock.unlock
|
145
161
|
end
|
146
162
|
|
147
163
|
def modify(io, handler, event)
|
148
164
|
@lock.lock
|
149
|
-
@wait_ios.push([io, handler, event])
|
150
|
-
@signal_out.write('
|
165
|
+
@wait_ios.push([OPERATION_MODIFY, io, handler, event])
|
166
|
+
@signal_out.write('a')
|
151
167
|
@lock.unlock
|
152
168
|
end
|
153
169
|
|
154
170
|
def self.instance
|
155
|
-
@instance ||= Event.new
|
171
|
+
@instance ||= Array.new(self.concurrent) { |i| Event.new }
|
156
172
|
end
|
157
173
|
|
158
174
|
def self.push(io, listener, type)
|
159
|
-
|
175
|
+
i = io.fileno % self.concurrent
|
176
|
+
inst = self.instance[i]
|
177
|
+
inst.push(io, listener, type)
|
178
|
+
# instance.push(io, listener, type)
|
160
179
|
end
|
161
180
|
|
162
181
|
def self.delete(io)
|
163
|
-
self.
|
182
|
+
i = io.fileno % self.concurrent
|
183
|
+
inst = self.instance[i]
|
184
|
+
inst.delete(io)
|
185
|
+
# self.instance.delete(io)
|
164
186
|
end
|
165
187
|
|
166
188
|
def self.modify(io, listener, type)
|
167
|
-
|
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
|
168
203
|
end
|
169
204
|
end
|
170
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
|