falcon 0.36.4 → 0.37.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (89) hide show
  1. checksums.yaml +4 -4
  2. data/lib/falcon/adapters/input.rb +32 -11
  3. data/lib/falcon/adapters/output.rb +20 -1
  4. data/lib/falcon/adapters/rack.rb +60 -39
  5. data/lib/falcon/adapters/response.rb +23 -1
  6. data/lib/falcon/adapters/rewindable.rb +10 -3
  7. data/lib/falcon/command.rb +2 -0
  8. data/lib/falcon/command/host.rb +22 -3
  9. data/lib/falcon/command/paths.rb +4 -0
  10. data/lib/falcon/command/proxy.rb +14 -0
  11. data/lib/falcon/command/redirect.rb +12 -0
  12. data/lib/falcon/command/serve.rb +27 -16
  13. data/lib/falcon/command/supervisor.rb +15 -1
  14. data/lib/falcon/command/top.rb +16 -0
  15. data/lib/falcon/command/virtual.rb +15 -0
  16. data/lib/falcon/configuration.rb +69 -7
  17. data/lib/falcon/controller/host.rb +12 -0
  18. data/lib/falcon/controller/proxy.rb +13 -0
  19. data/lib/falcon/controller/redirect.rb +7 -0
  20. data/lib/falcon/controller/serve.rb +17 -2
  21. data/lib/falcon/controller/virtual.rb +17 -0
  22. data/lib/falcon/endpoint.rb +8 -0
  23. data/lib/falcon/{configuration/proxy.rb → environments.rb} +9 -5
  24. data/lib/falcon/environments/application.rb +72 -0
  25. data/lib/falcon/{configuration/application.rb → environments/lets_encrypt_tls.rb} +21 -20
  26. data/lib/falcon/{configuration/lets_encrypt_tls.rb → environments/proxy.rb} +13 -6
  27. data/lib/falcon/{configuration → environments}/rack.rb +14 -2
  28. data/lib/falcon/{configuration → environments}/self_signed_tls.rb +9 -1
  29. data/lib/falcon/{configuration → environments}/supervisor.rb +19 -5
  30. data/lib/falcon/{configuration → environments}/tls.rb +39 -5
  31. data/lib/falcon/extensions/openssl.rb +1 -0
  32. data/lib/falcon/middleware/proxy.rb +26 -5
  33. data/lib/falcon/middleware/redirect.rb +11 -0
  34. data/lib/falcon/{verbose.rb → middleware/verbose.rb} +34 -26
  35. data/lib/falcon/proxy_endpoint.rb +21 -0
  36. data/lib/falcon/server.rb +8 -2
  37. data/lib/falcon/service/application.rb +24 -2
  38. data/lib/falcon/service/generic.rb +18 -0
  39. data/lib/falcon/service/proxy.rb +6 -0
  40. data/lib/falcon/service/supervisor.rb +13 -1
  41. data/lib/falcon/services.rb +21 -0
  42. data/lib/falcon/tls.rb +4 -2
  43. data/lib/falcon/version.rb +1 -1
  44. data/lib/rack/handler/falcon.rb +8 -2
  45. metadata +64 -121
  46. data/.editorconfig +0 -5
  47. data/.github/FUNDING.yml +0 -3
  48. data/.github/workflows/development.yml +0 -60
  49. data/.gitignore +0 -14
  50. data/.rspec +0 -3
  51. data/Gemfile +0 -17
  52. data/README.md +0 -316
  53. data/examples/beer/config.ru +0 -57
  54. data/examples/beer/falcon.rb +0 -8
  55. data/examples/benchmark/config.ru +0 -39
  56. data/examples/benchmark/falcon.rb +0 -6
  57. data/examples/csv/config.ru +0 -31
  58. data/examples/google/falcon.rb +0 -14
  59. data/examples/hello/config.ru +0 -22
  60. data/examples/hello/falcon.rb +0 -24
  61. data/examples/hello/preload.rb +0 -7
  62. data/examples/internet/config.ru +0 -54
  63. data/examples/memory/allocations.rb +0 -39
  64. data/examples/memory/config.ru +0 -14
  65. data/examples/push/client.rb +0 -29
  66. data/examples/push/config.ru +0 -28
  67. data/examples/push/index.html +0 -14
  68. data/examples/push/script.js +0 -2
  69. data/examples/push/style.css +0 -4
  70. data/examples/redis/Gemfile +0 -9
  71. data/examples/redis/config.ru +0 -28
  72. data/examples/sequel/Gemfile +0 -4
  73. data/examples/sequel/config.ru +0 -8
  74. data/examples/sequel/data.sqlite3 +0 -0
  75. data/examples/server/standalone.rb +0 -27
  76. data/examples/sinatra/Gemfile +0 -7
  77. data/examples/sinatra/Gemfile.lock +0 -53
  78. data/examples/sinatra/config.ru +0 -16
  79. data/examples/trailers/config.ru +0 -34
  80. data/examples/trailers/falcon.rb +0 -8
  81. data/falcon.gemspec +0 -45
  82. data/gems/rack1.gemfile +0 -4
  83. data/gems/rack3.gemfile +0 -4
  84. data/lib/falcon/adapters/early_hints.rb +0 -49
  85. data/logo-square.afdesign +0 -0
  86. data/logo.afdesign +0 -0
  87. data/logo.svg +0 -107
  88. data/server.rb +0 -21
  89. data/tasks/benchmark.rake +0 -103
