falcon 0.36.3 → 0.37.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 (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 +13 -2
  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 +22 -15
  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 +65 -122
  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
@@ -22,11 +22,23 @@
22
22
 
23
23
  load :application
24
24
 
25
- add(:rack, :application) do
25
+ # A rack application environment.
26
+ #
27
+ # Derived from {.application}.
28
+ #
29
+ # @scope Falcon Environments
30
+ # @name rack
31
+ environment(:rack, :application) do
32
+ # The rack configuration path.
33
+ # @attribute [String]
26
34
  config_path {::File.expand_path("config.ru", root)}
27
35
 
36
+ # Whether to enable the application layer cache.
37
+ # @attribute [String]
28
38
  cache false
29
39
 
40
+ # The middleware stack for the rack application.
41
+ # @attribute [Protocol::HTTP::Middleware]
30
42
  middleware do
31
43
  app, _ = ::Rack::Builder.parse_file(config_path)
32
44
 
@@ -35,4 +47,4 @@ add(:rack, :application) do
35
47
  cache: cache
36
48
  )
37
49
  end
38
- end
50
+ end
@@ -22,9 +22,17 @@
22
22
 
23
23
  require 'localhost/authority'
24
24
 
25
- add(:self_signed_tls) do
25
+ # A self-signed SSL context environment.
26
+ #
27
+ # @scope Falcon Environments
28
+ # @name self_signed_tls
29
+ environment(:self_signed_tls) do
30
+ # The default session identifier for the session cache.
31
+ # @attribute [String]
26
32
  ssl_session_id {"falcon"}
27
33
 
34
+ # The SSL context to use for incoming connections.
35
+ # @attribute [OpenSSL::SSL::SSLContext]
28
36
  ssl_context do
29
37
  contexts = Localhost::Authority.fetch(authority)
30
38
 
@@ -22,15 +22,29 @@
22
22
 
23
23
  require_relative '../service/supervisor'
24
24
 
25
- add(:supervisor) do
26
- start true
27
-
25
+ # A application process monitor environment.
26
+ #
27
+ # @scope Falcon Environments
28
+ # @name supervisor
29
+ environment(:supervisor) do
30
+ # The name of the supervisor
31
+ # @attribute [String]
28
32
  name "supervisor"
29
33
 
30
- ipc_path {::File.expand_path("supervisor.ipc", root)}
34
+ # The IPC path to use for communication with the supervisor.
35
+ # @attribute [String]
36
+ ipc_path do
37
+ ::File.expand_path("supervisor.ipc", root)
38
+ end
31
39
 
32
- endpoint {Async::IO::Endpoint.unix(ipc_path)}
40
+ # The endpoint the supervisor will bind to.
41
+ # @attribute [Async::IO::Endpoint]
42
+ endpoint do
43
+ Async::IO::Endpoint.unix(ipc_path)
44
+ end
33
45
 
46
+ # The service class to use for the supervisor.
47
+ # @attribute [Class]
34
48
  service do
35
49
  ::Falcon::Service::Supervisor
36
50
  end
@@ -24,19 +24,53 @@ require_relative '../extensions/openssl'
24
24
  require_relative '../controller/proxy'
25
25
  require_relative '../tls'
26
26
 
27
- add(:tls) do
27
+ # A general SSL context environment.
28
+ #
29
+ # @scope Falcon Environments
30
+ # @name tls
31
+ environment(:tls) do
32
+ # The default session identifier for the session cache.
33
+ # @attribute [String]
28
34
  ssl_session_id "falcon"
35
+
36
+ # The supported ciphers.
37
+ # @attribute [Array(String)]
29
38
  ssl_ciphers Falcon::TLS::SERVER_CIPHERS
30
39
 
31
- ssl_certificate_path {File.expand_path("ssl/certificate.pem", root)}
32
- ssl_certificates {OpenSSL::X509.load_certificates(ssl_certificate_path)}
40
+ # The public certificate path.
41
+ # @attribute [String]
42
+ ssl_certificate_path do
43
+ File.expand_path("ssl/certificate.pem", root)
44
+ end
45
+
46
+ # The list of certificates loaded from that path.
47
+ # @attribute [Array(OpenSSL::X509::Certificate)]
48
+ ssl_certificates do
49
+ OpenSSL::X509.load_certificates(ssl_certificate_path)
50
+ end
33
51
 
