falcon 0.36.4 → 0.37.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/falcon/adapters/input.rb +32 -11
- data/lib/falcon/adapters/output.rb +20 -1
- data/lib/falcon/adapters/rack.rb +60 -39
- data/lib/falcon/adapters/response.rb +23 -1
- data/lib/falcon/adapters/rewindable.rb +10 -3
- data/lib/falcon/command.rb +2 -0
- data/lib/falcon/command/host.rb +22 -3
- data/lib/falcon/command/paths.rb +4 -0
- data/lib/falcon/command/proxy.rb +14 -0
- data/lib/falcon/command/redirect.rb +12 -0
- data/lib/falcon/command/serve.rb +27 -16
- data/lib/falcon/command/supervisor.rb +15 -1
- data/lib/falcon/command/top.rb +16 -0
- data/lib/falcon/command/virtual.rb +15 -0
- data/lib/falcon/configuration.rb +69 -7
- data/lib/falcon/controller/host.rb +12 -0
- data/lib/falcon/controller/proxy.rb +13 -0
- data/lib/falcon/controller/redirect.rb +7 -0
- data/lib/falcon/controller/serve.rb +17 -2
- data/lib/falcon/controller/virtual.rb +17 -0
- data/lib/falcon/endpoint.rb +8 -0
- data/lib/falcon/{configuration/proxy.rb → environments.rb} +9 -5
- data/lib/falcon/environments/application.rb +72 -0
- data/lib/falcon/{configuration/application.rb → environments/lets_encrypt_tls.rb} +21 -20
- data/lib/falcon/{configuration/lets_encrypt_tls.rb → environments/proxy.rb} +13 -6
- data/lib/falcon/{configuration → environments}/rack.rb +14 -2
- data/lib/falcon/{configuration → environments}/self_signed_tls.rb +9 -1
- data/lib/falcon/{configuration → environments}/supervisor.rb +19 -5
- data/lib/falcon/{configuration → environments}/tls.rb +39 -5
- data/lib/falcon/extensions/openssl.rb +1 -0
- data/lib/falcon/middleware/proxy.rb +26 -5
- data/lib/falcon/middleware/redirect.rb +11 -0
- data/lib/falcon/{verbose.rb → middleware/verbose.rb} +34 -26
- data/lib/falcon/proxy_endpoint.rb +21 -0
- data/lib/falcon/server.rb +8 -2
- data/lib/falcon/service/application.rb +24 -2
- data/lib/falcon/service/generic.rb +18 -0
- data/lib/falcon/service/proxy.rb +6 -0
- data/lib/falcon/service/supervisor.rb +13 -1
- data/lib/falcon/services.rb +21 -0
- data/lib/falcon/tls.rb +4 -2
- data/lib/falcon/version.rb +1 -1
- data/lib/rack/handler/falcon.rb +8 -2
- metadata +64 -121
- data/.editorconfig +0 -5
- data/.github/FUNDING.yml +0 -3
- data/.github/workflows/development.yml +0 -60
- data/.gitignore +0 -14
- data/.rspec +0 -3
- data/Gemfile +0 -17
- data/README.md +0 -316
- data/examples/beer/config.ru +0 -57
- data/examples/beer/falcon.rb +0 -8
- data/examples/benchmark/config.ru +0 -39
- data/examples/benchmark/falcon.rb +0 -6
- data/examples/csv/config.ru +0 -31
- data/examples/google/falcon.rb +0 -14
- data/examples/hello/config.ru +0 -22
- data/examples/hello/falcon.rb +0 -24
- data/examples/hello/preload.rb +0 -7
- data/examples/internet/config.ru +0 -54
- data/examples/memory/allocations.rb +0 -39
- data/examples/memory/config.ru +0 -14
- data/examples/push/client.rb +0 -29
- data/examples/push/config.ru +0 -28
- data/examples/push/index.html +0 -14
- data/examples/push/script.js +0 -2
- data/examples/push/style.css +0 -4
- data/examples/redis/Gemfile +0 -9
- data/examples/redis/config.ru +0 -28
- data/examples/sequel/Gemfile +0 -4
- data/examples/sequel/config.ru +0 -8
- data/examples/sequel/data.sqlite3 +0 -0
- data/examples/server/standalone.rb +0 -27
- data/examples/sinatra/Gemfile +0 -7
- data/examples/sinatra/Gemfile.lock +0 -53
- data/examples/sinatra/config.ru +0 -16
- data/examples/trailers/config.ru +0 -34
- data/examples/trailers/falcon.rb +0 -8
- data/falcon.gemspec +0 -45
- data/gems/rack1.gemfile +0 -4
- data/gems/rack3.gemfile +0 -4
- data/lib/falcon/adapters/early_hints.rb +0 -49
- data/logo-square.afdesign +0 -0
- data/logo.afdesign +0 -0
- data/logo.svg +0 -107
- data/server.rb +0 -21
- data/tasks/benchmark.rake +0 -103
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d83047a262d8025a52ecb7f3bf232c36f717ea646058492f93c5888c467fb6da
|
4
|
+
data.tar.gz: a8bb6889126af343d5227f4994ee7d1c7498c5b8537c58a254e2fae8195592d1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ec87c1b000c5a480758a265cbdd293b3baf16e02428c4c217669617462e9ac41d510b369b41609244b44248f5d86b54d164c7410312d2d962eee0df95456e4ac
|
7
|
+
data.tar.gz: 4cd538e294829195e22e52828b2e1362908e3b454ec510024a5db6b9b710713346c0bce91e6653313bead8f1c6a3d6b83ab39a16df60e4dc29b6baabc48276c7
|
@@ -25,8 +25,14 @@ require 'protocol/http/body/rewindable'
|
|
25
25
|
|
26
26
|
module Falcon
|
27
27
|
module Adapters
|
28
|
-
#
|
28
|
+
# Wraps a streaming input body into the interface required by `rack.input`.
|
29
|
+
#
|
30
|
+
# The input stream is an `IO`-like object which contains the raw HTTP POST data. When applicable, its external encoding must be `ASCII-8BIT` and it must be opened in binary mode, for Ruby 1.9 compatibility. The input stream must respond to `gets`, `each`, `read` and `rewind`.
|
31
|
+
#
|
32
|
+
# This implementation is not always rewindable, to avoid buffering the input when handling large uploads. See {Rewindable} for more details.
|
29
33
|
class Input
|
34
|
+
# Initialize the input wrapper.
|
35
|
+
# @parameter body [Protocol::HTTP::Body::Readable]
|
30
36
|
def initialize(body)
|
31
37
|
@body = body
|
32
38
|
|
@@ -35,9 +41,13 @@ module Falcon
|
|
35
41
|
@finished = @body.nil?
|
36
42
|
end
|
37
43
|
|
44
|
+
# The input body.
|
45
|
+
# @attribute [Protocol::HTTP::Body::Readable]
|
38
46
|
attr :body
|
39
47
|
|
40
|
-
#
|
48
|
+
# Enumerate chunks of the request body.
|
49
|
+
# @yields {|chunk| ...}
|
50
|
+
# @parameter chunk [String]
|
41
51
|
def each(&block)
|
42
52
|
return to_enum unless block_given?
|
43
53
|
|
@@ -46,8 +56,11 @@ module Falcon
|
|
46
56
|
end
|
47
57
|
end
|
48
58
|
|
49
|
-
#
|
50
|
-
#
|
59
|
+
# Rewind the input stream back to the start.
|
60
|
+
#
|
61
|
+
# `rewind` must be called without arguments. It rewinds the input stream back to the beginning. It must not raise Errno::ESPIPE: that is, it may not be a pipe or a socket. Therefore, handler developers must buffer the input data into some rewindable object if the underlying input stream is not rewindable.
|
62
|
+
#
|
63
|
+
# @returns [Boolean] Whether the body could be rewound.
|
51
64
|
def rewind
|
52
65
|
if @body and @body.respond_to? :rewind
|
53
66
|
# If the body is not rewindable, this will fail.
|
@@ -61,10 +74,13 @@ module Falcon
|
|
61
74
|
return false
|
62
75
|
end
|
63
76
|
|
64
|
-
#
|
65
|
-
#
|
66
|
-
#
|
67
|
-
#
|
77
|
+
# Read data from the input stream.
|
78
|
+
#
|
79
|
+
# `read` behaves like `IO#read`. Its signature is `read(length = nil, buffer = nil)`. If given, length must be a non-negative `Integer` (>= 0) or `nil`, and buffer must be a `String` and may not be nil. If `length` is given and not `nil`, then this method reads at most `length` bytes from the input stream. If `length` is not given or `nil`, then this method reads all data. When the end is reached, this method returns `nil` if `length` is given and not `nil`, or an empty `String` if `length` is not given or is `nil`. If `buffer` is given, then the read data will be placed into the `buffer` instead of a newly created `String` object.
|
80
|
+
#
|
81
|
+
# @parameter length [Integer] the amount of data to read
|
82
|
+
# @parameter buffer [String] the buffer which will receive the data
|
83
|
+
# @returns a buffer containing the data
|
68
84
|
def read(length = nil, buffer = nil)
|
69
85
|
buffer ||= Async::IO::Buffer.new
|
70
86
|
buffer.clear
|
@@ -93,12 +109,17 @@ module Falcon
|
|
93
109
|
return buffer
|
94
110
|
end
|
95
111
|
|
112
|
+
# Has the input stream been read completely?
|
113
|
+
# @returns [Boolean]
|
96
114
|
def eof?
|
97
115
|
@finished and @buffer.nil?
|
98
116
|
end
|
99
117
|
|
100
|
-
#
|
101
|
-
#
|
118
|
+
# Read the next chunk of data from the input stream.
|
119
|
+
#
|
120
|
+
# `gets` must be called without arguments and return a `String`, or `nil` when the input stream has no more data.
|
121
|
+
#
|
122
|
+
# @returns [String | Nil] The next chunk from the body.
|
102
123
|
def gets
|
103
124
|
if @buffer.nil?
|
104
125
|
return read_next
|
@@ -109,7 +130,7 @@ module Falcon
|
|
109
130
|
end
|
110
131
|
end
|
111
132
|
|
112
|
-
#
|
133
|
+
# Close and discard the remainder of the input stream.
|
113
134
|
def close
|
114
135
|
@body&.close
|
115
136
|
end
|
@@ -26,11 +26,15 @@ require 'protocol/http/body/file'
|
|
26
26
|
module Falcon
|
27
27
|
module Adapters
|
28
28
|
# Wraps the rack response body.
|
29
|
-
#
|
29
|
+
#
|
30
|
+
# The `rack` body must respond to `each` and must only yield `String` values. If the body responds to `close`, it will be called after iteration. If the body is replaced by a middleware after action, the original body must be closed first, if it responds to `close`. If the body responds to `to_path`, it must return a String identifying the location of a file whose contents are identical to that produced by calling `each`; this may be used by the server as an alternative, possibly more efficient way to transport the response. The body commonly is an `Array` of strings, the application instance itself, or a `File`-like object.
|
30
31
|
class Output < ::Protocol::HTTP::Body::Readable
|
31
32
|
CONTENT_LENGTH = 'content-length'.freeze
|
32
33
|
|
33
34
|
# Wraps an array into a buffered body.
|
35
|
+
# @parameter status [Integer] The response status.
|
36
|
+
# @parameter headers [Protocol::HTTP::Headers] The response headers.
|
37
|
+
# @parameter body [Object] The `rack` response body.
|
34
38
|
def self.wrap(status, headers, body)
|
35
39
|
# In no circumstance do we want this header propagating out:
|
36
40
|
if length = headers.delete(CONTENT_LENGTH)
|
@@ -51,6 +55,9 @@ module Falcon
|
|
51
55
|
end
|
52
56
|
end
|
53
57
|
|
58
|
+
# Initialize the output wrapper.
|
59
|
+
# @parameter body [Object] The rack response body.
|
60
|
+
# @parameter length [Integer] The rack response length.
|
54
61
|
def initialize(body, length)
|
55
62
|
@length = length
|
56
63
|
@body = body
|
@@ -64,10 +71,17 @@ module Falcon
|
|
64
71
|
# The content length of the rack response body.
|
65
72
|
attr :length
|
66
73
|
|
74
|
+
# Whether the body is empty.
|
67
75
|
def empty?
|
68
76
|
@length == 0 or (@body.respond_to?(:empty?) and @body.empty?)
|
69
77
|
end
|
70
78
|
|
79
|
+
# Whether the body can be read immediately.
|
80
|
+
def ready?
|
81
|
+
true
|
82
|
+
end
|
83
|
+
|
84
|
+
# Close the response body.
|
71
85
|
def close(error = nil)
|
72
86
|
if @body and @body.respond_to?(:close)
|
73
87
|
@body.close
|
@@ -79,12 +93,17 @@ module Falcon
|
|
79
93
|
super
|
80
94
|
end
|
81
95
|
|
96
|
+
# Enumerate the response body.
|
97
|
+
# @yields {|chunk| ...}
|
98
|
+
# @parameter chunk [String]
|
82
99
|
def each(&block)
|
83
100
|
@body.each(&block)
|
84
101
|
ensure
|
85
102
|
self.close($!)
|
86
103
|
end
|
87
104
|
|
105
|
+
# Read the next chunk from the response body.
|
106
|
+
# @returns [String | Nil]
|
88
107
|
def read
|
89
108
|
@chunks ||= @body.to_enum(:each)
|
90
109
|
|
data/lib/falcon/adapters/rack.rb
CHANGED
@@ -24,47 +24,53 @@ require 'rack'
|
|
24
24
|
|
25
25
|
require_relative 'input'
|
26
26
|
require_relative 'response'
|
27
|
-
require_relative 'early_hints'
|
28
27
|
|
29
28
|
require 'async/logger'
|
30
29
|
|
31
30
|
module Falcon
|
32
31
|
module Adapters
|
33
32
|
class Rack
|
34
|
-
# CGI keys
|
35
|
-
HTTP_HOST = 'HTTP_HOST'.freeze
|
36
|
-
PATH_INFO = 'PATH_INFO'.freeze
|
37
|
-
REQUEST_METHOD = 'REQUEST_METHOD'.freeze
|
38
|
-
REQUEST_PATH = 'REQUEST_PATH'.freeze
|
39
|
-
REQUEST_URI = 'REQUEST_URI'.freeze
|
40
|
-
SCRIPT_NAME = 'SCRIPT_NAME'.freeze
|
41
|
-
QUERY_STRING = 'QUERY_STRING'.freeze
|
42
|
-
SERVER_PROTOCOL = 'SERVER_PROTOCOL'.freeze
|
43
|
-
SERVER_NAME = 'SERVER_NAME'.freeze
|
44
|
-
SERVER_PORT = 'SERVER_PORT'.freeze
|
45
|
-
REMOTE_ADDR = 'REMOTE_ADDR'.freeze
|
46
|
-
CONTENT_TYPE = 'CONTENT_TYPE'.freeze
|
47
|
-
CONTENT_LENGTH = 'CONTENT_LENGTH'.freeze
|
33
|
+
# CGI keys <https://tools.ietf.org/html/rfc3875#section-4.1>:
|
48
34
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
35
|
+
HTTP_HOST = 'HTTP_HOST'
|
36
|
+
PATH_INFO = 'PATH_INFO'
|
37
|
+
REQUEST_METHOD = 'REQUEST_METHOD'
|
38
|
+
REQUEST_PATH = 'REQUEST_PATH'
|
39
|
+
REQUEST_URI = 'REQUEST_URI'
|
40
|
+
SCRIPT_NAME = 'SCRIPT_NAME'
|
41
|
+
QUERY_STRING = 'QUERY_STRING'
|
42
|
+
SERVER_PROTOCOL = 'SERVER_PROTOCOL'
|
43
|
+
SERVER_NAME = 'SERVER_NAME'
|
44
|
+
SERVER_PORT = 'SERVER_PORT'
|
45
|
+
REMOTE_ADDR = 'REMOTE_ADDR'
|
46
|
+
CONTENT_TYPE = 'CONTENT_TYPE'
|
47
|
+
CONTENT_LENGTH = 'CONTENT_LENGTH'
|
62
48
|
|
63
|
-
|
49
|
+
# Rack environment variables:
|
64
50
|
|
65
|
-
|
66
|
-
|
51
|
+
RACK_VERSION = 'rack.version'
|
52
|
+
RACK_ERRORS = 'rack.errors'
|
53
|
+
RACK_LOGGER = 'rack.logger'
|
54
|
+
RACK_INPUT = 'rack.input'
|
55
|
+
RACK_MULTITHREAD = 'rack.multithread'
|
56
|
+
RACK_MULTIPROCESS = 'rack.multiprocess'
|
57
|
+
RACK_RUNONCE = 'rack.run_once'
|
58
|
+
RACK_URL_SCHEME = 'rack.url_scheme'
|
59
|
+
RACK_HIJACK = 'rack.hijack'
|
60
|
+
RACK_IS_HIJACK = 'rack.hijack?'
|
61
|
+
RACK_HIJACK_IO = 'rack.hijack_io'
|
67
62
|
|
63
|
+
# Async::HTTP specific metadata:
|
64
|
+
|
65
|
+
ASYNC_HTTP_REQUEST = "async.http.request"
|
66
|
+
|
67
|
+
# Header constants:
|
68
|
+
|
69
|
+
HTTP_X_FORWARDED_PROTO = 'HTTP_X_FORWARDED_PROTO'
|
70
|
+
|
71
|
+
# Initialize the rack adaptor middleware.
|
72
|
+
# @parameter app [Object] The rack middleware.
|
73
|
+
# @parameter logger [Console::Logger] The logger to use.
|
68
74
|
def initialize(app, logger = Async.logger)
|
69
75
|
@app = app
|
70
76
|
|
@@ -73,7 +79,12 @@ module Falcon
|
|
73
79
|
@logger = logger
|
74
80
|
end
|
75
81
|
|
76
|
-
#
|
82
|
+
# Unwrap raw HTTP headers into the CGI-style expected by Rack middleware.
|
83
|
+
#
|
84
|
+
# Rack separates multiple headers with the same key, into a single field with multiple lines.
|
85
|
+
#
|
86
|
+
# @parameter headers [Protocol::HTTP::Headers] The raw HTTP request headers.
|
87
|
+
# @parameter env [Hash] The rack request `env`.
|
77
88
|
def unwrap_headers(headers, env)
|
78
89
|
headers.each do |key, value|
|
79
90
|
http_key = "HTTP_#{key.upcase.tr('-', '_')}"
|
@@ -86,7 +97,15 @@ module Falcon
|
|
86
97
|
end
|
87
98
|
end
|
88
99
|
|
89
|
-
# Process the incoming request into a valid rack env
|
100
|
+
# Process the incoming request into a valid rack `env`.
|
101
|
+
#
|
102
|
+
# - Set the `env['CONTENT_TYPE']` and `env['CONTENT_LENGTH']` based on the incoming request body.
|
103
|
+
# - Set the `env['HTTP_HOST']` header to the request authority.
|
104
|
+
# - Set the `env['HTTP_X_FORWARDED_PROTO']` header to the request scheme.
|
105
|
+
# - Set `env['REMOTE_ADDR']` to the request remote adress.
|
106
|
+
#
|
107
|
+
# @parameter request [Protocol::HTTP::Request] The incoming request.
|
108
|
+
# @parameter env [Hash] The rack `env`.
|
90
109
|
def unwrap_request(request, env)
|
91
110
|
if content_type = request.headers.delete('content-type')
|
92
111
|
env[CONTENT_TYPE] = content_type
|
@@ -113,6 +132,9 @@ module Falcon
|
|
113
132
|
end
|
114
133
|
end
|
115
134
|
|
135
|
+
# Build a rack `env` from the incoming request and apply it to the rack middleware.
|
136
|
+
#
|
137
|
+
# @parameter request [Protocol::HTTP::Request] The incoming request.
|
116
138
|
def call(request)
|
117
139
|
request_path, query_string = request.path.split('?', 2)
|
118
140
|
server_name, server_port = (request.authority || '').split(':', 2)
|
@@ -151,8 +173,8 @@ module Falcon
|
|
151
173
|
RACK_URL_SCHEME => request.scheme,
|
152
174
|
|
153
175
|
# I'm not sure what sane defaults should be here:
|
154
|
-
SERVER_NAME => server_name
|
155
|
-
SERVER_PORT => server_port
|
176
|
+
SERVER_NAME => server_name,
|
177
|
+
SERVER_PORT => server_port,
|
156
178
|
|
157
179
|
# We support both request and response hijack.
|
158
180
|
RACK_IS_HIJACK => true,
|
@@ -160,10 +182,6 @@ module Falcon
|
|
160
182
|
|
161
183
|
self.unwrap_request(request, env)
|
162
184
|
|
163
|
-
if request.push?
|
164
|
-
env[RACK_EARLY_HINTS] = EarlyHints.new(request)
|
165
|
-
end
|
166
|
-
|
167
185
|
full_hijack = false
|
168
186
|
|
169
187
|
if request.hijack?
|
@@ -194,6 +212,9 @@ module Falcon
|
|
194
212
|
return failure_response(exception)
|
195
213
|
end
|
196
214
|
|
215
|
+
# Generate a suitable response for the given exception.
|
216
|
+
# @parameter exception [Exception]
|
217
|
+
# @returns [Protocol::HTTP::Response]
|
197
218
|
def failure_response(exception)
|
198
219
|
Protocol::HTTP::Response.for_exception(exception)
|
199
220
|
end
|
@@ -29,10 +29,22 @@ require 'time'
|
|
29
29
|
|
30
30
|
module Falcon
|
31
31
|
module Adapters
|
32
|
+
# A wrapper for a `Rack` response.
|
33
|
+
#
|
34
|
+
# A Rack response consisting of `[status, headers, body]` includes various rack-specific elements, including:
|
35
|
+
#
|
36
|
+
# - A `headers['rack.hijack']` callback which bypasses normal response handling.
|
37
|
+
# - Potentially invalid content length.
|
38
|
+
# - Potentially invalid body when processing a `HEAD` request.
|
39
|
+
# - Newline-separated header values.
|
40
|
+
# - Other `rack.` specific header key/value pairs.
|
41
|
+
#
|
42
|
+
# This wrapper takes those issues into account and adapts the rack response tuple into a {Protocol::HTTP::Response}.
|
32
43
|
class Response < ::Protocol::HTTP::Response
|
33
44
|
IGNORE_HEADERS = Middleware::Proxy::HOP_HEADERS
|
34
45
|
|
35
|
-
#
|
46
|
+
# Process the rack response headers into into a {Protocol::HTTP::Headers} instance, along with any extra `rack.` metadata.
|
47
|
+
# @returns [Tuple(Protocol::HTTP::Headers, Hash)]
|
36
48
|
def self.wrap_headers(fields)
|
37
49
|
headers = ::Protocol::HTTP::Headers.new
|
38
50
|
meta = {}
|
@@ -52,6 +64,11 @@ module Falcon
|
|
52
64
|
return headers, meta
|
53
65
|
end
|
54
66
|
|
67
|
+
# Wrap a rack response.
|
68
|
+
# @parameter status [Integer] The rack response status.
|
69
|
+
# @parameter headers [Duck(:each)] The rack response headers.
|
70
|
+
# @parameter body [Duck(:each, :close) | Nil] The rack response body.
|
71
|
+
# @parameter request [Protocol::HTTP::Request] The original request.
|
55
72
|
def self.wrap(status, headers, body, request = nil)
|
56
73
|
headers, meta = wrap_headers(headers)
|
57
74
|
|
@@ -83,6 +100,11 @@ module Falcon
|
|
83
100
|
return self.new(status, headers, body, protocol)
|
84
101
|
end
|
85
102
|
|
103
|
+
# Initialize the response wrapper.
|
104
|
+
# @parameter status [Integer] The response status.
|
105
|
+
# @parameter headers [Protocol::HTTP::Headers] The response headers.
|
106
|
+
# @parameter body [Protocol::HTTP::Body] The response body.
|
107
|
+
# @parameter protocol [String] The response protocol for upgraded requests.
|
86
108
|
def initialize(status, headers, body, protocol = nil)
|
87
109
|
super(nil, status, headers, body, protocol)
|
88
110
|
end
|
@@ -24,8 +24,9 @@ require 'protocol/http/body/rewindable'
|
|
24
24
|
|
25
25
|
module Falcon
|
26
26
|
module Adapters
|
27
|
-
# Content
|
27
|
+
# Content-type driven input buffering, specific to the needs of `rack`.
|
28
28
|
class Rewindable < ::Protocol::HTTP::Middleware
|
29
|
+
# Media types that require buffering.
|
29
30
|
BUFFERED_MEDIA_TYPES = %r{
|
30
31
|
application/x-www-form-urlencoded|
|
31
32
|
multipart/form-data|
|
@@ -35,10 +36,15 @@ module Falcon
|
|
35
36
|
|
36
37
|
POST = 'POST'.freeze
|
37
38
|
|
39
|
+
# Initialize the rewindable middleware.
|
40
|
+
# @parameter app [Protocol::HTTP::Middleware] The middleware to wrap.
|
38
41
|
def initialize(app)
|
39
42
|
super(app)
|
40
43
|
end
|
41
44
|
|
45
|
+
# Determine whether the request needs a rewindable body.
|
46
|
+
# @parameter request [Protocol::HTTP::Request]
|
47
|
+
# @returns [Boolean]
|
42
48
|
def needs_rewind?(request)
|
43
49
|
content_type = request.headers['content-type']
|
44
50
|
|
@@ -53,8 +59,9 @@ module Falcon
|
|
53
59
|
return false
|
54
60
|
end
|
55
61
|
|
56
|
-
# Wrap the request body in a rewindable buffer.
|
57
|
-
# @
|
62
|
+
# Wrap the request body in a rewindable buffer if required.
|
63
|
+
# @parameter request [Protocol::HTTP::Request]
|
64
|
+
# @returns [Protocol::HTTP::Response] the response.
|
58
65
|
def call(request)
|
59
66
|
if body = request.body and needs_rewind?(request)
|
60
67
|
request.body = Async::HTTP::Body::Rewindable.new(body)
|
data/lib/falcon/command.rb
CHANGED
data/lib/falcon/command/host.rb
CHANGED
@@ -25,20 +25,29 @@ require_relative '../configuration'
|
|
25
25
|
require_relative '../version'
|
26
26
|
|
27
27
|
require 'samovar'
|
28
|
+
require 'bundler'
|
28
29
|
|
29
30
|
module Falcon
|
30
31
|
module Command
|
32
|
+
# Implements the `falcon host` command. Designed for *deployment*.
|
33
|
+
#
|
34
|
+
# Manages a {Controller::Host} instance which is responsible for running applications in a production environment.
|
31
35
|
class Host < Samovar::Command
|
32
36
|
self.description = "Host the specified applications."
|
33
37
|
|
38
|
+
# One or more paths to the configuration files.
|
39
|
+
# @name paths
|
40
|
+
# @attribute [Array(String)]
|
34
41
|
many :paths, "Service configuration paths.", default: ["falcon.rb"]
|
35
42
|
|
43
|
+
# The container class to use.
|
36
44
|
def container_class
|
37
45
|
Async::Container.best_container_class
|
38
46
|
end
|
39
47
|
|
40
|
-
|
41
|
-
|
48
|
+
# Generate a configuration based on the specified {paths}.
|
49
|
+
def configuration
|
50
|
+
configuration = Configuration.new
|
42
51
|
|
43
52
|
@paths.each do |path|
|
44
53
|
path = File.expand_path(path)
|
@@ -48,10 +57,12 @@ module Falcon
|
|
48
57
|
return configuration
|
49
58
|
end
|
50
59
|
|
60
|
+
# Prepare a new controller for the command.
|
51
61
|
def controller
|
52
62
|
Controller::Host.new(self)
|
53
63
|
end
|
54
64
|
|
65
|
+
# Prepare the environment and run the controller.
|
55
66
|
def call
|
56
67
|
Async.logger.info(self) do |buffer|
|
57
68
|
buffer.puts "Falcon Host v#{VERSION} taking flight!"
|
@@ -60,7 +71,15 @@ module Falcon
|
|
60
71
|
buffer.puts "- To reload: kill -HUP #{Process.pid}"
|
61
72
|
end
|
62
73
|
|
63
|
-
|
74
|
+
begin
|
75
|
+
Bundler.require(:preload)
|
76
|
+
rescue Bundler::GemfileNotFound
|
77
|
+
# Ignore.
|
78
|
+
end
|
79
|
+
|
80
|
+
if GC.respond_to?(:compact)
|
81
|
+
GC.compact
|
82
|
+
end
|
64
83
|
|
65
84
|
self.controller.run
|
66
85
|
end
|