falcon 0.13.1 → 0.14.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: 6f728c3a72089d8f91c140943d4699efd7d6c3ad8e051d98d39c09afb0b419ec
4
- data.tar.gz: 0fa4c83bf6f5758856b2d0a85c900bfc0137e0836b27cd2f5746910106e03d19
3
+ metadata.gz: a9163a32adea87fe689f8a9b00a1b31bfde1a01686c74456b3051975d5be8bf7
4
+ data.tar.gz: d623a98e678f74dc3e077efd42f5857c4b63d1840b3489be5438727f6d4120f6
5
5
  SHA512:
6
- metadata.gz: 5424db201eeaae4b30d96e2f665c196e71cc110dd824532e6de38f082242b9d60c97fb92a9e764449b01af3b78e79627a6608cfc798ba1c83970d5b067a7492c
7
- data.tar.gz: 1c894b71e6aca8e280b8031dc0ca4b8609f30a05724264aaba64cee7e1a819a92d8775fa145900b63304dd2b14d1386fb003773445cd3f2aae68933a33c110bb
6
+ metadata.gz: 19340fbf6cd77a877900a70efaeb9c7847570e327e4bf732c2d05e4a05b83d2e1f34da906d82a2668c1c8fbde388da058b3d4f764af9ac6d41814e390b37d320
7
+ data.tar.gz: 624adfe16a0ce38e41fc9eb837039774e496689ce9de19b594fa372c0b99b467c6d73b505249e12c9515f03dbeda298bef46aff456c0ad04440811aab9b54fe9
data/Gemfile CHANGED
@@ -1,8 +1,11 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
- # Specify your gem's dependencies in async-io.gemspec
4
3
  gemspec
5
4
 
5
+ group :development do
6
+ gem 'ruby-prof'
7
+ end
8
+
6
9
  group :test do
7
10
  gem 'simplecov'
8
11
  gem 'coveralls', require: false
@@ -16,9 +16,9 @@ Gem::Specification.new do |spec|
16
16
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
17
  spec.require_paths = ["lib"]
18
18
 
19
- spec.add_dependency("async-io", "~> 1.6")
20
- spec.add_dependency("async-http", "~> 0.19.0")
21
- spec.add_dependency("async-container", "~> 0.4.0")
19
+ spec.add_dependency("async-io", "~> 1.9")
20
+ spec.add_dependency("async-http", "~> 0.21.0")
21
+ spec.add_dependency("async-container", "~> 0.5.0")
22
22
 
23
23
  spec.add_dependency("rack", ">= 1.0")
24
24
 
@@ -20,3 +20,6 @@
20
20
 
21
21
  require_relative "falcon/command"
22
22
  require_relative "falcon/server"
23
+
24
+ require_relative 'falcon/hosts'
25
+ require_relative 'falcon/proxy'
@@ -24,6 +24,8 @@ require_relative '../adapters/rack'
24
24
 
25
25
  require 'async/container'
26
26
  require 'async/io/trap'
27
+ require 'async/io/host_endpoint'
28
+ require 'async/io/shared_endpoint'
27
29
 
28
30
  require 'samovar'
29
31
 
@@ -73,7 +75,13 @@ module Falcon
73
75
  def run(verbose)
74
76
  app, options = load_app(verbose)
75
77
 
76
- endpoint = Async::IO::Endpoint.parse(@options[:bind], reuse_port: true)
78
+ endpoint = nil
79
+
80
+ Async::Reactor.run do
81
+ endpoint = Async::IO::SharedEndpoint.bound(
82
+ Async::IO::Endpoint.parse(@options[:bind], reuse_port: true)
83
+ )
84
+ end
77
85
 
78
86
  Async.logger.info "Falcon taking flight! Binding to #{endpoint} [#{container_class} with concurrency: #{@options[:concurrency]}]"
79
87
 