52
+ # The main certificate.
53
+ # @attribute [OpenSSL::X509::Certificate]
34
54
  ssl_certificate {ssl_certificates[0]}
55
+
56
+ # The certificate chain.
57
+ # @attribute [Array(OpenSSL::X509::Certificate)]
35
58
  ssl_certificate_chain {ssl_certificates[1..-1]}
36
59
 
37
- ssl_private_key_path {File.expand_path("ssl/private.key", root)}
38
- ssl_private_key {OpenSSL::PKey::RSA.new(File.read(ssl_private_key_path))}
60
+ # The private key path.
61
+ # @attribute [String]
62
+ ssl_private_key_path do
63
+ File.expand_path("ssl/private.key", root)
64
+ end
65
+
66
+ # The private key.
67
+ # @attribute [OpenSSL::PKey::RSA]
68
+ ssl_private_key do
69
+ OpenSSL::PKey::RSA.new(File.read(ssl_private_key_path))
70
+ end
39
71
 
72
+ # The SSL context to use for incoming connections.
73
+ # @attribute [OpenSSL::SSL::SSLContext]
40
74
  ssl_context do
41
75
  OpenSSL::SSL::SSLContext.new.tap do |context|
42
76
  context.add_certificate(ssl_certificate, ssl_private_key, ssl_certificate_chain)
@@ -25,6 +25,7 @@ require 'openssl/x509'
25
25
  module OpenSSL::X509
26
26
  CERTIFICATE_PATTERN = /-----BEGIN CERTIFICATE-----.*?-----END CERTIFICATE-----/m
27
27
 
28
+ # An extension to load an array of certificates from a file at the given path.
28
29
  def self.load_certificates(path)
29
30
  File.read(path).scan(CERTIFICATE_PATTERN).collect do |text|
30
31
  Certificate.new(text)
@@ -26,6 +26,7 @@ require 'protocol/http/middleware'
26
26
 
27
27
  module Falcon
28
28
  module Middleware
29
+ # A static middleware which always returns a 400 bad request response.
29
30
  module BadRequest
30
31
  def self.call(request)
31
32
  return Protocol::HTTP::Response[400, {}, []]
@@ -35,14 +36,17 @@ module Falcon
35
36
  end
36
37
  end
37
38
 
39
+ # A HTTP middleware for proxying requests to a given set of hosts.
40
+ # Typically used for implementing virtual servers.
38
41
  class Proxy < Protocol::HTTP::Middleware
39
- FORWARDED = 'forwarded'.freeze
40
- X_FORWARDED_FOR = 'x-forwarded-for'.freeze
41
- X_FORWARDED_PROTO = 'x-forwarded-proto'.freeze
42
+ FORWARDED = 'forwarded'
43
+ X_FORWARDED_FOR = 'x-forwarded-for'
44
+ X_FORWARDED_PROTO = 'x-forwarded-proto'
42
45
 
43
- VIA = 'via'.freeze
44
- CONNECTION = 'connection'.freeze
46
+ VIA = 'via'
47
+ CONNECTION = 'connection'
45
48
 
49
+ # HTTP hop headers which *should* not be passed through the proxy.
46
50
  HOP_HEADERS = [
47
51
  'connection',
48
52
  'keep-alive',
@@ -52,6 +56,9 @@ module Falcon
52
56
  'upgrade',
53
57
  ]
54
58
 
59
+ # Initialize the proxy middleware.
60
+ # @parameter app [Protocol::HTTP::Middleware] The middleware to use if a request can't be proxied.
61
+ # @parameter hosts [Array(Service::Proxy)] The host applications to proxy to.
55
62
  def initialize(app, hosts)
56
63
  super(app)
57
64
 
@@ -63,18 +70,26 @@ module Falcon
63
70
  @count = 0
64
71
  end
65
72
 
73
+ # The number of requests that have been proxied.
74
+ # @attribute [Integer]
66
75
  attr :count
67
76
 
77
+ # Close all the connections to the upstream hosts.
68
78
  def close
