em-midori 0.1.8 → 0.1.9

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
  SHA1:
3
- metadata.gz: bded68c0e7831eb9e36ee2b49dec6c61c760a815
4
- data.tar.gz: de5f760feffc996aa88443e3db67eab589d179d8
3
+ metadata.gz: 44777e7d5978bba6e8fd91c8cc640ef40270a7f3
4
+ data.tar.gz: dd69a2679cf440bc4ea7a29f52b6e0652d917b7c
5
5
  SHA512:
6
- metadata.gz: 6ffe68ce957c773adf24da6a5e0c4c94deca32d0a617e29ee9ac10eacf4883e5e1d6cf0c94b0698fa30e9bde26599f4425bd081b38d945d0baa6b97a3d4a620e
7
- data.tar.gz: 220b8c5f8ec1af61be67526d24975ccd1387384afd41c7014f7176a4bf2468c4fd061475eb147fd9bc8655e5f06b5b2677f382774bc3d8d1f954ff4b98a8b5b8
6
+ metadata.gz: f9716b1feb7f5f7609eae2c662bff23b1bc4900024f02124faf4b4200803a9e91dec19f851d2a68a5504c0ae4bb2a731aa0fd35c88401b0fa59adebb8597e73e
7
+ data.tar.gz: ddbb5d354658e519d9a8c4c29b134e24e28e1fa2e0ff2d2b35878f08fc9047b7510fade34e331699b827115359869e65acf6e5569d2010ee75a0a7d21ee840c7
@@ -1,6 +1,6 @@
1
1
  require 'digest/sha1'
2
2
  require 'stringio'
3
- require 'eventmachine'
3
+ require 'nio'
4
4
  require 'fiber'
5
5
  require 'logger'
6
6
  require 'http/parser'
@@ -9,6 +9,7 @@ require 'socket'
9
9
 
10
10
  require_relative 'midori/core_ext/configurable'
11
11
  require_relative 'midori/core_ext/string'
12
+ require_relative 'midori/core_ext/event_loop'
12
13
  require_relative 'midori/core_ext/promise'
13
14
  require_relative 'midori/core_ext/promise_exception'
14
15
  require_relative 'midori/core_ext/define_class'
@@ -19,13 +20,14 @@ require_relative 'midori/version'
19
20
  require_relative 'midori/const'
20
21
  require_relative 'midori/exception'
21
22
  require_relative 'midori/clean_room'
23
+ require_relative 'midori/server'
24
+ require_relative 'midori/connection'
22
25
  require_relative 'midori/request'
23
26
  require_relative 'midori/response'
24
27
  require_relative 'midori/api'
25
28
  require_relative 'midori/api_engine'
26
29
  require_relative 'midori/route'
27
30
  require_relative 'midori/sandbox'
28
- require_relative 'midori/server'
29
31
  require_relative 'midori/websocket'
30
32
  require_relative 'midori/eventsource'
31
33
  require_relative 'midori/middleware'
@@ -0,0 +1,52 @@
1
+ class Midori::Connection
2
+ include Midori::Server
3
+
4
+ attr_accessor :data
5
+
6
+ def initialize(socket)
7
+ @registered = false
8
+ @socket = socket
9
+ @monitor = nil
10
+ @close_flag = false
11
+ @data = ''
12
+ listen(socket)
13
+ end
14
+
15
+ def listen(socket)
16
+ EventLoop.register(socket, :rw) do |monitor|
17
+ @monitor = monitor
18
+ if monitor.readable?
19
+ receive_data(monitor)
20
+ end
21
+ if monitor.writable?
22
+ if !@data.empty?
23
+ monitor.io.write_nonblock(@data)
24
+ @data = ''
25
+ elsif @close_flag
26
+ close_connection
27
+ end
28
+ end
29
+ end
30
+ end
31
+
32
+ def send_data(data)
33
+ if @monitor.writable?
34
+ @socket.write_nonblock(data)
35
+ else
36
+ @data << data
37
+ end
38
+ end
39
+
40
+ def close_connection
41
+ begin
42
+ EventLoop.unregister @socket
43
+ @socket.close
44
+ rescue => e
45
+ puts e
46
+ end
47
+ end
48
+
49
+ def close_connection_after_writing
50
+ @close_flag = true
51
+ end
52
+ end
@@ -0,0 +1,38 @@
1
+ module EventLoop
2
+ class << self
3
+ SELECTOR = NIO::Selector.new
4
+
5
+ def register(io, interest=(:rw), &callback)
6
+ monitor = SELECTOR.register(io, interest)
7
+ monitor.value = callback
8
+ end
9
+
10
+ def unregister(io)
11
+ SELECTOR.deregister(io)
12
+ end
13
+
14
+ def run_once
15
+ SELECTOR.select(1) do |monitor| # Timeout for 1 secs
16
+ monitor.value.call(monitor)
17
+ end
18
+ end
19
+
20
+ def start
21
+ return if running?
22
+ @stop = false
23
+ until @stop
24
+ run_once
25
+ end
26
+ @stop = nil
27
+ end
28
+
29
+ def stop
30
+ @stop = true
31
+ end
32
+
33
+ def running?
34
+ @stop = true if @stop.nil?
35
+ !@stop
36
+ end
37
+ end
38
+ end
@@ -4,67 +4,40 @@
4
4
  class Promise
