midori.rb 0.5.4 → 0.9.0

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: 6f47091da1ed0c2aa5186718051d4c0f67cf691eb0c0cf579bbfeff0fa007e1e
4
- data.tar.gz: 172665350943d2de1faae70882bfad72a799756edc3d993185ea6a6d0cb1661e
3
+ metadata.gz: 39f2783e703297e0c67b6190fe38776e92397215917bb1883da0fb7b8e218590
4
+ data.tar.gz: 8a150f94267e00f92e06ee41d3dd356eb03dc13349c940bf20146bfe008ebf69
5
5
  SHA512:
6
- metadata.gz: 1a6801c29d82920d82cd25760ca929f3be55a402169d5518d34941409949e99e799e4a1170b267ccfe7707b66c00ace0b5b0674fffb1ebc3dfd842607a05025e
7
- data.tar.gz: 6ee0eb8e0b7e496ca8c2a8f71e7d5e03d28172bd10839ae92e2ef01981076b3f9aba50d9ae1e7c1e6374b9b93c27fe8b63e1260de40eef8c67595f9dce45c7d2
6
+ metadata.gz: 9d2238695402c0b3cb064d52feadb6b71ef685dcc2b30f79cbdd7dd680de5d26bca3057abbb986bb611831c740f66e3ff7ee18bece2b675c51298012baa348a4
7
+ data.tar.gz: ecf3a5ae2f89baaa5215f1884ecfcd7972128e92f76f892c332f05b278be1d5fbc7a3862a8169a232025826df3e825636a55e09218ba7539bf44240a4494cd8a
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+ $LOAD_PATH.unshift File.expand_path("./lib", __dir__)
3
+
4
+ require 'evt'
5
+ require 'midori'
6
+
7
+ Fiber.set_scheduler Evt::Scheduler.new
8
+
9
+ class HelloWorldAPI < Midori::API
10
+ get '/' do
11
+ 'Ohayou Midori'
12
+ end
13
+ end
14
+
15
+ Fiber.schedule do
16
+ Midori::Runner.new(HelloWorldAPI).start
17
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+ $LOAD_PATH.unshift File.expand_path("./lib", __dir__)
3
+
4
+ require 'evt'
5
+ require 'midori'
6
+ require 'midori-contrib/redic'
7
+
8
+ Fiber.set_scheduler Evt::Scheduler.new
9
+ REDIS = Redic.new
10
+
11
+ class HelloWorldAPI < Midori::API
12
+ get '/' do
13
+ REDIS.call 'GET', 'foo'
14
+ end
15
+ end
16
+
17
+ Fiber.schedule do
18
+ Midori::Runner.new(HelloWorldAPI).start
19
+ end
@@ -1,32 +1,86 @@
1
1
  #include <ruby.h>
2
+ #include <ruby/encoding.h>
2
3
 
3
4
  VALUE Midori = Qnil;
5
+ VALUE MidoriException = Qnil;
4
6
  VALUE MidoriWebSocket = Qnil;
5
7
 
8
+ VALUE ContinousFrameException = Qnil;
9
+ VALUE OpCodeException = Qnil;
10
+ VALUE NotMaskedException = Qnil;
11
+
6
12
  void Init_midori_ext();
7
- VALUE method_midori_websocket_mask(VALUE self, VALUE payload, VALUE mask);
13
+ VALUE method_midori_websocket_decode(VALUE self, VALUE data);
8
14
 
9
- void Init_midori_ext() {
15
+ void Init_midori_ext()
16
+ {
10
17
  Midori = rb_define_module("Midori");
11
18
  MidoriWebSocket = rb_define_class_under(Midori, "WebSocket", rb_cObject);
12
- rb_define_protected_method(MidoriWebSocket, "mask", method_midori_websocket_mask, 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);
13
24
  }
14
25
 
15
- VALUE method_midori_websocket_mask(VALUE self, VALUE payload, VALUE mask) {
16
- long n = RARRAY_LEN(payload), i, p, m;
17
- VALUE unmasked = rb_ary_new2(n);
18
-
19
- int mask_array[] = {
20
- NUM2INT(rb_ary_entry(mask, 0)),
21
- NUM2INT(rb_ary_entry(mask, 1)),
22
- NUM2INT(rb_ary_entry(mask, 2)),
23
- NUM2INT(rb_ary_entry(mask, 3))
24
- };
25
-
26
- for (i = 0; i < n; i++) {
27
- p = NUM2INT(rb_ary_entry(payload, i));
28
- m = mask_array[i % 4];
29
- rb_ary_store(unmasked, i, INT2NUM(p ^ m));
26
+ VALUE method_midori_websocket_decode(VALUE self, VALUE data)
27
+ {
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");
33
+
34
+ byte = NUM2INT(rb_funcall(data, getbyte, 0));
35
+ fin = byte & 0x80;
36
+ opcode = byte & 0x0f;
37
+
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)
46
+ {
47
+ rb_funcall(self, close, 0);
48
+ }
49
+
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
+ }
55
+
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
+ }
63
+
64
+ for (i = 0; i < n; i++)
65
+ {
66
+ result[i] = NUM2INT(rb_funcall(data, getbyte, 0)) ^ mask_array[i % 4];
30
67
  }
31
- return unmasked;
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);
81
+ }
82
+
83
+ xfree(mask_array);
84
+ xfree(result);
85
+ return Qnil;
32
86
  }
