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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f6d14d7e6c7a05f01d72029dc8d09ad83c37328d392d8c745b938025181d9812
4
- data.tar.gz: dc2d8754f72ff25354f33fd0a15a0e23a1617eaa4a82ec94b21f9e47b2ed5eda
3
+ metadata.gz: 2adfa1f5075c05406725691e2e254c7ac1e0f3ff9aba2e8787d9567323681654
4
+ data.tar.gz: f6f22a1df53862dbc956f53b188a7e4b023c6ef85e58f879c4fcdaefb0539e38
5
5
  SHA512:
6
- metadata.gz: 0f4bf5b25cb38d3f5b8cf2a8f685956086d920ccc9cee3c838cc1fc3a94c1e482576f193d4d6c03505104b32d403a959da924e7243105f87b5faebc5d2d17b3e
7
- data.tar.gz: cc1e70247c020875415d73e2bba4190c9f33c3ad52dacff0574eb89b5a6f3e1ee2c68efbbd330e4af83e9b86270d64fb26ac27133a30a8ebe68ed0cbbfbffd07
6
+ metadata.gz: ab4f42932ef1851b8529c6eaa3d3a10d8a21cdd97e3702e94717a0bb8076ab981cd352d275e700f93203f4c790b75993b066e7c59f6b2c4fa6aa39f5479bd210
7
+ data.tar.gz: cab7f16b86eaa3e532ab5d40265e057c7b998d4122d002db14690e78e243191c2888795a77364a45bcd3112f6524ef194e71eb239b44026831a4ce789dd5381b
@@ -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 method_midori_websocket_mask(VALUE self, VALUE payload, VALUE mask);
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
- rb_define_protected_method(MidoriWebSocket, "mask", method_midori_websocket_mask, 2);
16
- rb_define_protected_method(MidoriWebSocket, "mask_str", method_midori_websocket_mask_str, 2);
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 method_midori_websocket_mask(VALUE self, VALUE payload, VALUE mask)
26
+ VALUE method_midori_websocket_decode(VALUE self, VALUE data)
20
27
  {
21
- long n = RARRAY_LEN(payload), i, p, m;
22
- VALUE unmasked = rb_ary_new2(n);
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
- int mask_array[] = {
25
- NUM2INT(rb_ary_entry(mask, 0)),
26
- NUM2INT(rb_ary_entry(mask, 1)),
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
- for (i = 0; i < n; i++)
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
- p = NUM2INT(rb_ary_entry(payload, i));
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
- VALUE method_midori_websocket_mask_str(VALUE self, VALUE payload, VALUE mask)
40
- {
41
- long n = RARRAY_LEN(payload), i, p, m;
42
- char result[n];
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
- int mask_array[] = {
45
- NUM2INT(rb_ary_entry(mask, 0)),
46
- NUM2INT(rb_ary_entry(mask, 1)),
47
- NUM2INT(rb_ary_entry(mask, 2)),
48
- NUM2INT(rb_ary_entry(mask, 3))};
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
- p = NUM2INT(rb_ary_entry(payload, i));
53
- m = mask_array[i % 4];
54
- result[i] = p ^ m;
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
- return rb_enc_str_new(result, n, rb_utf8_encoding());
83
+ xfree(mask_array);
84
+ xfree(result);
85
+ return Qnil;
58
86
  }
@@ -3,12 +3,11 @@ require 'digest/sha1'
3
3
  require 'stringio'
4
4
  require 'fiber'
5
5
  require 'logger'
6
- require 'http/parser'
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'
@@ -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
@@ -26,15 +26,15 @@ class Midori::Request
26
26
  @body_parsed = false
27
27
  @is_websocket = false
28
28
  @is_eventsource = false
29
- @parser = Http::Parser.new
29
+ @parser = Mizu::Parser.new
30
30
  @params = {}
31
31
  @query_params = Hash.new(Array.new)
32
32
  @cookie = {}
33
33
  @body = ''
34
- @parser.on_headers_complete = proc do
35
- @protocol = @parser.http_version
36
- @method = @parser.http_method
37
- @path = @parser.request_url
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
- offset = @parser << data
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
@@ -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? && !@body.empty?
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
@@ -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.join('.')}\" #{@response.status} #{sprintf("%.6f", now_time.to_f - start_time.to_f)}".green
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 = Midori::Request.new
128
+ @request.reset!
129
129
  end
130
130
  end
@@ -1,5 +1,5 @@
1
1
  # Midori Module
2
2
  module Midori
3
3
  # Current Version Code
4
- VERSION = '0.7.0'.freeze
4
+ VERSION = '0.7.1'.freeze
5
5
  end
@@ -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 << msg.size << msg
74
- @connection.send_data(output.pack("CCA#{msg.size}"))
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 << msg.size
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.0
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: 2018-07-10 00:00:00.000000000 Z
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: midori_http_parser
42
+ name: mizu
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: 0.6.1
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.6.1
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
- rubyforge_project:
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