midori.rb 0.7.0 → 0.7.1

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: 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