@@ -1,20 +1,19 @@
1
1
  require 'cgi'
2
+ require 'evt'
2
3
  require 'digest/sha1'
3
4
  require 'stringio'
4
5
  require 'fiber'
5
6
  require 'logger'
6
- require 'http/parser'
7
+ require 'mizu'
7
8
  require 'mustermann'
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'
15
14
  require_relative 'midori/core_ext/proc'
15
+ require_relative 'midori/core_ext/socket'
16
16
  require_relative 'midori/core_ext/string'
17
- require_relative 'midori/core_ext/tcp_server'
18
17
 
19
18
  require_relative 'midori/version'
20
19
 
@@ -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'
@@ -4,7 +4,6 @@ class Midori::Configure
4
4
  extend Configurable
5
5
 
6
6
  set :logger, ::Logger.new(STDOUT)
7
- set :protocol, :http
8
7
  set :bind, '127.0.0.1'
9
8
  set :port, 8080
10
9
  set :route_type, :sinatra
@@ -14,4 +13,8 @@ class Midori::Configure
14
13
  set :trusted_proxies, /\A127\.0\.0\.1\Z|\A(10|172\.(1[6-9]|2[0-9]|30|31)|192\.168)\.|
15
14
  \A::1\Z|\Afd[0-9a-f]{2}:.+|\Alocalhost\Z|\Aunix\Z|\Aunix:/ix
16
15
  set :tcp_fast_open, true
16
+ set :socket_reuse_port, false
17
+ set :keep_alive, true
18
+ set :keep_alive_timeout, 75
19
+ set :keep_alive_requests, 1000
17
20
  end
@@ -11,22 +11,18 @@ class Midori::Connection
11
11
  # @param [IO] socket raw socket
12
12
  def initialize(socket)
13
13
  @registered = false
14
- @socket = socket
15
- @monitor = nil
14
+ @socket = socket[0]
15
+ @peer_addr = socket[1].ip_unpack
16
16
  @close_flag = false
17
17
  @buffer = ''
18
- listen(socket)
19
18
  end
20
19
 
21
20
  # Register events of connection
22
- # @param [IO] socket raw socket
23
- def listen(socket)
24
- EventLoop.register(socket, :rw) do |monitor|
25
- @monitor = monitor
26
- if monitor.readable?
27
- receive_data(monitor)
28
- end
29
- if monitor.writable?
21
+ # @param [Array] socket raw socket
22
+ def listen
23
+ Fiber.schedule do
24
+ until @socket.closed?
25
+ receive_data(@socket)
30
26
  if !@buffer.empty?
31
27
  send_buffer
32
28
  elsif @close_flag
@@ -45,27 +41,23 @@ class Midori::Connection
45
41
  end
46
42
 
47
43
  # Send buffer immediately
44
+ # @return [nil] nil
48
45
  private def send_buffer
49
- if @monitor.writable?
50
- written = @socket.write_nonblock(@buffer)
51
- @buffer = @buffer.byteslice(written..-1)
52
- end
46
+ @socket.write(@buffer) unless @socket.closed?
53
47
  nil
54
- rescue IO::EAGAINWaitWritable => _e
55
- # :nocov:
56
- # Unknown Reason Resource Conflict
57
- nil
58
- # :nocov:
59
48
  end
60
49
 
61
50
  # Close the connection
51
+ # @return [nil] nil
62
52
  def close_connection
63
- EventLoop.deregister @socket
64
53
  @socket.close
