rakie 0.0.3 → 0.0.9

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8361c3cfc878d6edc052170cc5fbc5f555d173562c7325d9a939c567402e8b4f
4
- data.tar.gz: cc2f7fa419c2fcb750ec70305b942336974cd7e35ad336edc38436027f74e6dd
3
+ metadata.gz: 1e8894b95a11cbe14455b61e0df6fbbd6e9e8ded9e6a723ca2a36e9d6d05256d
4
+ data.tar.gz: 52d125bf806aacbde166336054a14ddbbc7f2bf5dab097a5050c5339e94e3a86
5
5
  SHA512:
6
- metadata.gz: 1b39af452c4d98899dd787a38ce1394b1781eaee28ab3761b46b824ec4b426f76c27d451da8ae1fb01545676dd572a19f9c9dae09839221a17a5ee807a151f72
7
- data.tar.gz: c7953919abc5572b8fba09a5afdf5e32034c0bc2e4fdbca4f649f67f9ec635a10cf49888a194ce67f17afda0424becd1e42467cd9b73e2a990670fd29e2c2c50
6
+ metadata.gz: 047cf559ae5bd1a4bb90f1ad04c83f9635d2d0368606612917a8a78d1799138167666d768ebd62c84998153a478281c3adc24bcc3071af8146190649e31c3141
7
+ data.tar.gz: c2c07ca6562b9631521e4482653a7af948f8f39c20a001f2220dea1a0dfca808473b0b9ffe53b76976aa1962e81a1a1ce2bd1216377ee737acb367a27109c7c7
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,11 +16,14 @@ module Rakie
14
16
  def on_read(io)
15
17
  begin
16
18
  loop do
17
- @read_buffer << io.read_nonblock(4096)
19
+ @read_buffer << io.read_nonblock(DEFAULT_BUFFER_SIZE)
18
20
  end
19
21
 
20
22
  rescue IO::EAGAINWaitReadable
21
- Log.debug("Channel read finished")
23
+ Log.debug("Channel read pending")
24
+
25
+ rescue IO::EWOULDBLOCKWaitReadable
26
+ Log.debug("Channel read pending")
22
27
 
23
28
  rescue Exception => e
24
29
  # Process the last message on exception
@@ -32,6 +37,7 @@ module Rakie
32
37
  end
33
38
 
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,7 +45,6 @@ module Rakie
39
45
  end
40
46
 
41
47
  @read_buffer = @read_buffer[len .. -1]
42
- Log.debug("Channel handle on_recv")
43
48
  end
44
49
 
45
50
  return Event::HANDLE_CONTINUED
@@ -65,19 +70,27 @@ module Rakie
65
70
 
66
71
  def on_write(io)
67
72
  len = 0
73
+ offset = 0
68
74
 
69
75
  begin
70
76
  while @write_buffer.length > 0
71
77
  len = io.write_nonblock(@write_buffer)
78
+ offset += len
72
79
  @write_buffer = @write_buffer[len .. -1]
73
80
  end
74
81
 
75
82
  Log.debug("Channel write finished")
76
83
 
77
84
  rescue IO::EAGAINWaitWritable
78
- self.handle_write(len)
85
+ self.handle_write(offset)
79
86
 
80
- Log.debug("Channel write continue")
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")
81
94
  return Event::HANDLE_CONTINUED
82
95
 
83
96
  rescue
@@ -85,17 +98,26 @@ module Rakie
85
98
  return Event::HANDLE_FAILED
86
99
  end
87
100
 
88
- self.handle_write(len)
101
+ self.handle_write(offset)
89
102
 
90
103
  return Event::HANDLE_FINISHED
91
104
  end
92
105
 
93
- def on_close(io)
106
+ def on_detach(io)
107
+ if io.closed?
108
+ return
109
+ end
110
+
94
111
  begin
95
112
  io.close
96
113
 
97
114
  rescue
98
115
  Log.debug("Channel is already closed")
116
+ return
117
+ end
118
+
119
+ if @delegate
120
+ @delegate.on_close(self)
99
121
  end
100
122
 
101
123
  Log.debug("Channel close ok")
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.on_close(new_io)
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] = @ios[io] & ~READ_EVENT
108
+ @ios[io] &= ~READ_EVENT
109
109
  Log.debug("Event remove read #{io}")
110
110
 
111
111
  elsif result == HANDLE_FAILED