@@ -26,7 +26,13 @@ require 'async/container/controller'
26
26
 
27
27
  module Falcon
28
28
  module Controller
29
+ # A generic controller for serving an application.
30
+ # Hosts several {Services} based on the command configuration.
31
+ #
32
+ # The configuration is provided by {Command::Host} and is typically loaded from a `falcon.rb` file. See {Configuration#load_file} for more details.
29
33
  class Host < Async::Container::Controller
34
+ # Initialize the virtual controller.
35
+ # @parameter command [Command::Host] The user-specified command-line options.
30
36
  def initialize(command, **options)
31
37
  @command = command
32
38
 
@@ -36,20 +42,26 @@ module Falcon
36
42
  super(**options)
37
43
  end
38
44
 
45
+ # Create the controller as specified by the command.
46
+ # e.g. `Async::Container::Forked`.
39
47
  def create_container
40
48
  @command.container_class.new
41
49
  end
42
50
 
51
+ # Start all specified services.
43
52
  def start
44
53
  @services.start
45
54
 
46
55
  super
47
56
  end
48
57
 
58
+ # Setup all specified services into the container.
59
+ # @parameter container [Async::Container::Generic]
49
60
  def setup(container)
50
61
  @services.setup(container)
51
62
  end
52
63
 
64
+ # Stop all specified services.
53
65
  def stop(*)
54
66
  @services.stop
55
67
 
@@ -30,9 +30,14 @@ require_relative '../tls'
30
30
 
31
31
  module Falcon
32
32
  module Controller
33
+ # A controller for proxying requests.
33
34
  class Proxy < Serve
35
+ # The default SSL session identifier.
34
36
  DEFAULT_SESSION_ID = "falcon"
35
37
 
38
+ # Initialize the proxy controller.
39
+ # @parameter command [Command::Proxy] The user-specified command-line options.
40
+ # @parameter session_id [String] The SSL session identifier to use for the session cache.
36
41
  def initialize(command, session_id: DEFAULT_SESSION_ID, **options)
37
42
  super(command, **options)
38
43
 
@@ -40,14 +45,19 @@ module Falcon
40
45
  @hosts = {}
41
46
  end
42
47
 
48
+ # Load the {Middleware::Proxy} application with the specified hosts.
43
49
  def load_app
44
50
  return Middleware::Proxy.new(Middleware::BadRequest, @hosts)
45
51
  end
46
52
 
53
+ # The name of the controller which is used for the process title.
47
54
  def name
48
55
  "Falcon Proxy Server"
49
56
  end
50
57
 
58
+ # Look up the host context for the given hostname, and update the socket hostname if necessary.
59
+ # @parameter socket [OpenSSL::SSL::SSLSocket] The incoming connection.
60
+ # @parameter hostname [String] The negotiated hostname.
51
61
  def host_context(socket, hostname)
52
62
  if host = @hosts[hostname]
53
63
  Async.logger.debug(self) {"Resolving #{hostname} -> #{host}"}
@@ -62,6 +72,7 @@ module Falcon
62
72
  end
63
73
  end
64
74
 
75
+ # Generate an SSL context which delegates to {host_context} to multiplex based on hostname.
65
76
  def ssl_context
66
77
  @server_context ||= OpenSSL::SSL::SSLContext.new.tap do |context|
