midori.rb 0.7.0 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/ext/midori/websocket.c +59 -31
- data/lib/midori.rb +2 -2
- data/lib/midori/eventsource.rb +1 -0
- data/lib/midori/request.rb +20 -8
- data/lib/midori/response.rb +1 -1
- data/lib/midori/server.rb +2 -2
- data/lib/midori/version.rb +1 -1
- data/lib/midori/websocket.rb +19 -40
- metadata +6 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2adfa1f5075c05406725691e2e254c7ac1e0f3ff9aba2e8787d9567323681654
|
4
|
+
data.tar.gz: f6f22a1df53862dbc956f53b188a7e4b023c6ef85e58f879c4fcdaefb0539e38
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ab4f42932ef1851b8529c6eaa3d3a10d8a21cdd97e3702e94717a0bb8076ab981cd352d275e700f93203f4c790b75993b066e7c59f6b2c4fa6aa39f5479bd210
|
7
|
+
data.tar.gz: cab7f16b86eaa3e532ab5d40265e057c7b998d4122d002db14690e78e243191c2888795a77364a45bcd3112f6524ef194e71eb239b44026831a4ce789dd5381b
|
data/ext/midori/websocket.c
CHANGED
@@ -2,57 +2,85 @@
|
|
2
2
|
#include <ruby/encoding.h>
|
3
3
|
|
4
4
|
VALUE Midori = Qnil;
|
5
|
+
VALUE MidoriException = Qnil;
|
5
6
|
VALUE MidoriWebSocket = Qnil;
|
6
7
|
|
8
|
+
VALUE ContinousFrameException = Qnil;
|
9
|
+
VALUE OpCodeException = Qnil;
|
10
|
+
VALUE NotMaskedException = Qnil;
|
11
|
+
|
7
12
|
void Init_midori_ext();
|
8
|
-
VALUE
|
9
|
-
VALUE method_midori_websocket_mask_str(VALUE self, VALUE payload, VALUE mask);
|
13
|
+
VALUE method_midori_websocket_decode(VALUE self, VALUE data);
|
10
14
|
|
11
15
|
void Init_midori_ext()
|
12
16
|
{
|
13
17
|
Midori = rb_define_module("Midori");
|
14
18
|
MidoriWebSocket = rb_define_class_under(Midori, "WebSocket", rb_cObject);
|
15
|
-
|
16
|
-
|
19
|
+
MidoriException = rb_define_module_under(Midori, "Exception");
|
20
|
+
ContinousFrameException = rb_const_get(MidoriException, rb_intern("ContinuousFrame"));
|
21
|
+
OpCodeException = rb_const_get(MidoriException, rb_intern("OpCodeError"));
|
22
|
+
NotMaskedException = rb_const_get(MidoriException, rb_intern("NotMasked"));
|
23
|
+
rb_define_method(MidoriWebSocket, "decode", method_midori_websocket_decode, 1);
|
17
24
|
}
|
18
25
|
|
19
|
-
VALUE
|
26
|
+
VALUE method_midori_websocket_decode(VALUE self, VALUE data)
|
20
27
|
{
|
21
|
-
|
22
|
-
|
28
|
+
int byte, opcode, i, n, fin;
|
29
|
+
char *result;
|
30
|
+
int *mask_array;
|
31
|
+
ID getbyte = rb_intern("getbyte");
|
32
|
+
ID close = rb_intern("close");
|
23
33
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
NUM2INT(rb_ary_entry(mask, 2)),
|
28
|
-
NUM2INT(rb_ary_entry(mask, 3))};
|
34
|
+
byte = NUM2INT(rb_funcall(data, getbyte, 0));
|
35
|
+
fin = byte & 0x80;
|
36
|
+
opcode = byte & 0x0f;
|
29
37
|
|
30
|
-
|
38
|
+
if (fin != 0x80)
|
39
|
+
rb_raise(ContinousFrameException, "Continous Frame hasn't been implemented yet");
|
40
|
+
|
41
|
+
rb_iv_set(self, "@opcode", INT2NUM(opcode));
|
42
|
+
if (opcode != 0x1 && opcode != 0x2 && opcode != 0x8 && opcode != 0x9 && opcode != 0xA)
|
43
|
+
rb_raise(OpCodeException, "OpCode %d not supported", opcode);
|
44
|
+
|
45
|
+
if (opcode == 0x8)
|
31
46
|
{
|
32
|
-
|
33
|
-
m = mask_array[i % 4];
|
34
|
-
rb_ary_store(unmasked, i, INT2NUM(p ^ m));
|
47
|
+
rb_funcall(self, close, 0);
|
35
48
|
}
|
36
|
-
return unmasked;
|
37
|
-
}
|
38
49
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
50
|
+
byte = NUM2INT(rb_funcall(data, getbyte, 0));
|
51
|
+
if ((byte & 0x80) != 0x80)
|
52
|
+
{
|
53
|
+
rb_raise(NotMaskedException, "Messages from client MUST be masked");
|
54
|
+
}
|
43
55
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
56
|
+
n = byte & 0x7f;
|
57
|
+
result = (char *)xmalloc(n);
|
58
|
+
mask_array = (int *)xmalloc(4);
|
59
|
+
|
60
|
+
for (i = 0; i < 4; i++) {
|
61
|
+
mask_array[i] = NUM2INT(rb_funcall(data, getbyte, 0));
|
62
|
+
}
|
49
63
|
|
50
64
|
for (i = 0; i < n; i++)
|
51
65
|
{
|
52
|
-
|
53
|
-
|
54
|
-
|
66
|
+
result[i] = NUM2INT(rb_funcall(data, getbyte, 0)) ^ mask_array[i % 4];
|
67
|
+
}
|
68
|
+
|
69
|
+
if (opcode == 0x1 || opcode == 0x9 || opcode == 0xA)
|
70
|
+
{
|
71
|
+
rb_iv_set(self, "@msg", rb_enc_str_new(result, n, rb_utf8_encoding()));
|
72
|
+
}
|
73
|
+
else
|
74
|
+
{
|
75
|
+
VALUE result_arr = rb_ary_new2(n);
|
76
|
+
for (i = 0; i < n; i++)
|
77
|
+
{
|
78
|
+
rb_ary_store(result_arr, i, INT2NUM(result[i]));
|
79
|
+
}
|
80
|
+
rb_iv_set(self, "@msg", result_arr);
|
55
81
|
}
|
56
82
|
|
57
|
-
|
83
|
+
xfree(mask_array);
|
84
|
+
xfree(result);
|
85
|
+
return Qnil;
|
58
86
|
}
|
data/lib/midori.rb
CHANGED
@@ -3,12 +3,11 @@ require 'digest/sha1'
|
|
3
3
|
require 'stringio'
|
4
4
|
require 'fiber'
|
5
5
|
require 'logger'
|
6
|
-
require '
|
6
|
+
require 'mizu'
|
7
7
|
require 'mustermann'
|
8
8
|
require 'murasaki'
|
9
9
|
require 'socket'
|
10
10
|
|
11
|
-
require_relative 'midori_ext'
|
12
11
|
require_relative 'midori/core_ext/configurable'
|
13
12
|
require_relative 'midori/core_ext/define_class'
|
14
13
|
require_relative 'midori/core_ext/http_header'
|
@@ -37,3 +36,4 @@ require_relative 'midori/configure'
|
|
37
36
|
require_relative 'midori/runner'
|
38
37
|
require_relative 'midori/logger'
|
39
38
|
|
39
|
+
require_relative 'midori_ext'
|
data/lib/midori/eventsource.rb
CHANGED
@@ -14,6 +14,7 @@ class Midori::EventSource
|
|
14
14
|
# @param [String] data data to be sent
|
15
15
|
def send(data)
|
16
16
|
raise Midori::Exception::EventSourceTypeError unless data.is_a? String
|
17
|
+
# TODO: implement envents by standard
|
17
18
|
@connection.send_data(data.split("\n").map {|str| "data: #{str}\n"}.join + "\n")
|
18
19
|
@connection.close_connection_after_writing
|
19
20
|
end
|
data/lib/midori/request.rb
CHANGED
@@ -26,15 +26,15 @@ class Midori::Request
|
|
26
26
|
@body_parsed = false
|
27
27
|
@is_websocket = false
|
28
28
|
@is_eventsource = false
|
29
|
-
@parser =
|
29
|
+
@parser = Mizu::Parser.new
|
30
30
|
@params = {}
|
31
31
|
@query_params = Hash.new(Array.new)
|
32
32
|
@cookie = {}
|
33
33
|
@body = ''
|
34
|
-
@parser.
|
35
|
-
@protocol = @parser.
|
36
|
-
@method = @parser.
|
37
|
-
@path = @parser.
|
34
|
+
@parser.on_complete do
|
35
|
+
@protocol = @parser.version
|
36
|
+
@method = @parser.method
|
37
|
+
@path = @parser.path
|
38
38
|
# Turn header into case-insensitive due to RFC 2.6 Chapter 4.2
|
39
39
|
# https://www.ietf.org/rfc/rfc2616.txt
|
40
40
|
@parser.headers.each { |key, value| @header[key] = value }
|
@@ -50,10 +50,22 @@ class Midori::Request
|
|
50
50
|
@path.gsub!(/\?(.*?)$/, '')
|
51
51
|
@method = @method.to_sym
|
52
52
|
@parsed = true
|
53
|
-
:stop
|
54
53
|
end
|
55
54
|
end
|
56
55
|
|
56
|
+
def reset!
|
57
|
+
@header = HTTPHeader.new
|
58
|
+
@parsed = false
|
59
|
+
@body_parsed = false
|
60
|
+
@is_websocket = false
|
61
|
+
@is_eventsource = false
|
62
|
+
@parser.reset!
|
63
|
+
@params = {}
|
64
|
+
@query_params = Hash.new(Array.new)
|
65
|
+
@cookie = {}
|
66
|
+
@body = ''
|
67
|
+
end
|
68
|
+
|
57
69
|
# Init an request with String data
|
58
70
|
# @param [String] data
|
59
71
|
# @return [nil] nil
|
@@ -62,8 +74,8 @@ class Midori::Request
|
|
62
74
|
if @parsed
|
63
75
|
@body += data
|
64
76
|
else
|
65
|
-
|
66
|
-
@body += data[offset..-1] if @parsed
|
77
|
+
@parser << data
|
78
|
+
@body += data[@parser.offset..-1] if @parsed
|
67
79
|
end
|
68
80
|
|
69
81
|
# Set body parsed if body reaches content length
|
data/lib/midori/response.rb
CHANGED
@@ -21,7 +21,7 @@ class Midori::Response
|
|
21
21
|
# Generate header string from hash
|
22
22
|
# @return [String] generated string
|
23
23
|
def generate_header
|
24
|
-
@header['Content-Length'] = @body.bytesize if @header['Content-Length'].nil? &&
|
24
|
+
@header['Content-Length'] = @body.bytesize if @header['Content-Length'].nil? && @header['Upgrade'].nil? && @header['Content-Type'] != 'text/event-stream'
|
25
25
|
@header.map do |key, value|
|
26
26
|
"#{key}: #{value}\r\n"
|
27
27
|
end.join
|
data/lib/midori/server.rb
CHANGED
@@ -59,7 +59,7 @@ module Midori::Server
|
|
59
59
|
start_time = Time.now
|
60
60
|
@response = @api.receive(request, self)
|
61
61
|
now_time = Time.now
|
62
|
-
@logger.info "#{@request.ip} - - \"#{@request.method} #{@request.path} HTTP/#{@request.protocol
|
62
|
+
@logger.info "#{@request.ip} - - \"#{@request.method} #{@request.path} HTTP/#{@request.protocol}\" #{@response.status} #{sprintf("%.6f", now_time.to_f - start_time.to_f)}".green
|
63
63
|
call_event(:open) if @request.websocket?
|
64
64
|
rescue Midori::Exception::NotFound => e
|
65
65
|
@response = Midori::Sandbox.capture(e)
|
@@ -125,6 +125,6 @@ module Midori::Server
|
|
125
125
|
end
|
126
126
|
EventLoop.add_timer(@keep_alive_timer)
|
127
127
|
# Reset request
|
128
|
-
@request
|
128
|
+
@request.reset!
|
129
129
|
end
|
130
130
|
end
|
data/lib/midori/version.rb
CHANGED
data/lib/midori/websocket.rb
CHANGED
@@ -15,43 +15,6 @@ class Midori::WebSocket
|
|
15
15
|
@connection = connection
|
16
16
|
end
|
17
17
|
|
18
|
-
# Decode raw data send from client
|
19
|
-
# @param [StringIO] data raw data
|
20
|
-
def decode(data)
|
21
|
-
# Fin and Opcode
|
22
|
-
byte_tmp = data.getbyte
|
23
|
-
fin = byte_tmp & 0b10000000
|
24
|
-
@opcode = byte_tmp & 0b00001111
|
25
|
-
# NOT Support Multiple Fragments
|
26
|
-
raise Midori::Exception::ContinuousFrame unless fin
|
27
|
-
raise Midori::Exception::OpCodeError unless [0x1, 0x2, 0x8, 0x9, 0xA].include? @opcode
|
28
|
-
close if @opcode == 0x8 # Close Frame
|
29
|
-
# return if @opcode == 0x9 || @opcode == 0xA # Ping Pong
|
30
|
-
decode_mask(data)
|
31
|
-
end
|
32
|
-
|
33
|
-
# Decode masked message send from client
|
34
|
-
# @param [StringIO] data raw data
|
35
|
-
def decode_mask(data)
|
36
|
-
# Mask
|
37
|
-
byte_tmp = data.getbyte
|
38
|
-
is_masked = byte_tmp & 0b10000000
|
39
|
-
raise Midori::Exception::NotMasked unless is_masked == 128
|
40
|
-
# Payload
|
41
|
-
payload = byte_tmp & 0b01111111
|
42
|
-
mask = Array.new(4) { data.getbyte }
|
43
|
-
# Message
|
44
|
-
masked_msg = Array.new(payload) { data.getbyte }
|
45
|
-
if [0x1, 0x9, 0xA].include? opcode
|
46
|
-
@msg = mask_str(masked_msg, mask)
|
47
|
-
else # [0x2]
|
48
|
-
@msg = mask(masked_msg, mask)
|
49
|
-
end
|
50
|
-
# For debug
|
51
|
-
# data.rewind
|
52
|
-
# data.bytes {|byte| puts byte.to_s(16)}
|
53
|
-
end
|
54
|
-
|
55
18
|
# API definition for events
|
56
19
|
# @param [Symbol] event event name(open, message, close, ping, pong)
|
57
20
|
# @yield what to do after event matched
|
@@ -69,11 +32,27 @@ class Midori::WebSocket
|
|
69
32
|
# @param [Array<Integer>, String] msg data to send
|
70
33
|
def send(msg)
|
71
34
|
output = []
|
35
|
+
payload_length = []
|
36
|
+
if msg.size < 126
|
37
|
+
payload_length << msg.size
|
38
|
+
elsif msg.size < 65_536
|
39
|
+
payload_length << 126
|
40
|
+
payload_length.concat([msg.size].pack('n').unpack('C*'))
|
41
|
+
elsif msg.size < 2**63
|
42
|
+
payload_length << 127
|
43
|
+
payload_length.concat([msg.size >> 32, msg.size & 0xFFFFFFFF].pack('NN').unpack('C*'))
|
44
|
+
else
|
45
|
+
raise Midori::Exception::ContinuousFrame
|
46
|
+
end
|
47
|
+
|
72
48
|
if msg.is_a?String
|
73
|
-
output << 0b10000001
|
74
|
-
|
49
|
+
output << 0b10000001
|
50
|
+
output.concat payload_length
|
51
|
+
output.concat msg.unpack('C*')
|
52
|
+
@connection.send_data(output.pack('C*'))
|
75
53
|
elsif msg.is_a? Array
|
76
|
-
output << 0b10000010
|
54
|
+
output << 0b10000010
|
55
|
+
output.concat payload_length
|
77
56
|
output.concat msg
|
78
57
|
@connection.send_data(output.pack('C*'))
|
79
58
|
else
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: midori.rb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.7.
|
4
|
+
version: 0.7.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- HeckPsi Lab
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2019-07-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: murasaki
|
@@ -39,19 +39,19 @@ dependencies:
|
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '1.0'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
|
-
name:
|
42
|
+
name: mizu
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
45
|
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: 0.
|
47
|
+
version: 0.1.2
|
48
48
|
type: :runtime
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: 0.
|
54
|
+
version: 0.1.2
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: rake-compiler
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -132,8 +132,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
132
132
|
- !ruby/object:Gem::Version
|
133
133
|
version: '0'
|
134
134
|
requirements: []
|
135
|
-
|
136
|
-
rubygems_version: 2.7.7
|
135
|
+
rubygems_version: 3.0.1
|
137
136
|
signing_key:
|
138
137
|
specification_version: 4
|
139
138
|
summary: High performance ruby web framework
|