112
- handler.on_close(io)
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] = @ios[io] & ~WRITE_EVENT
133
+ @ios[io] &= ~WRITE_EVENT
134
134
  Log.debug("Event remove write #{io}")
135
135
 
136
136
  elsif result == HANDLE_FAILED
137
- handler.on_close(io)
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
- self.instance.push(io, listener, type)
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.instance.delete(io)
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
- self.instance.modify(io, listener, type)
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
data/lib/rakie/http.rb ADDED
@@ -0,0 +1,5 @@
1
+ module Rakie
2
+ class Http
3
+
4
+ end
5
+ 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,134 @@
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, :host, :port
17
+
18
+ def initialize(host: '127.0.0.1', port: 10086, delegate: nil)
19
+ @host = host
20
+ @port = port
21
+
22
+ @channel = TCPServerChannel.new(host, port, self)
23
+
24
+ # @type [Hash{Channel=>Session}]
25
+ @sessions = {}
26
+ @delegate = delegate
27
+ @opt = {}
28
+ end
29
+
30
+ def on_accept(channel)
31
+ channel.delegate = self
32
+ @sessions[channel] = Session.new
33
+ Log.debug("Rakie::HTTPServer accept client: #{channel}")
34
+ end
35
+
36
+ def on_recv(channel, data)
37
+ # Log.debug("Rakie::HTTPServer recv: #{data}")
38
+ session = @sessions[channel]
39
+
40
+ if session == nil
41
+ return 0
42
+ end
43
+
44
+ # @type [HttpRequest] request
45
+ request = session.request
46
+
47
+ if request.parse_status == ParseStatus::COMPLETE
48
+ request = HttpRequest.new
49
+ session.request = request
50
+ end
51
+
52
+ len = request.parse(data)
53
+
54
+ Log.debug("Rakie::HttpServer receive request: #{request.to_s}")
55
+
56
+ if request.parse_status == ParseStatus::COMPLETE
57
+ response = HttpResponse.new
58
+
59
+ if upgrade = request.headers["upgrade"]
60
+ if websocket_delegate = @opt[:websocket_delegate]
61
+ websocket_delegate.upgrade(request, response)
62
+ Log.debug("Rakie::HttpServer upgrade protocol")
63
+ end
64
+
65
+ elsif @delegate != nil
66
+ @delegate.handle(request, response)
67
+
68
+ else
69
+ response.headers["content-type"] = HttpMIME::HTML
70
+ response.content = "<html><body><h1>Rakie!</h1></body></html>"
71
+ end
72
+
73
+ if header_connection = request.headers["connection"]
74
+ response.headers["connection"] = header_connection
75
+ end
76
+
77
+ if response.content.length > 0
78
+ response.headers["content-length"] = response.content.length
79
+ end
80
+
81
+ response.headers["server"] = Rakie.full_version_s
82
+ session.responses << response
83
+ response_data = response.to_s
84
+
85
+ Log.debug("Rakie::HttpServer response: #{response_data}")
86
+
87
+ channel.write(response_data) # Response data
88
+
89
+ elsif request.parse_status == ParseStatus::ERROR
90
+ channel.close
91
+ @sessions.delete(channel)
92
+
93
+ Log.debug("Rakie::HttpServer: Illegal request")
94
+ return len
95
+ end
96
+
97
+ return len
98
+ end
99
+
100
+ def on_send(channel)
101
+ session = @sessions[channel]
102
+ # @type [HttpRequest]
103
+ last_response = session.responses.shift
104
+
105
+ if last_response
106
+ if connect_status = last_response.headers["connection"]
107
+ if connect_status.downcase == "close"
108
+ Log.debug("Rakie::HttpServer: send finish and close channel")
109
+ channel.close
110
+ @sessions.delete(channel)
111
+ end
112
+ end
113
+
114
+ if upgrade = last_response.headers["upgrade"]
115
+ websocket_delegate = @opt[:websocket_delegate]
116
+
117
+ if websocket_delegate
118
+ websocket_delegate.on_accept(channel)
119
+ @sessions.delete(channel)
120
+ return
121
+ end
122
+
123
+ Log.debug("Rakie::HttpServer: no websocket delegate and close channel")
124
+ channel.close
125
+ @sessions.delete(channel)
126
+ end
127
+ end
128
+ end
129
+
130
+ def on_close(channel)
131
+ @sessions.delete(channel)
132
+ end
133
+ end
134
+ 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
@@ -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
@@ -1,8 +1,24 @@
1
1
  module Rakie
