midori.rb 0.5.4 → 0.9.0

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