@@ -0,0 +1,124 @@
1
+ # Copyright, 2018, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in
11
+ # all copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ # THE SOFTWARE.
20
+
21
+ require 'async/io/endpoint'
22
+
23
+ module Falcon
24
+ class Host
25
+ def initialize
26
+ @app = nil
27
+ @endpoint = nil
28
+ @ssl_certificate = nil
29
+ @ssl_key = nil
30
+ end
31
+
32
+ attr_accessor :app
33
+
34
+ attr_accessor :endpoint
35
+
36
+ attr_accessor :ssl_certificate
37
+ attr_accessor :ssl_key
38
+
39
+ def ssl_certificate_path= path
40
+ @ssl_certificate = OpenSSL::X509::Certificate.new(File.read(path))
41
+ end
42
+
43
+ def ssl_key_path= path
44
+ @ssl_key = OpenSSL::PKey::RSA.new(File.read(path))
45
+ end
46
+
47
+ def ssl_context
48
+ if @ssl_key
49
+ OpenSSL::SSL::SSLContext.new.tap do |context|
50
+ context.cert = @ssl_certificate
51
+ context.key = @ssl_key
52
+
53
+ context.set_params
54
+ end
55
+ end
56
+ end
57
+
58
+ def start
59
+ if app = self.app
60
+ Async::Container::Forked.new do
61
+ server = Falcon::Server.new(app, self.server_endpoint)
62
+
63
+ server.run
64
+ end
65
+ end
66
+ end
67
+ end
68
+
69
+ class Hosts
70
+ DEFAULT_ALPN_PROTOCOLS = ['h2', 'http/1.1'].freeze
71
+
72
+ def initialize
73
+ @named = {}
74
+ @server_context = nil
75
+ @server_endpoint = nil
76
+ end
77
+
78
+ def each(&block)
79
+ @named.each(&block)
80
+ end
81
+
82
+ def endpoint
83
+ @server_endpoint ||= Async::HTTP::URLEndpoint.parse(
84
+ 'https://0.0.0.0',
85
+ ssl_context: self.ssl_context,
86
+ reuse_address: true
87
+ )
88
+ end
89
+
90
+ def ssl_context
91
+ @server_context ||= OpenSSL::SSL::SSLContext.new.tap do |context|
92
+ context.servername_cb = Proc.new do |socket, hostname|
93
+ self.host_context(socket, hostname)
94
+ end
95
+
96
+ context.alpn_protocols = DEFAULT_ALPN_PROTOCOLS
97
+
98
+ context.set_params
99
+ end
100
+ end
101
+
102
+ def host_context(socket, hostname)
103
+ if host = @named[hostname]
104
+ socket.hostname = hostname
105
+
106
+ return host.ssl_context
107
+ end
108
+ end
109
+
110
+ def add(name, host = Host.new, &block)
111
+ host = Host.new
112
+
113
+ yield host if block_given?
114
+
115
+ @named[name] = host.freeze
116
+ end
117
+
118
+ def client_endpoints
119
+ Hash[
120
+ @named.collect{|name, host| [name, host.endpoint]}
121
+ ]
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,76 @@
1
+ # Copyright, 2018, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in
11
+ # all copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ # THE SOFTWARE.
20
+
21
+ require 'async/http/client'
22
+
23
+ module Falcon
24
+ module BadRequest
25
+ def self.call(request, *)
26
+ return Async::HTTP::Response[400, {}, []]
27
+ end
28
+
29
+ def self.close
30
+ end
31
+ end
32
+
33
+ class Proxy < Async::HTTP::Middleware
34
+ def initialize(app, hosts)
35
+ super(app)
36
+
37
+ @server_context = nil
38
+
39
+ @hosts = hosts
40
+ @clients = {}
41
+
42
+ @count = 0
43
+ end
44
+
45
+ attr :count
46
+
47
+ def close
48
+ @clients.each_value(&:close)
49
+
50
+ super
51
+ end
52
+
53
+ def connect(endpoint)
54
+ @clients[endpoint] ||= Async::HTTP::Client.new(endpoint)
55
+ end
56
+
57
+ def lookup(request)
58
+ # Trailing dot and port is ignored/normalized.
59
+ if authority = request.authority.sub(/(\.)?(:\d+)?$/, '')
60
+ return @hosts[authority]
61
+ end
62
+ end
63
+
64
+ def call(request, *)
65
+ if endpoint = lookup(request)
66
+ @count += 1
67
+
68
+ client = connect(endpoint)
69
+
70
+ client.call(request)
71
+ else
72
+ super
73
+ end
74
+ end
75
+ end
76
+ end
@@ -32,8 +32,8 @@ module Falcon
32
32
  def annotate(request, peer: nil, address: nil)
33
33
  task = Async::Task.current
34
34
 
35
- # @logger.debug "#{request.method} #{request.path} #{request.version} from #{address.inspect}"
36
- # @logger.debug request.headers.inspect
35
+ @logger.debug(request.authority) {"#{request.method} #{request.path} #{request.version} from #{address.inspect}"}
36
+ @logger.debug(request.authority) {request.headers.inspect}
37
37
 
38
38
  task.annotate("#{request.method} #{request.path} from #{address.inspect}")
39
39
  end
@@ -46,9 +46,9 @@ module Falcon
46
46
  response = super
47
47
 
48
48
  statistics.wrap(response) do |statistics, error|
49
- @logger.info "#{request.method} #{request.path} #{request.version} -> #{response.status}; #{statistics.inspect}"
49
+ @logger.info(request.authority) {"#{request.method} #{request.path} #{request.version} -> #{response.status}; #{statistics.inspect}"}
50
50
  # @logger.info response.headers.inspect
51
- @logger.error "#{error.class}: #{error.message}" if error
51
+ @logger.error(request.authority) {"#{error.class}: #{error.message}"} if error
52
52
  end
53
53
 
54
54
  return response
@@ -19,5 +19,5 @@
19
19
  # THE SOFTWARE.
20
20
 
21
21
  module Falcon
22
- VERSION = "0.13.1"
22
+ VERSION = "0.14.0"
23
23
  end