5
5
  # Init a Promise
6
6
  # @param [Proc] callback an async method
7
- def initialize(callback)
7
+ def initialize(&callback)
8
8
  @callback = callback
9
9
  end
10
10
 
11
11
  # Define what to do after a method callbacks
12
12
  # @param [Proc] resolve what on callback
13
- # @param [Proc] reject what on callback failed
14
13
  # @return [nil] nil
15
- def then(resolve = ->() {}, reject = ->() {})
16
- @callback.call(resolve, reject)
17
- end
18
- end
19
-
20
- # A Syntactic Sugar for Promise
21
- class DeferPromise < Promise
22
- # A Syntactic Sugar for Promise
23
- # @param [Proc] deffered To do what asynchronous
24
- def initialize(deffered)
25
- super(->(resolve, _reject){
26
- EventMachine.defer(proc {
27
- begin
28
- deffered.call
29
- rescue StandardError => e
30
- PromiseException.new(e)
31
- end
32
- }, proc { |result| resolve.call(result) })
33
- })
14
+ def then(&resolve)
15
+ @callback.call(resolve)
34
16
  end
35
17
  end
36
18
 
37
19
  module Kernel
38
- # Logic dealing of async method
39
- # @param [Fiber] fiber a fiber to call
40
- def async_internal(fiber)
41
- chain = lambda do |result|
42
- return unless result.is_a?Promise
43
- result.then(lambda do |val|
20
+ def async_fiber(fiber)
21
+ chain = proc do |result|
22
+ next unless result.is_a? Promise
23
+ result.then do |val|
44
24
  chain.call(fiber.resume(val))
45
- end)
25
+ end
46
26
  end
47
27
  chain.call(fiber.resume)
48
28
  end
49
29
 
50
30
  # Define an async method
51
- # @param [Symbol] method_name method name
31
+ # @param [Symbol] method method name
52
32
  # @yield async method
53
33
  # @example
54
34
  # async :hello do
55
35
  # puts 'Hello'
56
36
  # end
57
- def async(method_name)
58
- define_singleton_method method_name, ->(*args) {
59
- async_internal(Fiber.new { yield(*args) })
60
- }
61
- end
62
-
63
- # Shortcut to init [DeferPromise]
64
- # @yield To do what asynchronous
65
- # @return [DerferPromise] instance
66
- def defer(&block)
67
- DeferPromise.new(block)
37
+ def async(method)
38
+ define_singleton_method method do |*args|
39
+ async_fiber(Fiber.new {yield(*args)})
40
+ end
68
41
  end
69
42
 
70
43
  # Block the I/O to wait for async method response
@@ -1,11 +1,11 @@
1
1
  ##
2
2
  # A special error as containers of errors inside [Promise]
3
- # @attr [StandardError] raw_exception raw execption raised
3
+ # @attr [StandardError] raw_exception raw exception raised
4
4
  class PromiseException < StandardError
5
5
  attr_reader :raw_exception
6
6
 
7
7
  # Init PromiseException
