em-midori 0.0.5 → 0.0.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +2 -0
- data/.resources/slogan.png +0 -0
- data/.travis.yml +4 -4
- data/lib/em-midori.rb +6 -2
- data/lib/em-midori/api.rb +33 -28
- data/lib/em-midori/clean_room.rb +3 -2
- data/lib/em-midori/const.rb +48 -0
- data/lib/em-midori/debug.rb +29 -0
- data/lib/em-midori/define_class.rb +7 -0
- data/lib/em-midori/em_midori.rb +5 -6
- data/lib/em-midori/error.rb +7 -0
- data/lib/em-midori/request.rb +61 -0
- data/lib/em-midori/response.rb +10 -47
- data/lib/em-midori/route.rb +8 -0
- data/lib/em-midori/server.rb +71 -8
- data/lib/em-midori/version.rb +1 -1
- data/lib/em-midori/websocket.rb +63 -0
- metadata +23 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 49747119b2524c9b0f81ca22a1c9f27010b880de
|
4
|
+
data.tar.gz: d32a3a67fd807cdeb7c49d5b7a90aacd24f24f43
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 51732ede10120a05f89e960826aadffb5342efec233eaaff238a9b82c887c905e09cae0357b8c6802f05db0f77e011c709cd0fa2b3bcce366e8aee92297a524e
|
7
|
+
data.tar.gz: c7e2497cdeed26883d3a5dff6d1195a77c27926ad46e01fd3a0bb556abcdd8bb0f5fd14a7191cbb7c7f881450c23894ff7f5e389db6551a7751fecbfc0976ce3
|
data/.gitignore
CHANGED
Binary file
|
data/.travis.yml
CHANGED
@@ -9,16 +9,16 @@ rvm:
|
|
9
9
|
|
10
10
|
jdk:
|
11
11
|
- openjdk7
|
12
|
-
-
|
12
|
+
- oraclejdk8
|
13
13
|
|
14
14
|
matrix:
|
15
15
|
exclude:
|
16
16
|
- rvm: 2.2.5
|
17
|
-
jdk:
|
17
|
+
jdk: openjdk7
|
18
18
|
- rvm: 2.3.1
|
19
|
-
jdk:
|
19
|
+
jdk: openjdk7
|
20
20
|
- rvm: rbx-3.20
|
21
|
-
jdk:
|
21
|
+
jdk: openjdk7
|
22
22
|
allow_failures:
|
23
23
|
- rvm: jruby-head
|
24
24
|
- rvm: rbx-3.20
|
data/lib/em-midori.rb
CHANGED
@@ -1,10 +1,14 @@
|
|
1
|
+
require 'digest/sha1'
|
1
2
|
require 'em-midori/version'
|
3
|
+
require 'em-midori/debug'
|
4
|
+
require 'em-midori/const'
|
2
5
|
require 'em-midori/define_class'
|
6
|
+
require 'em-midori/error'
|
3
7
|
require 'em-midori/clean_room'
|
4
8
|
require 'em-midori/em_midori'
|
5
9
|
require 'em-midori/request'
|
6
10
|
require 'em-midori/response'
|
7
11
|
require 'em-midori/api'
|
12
|
+
require 'em-midori/route'
|
8
13
|
require 'em-midori/server'
|
9
|
-
|
10
|
-
# Midori.run(Midori::API, '0.0.0.0', 8080)
|
14
|
+
require 'em-midori/websocket'
|
data/lib/em-midori/api.rb
CHANGED
@@ -155,6 +155,13 @@ class Midori::API
|
|
155
155
|
# end
|
156
156
|
def eventsource(path, &block) end
|
157
157
|
|
158
|
+
# Implementation of route DSL
|
159
|
+
# === Attributes
|
160
|
+
# * +method+ [+String+] - HTTP method
|
161
|
+
# * +path+ [+String+, +Regexp+] - path definition
|
162
|
+
# * +block+ [+Proc+] - process to run when route matched
|
163
|
+
# === Returns
|
164
|
+
# nil
|
158
165
|
def add_route(method, path, block)
|
159
166
|
@route = Array.new if @route.nil?
|
160
167
|
if path.class == String
|
@@ -165,24 +172,33 @@ class Midori::API
|
|
165
172
|
nil
|
166
173
|
end
|
167
174
|
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
175
|
+
# Process after receive data from client
|
176
|
+
# === Attributes
|
177
|
+
# * +request+ [+StringIO+] - Http Raw Request
|
178
|
+
# === Returns
|
179
|
+
# [+Midori::Response+] - Http response
|
180
|
+
def receive(request, connection=nil)
|
181
|
+
@route.each do |route|
|
182
|
+
matched = match(route.method, route.path, request.method, request.path)
|
183
|
+
if matched
|
184
|
+
clean_room = CleanRoom.new(request)
|
185
|
+
if request.websocket?
|
186
|
+
# Send 101 Switching Protocol
|
187
|
+
connection.send_data Midori::Response.new(101, {
|
188
|
+
'Upgrade' => 'websocket',
|
189
|
+
'Connection' => 'Upgrade',
|
190
|
+
'Sec-WebSocket-Accept' => Digest::SHA1.base64digest(request.header['Sec-WebSocket-Key'] +'258EAFA5-E914-47DA-95CA-C5AB0DC85B11')
|
191
|
+
}, '')
|
192
|
+
result = lambda {clean_room.instance_exec(connection.websocket, *matched, &route.function)}.call
|
193
|
+
return Midori::Response.new
|
194
|
+
else
|
195
|
+
result = lambda {clean_room.instance_exec(*matched, &route.function)}.call
|
196
|
+
end
|
176
197
|
clean_room.body = result if result.class == String
|
177
198
|
return clean_room.response
|
178
|
-
rescue => e
|
179
|
-
puts e
|
180
|
-
return Midori::Response.new(500, {}, 'Internal Server Error')
|
181
199
|
end
|
182
200
|
end
|
183
|
-
|
184
|
-
# 404
|
185
|
-
Midori::Response.new(404, {}, '404 Not Found')
|
201
|
+
raise Midori::Error::NotFound
|
186
202
|
end
|
187
203
|
|
188
204
|
# Match route with given definition
|
@@ -196,10 +212,9 @@ class Midori::API
|
|
196
212
|
# else returns an array of parameter string matched
|
197
213
|
# === Examples
|
198
214
|
# match('GET', /^\/user\/(.*?)\/order\/(.*?)$/, '/user/foo/order/bar') # => ['foo', 'bar']
|
199
|
-
def match(method, path,
|
200
|
-
|
201
|
-
|
202
|
-
result = request[1].match(path)
|
215
|
+
def match(method, path, request_method, request_path)
|
216
|
+
if request_method == method
|
217
|
+
result = request_path.match(path)
|
203
218
|
return result.to_a[1..-1] if result
|
204
219
|
false
|
205
220
|
else
|
@@ -233,14 +248,4 @@ class Midori::API
|
|
233
248
|
add_route(method.upcase, args[0], block) #args[0]: path
|
234
249
|
end
|
235
250
|
end
|
236
|
-
|
237
251
|
end
|
238
|
-
|
239
|
-
class Midori::Route
|
240
|
-
attr_accessor :method, :path, :function
|
241
|
-
def initialize(method, path, function)
|
242
|
-
@method = method
|
243
|
-
@path = path
|
244
|
-
@function = function
|
245
|
-
end
|
246
|
-
end
|
data/lib/em-midori/clean_room.rb
CHANGED
@@ -0,0 +1,48 @@
|
|
1
|
+
module Midori::Const
|
2
|
+
STATUS_CODE = {
|
3
|
+
100 => '100 Continue',
|
4
|
+
101 => '101 Switching Protocols',
|
5
|
+
200 => '200 OK',
|
6
|
+
201 => '201 Created',
|
7
|
+
202 => '202 Accepted',
|
8
|
+
203 => '203 Non-Authoritative Information',
|
9
|
+
204 => '204 No Content',
|
10
|
+
205 => '205 Reset Content',
|
11
|
+
206 => '206 Partial Content',
|
12
|
+
300 => '300 Multiple Choices',
|
13
|
+
301 => '301 Moved Permanently',
|
14
|
+
304 => '304 Not Modified',
|
15
|
+
305 => '305 Use Proxy',
|
16
|
+
307 => '307 Temporary Redirect',
|
17
|
+
400 => '400 Bad Request',
|
18
|
+
401 => '401 Unauthorized',
|
19
|
+
402 => '402 Payment Required',
|
20
|
+
403 => '403 Forbidden',
|
21
|
+
404 => '404 Not Found',
|
22
|
+
405 => '405 Method Not Allowed',
|
23
|
+
406 => '406 Not Acceptable',
|
24
|
+
407 => '407 Proxy Authentication Required',
|
25
|
+
408 => '408 Request Time-out',
|
26
|
+
409 => '409 Conflict',
|
27
|
+
410 => '410 Gone',
|
28
|
+
411 => '411 Length Required',
|
29
|
+
412 => '412 Precondition Failed',
|
30
|
+
413 => '413 Request Entity Too Large',
|
31
|
+
414 => '414 Request-URI Too Large',
|
32
|
+
415 => '415 Unsupported Media Type',
|
33
|
+
416 => '416 Requested range not satisfiable',
|
34
|
+
417 => '417 Expectation Failed',
|
35
|
+
500 => '500 Internal Server Error',
|
36
|
+
501 => '501 Not Implemented',
|
37
|
+
502 => '502 Bad Gateway',
|
38
|
+
503 => '503 Service Unavailable',
|
39
|
+
504 => '504 Gateway Time-out',
|
40
|
+
505 => '505 HTTP Version not supported'
|
41
|
+
}
|
42
|
+
STATUS_CODE.default= '500 Internal Server Error'
|
43
|
+
STATUS_CODE.freeze
|
44
|
+
|
45
|
+
DEFAULT_HEADER = {
|
46
|
+
"Server" => "Midori/#{Midori::VERSION}"
|
47
|
+
}
|
48
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
class String
|
2
|
+
def colorize(color_code)
|
3
|
+
"\e[#{color_code}m#{self}\e[0m"
|
4
|
+
end
|
5
|
+
|
6
|
+
def red
|
7
|
+
colorize(31)
|
8
|
+
end
|
9
|
+
|
10
|
+
def green
|
11
|
+
colorize(32)
|
12
|
+
end
|
13
|
+
|
14
|
+
def yellow
|
15
|
+
colorize(33)
|
16
|
+
end
|
17
|
+
|
18
|
+
def blue
|
19
|
+
colorize(34)
|
20
|
+
end
|
21
|
+
|
22
|
+
def pink
|
23
|
+
colorize(35)
|
24
|
+
end
|
25
|
+
|
26
|
+
def light_blue
|
27
|
+
colorize(36)
|
28
|
+
end
|
29
|
+
end
|
@@ -6,4 +6,11 @@ module Kernel #:nodoc:
|
|
6
6
|
Object.const_get(name).class_eval(&Proc.new) if block_given?
|
7
7
|
Object.const_get(name)
|
8
8
|
end
|
9
|
+
|
10
|
+
def define_error(*args)
|
11
|
+
args.each do |arg|
|
12
|
+
class_name = arg.to_s.split('_').map {|word| word[0] = word[0].upcase; word}.join
|
13
|
+
define_class(class_name, StandardError)
|
14
|
+
end
|
15
|
+
end
|
9
16
|
end
|
data/lib/em-midori/em_midori.rb
CHANGED
@@ -1,24 +1,23 @@
|
|
1
1
|
require 'eventmachine'
|
2
2
|
|
3
3
|
module Midori
|
4
|
-
def self.run(api=
|
4
|
+
def self.run(api=Midori::API, ip=nil, port=nil)
|
5
5
|
ip ||= '127.0.0.1'
|
6
6
|
port ||= 8081
|
7
7
|
EventMachine.run do
|
8
|
-
puts "Midori #{Midori::VERSION} is now running on #{ip}:#{port}"
|
9
|
-
Midori::Server
|
10
|
-
@midori_server = EventMachine.start_server ip, port, Midori::Server
|
8
|
+
puts "Midori #{Midori::VERSION} is now running on #{ip}:#{port}".blue
|
9
|
+
@midori_server = EventMachine.start_server ip, port, Midori::Server, api
|
11
10
|
end
|
12
11
|
end
|
13
12
|
|
14
13
|
def self.stop
|
15
14
|
if @midori_server.nil?
|
16
|
-
puts 'Midori Server has NOT been started'
|
15
|
+
puts 'Midori Server has NOT been started'.red
|
17
16
|
return false
|
18
17
|
else
|
19
18
|
EventMachine.stop_server(@midori_server)
|
20
19
|
@midori_server = nil
|
21
|
-
puts 'Goodbye Midori'
|
20
|
+
puts 'Goodbye Midori'.blue
|
22
21
|
return true
|
23
22
|
end
|
24
23
|
end
|
data/lib/em-midori/request.rb
CHANGED
@@ -1,3 +1,64 @@
|
|
1
|
+
require 'stringio'
|
2
|
+
|
1
3
|
class Midori::Request
|
4
|
+
attr_accessor :ip, :port,
|
5
|
+
:protocol, :method, :path, :query_string,
|
6
|
+
:header, :body
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@parsed = false
|
10
|
+
@is_websocket = false
|
11
|
+
@is_eventsource = false
|
12
|
+
end
|
13
|
+
|
14
|
+
# Init an request with StringIO data
|
15
|
+
# === Attributes
|
16
|
+
# * +data+ [+StringIO+] - Request data
|
17
|
+
def parse(data)
|
18
|
+
@header = Hash.new
|
19
|
+
|
20
|
+
# Parse request
|
21
|
+
line = data.gets.split
|
22
|
+
@protocol = line[2]
|
23
|
+
@method = line[0]
|
24
|
+
@query_string = line[1].match(/\?(.*?)$/)
|
25
|
+
unless @query_string.nil?
|
26
|
+
@query_string = @query_string[1]
|
27
|
+
end
|
28
|
+
@path = line[1].gsub(/\?(.*?)$/, '')
|
29
|
+
|
30
|
+
# Parse header
|
31
|
+
while (line = data.gets) != "\r\n"
|
32
|
+
line = line.split
|
33
|
+
@header[line[0][0..-2]] = line[1..-1].join(' ')
|
34
|
+
end
|
35
|
+
|
36
|
+
# Deal with WebSocket
|
37
|
+
if @header['Upgrade'] == 'websocket' && @header['Connection'] == 'Upgrade'
|
38
|
+
@method = 'WEBSOCKET'
|
39
|
+
@is_websocket = true
|
40
|
+
end
|
41
|
+
|
42
|
+
# Deal with EventSource
|
43
|
+
if @header['Accept'] == 'text/event-stream'
|
44
|
+
@method = 'EVENTSOURCE'
|
45
|
+
@is_eventsource = true
|
46
|
+
end
|
47
|
+
|
48
|
+
# Parse body
|
49
|
+
@body = data.read
|
50
|
+
@parsed = true
|
51
|
+
end
|
52
|
+
|
53
|
+
def parsed?
|
54
|
+
@parsed
|
55
|
+
end
|
56
|
+
|
57
|
+
def websocket?
|
58
|
+
@is_websocket
|
59
|
+
end
|
2
60
|
|
61
|
+
def eventsource?
|
62
|
+
@is_eventsource
|
63
|
+
end
|
3
64
|
end
|
data/lib/em-midori/response.rb
CHANGED
@@ -1,56 +1,19 @@
|
|
1
1
|
class Midori::Response
|
2
|
-
|
3
|
-
100 => '100 Continue',
|
4
|
-
101 => '101 Switching Protocols',
|
5
|
-
200 => '200 OK',
|
6
|
-
201 => '201 Created',
|
7
|
-
202 => '202 Accepted',
|
8
|
-
203 => '203 Non-Authoritative Information',
|
9
|
-
204 => '204 No Content',
|
10
|
-
205 => '205 Reset Content',
|
11
|
-
206 => '206 Partial Content',
|
12
|
-
300 => '300 Multiple Choices',
|
13
|
-
301 => '301 Moved Permanently',
|
14
|
-
304 => '304 Not Modified',
|
15
|
-
305 => '305 Use Proxy',
|
16
|
-
307 => '307 Temporary Redirect',
|
17
|
-
400 => '400 Bad Request',
|
18
|
-
401 => '401 Unauthorized',
|
19
|
-
402 => '402 Payment Required',
|
20
|
-
403 => '403 Forbidden',
|
21
|
-
404 => '404 Not Found',
|
22
|
-
405 => '405 Method Not Allowed',
|
23
|
-
406 => '406 Not Acceptable',
|
24
|
-
407 => '407 Proxy Authentication Required',
|
25
|
-
408 => '408 Request Time-out',
|
26
|
-
409 => '409 Conflict',
|
27
|
-
410 => '410 Gone',
|
28
|
-
411 => '411 Length Required',
|
29
|
-
412 => '412 Precondition Failed',
|
30
|
-
413 => '413 Request Entity Too Large',
|
31
|
-
414 => '414 Request-URI Too Large',
|
32
|
-
415 => '415 Unsupported Media Type',
|
33
|
-
416 => '416 Requested range not satisfiable',
|
34
|
-
417 => '417 Expectation Failed',
|
35
|
-
500 => '500 Internal Server Error',
|
36
|
-
501 => '501 Not Implemented',
|
37
|
-
502 => '502 Bad Gateway',
|
38
|
-
503 => '503 Service Unavailable',
|
39
|
-
504 => '504 Gateway Time-out',
|
40
|
-
505 => '505 HTTP Version not supported'
|
41
|
-
}
|
42
|
-
STATUS_CODE.default= '500 Internal Server Error'
|
43
|
-
STATUS_CODE.freeze
|
2
|
+
attr_accessor :status, :header, :body
|
44
3
|
|
45
|
-
|
46
|
-
|
47
|
-
def initialize(code=200, header={}, body='')
|
48
|
-
@status_code = STATUS_CODE[code]
|
4
|
+
def initialize(code=200, header= Midori::Const::DEFAULT_HEADER, body='')
|
5
|
+
@status = Midori::Const::STATUS_CODE[code]
|
49
6
|
@header = header
|
50
7
|
@body = body
|
51
8
|
end
|
52
9
|
|
10
|
+
def generate_header
|
11
|
+
@header.map do |key, value|
|
12
|
+
"#{key}: #{value}\r\n"
|
13
|
+
end.join
|
14
|
+
end
|
15
|
+
|
53
16
|
def to_s
|
54
|
-
"HTTP/1.1 #{@
|
17
|
+
"HTTP/1.1 #{@status}\r\n#{generate_header}\r\n#{@body}"
|
55
18
|
end
|
56
19
|
end
|
data/lib/em-midori/server.rb
CHANGED
@@ -1,15 +1,78 @@
|
|
1
|
+
require 'stringio'
|
2
|
+
|
1
3
|
module Midori::Server
|
2
|
-
|
3
|
-
|
4
|
+
attr_accessor :request, :api, :websocket
|
5
|
+
|
6
|
+
def initialize(api)
|
7
|
+
@api = api
|
8
|
+
@request = Midori::Request.new
|
9
|
+
@websocket = Midori::WebSocket.new(self)
|
4
10
|
end
|
5
11
|
|
6
12
|
def receive_data(data)
|
13
|
+
start_time = Time.now
|
14
|
+
data = StringIO.new(data)
|
7
15
|
port, ip = Socket.unpack_sockaddr_in(get_peername)
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
16
|
+
@request.ip = ip
|
17
|
+
@request.port = port
|
18
|
+
if @request.parsed?
|
19
|
+
websocket_request(data)
|
20
|
+
else
|
21
|
+
receive_new_request(data)
|
22
|
+
end
|
23
|
+
$stdout << "#{@request.ip} - - [#{Time.now.inspect}] \"#{@request.method} #{@request.path} #{@request.protocol}\" #{@response.status} #{(Time.now.to_f - start_time.to_f).round(5)}\n".green
|
14
24
|
end
|
25
|
+
|
26
|
+
def receive_new_request(data)
|
27
|
+
begin
|
28
|
+
@request.parse(data)
|
29
|
+
@response = @api.receive(request, self)
|
30
|
+
rescue Midori::Error::NotFound => _e
|
31
|
+
@response = Midori::Response.new(404, {}, '404 Not Found')
|
32
|
+
rescue => e
|
33
|
+
@response = Midori::Response.new(500, {}, 'Internal Server Error')
|
34
|
+
puts e.inspect.yellow
|
35
|
+
end
|
36
|
+
unless (@request.websocket? || @request.eventsource?)
|
37
|
+
send_data @response
|
38
|
+
close_connection_after_writing
|
39
|
+
end
|
40
|
+
if @request.websocket? && !@websocket.events[:open].nil?
|
41
|
+
call_event(:open)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def websocket_request(data)
|
46
|
+
begin
|
47
|
+
@websocket.decode(data)
|
48
|
+
case @websocket.opcode
|
49
|
+
when 0x1, 0x2
|
50
|
+
call_event(:message, @websocket.msg)
|
51
|
+
when 0x9
|
52
|
+
call_event(:ping)
|
53
|
+
@websocket.pong
|
54
|
+
when 0xA
|
55
|
+
call_event(:pong)
|
56
|
+
else
|
57
|
+
# Unknown Control Frame
|
58
|
+
raise Midori::Error::FrameEnd
|
59
|
+
end
|
60
|
+
rescue Midori::Error::FrameEnd => _e
|
61
|
+
unless @websocket.events[:close].nil?
|
62
|
+
call_event(:close)
|
63
|
+
end
|
64
|
+
send_data "\b" # Opcode 0x8
|
65
|
+
close_connection_after_writing
|
66
|
+
rescue => e
|
67
|
+
puts e.inspect.yellow
|
68
|
+
@response = Midori::Response.new(400, {}, 'Bad Request')
|
69
|
+
send_data @response
|
70
|
+
close_connection_after_writing
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def call_event(event, args=[])
|
75
|
+
lambda {@websocket.instance_exec(*args, &@websocket.events[event])}.call unless @websocket.events[event].nil?
|
76
|
+
end
|
77
|
+
|
15
78
|
end
|
data/lib/em-midori/version.rb
CHANGED
@@ -0,0 +1,63 @@
|
|
1
|
+
class Midori::WebSocket
|
2
|
+
attr_accessor :msg, :opcode, :events, :connection
|
3
|
+
|
4
|
+
def initialize(connection)
|
5
|
+
@events = Hash.new
|
6
|
+
@connection = connection
|
7
|
+
end
|
8
|
+
|
9
|
+
def decode(data)
|
10
|
+
# Fin and Opcode
|
11
|
+
byte_tmp = data.getbyte
|
12
|
+
fin = byte_tmp & 0b10000000
|
13
|
+
@opcode = byte_tmp & 0b00001111
|
14
|
+
raise Midori::Error::ContinuousFrame unless fin
|
15
|
+
raise Midori::Error::OpCodeError unless [0x1, 0x2, 0x8, 0x9, 0xA].include?opcode
|
16
|
+
raise Midori::Error::FrameEnd if @opcode == 0x8 # Close Frame
|
17
|
+
return if @opcode == 0x9 || @opcode == 0xA # Ping Pong
|
18
|
+
decode_mask(data)
|
19
|
+
end
|
20
|
+
|
21
|
+
def decode_mask(data)
|
22
|
+
# Mask
|
23
|
+
byte_tmp = data.getbyte
|
24
|
+
is_masked = byte_tmp & 0b10000000
|
25
|
+
raise Midori::Error::NotMasked unless is_masked
|
26
|
+
# Payload
|
27
|
+
payload = byte_tmp & 0b01111111
|
28
|
+
mask = 4.times.map {data.getbyte}
|
29
|
+
# Message
|
30
|
+
masked_msg = payload.times.map {data.getbyte}
|
31
|
+
@msg = masked_msg.each_with_index.map {|byte, i| byte ^ mask[i % 4]}
|
32
|
+
@msg = @msg.pack('C*').force_encoding('utf-8') if @opcode == 0x1
|
33
|
+
end
|
34
|
+
|
35
|
+
def on(event, &block) # open, message, close, ping, pong
|
36
|
+
@events[event] = block
|
37
|
+
end
|
38
|
+
|
39
|
+
def send(msg)
|
40
|
+
output = Array.new
|
41
|
+
if msg.is_a?String
|
42
|
+
output << 0b10000001 << msg.size << msg
|
43
|
+
elsif msg.is_a?Array
|
44
|
+
output << 0b10000010 << msg.size
|
45
|
+
output.concat msg
|
46
|
+
else
|
47
|
+
raise Midori::Error::OpCodeError
|
48
|
+
end
|
49
|
+
@connection.send_data(output.pack("CCA#{msg.size}"))
|
50
|
+
end
|
51
|
+
|
52
|
+
def ping
|
53
|
+
@connection.send_data "\t"
|
54
|
+
end
|
55
|
+
|
56
|
+
def pong
|
57
|
+
@connection.send_data "\n"
|
58
|
+
end
|
59
|
+
|
60
|
+
def close
|
61
|
+
Midori::Error::FrameEnd
|
62
|
+
end
|
63
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: em-midori
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- HeckPsi Lab
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-10-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: eventmachine
|
@@ -72,6 +72,20 @@ dependencies:
|
|
72
72
|
- - "~>"
|
73
73
|
- !ruby/object:Gem::Version
|
74
74
|
version: '3.0'
|
75
|
+
- !ruby/object:Gem::Dependency
|
76
|
+
name: faye-websocket
|
77
|
+
requirement: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - "~>"
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: '0.10'
|
82
|
+
type: :development
|
83
|
+
prerelease: false
|
84
|
+
version_requirements: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - "~>"
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '0.10'
|
75
89
|
description: An EventMachine Based Web Framework on Ruby
|
76
90
|
email: business@heckpsi.com
|
77
91
|
executables: []
|
@@ -82,6 +96,7 @@ files:
|
|
82
96
|
- ".gitignore"
|
83
97
|
- ".resources/midori_tokiwa.gif"
|
84
98
|
- ".resources/sapphire_kawashima.gif"
|
99
|
+
- ".resources/slogan.png"
|
85
100
|
- ".rspec"
|
86
101
|
- ".rubocop.yml"
|
87
102
|
- ".travis.yml"
|
@@ -89,12 +104,17 @@ files:
|
|
89
104
|
- lib/em-midori.rb
|
90
105
|
- lib/em-midori/api.rb
|
91
106
|
- lib/em-midori/clean_room.rb
|
107
|
+
- lib/em-midori/const.rb
|
108
|
+
- lib/em-midori/debug.rb
|
92
109
|
- lib/em-midori/define_class.rb
|
93
110
|
- lib/em-midori/em_midori.rb
|
111
|
+
- lib/em-midori/error.rb
|
94
112
|
- lib/em-midori/request.rb
|
95
113
|
- lib/em-midori/response.rb
|
114
|
+
- lib/em-midori/route.rb
|
96
115
|
- lib/em-midori/server.rb
|
97
116
|
- lib/em-midori/version.rb
|
117
|
+
- lib/em-midori/websocket.rb
|
98
118
|
homepage: https://github.com/heckpsi-lab/em-midori
|
99
119
|
licenses:
|
100
120
|
- MIT
|
@@ -120,3 +140,4 @@ signing_key:
|
|
120
140
|
specification_version: 4
|
121
141
|
summary: An EventMachine Based Web Framework on Ruby
|
122
142
|
test_files: []
|
143
|
+
has_rdoc:
|