69
79
  @clients.each_value(&:close)
70
80
 
71
81
  super
72
82
  end
73
83
 
84
+ # Establish a connection to the specified upstream endpoint.
85
+ # @parameter endpoint [Async::HTTP::Endpoint]
74
86
  def connect(endpoint)
75
87
  @clients[endpoint] ||= Async::HTTP::Client.new(endpoint)
76
88
  end
77
89
 
90
+ # Lookup the appropriate host for the given request.
91
+ # @parameter request [Protocol::HTTP::Request]
92
+ # @returns [Service::Proxy]
78
93
  def lookup(request)
79
94
  # Trailing dot and port is ignored/normalized.
80
95
  if authority = request.authority&.sub(/(\.)?(:\d+)?$/, '')
@@ -82,6 +97,8 @@ module Falcon
82
97
  end
83
98
  end
84
99
 
100
+ # Prepare the headers to be sent to an upstream host.
101
+ # In particular, we delete all connection and hop headers.
85
102
  def prepare_headers(headers)
86
103
  if connection = headers[CONNECTION]
87
104
  headers.extract(connection)
@@ -90,6 +107,8 @@ module Falcon
90
107
  headers.extract(HOP_HEADERS)
91
108
  end
92
109
 
110
+ # Prepare the request to be proxied to the specified host.
111
+ # In particular, we set appropriate {VIA}, {FORWARDED}, {X_FORWARDED_FOR} and {X_FORWARDED_PROTO} headers.
93
112
  def prepare_request(request, host)
94
113
  forwarded = []
95
114
 
@@ -124,6 +143,8 @@ module Falcon
124
143
  return request
125
144
  end
126
145
 
146
+ # Proxy the request if the authority matches a specific host.
147
+ # @parameter request [Protocol::HTTP::Request]
127
148
  def call(request)
128
149
  if host = lookup(request)
129
150
  @count += 1
@@ -24,6 +24,7 @@ require 'async/http/client'
24
24
 
25
25
  module Falcon
26
26
  module Middleware
27
+ # A static middleware which always returns a 404 not found response.
27
28
  module NotFound
28
29
  def self.call(request)
29
30
  return Protocol::HTTP::Response[404, {}, []]
@@ -33,7 +34,13 @@ module Falcon
33
34
  end
34
35
  end
35
36
 
37
+ # A HTTP middleware for redirecting a given set of hosts to a different endpoint.
38
+ # Typically used for implementing HTTP -> HTTPS redirects.
36
39
  class Redirect < Protocol::HTTP::Middleware
40
+ # Initialize the redirect middleware.
41
+ # @parameter app [Protocol::HTTP::Middleware] The middleware to wrap.
42
+ # @parameter hosts [Hash(String, Service::Proxy)] The map of hosts.
43
+ # @parameter endpoint [Endpoint] The template endpoint to use to build the redirect location.
37
44
  def initialize(app, hosts, endpoint)
38
45
  super(app)
39
46
 
@@ -41,6 +48,8 @@ module Falcon
41
48
  @endpoint = endpoint
42
49
  end
43
50
 
51
+ # Lookup the appropriate host for the given request.
52
+ # @parameter request [Protocol::HTTP::Request]
44
53
  def lookup(request)
45
54
  # Trailing dot and port is ignored/normalized.
46
55
  if authority = request.authority&.sub(/(\.)?(:\d+)?$/, '')
@@ -48,6 +57,8 @@ module Falcon
48
57
  end
49
58
  end
50
59
 
60
+ # Redirect the request if the authority matches a specific host.
61
+ # @parameter request [Protocol::HTTP::Request]
51
62
  def call(request)
52
63
  if host = lookup(request)
53
64
  if @endpoint.default_port?
@@ -24,36 +24,44 @@ require 'async/logger'
24
24
  require 'async/http/statistics'
25
25
 
26
26
  module Falcon