8
- # @param [StandardError] raw_exception raw execption raised
8
+ # @param [StandardError] raw_exception raw exception raised
9
9
  def initialize(raw_exception)
10
10
  super(nil)
11
11
  @raw_exception = raw_exception
@@ -16,7 +16,7 @@ class String
16
16
  def green
17
17
  colorize(32)
18
18
  end
19
-
19
+
20
20
  # color the string with yellow color
21
21
  def yellow
22
22
  colorize(33)
File without changes
@@ -12,7 +12,6 @@ class Midori::Runner
12
12
  @logger = configure.logger
13
13
  @bind = configure.bind
14
14
  @port = configure.port
15
- @before = configure.before
16
15
  @api = ((api.is_a?Midori::APIEngine) ? api : Midori::APIEngine.new(api, configure.route_type))
17
16
  end
18
17
 
@@ -20,24 +19,21 @@ class Midori::Runner
20
19
  # @return [Boolean] [true] running
21
20
  # @return [Boolean] [false] not running
22
21
  def running?
23
- !!@server_signature
22
+ !!@server
24
23
  end
25
24
 
26
25
  # Start the Midori server
27
26
  # @note This is an async method, but no callback
28
27
  def start
29
- return if running?
30
-
31
- EventMachine.set_simultaneous_accept_count(40) unless RUBY_PLATFORM == 'java'
32
- EventMachine.run do
33
- async :before do
34
- @before.call
35
- end
36
- before
37
- @logger.info "Midori #{Midori::VERSION} is now running on #{bind}:#{port}".blue
38
- @server_signature = EventMachine.start_server bind, port, Midori::Server, @api, @logger
28
+ return false if running? || EventLoop.running?
29
+ @logger.info "Midori #{Midori::VERSION} is now running on #{bind}:#{port}".blue
30
+ @server = TCPServer.new(@bind, @port)
31
+ EventLoop.register(@server, :r) do |monitor|
32
+ socket = monitor.io.accept_nonblock
33
+ connection = Midori::Connection.new(socket)
34
+ connection.server_initialize(@api, @logger)
39
35
  end
40
-
36
+ EventLoop.start
41
37
  nil
42
38
  end
43
39
 
@@ -47,10 +43,11 @@ class Midori::Runner
47
43
  # @return [Boolean] [false] nothing to stop
48
44
  def stop
49
45
  if running?
50
- @logger.info 'Goodbye Midori'.blue
51
- EventMachine.stop_server(@server_signature)
52
- @server_signature = nil
53
- EM.stop
46
+ @logger.info 'Stopping Midori'.blue
47
+ EventLoop.unregister @server
48
+ @server.close
49
+ @server = nil
50
+ EventLoop.stop
54
51
  true
55
52
  else
56
53
  @logger.error 'Midori Server has NOT been started'.red
@@ -1,5 +1,5 @@
1
1
  ##
2
- # Logic to EventMachine TCP Server, running inside +EM::Connection+
2
+ # Logic to EventMachine TCP Server, running inside +Midori::Connection+
3
3
  module Midori::Server
4
4
  # @!attribute request
5
5
  # @return [Midori::Request] raw request
@@ -14,7 +14,7 @@ module Midori::Server
14
14
  # Define server behaviour
15
15
  # @param [Class] api inherited from Midori::API
16
16
  # @param [Logger] logger global logger
17
- def initialize(api, logger)
17
+ def server_initialize(api, logger)
18
18
  @api = api
19
19
  @logger = logger
20
20
  @request = Midori::Request.new
@@ -23,22 +23,29 @@ module Midori::Server
23
23
  end
24
24
 
25
25
  # Logic of receiving data
26
- # @param [String] data raw data
27
- def receive_data(data)
26
+ # @param [String] monitor the socket able to read
27
+ def receive_data(monitor)
28
28
  lambda do
