em-midori 0.1.8 → 0.1.9

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