54
+ nil
65
55
  end
66
56
 
67
57
  # Close the connection after writing
58
+ # @return [nil] nil
68
59
  def close_connection_after_writing
69
60
  @close_flag = true
61
+ nil
70
62
  end
71
63
  end
@@ -1,9 +1,16 @@
1
- class TCPServer
1
+ class Socket
2
2
  def tcp_fast_open
3
3
  # macOS devices option is DIFFERENT from Linux and FreeBSD
4
4
  opt = (/darwin/ =~ RUBY_PLATFORM) ? 1 : 5
5
5
  # Magic number 6 may refer to Socket::SOL_TCP or Socket::IPPROTO_TCP
6
- self.setsockopt(6, Socket::TCP_FASTOPEN, opt)
6
+ setsockopt(6, Socket::TCP_FASTOPEN, opt)
7
+ true
8
+ rescue => _e
9
+ false
10
+ end
11
+
12
+ def reuse_port
13
+ setsockopt(Socket::SOL_SOCKET, Socket::SO_REUSEPORT, 1)
7
14
  true
8
15
  rescue => _e
9
16
  false
@@ -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,17 @@ class Midori::Request
26
26
  @body_parsed = false
27
27
  @is_websocket = false
28
28
  @is_eventsource = false
29
- @parser = Http::Parser.new
29
+ @ip = ''
30
+ @port = 0
31
+ @parser = Mizu::Parser.new
30
32
  @params = {}
31
33
  @query_params = Hash.new(Array.new)
32
34
  @cookie = {}
33
35
  @body = ''
34
- @parser.on_headers_complete = proc do
35
- @protocol = @parser.http_version
36
- @method = @parser.http_method
37
- @path = @parser.request_url
36
+ @parser.on_complete do
37
+ @protocol = @parser.version
38
+ @method = @parser.method
39
+ @path = @parser.path
38
40
  # Turn header into case-insensitive due to RFC 2.6 Chapter 4.2
39
41
  # https://www.ietf.org/rfc/rfc2616.txt
40
42
  @parser.headers.each { |key, value| @header[key] = value }
@@ -50,10 +52,22 @@ class Midori::Request
50
52
  @path.gsub!(/\?(.*?)$/, '')
51
53
  @method = @method.to_sym
52
54
  @parsed = true
53
- :stop
54
55
  end
55
56
  end
56
57
 
58
+ def reset!
59
+ @header = HTTPHeader.new
60
+ @parsed = false
61
+ @body_parsed = false
62
+ @is_websocket = false
63
+ @is_eventsource = false
64
+ @parser.reset!
65
+ @params = {}
66
+ @query_params = Hash.new(Array.new)
67
+ @cookie = {}
68
+ @body = ''
69
+ end
70
+
57
71
  # Init an request with String data
58
72
  # @param [String] data
59
73
  # @return [nil] nil
@@ -62,8 +76,9 @@ class Midori::Request
62
76
  if @parsed
63
77
  @body += data
64
78
  else
65
- offset = @parser << data
66
- @body += data[offset..-1] if @parsed
79
+ return nil if data.nil?
80
+ @parser << data
81
+ @body += data[@parser.offset..-1] if @parsed
67
82
  end
68
83
 
69
84
  # 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
@@ -28,25 +28,36 @@ class Midori::Runner
28
28
  # Start the Midori server
29
29
  # @note This is an async method, but no callback
30
30
  def start
31
- return false if running? || EventLoop.running?
31
+ return false if running?
32
32
  @logger.info "Midori #{Midori::VERSION} is now running on #{bind}:#{port}".blue
33
- @server = TCPServer.new(@bind, @port)
34
- tfo = @server.tcp_fast_open if Midori::Configure.tcp_fast_open
35
- @logger.warn 'Failed to use TCP Fast Open feature on your OS'.yellow unless tfo
36
- async_fiber(Fiber.new do
33
+ init_socket
34
+ Fiber.schedule do
37
35
  @logger.info 'Midori is booting...'.blue
38
36
  @before.call
39
37
  @logger.info 'Midori is serving...'.blue
40
- EventLoop.register(@server, :r) do |monitor|
41
- socket = monitor.io.accept_nonblock
42
- connection = Midori::Connection.new(socket)
43
- connection.server_initialize(@api, @logger)
38
+ Fiber.schedule do
39
+ loop do
40
+ socket = @server.accept
41
+ connection = Midori::Connection.new(socket)
42
+ connection.server_initialize(@api, @logger)
43
+ connection.listen
44
+ end
44
45
  end
45
- end)
46
- EventLoop.start
46
+ end
47
47
  nil