67
78
  context.servername_cb = Proc.new do |socket, hostname|
@@ -81,6 +92,7 @@ module Falcon
81
92
  end
82
93
  end
83
94
 
95
+ # The endpoint the server will bind to.
84
96
  def endpoint
85
97
  @command.endpoint.with(
86
98
  ssl_context: self.ssl_context,
@@ -88,6 +100,7 @@ module Falcon
88
100
  )
89
101
  end
90
102
 
103
+ # Builds a map of host redirections.
91
104
  def start
92
105
  configuration = @command.configuration
93
106
 
@@ -28,27 +28,34 @@ require_relative '../service/proxy'
28
28
 
29
29
  module Falcon
30
30
  module Controller
31
+ # A controller for redirecting requests.
31
32
  class Redirect < Serve
33
+ # Initialize the redirect controller.
34
+ # @parameter command [Command::Redirect] The user-specified command-line options.
32
35
  def initialize(command, **options)
33
36
  super(command, **options)
34
37
 
35
38
  @hosts = {}
36
39
  end
37
40
 
41
+ # Load the {Middleware::Redirect} application with the specified hosts.
38
42
  def load_app
39
43
  return Middleware::Redirect.new(Middleware::NotFound, @hosts, @command.redirect_endpoint)
40
44
  end
41
45
 
46
+ # The name of the controller which is used for the process title.
42
47
  def name
43
48
  "Falcon Redirect Server"
44
49
  end
45
50
 
51
+ # The endpoint the server will bind to.
46
52
  def endpoint
47
53
  @command.endpoint.with(
48
54
  reuse_address: true,
49
55
  )
50
56
  end
51
57
 
58
+ # Builds a map of host redirections.
52
59
  def start
53
60
  configuration = @command.configuration
54
61
 
@@ -29,7 +29,11 @@ require 'async/io/shared_endpoint'
29
29
 
30
30
  module Falcon
31
31
  module Controller
32
+ # A generic controller for serving an application.
33
+ # Uses {Server} for handling incoming requests.
32
34
  class Serve < Async::Container::Controller
35
+ # Initialize the server controller.
36
+ # @parameter command [Command::Serve] The user-specified command-line options.
33
37
  def initialize(command, **options)
34
38
  @command = command
35
39
 
@@ -40,34 +44,44 @@ module Falcon
40
44
  super(**options)
41
45
  end
42
46
 
47
+ # Create the controller as specified by the command.
48
+ # e.g. `Async::Container::Forked`.
43
49
  def create_container
44
50
  @command.container_class.new
45
51
  end
46
52
 
53
+ # The endpoint the server will bind to.
47
54
  def endpoint
48
55
  @command.endpoint
49
56
  end
50
57
 
58
+ # @returns [Protocol::HTTP::Middleware] an instance of the application to be served.
51
59
  def load_app
52
60
  @command.load_app
53
61
  end
54
62
 
63
+ # Prepare the bound endpoint for the server.
55
64
  def start
56
65
  @endpoint ||= self.endpoint
57
66
 
58
- @bound_endpoint = Async::Reactor.run do
67
+ @bound_endpoint = Async do
59
68
  Async::IO::SharedEndpoint.bound(@endpoint)
60
69
  end.wait
61
70
 
71
+ Async.logger.info(self) { "Starting #{name} on #{@endpoint.to_url}" }
72
+
62
73
  @debug_trap.ignore!
63
74
 
64
75
  super
65
76
  end
66
77
 
78
+ # The name of the controller which is used for the process title.
67
79
  def name
68
80
  "Falcon Server"
69
81
  end
70
82
 
83
+ # Setup the container with the application instance.
84
+ # @parameter container [Async::Container::Generic]
71
85
  def setup(container)
72
86
  container.run(name: self.name, restart: true, **@command.container_options) do |instance|
73
87
  Async do |task|
@@ -88,7 +102,7 @@ module Falcon
88
102
  end
89
103
  end
90
104
 
91
- server = Falcon::Server.new(app, @bound_endpoint, @endpoint.protocol, @endpoint.scheme)
105
+ server = Falcon::Server.new(app, @bound_endpoint, protocol: @endpoint.protocol, scheme: @endpoint.scheme)
92
106
 
93
107
  server.run
94
108
 
@@ -99,6 +113,7 @@ module Falcon
99
113
  end
100
114
  end
101
115
 
116
+ # Close the bound endpoint.
102
117
  def stop(*)
