falcon 0.36.2 → 0.36.7

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/early_hints.rb +8 -0
  3. data/lib/falcon/adapters/input.rb +32 -11
  4. data/lib/falcon/adapters/output.rb +20 -1
  5. data/lib/falcon/adapters/rack.rb +61 -34
  6. data/lib/falcon/adapters/response.rb +23 -1
  7. data/lib/falcon/adapters/rewindable.rb +10 -3
  8. data/lib/falcon/command.rb +2 -0
  9. data/lib/falcon/command/host.rb +13 -2
  10. data/lib/falcon/command/paths.rb +4 -0
  11. data/lib/falcon/command/proxy.rb +14 -0
  12. data/lib/falcon/command/redirect.rb +12 -0
  13. data/lib/falcon/command/serve.rb +22 -15
  14. data/lib/falcon/command/supervisor.rb +15 -1
  15. data/lib/falcon/command/top.rb +16 -0
  16. data/lib/falcon/command/virtual.rb +15 -0
  17. data/lib/falcon/configuration.rb +69 -7
  18. data/lib/falcon/controller/host.rb +12 -0
  19. data/lib/falcon/controller/proxy.rb +13 -0
  20. data/lib/falcon/controller/redirect.rb +7 -0
  21. data/lib/falcon/controller/serve.rb +16 -1
  22. data/lib/falcon/controller/virtual.rb +17 -0
  23. data/lib/falcon/endpoint.rb +8 -0
  24. data/lib/falcon/{configuration/proxy.rb → environments.rb} +9 -5
  25. data/lib/falcon/environments/application.rb +72 -0
  26. data/lib/falcon/{configuration/application.rb → environments/lets_encrypt_tls.rb} +21 -20
  27. data/lib/falcon/{configuration/lets_encrypt_tls.rb → environments/proxy.rb} +13 -6
  28. data/lib/falcon/{configuration → environments}/rack.rb +14 -2
  29. data/lib/falcon/{configuration → environments}/self_signed_tls.rb +9 -1
  30. data/lib/falcon/{configuration → environments}/supervisor.rb +19 -5
  31. data/lib/falcon/{configuration → environments}/tls.rb +39 -5
  32. data/lib/falcon/extensions/openssl.rb +1 -0
  33. data/lib/falcon/middleware/proxy.rb +26 -5
  34. data/lib/falcon/middleware/redirect.rb +11 -0
  35. data/lib/falcon/{verbose.rb → middleware/verbose.rb} +34 -26
  36. data/lib/falcon/proxy_endpoint.rb +21 -0
  37. data/lib/falcon/server.rb +8 -2
  38. data/lib/falcon/service/application.rb +23 -1
  39. data/lib/falcon/service/generic.rb +18 -0
  40. data/lib/falcon/service/proxy.rb +6 -0
  41. data/lib/falcon/service/supervisor.rb +14 -2
  42. data/lib/falcon/services.rb +21 -0
  43. data/lib/falcon/tls.rb +4 -2
  44. data/lib/falcon/version.rb +1 -1
  45. data/lib/rack/handler/falcon.rb +7 -1
  46. metadata +64 -120
  47. data/.editorconfig +0 -5
  48. data/.github/FUNDING.yml +0 -3
  49. data/.github/workflows/development.yml +0 -60
  50. data/.gitignore +0 -14
  51. data/.rspec +0 -3
  52. data/Gemfile +0 -17
  53. data/README.md +0 -316
  54. data/examples/beer/config.ru +0 -57
  55. data/examples/beer/falcon.rb +0 -8
  56. data/examples/benchmark/config.ru +0 -39
  57. data/examples/benchmark/falcon.rb +0 -6
  58. data/examples/csv/config.ru +0 -31
  59. data/examples/google/falcon.rb +0 -14
  60. data/examples/hello/config.ru +0 -22
  61. data/examples/hello/falcon.rb +0 -24
  62. data/examples/hello/preload.rb +0 -7
  63. data/examples/internet/config.ru +0 -54
  64. data/examples/memory/allocations.rb +0 -39
  65. data/examples/memory/config.ru +0 -14
  66. data/examples/push/client.rb +0 -29
  67. data/examples/push/config.ru +0 -28
  68. data/examples/push/index.html +0 -14
  69. data/examples/push/script.js +0 -2
  70. data/examples/push/style.css +0 -4
  71. data/examples/redis/Gemfile +0 -9
  72. data/examples/redis/config.ru +0 -28
  73. data/examples/sequel/Gemfile +0 -4
  74. data/examples/sequel/config.ru +0 -8
  75. data/examples/sequel/data.sqlite3 +0 -0
  76. data/examples/server/standalone.rb +0 -27
  77. data/examples/sinatra/Gemfile +0 -7
  78. data/examples/sinatra/Gemfile.lock +0 -53
  79. data/examples/sinatra/config.ru +0 -16
  80. data/examples/trailers/config.ru +0 -34
  81. data/examples/trailers/falcon.rb +0 -8
  82. data/falcon.gemspec +0 -45
  83. data/gems/rack1.gemfile +0 -4
  84. data/gems/rack3.gemfile +0 -4
  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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e13397b3663c4dc283f115f38856372010cfd01f2266604fefbcd1abbc8fefa9
