falcon 0.13.1 → 0.14.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: 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: {}