falcon 0.34.5 → 0.35.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.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +3 -2
  3. data/Gemfile +2 -0
  4. data/bin/falcon +1 -1
  5. data/bin/falcon-host +1 -1
  6. data/examples/beer/config.ru +25 -23
  7. data/examples/beer/falcon.rb +2 -0
  8. data/examples/hello/config.ru +1 -1
  9. data/examples/hello/falcon.rb +14 -2
  10. data/examples/hello/preload.rb +6 -0
  11. data/examples/trailers/config.ru +33 -0
  12. data/examples/trailers/falcon.rb +7 -0
  13. data/falcon.gemspec +3 -1
  14. data/lib/falcon.rb +0 -4
  15. data/lib/falcon/adapters/response.rb +2 -2
  16. data/lib/falcon/command.rb +3 -53
  17. data/lib/falcon/command/host.rb +22 -39
  18. data/lib/falcon/command/paths.rb +45 -0
  19. data/lib/falcon/{host.rb → command/proxy.rb} +39 -45
  20. data/lib/falcon/command/redirect.rb +72 -0
  21. data/lib/falcon/command/serve.rb +28 -58
  22. data/lib/falcon/command/supervisor.rb +5 -5
  23. data/lib/falcon/command/top.rb +79 -0
  24. data/lib/falcon/command/virtual.rb +18 -53
  25. data/lib/falcon/configuration.rb +1 -1
  26. data/lib/falcon/{configurations/host.rb → configuration/application.rb} +13 -11
  27. data/lib/falcon/{configurations → configuration}/lets_encrypt_tls.rb +0 -0
  28. data/lib/falcon/{configurations → configuration}/proxy.rb +2 -2
  29. data/lib/falcon/{configurations → configuration}/rack.rb +2 -2
  30. data/lib/falcon/{configurations → configuration}/self_signed_tls.rb +0 -0
  31. data/lib/falcon/{configurations → configuration}/supervisor.rb +2 -2
  32. data/lib/falcon/{configurations → configuration}/tls.rb +0 -0
  33. data/lib/falcon/controller/host.rb +58 -0
  34. data/lib/falcon/controller/proxy.rb +102 -0
  35. data/lib/falcon/{service.rb → controller/redirect.rb} +37 -24
  36. data/lib/falcon/controller/serve.rb +112 -0
  37. data/lib/falcon/controller/virtual.rb +89 -0
  38. data/lib/falcon/middleware/proxy.rb +143 -0
  39. data/lib/falcon/{redirection.rb → middleware/redirect.rb} +31 -29
  40. data/lib/falcon/proxy_endpoint.rb +1 -1
  41. data/lib/falcon/service/application.rb +113 -0
  42. data/lib/falcon/service/generic.rb +53 -0
  43. data/lib/falcon/service/supervisor.rb +95 -0
  44. data/lib/falcon/services.rb +32 -5
  45. data/lib/falcon/version.rb +1 -1
  46. data/lib/rack/handler/falcon.rb +2 -1
  47. data/logo-square.afdesign +0 -0
  48. metadata +43 -17
  49. data/lib/falcon/hosts.rb +0 -135
  50. data/lib/falcon/proxy.rb +0 -141
  51. data/lib/falcon/supervisor.rb +0 -106
@@ -18,15 +18,17 @@
18
18
  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
19
  # THE SOFTWARE.
20
20
 
21
- require_relative 'service'
21
+ require_relative 'service/generic'
22
22
 
23
23
  module Falcon
24
24
  class Services
25
25
  def initialize(configuration)
26
26
  @named = {}
27
27
 
28
- configuration.each(:start) do |environment|
29
- add(Service.new(environment))
28
+ configuration.each(:service) do |environment|
29
+ service = Service::Generic.wrap(environment)
30
+
31
+ add(service)
30
32
  end
31
33
  end
32
34
 
@@ -38,12 +40,37 @@ module Falcon
38
40
  @named[service.name] = service
39
41
  end
40
42
 
41
- def run(container = Async::Container.new, **options)
43
+ def start
42
44
  @named.each do |name, service|
43
- service.spawn(container)
45
+ Async.logger.debug(self) {"Starting #{name}..."}
46
+ service.start
47
+ end
48
+ end
49
+
50
+ def setup(container)
51
+ @named.each do |name, service|
52
+ Async.logger.debug(self) {"Setup #{name} into #{container}..."}
53
+ service.setup(container)
44
54
  end