4
- data.tar.gz: 3ed6941da3a65d6701118a0c4e24dbedb596aaf203cc80e95f18dea949e8bdc0
3
+ metadata.gz: cf341f482ade4d35f1b63da2c7b937d85f69a07537e5073233f5e2aeb771bcd6
4
+ data.tar.gz: ab45bb5d32c5a6afd41d83069da778e085f03b87704f2d67301ceaa251725772
5
5
  SHA512:
6
- metadata.gz: bea6cd8dac31f9264702e0a224119eb81404700e0014eb2d1274ff4ee07c736c0ede4e063ef80dd283b6ec449ebad8b5559bb8b4891c19e9f618e9f4148aaa3d
7
- data.tar.gz: 8547330b47d5690570da14db3d743e0679ada693d213eb822f011faf23376094cce236301917641fca997d8fb4be88e0b8dd341f0d0b19cba2a294faa0d13ba5
6
+ metadata.gz: 1c8bdfbfc3d7056695eed090fc49bf07e21517a251c5e7185b7450db9bf6648dd8f6d8d5f6b32472feeea0aa6f6cd2d384cac4dc8bfd06b20e924e88ae2a08ca
7
+ data.tar.gz: 22b1c2729420fbb1190dde29b0774f39ac86d52e75f2b0a5769c7ebde1ddba1a71dcaaa44bee27b3200ad349a2026641050bfccbcafe6d860d4ea868f7bd862b
@@ -24,17 +24,25 @@ require 'protocol/http/middleware'
24
24
 
25
25
  module Falcon
26
26
  module Adapters
27
+ # Provide an interface for advising the client to preload related resources.
27
28
  class EarlyHints
28
29
  PRELOAD = /<(?<path>.*?)>;.*?rel=preload/
29
30
 
31
+ # Initialize the early hints interface.
32
+ #
33
+ # @parameter request [Protocol::HTTP::Request]
30
34
  def initialize(request)
31
35
  @request = request
32
36
  end
33
37
 
38
+ # Advise the request that the specified path should be preloaded.
39
+ # @parameter path [String]
40
+ # @parameter preload [Boolean] whether the client should preload the resource.
34
41
  def push(path, preload: true, **options)
35
42
  @request.push(path)
36
43
  end
37
44
 
45
+ # Extract link headers and invoke {push}.
38
46
  def call(headers)
39
47
  headers.each do |key, value|
40
48
  if key.casecmp("link").zero? and match = PRELOAD.match(value)
@@ -25,8 +25,14 @@ require 'protocol/http/body/rewindable'
25
25
 
26
26
  module Falcon
27
27
  module Adapters