48
48
  end
49
49
 
50
+ private def init_socket
51
+ @server = Socket.new Socket::AF_INET, Socket::SOCK_STREAM
52
+ @server.reuse_port if Midori::Configure.socket_reuse_port
53
+ @server.bind Addrinfo.tcp @bind, @port
54
+ @server.listen Socket::SOMAXCONN
55
+ if Midori::Configure.tcp_fast_open
56
+ tfo = @server.tcp_fast_open
57
+ @logger.warn 'Failed to use TCP Fast Open feature on your OS'.yellow unless tfo
58
+ end
59
+ end
60
+
50
61
  # Stop the Midori server
51
62
  # @note This is an async method, but no callback
52
63
  # @return [Boolean] [true] stop successfully
@@ -54,10 +65,8 @@ class Midori::Runner
54
65
  def stop
55
66
  if running?
56
67
  @logger.info 'Stopping Midori'.blue
57
- EventLoop.deregister @server
58
68
  @server.close
59
69
  @server = nil
60
- EventLoop.stop
61
70
  true
62
71
  else
63
72
  @logger.error 'Midori Server has NOT been started'.red
@@ -17,40 +17,43 @@ module Midori::Server
17
17
  def server_initialize(api, logger)
18
18
  @api = api
19
19
  @logger = logger
20
+
20
21
  @request = Midori::Request.new
21
22
  @websocket = Midori::WebSocket.new(self)
22
23
  @eventsource = Midori::EventSource.new(self)
24
+
25
+ # Add keep-alive parameters
26
+ @keep_alive_timer = nil
27
+ @keep_alive_count = 1
23
28
  end
24
29
 
25
30
  # Logic of receiving data
26
- # @param [String] monitor the socket able to read
27
- def receive_data(monitor)
28
- lambda do
29
- async_fiber(Fiber.new do
30
- begin
31
- _sock_domain, remote_port, _remote_hostname, remote_ip = monitor.io.peeraddr
32
- port, ip = remote_port, remote_ip
33
- @request.ip = ip
34
- @request.port = port
35
- data = monitor.io.read_nonblock(16_384)
36
- if @request.parsed? && @request.body_parsed?
37
- websocket_request(StringIO.new(data))
38
- else
39
- @request.parse(data)
40
- receive_new_request if @request.parsed && @request.body_parsed?
41
- end
42
- rescue EOFError, Errno::ENOTCONN => _e
43
- close_connection
44
- # Ignore client's disconnection
45
- rescue => e
46
- # :nocov:
47
- # Leave for corner cases
48
- close_connection
49
- @logger.warn "#{@request.ip} - - #{e.class} #{e.backtrace.join("\n")}".yellow
50
- # :nocov:
51
- end
52
- end)
53
- end.call
31
+ # @param [Scoket] the socket able to read
32
+ def receive_data(socket)
33
+ begin
34
+ @request.ip, @request.port = @peer_addr
35
+ if @request.parsed? && !(@request.body_parsed?)
36
+ data = socket.read @request.body.bytesize - @request.header['Content-Length']
37
+ else
38
+ data = socket.readline
39
+ end
40
+
41
+ if @request.websocket?
42
+ websocket_request(StringIO.new(data))
43
+ else
44
+ @request.parse(data)
45
+ receive_new_request if @request.parsed? && @request.body_parsed?
46
+ end
47
+ rescue EOFError, Errno::ENOTCONN => _e
48
+ close_connection
49
+ # Ignore client's disconnection
50
+ rescue => e
51
+ # :nocov:
52
+ # Leave for corner cases
53
+ close_connection
54
+ @logger.warn "#{@request.ip} - - #{e.class} #{e.backtrace.join("\n")}".yellow
55
+ # :nocov:
56
+ end
54
57
  end
55
58
 
56
59
  # Logic of receiving new request
@@ -59,7 +62,7 @@ module Midori::Server
59
62
  start_time = Time.now
60
63
  @response = @api.receive(request, self)
61
64
  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
65
+ @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
66
  call_event(:open) if @request.websocket?
64
67
  rescue Midori::Exception::NotFound => e
65
68
  @response = Midori::Sandbox.capture(e)
@@ -68,9 +71,10 @@ module Midori::Server
68
71
  @logger.error e.inspect.red
