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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8361c3cfc878d6edc052170cc5fbc5f555d173562c7325d9a939c567402e8b4f
4
- data.tar.gz: cc2f7fa419c2fcb750ec70305b942336974cd7e35ad336edc38436027f74e6dd
3
+ metadata.gz: 754086391c9ec9e2eb623d8d6a1ead9ca9188d49fb0c391b26c4a9e36db88236
4
+ data.tar.gz: 6ac34e0868c8bc1748e7f572266b4223a91f676d9d7c11b22fa5a7e10586713c
5
5
  SHA512:
6
- metadata.gz: 1b39af452c4d98899dd787a38ce1394b1781eaee28ab3761b46b824ec4b426f76c27d451da8ae1fb01545676dd572a19f9c9dae09839221a17a5ee807a151f72
7
- data.tar.gz: c7953919abc5572b8fba09a5afdf5e32034c0bc2e4fdbca4f649f67f9ec635a10cf49888a194ce67f17afda0424becd1e42467cd9b73e2a990670fd29e2c2c50
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(4096)
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 on_close(io)
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.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,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
@@ -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, 5]
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,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.3
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: