falcon 0.34.5 → 0.35.0

Sign up to get free protection for your applications and to get access to all the features.
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