27
- class Verbose < Protocol::HTTP::Middleware
28
- def initialize(app, logger = Async.logger)
29
- super(app)
30
-
31
- @logger = logger
32
- end
33
-
34
- def annotate(request)
35
- task = Async::Task.current
36
- address = request.remote_address
37
-
38
- @logger.info(request) {"Headers: #{request.headers.to_h} from #{address.inspect}"}
39
-
40
- task.annotate("#{request.method} #{request.path} from #{address.inspect}")
41
- end
42
-
43
- def call(request)
44
- annotate(request)
45
-
46
- statistics = Async::HTTP::Statistics.start
47
-
48
- response = super
27
+ module Middleware
28
+ # A HTTP middleware for logging requests and responses.
29
+ class Verbose < Protocol::HTTP::Middleware
30
+ # Initialize the verbose middleware.
31
+ # @parameter app [Protocol::HTTP::Middleware] The middleware to wrap.
32
+ # @parameter logger [Console::Logger] The logger to use.
33
+ def initialize(app, logger = Async.logger)
34
+ super(app)
35
+
36
+ @logger = logger
37
+ end
49
38
 
50
- statistics.wrap(response) do |statistics, error|
51
- @logger.info(request) {"Responding with: #{response.status} #{response.headers.to_h}; #{statistics.inspect}"}
39
+ # Log details of the incoming request.
40
+ def annotate(request)
41
+ task = Async::Task.current
42
+ address = request.remote_address
43
+
44
+ @logger.info(request) {"Headers: #{request.headers.to_h} from #{address.inspect}"}
52
45
 
53
- @logger.error(request) {"#{error.class}: #{error.message}"} if error
46
+ task.annotate("#{request.method} #{request.path} from #{address.inspect}")
54
47
  end
55
48
 
56
- return response
49
+ # Log details of the incoming request using {annotate} and wrap the response to log response details too.
50
+ def call(request)
51
+ annotate(request)
52
+
53
+ statistics = Async::HTTP::Statistics.start
54
+
55
+ response = super
56
+
57
+ statistics.wrap(response) do |statistics, error|
58
+ @logger.info(request) {"Responding with: #{response.status} #{response.headers.to_h}; #{statistics.inspect}"}
59
+
60
+ @logger.error(request) {"#{error.class}: #{error.message}"} if error
61
+ end
62
+
63
+ return response
64
+ end
57
65
  end
58
66
  end
59
67
  end
@@ -23,7 +23,10 @@
23
23
  require 'async/io/unix_endpoint'
24
24
 
25
25
  module Falcon
26
+ # An endpoint suitable for proxing requests, typically via a unix pipe.
26
27
  class ProxyEndpoint < Async::IO::Endpoint
28
+ # Initialize the proxy endpoint.
29
+ # @parameter endpoint [Async::IO::Endpoint] The endpoint which will be used for connecting/binding.
27
30
  def initialize(endpoint, **options)
28
31
  super(**options)
29
32
 
@@ -34,28 +37,44 @@ module Falcon
34
37
  "\#<#{self.class} endpoint=#{@endpoint}>"
35
38
  end
36
39
 
40
+ # The actual endpoint for I/O.
41
+ # @attribute [Async::IO::Endpoint]
37
42
  attr :endpoint
38
43
 
44
+ # The protocol to use for this connection.
45
+ # @returns [Async::HTTP::Protocol] A specific protocol, e.g. {Async::HTTP::P}
39
46
  def protocol
40
47
  @options[:protocol]
41
48
  end
42
49
 
50
+ # The scheme to use for this endpoint.
51
+ # e.g. `"http"`.
52
+ # @returns [String]
43
53
  def scheme
44
54
  @options[:scheme]
45
55
  end
46
56
 
57
+ # The authority to use for this endpoint.
58
+ # e.g. `"myapp.com"`.
59
+ # @returns [String]
47
60
  def authority
48
61
  @options[:authority]
49
62
  end
50
63
 
64
+ # Connect to the endpoint.
51
65
  def connect(&block)
52
66
  @endpoint.connect(&block)
53
67
  end
54
68
 
69
+ # Bind to the endpoint.
55
70
  def bind(&block)
56
71
  @endpoint.bind(&block)
57
72
  end
58
73
 
74
+ # Enumerate the endpoint.
75
+ # If the endpoint has multiple underlying endpoints, this will enumerate them individually.
76
+ # @yields {|endpoint| ...}
77
+ # @parameter endpoint [ProxyEndpoint]
59
78
  def each