@@ -3,6 +3,8 @@ require 'rack/handler'
3
3
 
4
4
  require_relative '../../falcon'
5
5
 
6
+ require 'async/io/host_endpoint'
7
+
6
8
  module Rack
7
9
  module Handler
8
10
  module Falcon
@@ -0,0 +1,93 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $LOAD_PATH << File.expand_path('lib', __dir__)
4
+
5
+ require 'falcon'
6
+ require 'async/http/url_endpoint'
7
+ require 'async/container/controller'
8
+ require 'async/container/forked'
9
+
10
+ require 'async/clock'
11
+
12
+ Async.logger.level = Logger::INFO
13
+
14
+ hosts = Falcon::Hosts.new
15
+
16
+ hosts.add('map.local') do |host|
17
+ host.endpoint = Async::HTTP::URLEndpoint.parse('http://hana.local:8123')
18
+
19
+ # host.ssl_certificate_path = '/etc/letsencrypt/live/mc.oriontransfer.co.nz/fullchain.pem'
20
+ # host.ssl_key_path = '/etc/letsencrypt/live/mc.oriontransfer.co.nz/privkey.pem'
21
+ end
22
+
23
+ hosts.add('chick.local') do |host|
24
+ host.endpoint = Async::HTTP::URLEndpoint.parse('http://hana.local:8765')
25
+
26
+ # host.ssl_certificate_path = '/etc/letsencrypt/live/chick.nz/fullchain.pem'
27
+ # host.ssl_key_path = '/etc/letsencrypt/live/chick.nz/privkey.pem'
28
+ end
29
+
30
+ controller = Async::Container::Controller.new
31
+
32
+ hosts.each do |name, host|
33
+ if container = host.start
34
+ controller << container
35
+ end
36
+ end
37
+
38
+ #proxy = Falcon::Verbose.new(
39
+ proxy = Falcon::Proxy.new(Falcon::BadRequest, hosts.client_endpoints)
40
+ #)
41
+
42
+ debug_trap = Async::IO::Trap.new(:USR1)
43
+
44
+ require 'ruby-prof'
45
+
46
+ #controller << Async::Container::Forked.new do
47
+ Process.setproctitle("Falcon Proxy")
48
+
49
+ server = Falcon::Server.new(proxy, Async::HTTP::URLEndpoint.parse(
50
+ 'http://0.0.0.0:4433',
51
+ reuse_address: true
52
+ ))
53
+
54
+ # profile the code
55
+ profile = RubyProf::Profile.new(merge_fibers: true)
56
+
57
+ # begin
58
+ # profile.start
59
+
60
+ Async::Reactor.run do |task|
61
+ task.async do
62
+ debug_trap.install!
63
+ $stderr.puts "Send `kill -USR1 #{Process.pid}` for detailed status :)"
64
+
65
+ debug_trap.trap do
66
+ task.reactor.print_hierarchy($stderr)
67
+ # Async.logger.level = Logger::DEBUG
68
+ end
69
+ end
70
+
71
+ task.async do |task|
72
+ start_time = Async::Clock.now
73
+
74
+ while true
75
+ task.sleep(600)
76
+ duration = Async::Clock.now - start_time
77
+ puts "Handled #{proxy.count} requests; #{(proxy.count.to_f / duration.to_f).round(1)} requests per second."
78
+ end
79
+ end
80
+
81
+ server.run
82
+ end
83
+ # ensure
84
+ # profile.stop
85
+ #
86
+ # # print a flat profile to text
87
+ # printer = RubyProf::FlatPrinter.new(profile)
88
+ # printer.print($stdout)
89
+ # end
90
+ #end
91
+
92
+ #Process.setproctitle("Falcon Controller")
93
+ #controller.wait
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: falcon
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.13.1
4
+ version: 0.14.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Samuel Williams
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-04-20 00:00:00.000000000 Z
11
+ date: 2018-04-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: async-io
@@ -16,42 +16,42 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '1.6'
19
+ version: '1.9'
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.6'
26
+ version: '1.9'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: async-http
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: 0.19.0
33
+ version: 0.21.0
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: 0.19.0
40
+ version: 0.21.0
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: async-container
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: 0.4.0
47
+ version: 0.5.0
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.4.0
54
+ version: 0.5.0
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: rack
57
57
  requirement: !ruby/object:Gem::Requirement
@@ -162,10 +162,13 @@ files:
162
162
  - lib/falcon/adapters/rack.rb
163
163
  - lib/falcon/command.rb
164
164
  - lib/falcon/command/serve.rb
165
+ - lib/falcon/hosts.rb
166
+ - lib/falcon/proxy.rb
165
167
  - lib/falcon/server.rb
166
168
  - lib/falcon/verbose.rb
167
169
  - lib/falcon/version.rb
168
170
  - lib/rack/handler/falcon.rb
171
+ - server.rb
169
172
  homepage: https://github.com/socketry/falcon
170
173
  licenses: []
171
174
  metadata: {}