69
72
  @logger.warn e.backtrace.join("\n").yellow
70
73
  end
74
+
71
75
  unless @request.websocket? || @request.eventsource?
72
76
  send_data @response
73
- close_connection_after_writing
77
+ proceed_keep_alive
74
78
  end
75
79
  end
76
80
 
@@ -109,4 +113,20 @@ module Midori::Server
109
113
  def call_event(event, args = [])
110
114
  -> { @websocket.instance_exec(*args, &@websocket.events[event]) }.call unless @websocket.events[event].nil?
111
115
  end
116
+
117
+ private def proceed_keep_alive
118
+ # Detect if it should close connection
119
+ if !Midori::Configure.keep_alive || (@keep_alive_count >= Midori::Configure.keep_alive_requests)
120
+ close_connection_after_writing
121
+ return
122
+ end
123
+ # Add timeout for keep-alive
124
+ @keep_alive_count += 1
125
+ Fiber.schedule do
126
+ sleep Midori::Configure.keep_alive_timeout
127
+ close_connection
128
+ end
129
+ # Reset request
130
+ @request.reset!
131
+ end
112
132
  end
@@ -1,5 +1,5 @@
1
1
  # Midori Module
2
2
  module Midori
3
3
  # Current Version Code
4
- VERSION = '0.5.4'.freeze
4
+ VERSION = '0.9.0'.freeze
5
5
  end
@@ -15,40 +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
- @msg = self.mask(masked_msg, mask)
46
- @msg = @msg.pack('C*').force_encoding('utf-8') if [0x1, 0x9, 0xA].include? opcode
47
- # For debug
48
- # data.rewind
49
- # data.bytes {|byte| puts byte.to_s(16)}
50
- end
51
-
52
18
  # API definition for events
53
19
  # @param [Symbol] event event name(open, message, close, ping, pong)
54
20
  # @yield what to do after event matched
@@ -66,11 +32,27 @@ class Midori::WebSocket
66
32
  # @param [Array<Integer>, String] msg data to send
67
33
  def send(msg)
68
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
+
69
48
  if msg.is_a?String
70
- output << 0b10000001 << msg.size << msg
71
- @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*'))
72
53
  elsif msg.is_a? Array
73
- output << 0b10000010 << msg.size
54
+ output << 0b10000010
55
+ output.concat payload_length
74
56
  output.concat msg
75
57
  @connection.send_data(output.pack('C*'))
76
58
  else
metadata CHANGED
@@ -1,29 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: midori.rb
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.4
4
+ version: 0.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - HeckPsi Lab
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-06-28 00:00:00.000000000 Z
11
+ date: 2020-12-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: murasaki
14
+ name: evt
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: 0.2.1
19
+ version: 0.3.0
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: 0.2.1
26
+ version: 0.3.0
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: mustermann
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -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
@@ -78,6 +78,8 @@ files:
78
78
  - ".editorconfig"
79
79
  - ".github/ISSUE_TEMPLATE.md"
80
80
  - LICENSE
81
+ - example.rb
82
+ - example2.rb
81
83
  - ext/midori/extconf.rb
82
84
  - ext/midori/websocket.c
83
85
  - lib/midori.rb
@@ -91,8 +93,8 @@ files:
91
93
  - lib/midori/core_ext/define_class.rb
92
94
  - lib/midori/core_ext/http_header.rb
93
95
  - lib/midori/core_ext/proc.rb
96
+ - lib/midori/core_ext/socket.rb
94
97
  - lib/midori/core_ext/string.rb
95
- - lib/midori/core_ext/tcp_server.rb
96
98
  - lib/midori/env.rb
97
99
  - lib/midori/eventsource.rb
98
100
  - lib/midori/exception.rb
@@ -125,16 +127,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
125
127
  requirements:
126
128
  - - ">="
127
129
  - !ruby/object:Gem::Version
128
- version: 2.3.6
130
+ version: 3.0.0
129
131
  required_rubygems_version: !ruby/object:Gem::Requirement
130
132
  requirements:
131
133
  - - ">="
132
134
  - !ruby/object:Gem::Version
133
135
  version: '0'
134
136
  requirements: []
135
- rubyforge_project:
136
- rubygems_version: 2.7.6
137
- signing_key:
137
+ rubygems_version: 3.2.2
138
+ signing_key:
138
139
  specification_version: 4
139
140
  summary: High performance ruby web framework
140
141
  test_files: []