60
79
  return to_enum unless block_given?
61
80
 
@@ -64,6 +83,8 @@ module Falcon
64
83
  end
65
84
  end
66
85
 
86
+ # Create a proxy unix endpoint with the specific path.
87
+ # @returns [ProxyEndpoint]
67
88
  def self.unix(path, **options)
68
89
  self.new(::Async::IO::Endpoint.unix(path), **options)
69
90
  end
@@ -27,16 +27,22 @@ require 'protocol/http/content_encoding'
27
27
 
28
28
  require 'async/http/cache'
29
29
 
30
- require_relative 'verbose'
30
+ require_relative 'middleware/verbose'
31
+
31
32
  require_relative 'adapters/rewindable'
32
33
  require_relative 'adapters/rack'
33
34
 
34
35
  module Falcon
36
+ # A server listening on a specific endpoint, hosting a specific middleware.
35
37
  class Server < Async::HTTP::Server
38
+ # Wrap a rack application into a middleware suitable the server.
39
+ # @parameter rack_app [Proc | Object] A rack application/middleware.
40
+ # @parameter verbose [Boolean] Whether to add the {Verbose} middleware.
41
+ # @parameter cache [Boolean] Whether to add the {Async::HTTP::Cache} middleware.
36
42
  def self.middleware(rack_app, verbose: false, cache: true)
37
43
  ::Protocol::HTTP::Middleware.build do
38
44
  if verbose
39
- use Verbose
45
+ use Middleware::Verbose
40
46
  end
41
47
 
42
48
  if cache
@@ -27,6 +27,7 @@ require 'async/io/shared_endpoint'
27
27
 
28
28
  module Falcon
29
29
  module Service
30
+ # Implements an application server using an internal clear-text proxy.
30
31
  class Application < Proxy
31
32
  def initialize(environment)
32
33
  super
@@ -34,11 +35,20 @@ module Falcon
34
35
  @bound_endpoint = nil
35
36
  end
36
37
 
38
+ # The middleware that will be served by this application.
39
+ # @returns [Protocol::HTTP::Middleware]
37
40
  def middleware
38
41
  # In a multi-threaded container, we don't want to modify the shared evaluator's cache, so we create a new evaluator:
39
42
  @environment.evaluator.middleware
40
43
  end
41
44
 
45
+ # Number of instances to start.
46
+ # @returns [Integer | nil]
47
+ def count
48
+ @environment.evaluator.count
49
+ end
50
+
51
+ # Preload any resources specified by the environment.
42
52
  def preload!
43
53
  if scripts = @evaluator.preload
44
54
  scripts.each do |path|
@@ -49,6 +59,8 @@ module Falcon
49
59
  end
50
60
  end
51
61
 
62
+ # Prepare the bound endpoint for the application instances.
63
+ # Invoke {preload!} to load shared resources into the parent process.
52
64
  def start
53
65
  Async.logger.info(self) {"Binding to #{self.endpoint}..."}
54
66
 
@@ -61,15 +73,24 @@ module Falcon
61
73
  super
62
74
  end
63
75
 
76
+ # Setup instances of the application into the container.
77
+ # @parameter container [Async::Container::Generic]
64
78
  def setup(container)
65
79
  protocol = self.protocol
66
80
  scheme = self.scheme
67
81
 
68
- container.run(name: self.name, restart: true) do |instance|
82
+ run_options = {
83
+ name: self.name,
84
+ restart: true,
85
+ }
86
+
87
+ run_options[:count] = count unless count.nil?
88
+
89
+ container.run(**run_options) do |instance|
69
90
  Async(logger: logger) do |task|
70
91
  Async.logger.info(self) {"Starting application server for #{self.root}..."}
71
92
 
72
- server = Server.new(self.middleware, @bound_endpoint, protocol, scheme)
93
+ server = Server.new(self.middleware, @bound_endpoint, protocol: protocol, scheme: scheme)
73
94
 
74
95
  server.run
75
96
 
@@ -82,6 +103,7 @@ module Falcon
82
103
  super
83
104
  end
84
105
 
106
+ # Close the bound endpoint.
85
107
  def stop
86
108
  @bound_endpoint&.close
87
109
  @bound_endpoint = nil