103
118
  @bound_endpoint&.close
104
119
 
@@ -24,7 +24,13 @@ require 'async/container/controller'
24
24
 
25
25
  module Falcon
26
26
  module Controller
27
+ # A controller which mananages several virtual hosts.
28
+ # Spawns instances of {Proxy} and {Redirect} to handle incoming requests.
29
+ #
30
+ # A virtual host is an application bound to a specific authority (essentially a hostname). The virtual controller manages multiple hosts and allows a single server to host multiple applications easily.
27
31
  class Virtual < Async::Container::Controller
32
+ # Initialize the virtual controller.
33
+ # @parameter command [Command::Virtual] The user-specified command-line options.
28
34
  def initialize(command, **options)
29
35
  @command = command
30
36
 
@@ -33,6 +39,8 @@ module Falcon
33
39
  trap(SIGHUP, &self.method(:reload))
34
40
  end
35
41
 
42
+ # Drop privileges according to the user and group of the specified path.
43
+ # @parameter path [String] The path to the application directory.
36
44
  def assume_privileges(path)
37
45
  stat = File.stat(path)
38
46
 
@@ -46,6 +54,10 @@ module Falcon
46
54
  }
47
55
  end
48
56
 
57
+ # Spawn an application instance from the specified path.
58
+ # @parameter path [String] The path to the application directory.
59
+ # @parameter container [Async::Container::Generic] The container to spawn into.
60
+ # @parameter options [Options] The options which are passed to `exec`.
49
61
  def spawn(path, container, **options)
50
62
  container.spawn(name: "Falcon Application", restart: true, key: path) do |instance|
51
63
  env = assume_privileges(path)
@@ -56,10 +68,15 @@ module Falcon
56
68
  end
57
69
  end
58
70
 
71
+ # The path to the falcon executable from this gem.
72
+ # @returns [String]
59
73
  def falcon_path
60
74
  File.expand_path("../../../bin/falcon", __dir__)
61
75
  end
62
76
 
77
+ # Setup the container with {Redirect} and {Proxy} child processes.
78
+ # These processes are gracefully restarted if they are already running.
79
+ # @parameter container [Async::Container::Generic]
63
80
  def setup(container)
64
81
  if proxy = container[:proxy]
65
82
  proxy.kill(:HUP)
@@ -24,11 +24,19 @@ require 'async/http/endpoint'
24
24
  require 'localhost/authority'
25
25
 
26
26
  module Falcon
27
+ # An HTTP-specific endpoint which adds localhost TLS.
27
28
  class Endpoint < Async::HTTP::Endpoint
29
+ # The SSL context to use, which invokes {build_ssl_context} if not otherwise specified.
30
+ # @returns [OpenSSL::SSL::SSLContext]
28
31
  def ssl_context
29
32
  @options[:ssl_context] || build_ssl_context
30
33
  end
31
34
 
35
+ # Build an appropriate SSL context for the given hostname.
36
+ #
37
+ # Uses {Localhost::Authority} to generate self-signed certficates.
38
+ #
39
+ # @returns [OpenSSL::SSL::SSLContext]
32
40
  def build_ssl_context(hostname = self.hostname)
33
41
  authority = Localhost::Authority.fetch(hostname)
34
42
 
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Copyright, 2019, by Samuel G. D. Williams. <http://www.codeotaku.com>
3
+ # Copyright, 2018, by Samuel G. D. Williams. <http://www.codeotaku.com>
4
4
  #
5
5
  # Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  # of this software and associated documentation files (the "Software"), to deal
@@ -20,8 +20,12 @@
20
20
  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
21
  # THE SOFTWARE.
22
22
 