2
2
  class TCPChannel < Channel
3
- def initialize(ip, port, delegate=nil)
4
- @io = TCPSocket.new(ip, port)
5
- super(@io, delegate)
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 < Channel
3
- def initialize(ip, port=nil, delegate=nil)
4
- io = nil
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 = ip
8
- io = TCPServer.new(ip)
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(io, delegate)
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
- client_io, = io.accept_nonblock
22
- channel = Channel.new(client_io)
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,11 @@
1
+ module Rakie
2
+ VERSION = [0, 0, 9]
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,144 @@
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 = "Rakie::Websocket: op pong"
46
+
47
+ elsif message.op_code == WebsocketMessage::OP_PONG
48
+ response.fin = true
49
+ response.op_code = WebsocketMessage::OP_PING
50
+ response.payload = "Rakie::Websocket: op ping"
51
+
52
+ elsif message.op_code == WebsocketMessage::OP_CLOSE
53
+ channel.close
54
+
55
+ Log.debug("Rakie::Websocket: op close")
56
+ return 0
57
+
58
+ elsif @delegate
59
+ @delegate.on_message(self, message.payload)
60
+ return len
61
+
62
+ else
63
+ response.fin = true
64
+ response.op_code = WebsocketMessage::OP_TEXT
65
+ response.payload = "Rakie!"
66
+ end
67
+
68
+ response_data = response.to_s
69
+
70
+ Log.debug("Rakie::Websocket response: #{response_data}")
71
+
72
+ channel.write(response_data) # Response data
73
+
74
+ elsif message.parse_status == ParseStatus::ERROR
75
+ channel.close
76
+
77
+ Log.debug("Rakie::Websocket: Illegal message")
78
+ return 0
79
+ end
80
+
81
+ return len
82
+ end
83
+
84
+ def on_send(channel)
85
+ # @type [WebsocketMessage]
86
+ last_message = @send_messages.shift
87
+
88
+ if last_message
89
+ if last_message.op_code == WebsocketMessage::OP_CLOSE
90
+ Log.debug("Rakie::Websocket: send finish and close channel")
91
+ channel.close
92
+ end
93
+ end
94
+ end
95
+
96
+ def on_close(channel)
97
+ if @delegate
98
+ @delegate.on_disconnect(self)
99
+ end
100
+ end
101
+
102
+ def send(message, is_binary=false)
103
+ ws_message = WebsocketMessage.new
104
+ ws_message.fin = true
105
+ ws_message.op_code = WebsocketMessage::OP_TEXT
106
+ ws_message.payload = message
107
+
108
+ if is_binary
109
+ ws_message.op_code = WebsocketMessage::OP_BIN
110
+ end
111
+
112
+ if @client_side
113
+ ws_message.mask = true
114
+ ws_message.refresh_masking
115
+ end
116
+
117
+ send_message = ws_message.to_s
118
+ @send_messages << ws_message
119
+
120
+ Log.debug("Rakie::Websocket send: #{send_message}")
121
+
122
+ @channel.write(send_message) # Response data
123
+ end
124
+
125
+ def close
126
+ ws_message = WebsocketMessage.new
127
+ ws_message.fin = true
128
+ ws_message.op_code = WebsocketMessage::OP_CLOSE
129
+
130
+ if @client_side
131
+ ws_message.mask = true
132
+ ws_message.refresh_masking
133
+ end
134
+
135
+ ws_message.payload = "close"
136
+
137
+ send_message = ws_message.to_s
138
+
139
+ Log.debug("Rakie::Websocket send close: #{send_message}")
140
+
141
+ @channel.write(send_message) # Response data
142
+ end
143
+ end
144
+ 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"
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.3
4
+ version: 0.0.9
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:
@@ -34,9 +43,9 @@ require_paths:
34
43
  - lib
35
44
  required_ruby_version: !ruby/object:Gem::Requirement
36
45
  requirements:
37
- - - ">="
46
+ - - ">"
38
47
  - !ruby/object:Gem::Version
39
- version: '0'
48
+ version: '2.5'
40
49
  required_rubygems_version: !ruby/object:Gem::Requirement
41
50
  requirements:
42
51
  - - ">="