45
55
 
46
56
  return container
47
57
  end
58
+
59
+ def stop
60
+ failed = false
61
+
62
+ @named.each do |name, service|
63
+ Async.logger.debug(self) {"Stopping #{name}..."}
64
+
65
+ begin
66
+ service.stop
67
+ rescue
68
+ failed = true
69
+ Async.logger.error(self, $!)
70
+ end
71
+ end
72
+
73
+ return failed
74
+ end
48
75
  end
49
76
  end
@@ -19,5 +19,5 @@
19
19
  # THE SOFTWARE.
20
20
 
21
21
  module Falcon
22
- VERSION = "0.34.5"
22
+ VERSION = "0.35.0"
23
23
  end
@@ -3,6 +3,7 @@ require 'rack/handler'
3
3
 
4
4
  require_relative '../../falcon'
5
5
 
6
+ require 'async/reactor'
6
7
  require 'async/io/host_endpoint'
7
8
 
8
9
  module Rack
@@ -26,7 +27,7 @@ module Rack
26
27
 
27
28
  server = ::Falcon::Server.new(app, endpoint, Async::HTTP::Protocol::HTTP1, SCHEME)
28
29
  yield server if block_given?
29
-
30
+
30
31
  Async::Reactor.run do
31
32
  server.run
32
33
  end
Binary file
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.34.5
4
+ version: 0.35.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: 2020-01-03 00:00:00.000000000 Z
11
+ date: 2020-01-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: async
@@ -58,14 +58,14 @@ dependencies:
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: 0.14.0
61
+ version: 0.16.0
62
62
  type: :runtime
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: 0.14.0
68
+ version: 0.16.0
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: rack
71
71
  requirement: !ruby/object:Gem::Requirement
@@ -122,6 +122,20 @@ dependencies:
122
122
  - - "~>"
123
123
  - !ruby/object:Gem::Version
124
124
  version: '1.11'
125
+ - !ruby/object:Gem::Dependency
126
+ name: process-metrics
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: 0.1.0
132
+ type: :runtime
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: 0.1.0
125
139
  - !ruby/object:Gem::Dependency
126
140
  name: async-rspec
127
141
  requirement: !ruby/object:Gem::Requirement
@@ -247,6 +261,7 @@ files:
247
261
  - examples/csv/config.ru
248
262
  - examples/hello/config.ru
249
263
  - examples/hello/falcon.rb
264
+ - examples/hello/preload.rb
250
265
  - examples/internet/config.ru
251
266
  - examples/memory/allocations.rb
252
267
  - examples/memory/config.ru
@@ -260,6 +275,8 @@ files:
260
275
  - examples/sinatra/Gemfile
261
276
  - examples/sinatra/Gemfile.lock
262
277
  - examples/sinatra/config.ru
278
+ - examples/trailers/config.ru
279
+ - examples/trailers/falcon.rb
263
280
  - falcon.gemspec
264
281
  - gems/rack1.gemfile
265
282
  - gems/rack3.gemfile
@@ -272,31 +289,40 @@ files:
272
289
  - lib/falcon/adapters/rewindable.rb
273
290
  - lib/falcon/command.rb
274
291
  - lib/falcon/command/host.rb
292
+ - lib/falcon/command/paths.rb
293
+ - lib/falcon/command/proxy.rb
294
+ - lib/falcon/command/redirect.rb
275
295
  - lib/falcon/command/serve.rb
276
296
  - lib/falcon/command/supervisor.rb
297
+ - lib/falcon/command/top.rb
277
298
  - lib/falcon/command/virtual.rb
278
299
  - lib/falcon/configuration.rb
279
- - lib/falcon/configurations/host.rb
280
- - lib/falcon/configurations/lets_encrypt_tls.rb
281
- - lib/falcon/configurations/proxy.rb
282
- - lib/falcon/configurations/rack.rb
283
- - lib/falcon/configurations/self_signed_tls.rb
284
- - lib/falcon/configurations/supervisor.rb
285
- - lib/falcon/configurations/tls.rb
300
+ - lib/falcon/configuration/application.rb
301
+ - lib/falcon/configuration/lets_encrypt_tls.rb
302
+ - lib/falcon/configuration/proxy.rb
303
+ - lib/falcon/configuration/rack.rb
304
+ - lib/falcon/configuration/self_signed_tls.rb
305
+ - lib/falcon/configuration/supervisor.rb
306
+ - lib/falcon/configuration/tls.rb
307
+ - lib/falcon/controller/host.rb
308
+ - lib/falcon/controller/proxy.rb
309
+ - lib/falcon/controller/redirect.rb
310
+ - lib/falcon/controller/serve.rb
311
+ - lib/falcon/controller/virtual.rb
286
312
  - lib/falcon/endpoint.rb