28
- # 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.
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
- # each must be called without arguments and only yield Strings.
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
- # 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.
50
- # @return [Boolean] whether the body could be rewound.
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
- # read behaves like IO#read. Its signature is read([length, [buffer]]). 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 until EOF. When EOF is reached, this method returns nil if length is given and not nil, or “” if length is not given or is nil. If buffer is given, then the read data will be placed into buffer instead of a newly created String object.
65
- # @param length [Integer] the amount of data to read
66
- # @param buffer [String] the buffer which will receive the data
67
- # @return a buffer containing the data
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
- # gets must be called without arguments and return a string, or nil on EOF.
101
- # @return [String, nil] The next chunk from the body.
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
- # close must never be called on the input stream. huh?
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
- # The Body must respond to each and must only yield String values. The Body itself should not be an instance of String, as this will break in Ruby 1.9. 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.
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
 
@@ -31,40 +31,48 @@ require 'async/logger'
31
31
  module Falcon
32
32
  module Adapters
33
33
  class Rack
34
- # CGI keys (https://tools.ietf.org/html/rfc3875#section-4.1)
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
34
+ # CGI keys <https://tools.ietf.org/html/rfc3875#section-4.1>:
48
35
 
49
- # Rack environment variables
50
- RACK_VERSION = 'rack.version'.freeze
51
- RACK_ERRORS = 'rack.errors'.freeze
52
- RACK_LOGGER = 'rack.logger'.freeze
53
- RACK_INPUT = 'rack.input'.freeze
54
- RACK_MULTITHREAD = 'rack.multithread'.freeze
55
- RACK_MULTIPROCESS = 'rack.multiprocess'.freeze
56
- RACK_RUNONCE = 'rack.run_once'.freeze
57
- RACK_URL_SCHEME = 'rack.url_scheme'.freeze
58
- RACK_HIJACK = 'rack.hijack'.freeze
59
- RACK_IS_HIJACK = 'rack.hijack?'.freeze
60
- RACK_HIJACK_IO = 'rack.hijack_io'.freeze
61
- RACK_EARLY_HINTS = "rack.early_hints".freeze
36
+ HTTP_HOST = 'HTTP_HOST'
37
+ PATH_INFO = 'PATH_INFO'
38
+ REQUEST_METHOD = 'REQUEST_METHOD'
39
+ REQUEST_PATH = 'REQUEST_PATH'
40
+ REQUEST_URI = 'REQUEST_URI'
41
+ SCRIPT_NAME = 'SCRIPT_NAME'
42
+ QUERY_STRING = 'QUERY_STRING'
43
+ SERVER_PROTOCOL = 'SERVER_PROTOCOL'
44
+ SERVER_NAME = 'SERVER_NAME'
45
+ SERVER_PORT = 'SERVER_PORT'
46
+ REMOTE_ADDR = 'REMOTE_ADDR'
47
+ CONTENT_TYPE = 'CONTENT_TYPE'
48
+ CONTENT_LENGTH = 'CONTENT_LENGTH'
62
49
 
63
- ASYNC_HTTP_REQUEST = "async.http.request".freeze
50
+ # Rack environment variables:
64
51
 
65
- # Header constants
66
- HTTP_X_FORWARDED_PROTO = 'HTTP_X_FORWARDED_PROTO'.freeze
52
+ RACK_VERSION = 'rack.version'
53
+ RACK_ERRORS = 'rack.errors'
54
+ RACK_LOGGER = 'rack.logger'
55
+ RACK_INPUT = 'rack.input'
56
+ RACK_MULTITHREAD = 'rack.multithread'
57
+ RACK_MULTIPROCESS = 'rack.multiprocess'
58
+ RACK_RUNONCE = 'rack.run_once'
59
+ RACK_URL_SCHEME = 'rack.url_scheme'
60
+ RACK_HIJACK = 'rack.hijack'
61
+ RACK_IS_HIJACK = 'rack.hijack?'
62
+ RACK_HIJACK_IO = 'rack.hijack_io'
63
+ RACK_EARLY_HINTS = "rack.early_hints"
67
64
 