29
- async_internal(Fiber.new do
30
- start_time = Time.now
31
- port, ip = Socket.unpack_sockaddr_in(get_peername)
32
- @request.ip = ip
33
- @request.port = port
34
- if @request.parsed?
35
- websocket_request(StringIO.new(data))
36
- else
37
- receive_new_request(data)
38
- end
39
- now_time = Time.now
40
- @logger.info "#{@request.ip} - - \"#{@request.method} #{@request.path} HTTP/#{@request.protocol.join('.')}\" #{@response.status} #{(now_time.to_f - start_time.to_f).round(6)}".green
41
- end)
29
+ async_fiber(Fiber.new do
30
+ begin
31
+ start_time = Time.now
32
+ _sock_domain, remote_port, _remote_hostname, remote_ip = monitor.io.peeraddr
33
+ port, ip = remote_port, remote_ip
34
+ @request.ip = ip
35
+ @request.port = port
36
+ data = monitor.io.read_nonblock(16_384)
37
+ if @request.parsed?
38
+ websocket_request(StringIO.new(data))
39
+ else
40
+ receive_new_request(data)
41
+ end
42
+ now_time = Time.now
43
+ @logger.info "#{@request.ip} - - \"#{@request.method} #{@request.path} HTTP/#{@request.protocol.join('.')}\" #{@response.status} #{(now_time.to_f - start_time.to_f).round(6)}".green
44
+ rescue
45
+ close_connection
46
+ @logger.error "#{@request.ip} - - Reached an EOF Error".red
47
+ end
48
+ end)
42
49
  end.call
43
50
  end
44
51
 
@@ -1,5 +1,5 @@
1
1
  # Midori Module
2
2
  module Midori
3
3
  # Current Version Code
4
- VERSION = '0.1.8'.freeze
4
+ VERSION = '0.1.9'.freeze
5
5
  end
metadata CHANGED
@@ -1,29 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: em-midori
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.8
4
+ version: 0.1.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - HeckPsi Lab
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-01-17 00:00:00.000000000 Z
11
+ date: 2017-02-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: eventmachine
14
+ name: nio4r
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '1.2'
19
+ version: '2.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: '1.2'
26
+ version: '2.0'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: mustermann
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -60,16 +60,16 @@ extensions: []
60
60
  extra_rdoc_files: []
61
61
  files:
62
62
  - LICENSE
63
- - lib/em-midori
64
- - lib/em-midori.rb
65
63
  - lib/midori.rb
66
64
  - lib/midori/api.rb
67
65
  - lib/midori/api_engine.rb
68
66
  - lib/midori/clean_room.rb
69
67
  - lib/midori/configure.rb
68
+ - lib/midori/connection.rb
70
69
  - lib/midori/const.rb
71
70
  - lib/midori/core_ext/configurable.rb
72
71
  - lib/midori/core_ext/define_class.rb
72
+ - lib/midori/core_ext/event_loop.rb
73
73
  - lib/midori/core_ext/proc.rb
74
74
  - lib/midori/core_ext/promise.rb
75
75
  - lib/midori/core_ext/promise_exception.rb
@@ -77,11 +77,7 @@ files:
77
77
  - lib/midori/core_ext/string.rb
78
78
  - lib/midori/eventsource.rb
79
79
  - lib/midori/exception.rb
80
- - lib/midori/extension/file.rb
81
- - lib/midori/extension/ohm.rb
82
- - lib/midori/extension/postgres.rb
83
- - lib/midori/extension/redis.rb
84
- - lib/midori/extension/sequel.rb
80
+ - lib/midori/extension/.gitkeep
85
81
  - lib/midori/middleware.rb
86
82
  - lib/midori/request.rb
87
83
  - lib/midori/response.rb
@@ -127,7 +123,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
127
123
  requirements:
128
124
  - - ">="
129
125
  - !ruby/object:Gem::Version
130
- version: 2.1.0
126
+ version: 2.2.6
131
127
  required_rubygems_version: !ruby/object:Gem::Requirement
132
128
  requirements:
133
129
  - - ">="