287
313
  - lib/falcon/extensions/openssl.rb
288
- - lib/falcon/host.rb
289
- - lib/falcon/hosts.rb
290
- - lib/falcon/proxy.rb
314
+ - lib/falcon/middleware/proxy.rb
315
+ - lib/falcon/middleware/redirect.rb
291
316
  - lib/falcon/proxy_endpoint.rb
292
- - lib/falcon/redirection.rb
293
317
  - lib/falcon/server.rb
294
- - lib/falcon/service.rb
318
+ - lib/falcon/service/application.rb
319
+ - lib/falcon/service/generic.rb
320
+ - lib/falcon/service/supervisor.rb
295
321
  - lib/falcon/services.rb
296
- - lib/falcon/supervisor.rb
297
322
  - lib/falcon/verbose.rb
298
323
  - lib/falcon/version.rb
299
324
  - lib/rack/handler/falcon.rb
325
+ - logo-square.afdesign
300
326
  - logo.afdesign
301
327
  - logo.svg
302
328
  - server.rb
@@ -1,135 +0,0 @@
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
- require_relative 'host'
24
- require_relative 'proxy'
25
- require_relative 'redirection'
26
-
27
- require 'async/container'
28
- require 'async/container/controller'
29
- require 'async/http/endpoint'
30
-
31
- module Falcon
32
- class Hosts
33
- SERVER_CIPHERS = "EECDH+CHACHA20:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5".freeze
34
-
35
- def initialize(configuration)
36
- @named = {}
37
- @server_context = nil
38
- @server_endpoint = nil
39
-
40
- configuration.each(:authority) do |environment|
41
- add(Host.new(environment))
42
- end
43
- end
44
-
45
- def each(&block)
46
- @named.each_value(&block)
47
- end
48
-
49
- def endpoint
50
- @server_endpoint ||= Async::HTTP::Endpoint.parse(
51
- 'https://[::]',
52
- ssl_context: self.ssl_context,
53
- reuse_address: true
54
- )
55
- end
56
-
57
- def ssl_context
58
- @server_context ||= OpenSSL::SSL::SSLContext.new.tap do |context|
59
- context.servername_cb = Proc.new do |socket, hostname|
60
- self.host_context(socket, hostname)
61
- end
62
-
63
- context.session_id_context = "falcon"
64
-
65
- context.set_params(
66
- ciphers: SERVER_CIPHERS,
67
- verify_mode: OpenSSL::SSL::VERIFY_NONE,
68
- )
69
-
70
- context.setup
71
- end
72
- end
73
-
74
- def host_context(socket, hostname)
75
- if host = @named[hostname]
76
- Async.logger.debug(self) {"Resolving #{hostname} -> #{host}"}
77
-
78
- socket.hostname = hostname
79
-
80
- return host.ssl_context
81
- else
82
- Async.logger.warn(self) {"Unable to resolve #{hostname}!"}
83
-
84
- return nil
85
- end
86
- end
87
-
88
- def add(host)
89
- @named[host.authority] = host
90
- end
91
-
92
- def proxy
93
- Proxy.new(Falcon::BadRequest, @named)
94
- end
95
-
96
- def redirection(secure_endpoint)
97
- Redirection.new(Falcon::BadRequest, @named, secure_endpoint)
98
- end
99
-
100
- def run(container = Async::Container.new, **options)
101
- secure_endpoint = Async::HTTP::Endpoint.parse(options[:bind_secure], ssl_context: self.ssl_context)
102
- insecure_endpoint = Async::HTTP::Endpoint.parse(options[:bind_insecure])
103
-
104
- secure_endpoint_bound = insecure_endpoint_bound = nil
105
-
106
- Async::Reactor.run do
107
- secure_endpoint_bound = Async::IO::SharedEndpoint.bound(secure_endpoint)
108
- insecure_endpoint_bound = Async::IO::SharedEndpoint.bound(insecure_endpoint)
109
- end.wait
110
-
111
- container.run(name: "Falcon Proxy", restart: true) do |task, instance|
112
- proxy = self.proxy
113
-
114
- proxy_server = Falcon::Server.new(proxy, secure_endpoint_bound, secure_endpoint.protocol, secure_endpoint.scheme)
115
-
116
- proxy_server.run
117
- end
118
-
119
- container.run(name: "Falcon Redirector", restart: true) do |task, instance|
120
- redirection = self.redirection(secure_endpoint)
121
-
122
- redirection_server = Falcon::Server.new(redirection, insecure_endpoint_bound, insecure_endpoint.protocol, insecure_endpoint.scheme)
123
-
124
- redirection_server.run
125
- end
126
-
127
- container.attach do
128
- secure_endpoint_bound.close
129
- insecure_endpoint_bound.close
130
- end
131
-
132
- return container
133
- end
134
- end
135
- end
@@ -1,141 +0,0 @@
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
- require 'protocol/http/headers'
23
- require 'protocol/http/middleware'
24
-
25
- module Falcon
26
- module BadRequest
27
- def self.call(request)
28
- return Protocol::HTTP::Response[400, {}, []]
29
- end
30
-
31
- def self.close
32
- end
33
- end
34
-
35
- class Proxy < Protocol::HTTP::Middleware
36
- FORWARDED = 'forwarded'.freeze
37
- X_FORWARDED_FOR = 'x-forwarded-for'.freeze
38
- X_FORWARDED_PROTO = 'x-forwarded-proto'.freeze
39
-
40
- VIA = 'via'.freeze
41
- CONNECTION = 'connection'.freeze
42
-
43
- HOP_HEADERS = [
44
- 'connection',
45
- 'keep-alive',
46
- 'public',
47
- 'proxy-authenticate',
48
- 'transfer-encoding',
49
- 'upgrade',
50
- ]
51
-
52
- def initialize(app, hosts)
53
- super(app)
54
-
55
- @server_context = nil
56
-
57
- @hosts = hosts
58
- @clients = {}
59
-
60
- @count = 0
61
- end
62
-
63
- attr :count
64
-
65
- def close
66
- @clients.each_value(&:close)
67
-
68
- super
69
- end
70
-
71
- def connect(endpoint)
72
- @clients[endpoint] ||= Async::HTTP::Client.new(endpoint)
73
- end
74
-
75
- def lookup(request)
76
- # Trailing dot and port is ignored/normalized.
77
- if authority = request.authority&.sub(/(\.)?(:\d+)?$/, '')
78
- return @hosts[authority]
79
- end
80
- end
81
-
82
- def prepare_headers(headers)
83
- if connection = headers[CONNECTION]
84
- headers.extract(connection)
85
- end
86
-
87
- headers.extract(HOP_HEADERS)
88
- end
89
-
90
- def prepare_request(request, host)
91
- forwarded = []
92
-
93
- Async.logger.debug(self) do |buffer|
94
- buffer.puts "Request authority: #{request.authority}"
95
- buffer.puts "Host authority: #{host.authority}"
96
- buffer.puts "Request: #{request.method} #{request.path} #{request.version}"
97
- buffer.puts "Request headers: #{request.headers.inspect}"
98
- end
99
-
100
- # The authority of the request must match the authority of the endpoint we are proxying to, otherwise SNI and other things won't work correctly.
101
- request.authority = host.authority
102
-
103
- if address = request.remote_address
104
- request.headers.add(X_FORWARDED_FOR, address.ip_address)
105
- forwarded << "for=#{address.ip_address}"
106
- end
107
-
108
- if scheme = request.scheme
109
- request.headers.add(X_FORWARDED_PROTO, scheme)
110
- forwarded << "proto=#{scheme}"
111
- end
112
-
113
- unless forwarded.empty?
114
- request.headers.add(FORWARDED, forwarded.join(';'))
115
- end
116
-
117
- request.headers.add(VIA, "#{request.version} #{self.class}")
118
-
119
- self.prepare_headers(request.headers)
120
-
121
- return request
122
- end
123
-
124
- def call(request)
125
- if host = lookup(request)
126
- @count += 1
127
-
128
- request = self.prepare_request(request, host)
129
-
130
- client = connect(host.endpoint)
131
-
132
- client.call(request)
133
- else
134
- super
135
- end
136
- rescue
137
- Async.logger.error(self) {$!}
138
- return Protocol::HTTP::Response[502, {'content-type' => 'text/plain'}, ["#{$!.inspect}: #{$!.backtrace.join("\n")}"]]
139
- end
140
- end
141
- end