23
- add(:proxy) do
24
- endpoint {::Async::HTTP::Endpoint.parse(url)}
25
-
26
- service ::Falcon::Service::Proxy
23
+ require 'build/environment'
24
+
25
+ module Falcon
26
+ # Pre-defined environments for hosting web applications.
27
+ #
28
+ # See {Configuration::Loader#load} for more details.
29
+ module Environments
30
+ end
27
31
  end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright, 2019, by Samuel G. D. Williams. <http://www.codeotaku.com>
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the "Software"), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in
13
+ # all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ # THE SOFTWARE.
22
+
23
+ require_relative '../proxy_endpoint'
24
+ require_relative '../server'
25
+
26
+ require_relative '../service/application'
27
+
28
+ # A general application environment.
29
+ # Suitable for use with any {Protocol::HTTP::Middleware}.
30
+ #
31
+ # @scope Falcon Environments
32
+ # @name application
33
+ environment(:application) do
34
+ # The middleware stack for the application.
35
+ # @attribute [Protocol::HTTP::Middleware]
36
+ middleware do
37
+ ::Protocol::HTTP::Middleware::HelloWorld
38
+ end
39
+
40
+ # The scheme to use to communicate with the application.
41
+ # @attribute [String]
42
+ scheme 'https'
43
+
44
+ # The protocol to use to communicate with the application.
45
+ #
46
+ # Typically one of {Async::HTTP::Protocol::HTTP1} or {Async::HTTP::Protocl::HTTP2}.
47
+ #
48
+ # @attribute [Async::HTTP::Protocol]
49
+ protocol {Async::HTTP::Protocol::HTTP2}
50
+
51
+ # The IPC path to use for communication with the application.
52
+ # @attribute [String]
53
+ ipc_path {::File.expand_path("application.ipc", root)}
54
+
55
+ # The endpoint that will be used for communicating with the application server.
56
+ # @attribute [Async::IO::Endpoint]
57
+ endpoint do
58
+ ::Falcon::ProxyEndpoint.unix(ipc_path,
59
+ protocol: protocol,
60
+ scheme: scheme,
61
+ authority: authority
62
+ )
63
+ end
64
+
65
+ # The service class to use for the application.
66
+ # @attribute [Class]
67
+ service ::Falcon::Service::Application
68
+
69
+ # Number of instances to start.
70
+ # @attribute [Integer | nil]
71
+ count nil
72
+ end
@@ -20,27 +20,28 @@
20
20
  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
21
  # THE SOFTWARE.
22
22
 
23
- require_relative '../proxy_endpoint'
24
- require_relative '../server'
23
+ load(:tls)
25
24
 
26
- require_relative '../service/application'
27
-
28
- add(:application) do
29
- middleware do
30
- ::Protocol::HTTP::Middleware::HelloWorld
31
- end
25
+ # A Lets Encrypt SSL context environment.
26
+ #
27
+ # Derived from {.tls}.
28
+ #
29
+ # @scope Falcon Environments
30
+ # @name lets_encrypt_tls
31
+ environment(:lets_encrypt_tls, :tls) do
32
+ # The Lets Encrypt certificate store path.
33
+ # @parameter [String]
34
+ lets_encrypt_root '/etc/letsencrypt/live'
32
35
 
33
- scheme 'https'
34
- protocol {Async::HTTP::Protocol::HTTP2}
35
- ipc_path {::File.expand_path("application.ipc", root)}
36
-
37
- endpoint do
38
- ::Falcon::ProxyEndpoint.unix(ipc_path,
39
- protocol: protocol,
40
- scheme: scheme,
41
- authority: authority
42
- )
36
+ # The public certificate path.
37
+ # @attribute [String]
38
+ ssl_certificate_path do
39
+ File.join(lets_encrypt_root, authority, "fullchain.pem")
43
40
  end
44
41
 
45
- service ::Falcon::Service::Application
46
- end
42
+ # The private key path.
43
+ # @attribute [String]
44
+ ssl_private_key_path do
45
+ File.join(lets_encrypt_root, authority, "privkey.pem")
46
+ end
47
+ end
@@ -20,11 +20,18 @@
20
20
  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
21
  # THE SOFTWARE.
22
22
 
23
- load(:tls)
24
-
25
- add(:lets_encrypt_tls, :tls) do
26
- lets_encrypt_root '/etc/letsencrypt/live'
23
+ # A HTTP proxy environment.
24
+ #
25
+ # Derived from {.application}.
26
+ #
27
+ # @scope Falcon Environments
28
+ # @name rack
29
+ environment(:proxy) do
30
+ # The upstream endpoint that will handle incoming requests.
31
+ # @attribute [Async::HTTP::Endpoint]
32
+ endpoint {::Async::HTTP::Endpoint.parse(url)}
27
33
 
28
- ssl_certificate_path {File.join(lets_encrypt_root, authority, "fullchain.pem")}
29
- ssl_private_key_path {File.join(lets_encrypt_root, authority, "privkey.pem")}
34
+ # The service class to use for the proxy.
35
+ # @attribute [Class]
36
+ service ::Falcon::Service::Proxy
30
37
  end