@@ -1 +0,0 @@
1
- lib/midori
@@ -1 +0,0 @@
1
- require 'midori'
@@ -1,17 +0,0 @@
1
- ##
2
- # Midori Extension of File reading and writing
3
- class Midori::File
4
- class << self
5
- # Same APIs as File.read
6
- # @param [Array] args args of File.read
7
- def read(*args)
8
- await(defer{File.read(*args)})
9
- end
10
-
11
- # Same APIs as File.write
12
- # @param [Array] args args of File.write
13
- def write(*args)
14
- await(defer{File.write(*args)})
15
- end
16
- end
17
- end
@@ -1,16 +0,0 @@
1
- safe_require 'ohm', 'gem install ohm'
2
-
3
- ##
4
- # Midori Extension of ohm through meta programming Redic
5
- class Redic
6
- # Call a redis request asynchronously
7
- # @param [Array] args args of Redic.call
8
- def call(*args)
9
- await(defer{
10
- @client.connect do
11
- @client.write(args)
12
- @client.read
13
- end
14
- })
15
- end
16
- end
@@ -1,59 +0,0 @@
1
- safe_require 'postgres-pr/message', 'gem install postgres-pr'
2
-
3
- ##
4
- # Midori Extension for Postgres Driver
5
- # @attr [Integer] connected Connection Status
6
- class Midori::Postgres
7
- attr_reader :connected
8
-
9
- # Init a Postgres Connection
10
- # @param [Array] args args of EM.connect
11
- def initialize(*args)
12
- @connected = false
13
- @db = EM.connect(*args, EM::P::Postgres3)
14
- end
15
-
16
- # Connect the Postgres server
17
- # @param [String] db_name database name
18
- # @param [String] username username
19
- # @param [password] password password
20
- # @return [nil] nil
21
- def connect(db_name, username, password)
22
- await(Promise.new(->(resolve, _reject) {
23
- @db.connect(db_name, username, password).callback do |status|
24
- @connected = status
25
- resolve.call(status)
26
- end
27
- }))
28
- end
29
-
30
- # Make SQL query
31
- # @param [String] sql sql query
32
- # @return [Midori::Postgres::Result] query result
33
- def query(sql)
34
- await(Promise.new(->(resolve, _reject) {
35
- begin
36
- @db.query(sql).callback do |status, result, errors|
37
- @connected = status
38
- resolve.call(Midori::Postgres::Result.new(result, errors))
39
- end
40
- end
41
- }))
42
- end
43
- end
44
-
45
- ##
46
- # Postgres Result for Midori Postgres Driver Extension
47
- # @attr [Array] result result if success
48
- # @attr [Array] errors exceptions met
49
- class Midori::Postgres::Result
50
- attr_reader :result, :errors
51
-
52
- # Init a Postgres Result
53
- # @param [Array] result result if success
54
- # @param [Array] errors exceptions met
55
- def initialize(result, errors)
56
- @result = result
57
- @errors = errors
58
- end
59
- end
@@ -1,30 +0,0 @@
1
- safe_require 'em-hiredis', 'gem install em-hiredis'
2
-
3
- ##
4
- # Midori Extension for Redis Driver
5
- class Midori::Redis
6
-
7
- # Init a Redis Connection
8
- # @param [Array] args args EM::Hiredis.connect
9
- def initialize(*args)
10
- @connection = EM::Hiredis.connect(*args)
11
- @connection
12
- end
13
-
14
- # Call a redis request asynchronously
15
- # @param [String] sys method name
16
- # @param [Array] args args of the method calling
17
- def method_missing(sys, *args)
18
- await(Promise.new(->(resolve, _reject) {
19
- @connection.send(sys, *args).callback do |*ret_args|
20
- resolve.call(*ret_args)
21
- end
22
- }))
23
- end
24
-
25
- # Return raw pubsub mode
26
- # @return [EM::Hiredis::Pubsub] raw pubsub
27
- def pubsub
28
- @connection.pubsub
29
- end
30
- end
@@ -1,16 +0,0 @@
1
- safe_require 'sequel', 'gem install sequel'
2
- require 'sequel/adapters/postgres'
3
-
4
- ##
5
- # Midori Extension of sequel postgres through meta programming
6
- class Sequel::Postgres::Adapter
7
- # Call a sql request asynchronously
8
- # @param [String] sql sql request
9
- # @param [Array] args args to send
10
- # @return [Array] sql query result
11
- def execute_query(sql, args)
12
- @db.log_connection_yield(sql, self, args) do
13
- args ? await(defer{async_exec(sql, args)}) : await(defer{async_exec(sql)})
14
- end
15
- end
16
- end