65
+ # Async::HTTP specific metadata:
66
+
67
+ ASYNC_HTTP_REQUEST = "async.http.request"
68
+
69
+ # Header constants:
70
+
71
+ HTTP_X_FORWARDED_PROTO = 'HTTP_X_FORWARDED_PROTO'
72
+
73
+ # Initialize the rack adaptor middleware.
74
+ # @parameter app [Object] The rack middleware.
75
+ # @parameter logger [Console::Logger] The logger to use.
68
76
  def initialize(app, logger = Async.logger)
69
77
  @app = app
70
78
 
@@ -73,7 +81,12 @@ module Falcon
73
81
  @logger = logger
74
82
  end
75
83
 
76
- # Rack separates multiple headers with the same key, into a single field with multiple "lines".
84
+ # Unwrap raw HTTP headers into the CGI-style expected by Rack middleware.
85
+ #
86
+ # Rack separates multiple headers with the same key, into a single field with multiple lines.
87
+ #
88
+ # @parameter headers [Protocol::HTTP::Headers] The raw HTTP request headers.
89
+ # @parameter env [Hash] The rack request `env`.
77
90
  def unwrap_headers(headers, env)
78
91
  headers.each do |key, value|
79
92
  http_key = "HTTP_#{key.upcase.tr('-', '_')}"
@@ -86,7 +99,15 @@ module Falcon
86
99
  end
87
100
  end
88
101
 
89
- # Process the incoming request into a valid rack env.
102
+ # Process the incoming request into a valid rack `env`.
103
+ #
104
+ # - Set the `env['CONTENT_TYPE']` and `env['CONTENT_LENGTH']` based on the incoming request body.
105
+ # - Set the `env['HTTP_HOST']` header to the request authority.
106
+ # - Set the `env['HTTP_X_FORWARDED_PROTO']` header to the request scheme.
107
+ # - Set `env['REMOTE_ADDR']` to the request remote adress.
108
+ #
109
+ # @parameter request [Protocol::HTTP::Request] The incoming request.
110
+ # @parameter env [Hash] The rack `env`.
90
111
  def unwrap_request(request, env)
91
112
  if content_type = request.headers.delete('content-type')
92
113
  env[CONTENT_TYPE] = content_type
@@ -113,6 +134,9 @@ module Falcon
113
134
  end
114
135
  end
115
136
 
137
+ # Build a rack `env` from the incoming request and apply it to the rack middleware.
138
+ #
139
+ # @parameter request [Protocol::HTTP::Request] The incoming request.
116
140
  def call(request)
117
141
  request_path, query_string = request.path.split('?', 2)
118
142
  server_name, server_port = (request.authority || '').split(':', 2)
@@ -151,8 +175,8 @@ module Falcon
151
175
  RACK_URL_SCHEME => request.scheme,
152
176
 
153
177
  # I'm not sure what sane defaults should be here:
154
- SERVER_NAME => server_name || '',
155
- SERVER_PORT => server_port || '',
178
+ SERVER_NAME => server_name,
179
+ SERVER_PORT => server_port,
156
180
 
157
181
  # We support both request and response hijack.
158
182
  RACK_IS_HIJACK => true,
@@ -194,6 +218,9 @@ module Falcon
194
218
  return failure_response(exception)
195
219
  end
196
220
 
221
+ # Generate a suitable response for the given exception.
222
+ # @parameter exception [Exception]
223
+ # @returns [Protocol::HTTP::Response]
197
224
  def failure_response(exception)
198
225
  Protocol::HTTP::Response.for_exception(exception)
199
226
  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
- # Append a list of newline encoded headers.
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 type driven input buffering.
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
- # @return [Protocol::HTTP::Response] the response.
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)
@@ -24,6 +24,8 @@ require_relative 'command/top'
24
24
 
25
25
  module Falcon
26
26
  module Command
27
+ # The main entry point for the `falcon` executable.
28
+ # @parameter arguments [Array(String)] The command line arguments.
27
29
  def self.call(*arguments)
28
30
  Top.call(*arguments)
29
31
  end
@@ -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
- def configuration(verbose = false)
41
- configuration = Configuration.new(verbose)
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!"