falcon 0.40.1 → 0.42.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 94f85c9e2ed95f6a43fda46cc07a986436ec7d874294b28288bcb004d269ea3c
4
- data.tar.gz: f56850aab821d12cd73cdc1545ad2691bce81d1121ae543feb3d29f6e8ae28da
3
+ metadata.gz: a1959720f161f341e3f36ab467a8553d77ab84792807fb0e5a9388ace47d59b3
4
+ data.tar.gz: b9c41ff2552dd041fd38b9d8fc6b0c1adcf1adb5b6be07505ffa9e18b2f14dab
5
5
  SHA512:
6
- metadata.gz: 047ac6d846d54362c8cc220f0264c60f9c951c557a61ca9c34636a93198acf04ab1b6e0b7ff10a684a8700f4b6a158dd57abf453cfa71bd5e9dee949eeedccdf
7
- data.tar.gz: 425b8131c9cbbe18684492213351e480a42d8037fc198c234ce7cd19e5f3f61b5bf59145b8335e8508928ee766ddcbff8992925084c2f5dbd5cfe1f0207de03b
6
+ metadata.gz: bcadfdf7cfcd50fe738b9f3a807d8ae4ea905d9194137d54439765746ba70f4981cf8ee04fe4ac481821512c9e7a99cc50a6f5c32100c32a41ca4c05b56fb037
7
+ data.tar.gz: a6e6f4ac6cf81b51cefa65e99b58056e2d131bf758523430cf2cb24220b09a13adf033127fd62921d0f5c51d04570825f538ae8cdf5569aa32950d65d30310b2
checksums.yaml.gz.sig CHANGED
Binary file
data/lib/.DS_Store ADDED
Binary file
@@ -20,7 +20,6 @@
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 '../extensions/openssl'
24
23
  require_relative '../controller/proxy'
25
24
  require_relative '../tls'
26
25
 
@@ -46,7 +45,7 @@ environment(:tls) do
46
45
  # The list of certificates loaded from that path.
47
46
  # @attribute [Array(OpenSSL::X509::Certificate)]
48
47
  ssl_certificates do
49
- OpenSSL::X509.load_certificates(ssl_certificate_path)
48
+ OpenSSL::X509::Certificate.load_file(ssl_certificate_path)
50
49
  end
51
50
 
52
51
  # The main certificate.
data/lib/falcon/server.rb CHANGED
@@ -28,9 +28,7 @@ require 'protocol/http/content_encoding'
28
28
  require 'async/http/cache'
29
29
 
30
30
  require_relative 'middleware/verbose'
31
-
32
- require_relative 'adapters/rewindable'
33
- require_relative 'adapters/rack'
31
+ require 'protocol/rack'
34
32
 
35
33
  module Falcon
36
34
  # A server listening on a specific endpoint, hosting a specific middleware.
@@ -50,8 +48,7 @@ module Falcon
50
48
  end
51
49
 
52
50
  use ::Protocol::HTTP::ContentEncoding
53
- use Adapters::Rewindable
54
- use Adapters::Rack
51
+ use ::Protocol::Rack::Adapter
55
52
 
56
53
  run rack_app
57
54
  end
data/lib/falcon/tls.rb CHANGED
@@ -20,8 +20,6 @@
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 'extensions/openssl'
24
-
25
23
  module Falcon
26
24
  module TLS
27
25
  # The list of supported ciphers.
@@ -21,5 +21,5 @@
21
21
  # THE SOFTWARE.
22
22
 
23
23
  module Falcon
24
- VERSION = "0.40.1"
24
+ VERSION = "0.42.1"
25
25
  end
@@ -27,8 +27,7 @@ module Rack
27
27
  # Run the specified app using the given options:
28
28
  # @parameter app [Object] The rack middleware.
29
29
  def self.run(app, **options)
30
- app = ::Falcon::Adapters::Rack.new(app)
31
- app = ::Falcon::Adapters::Rewindable.new(app)
30
+ app = ::Protocol::Rack::Adapter.new(app)
32
31
 
33
32
  Sync do |task|
34
33
  endpoint = endpoint_for(**options)
data.tar.gz.sig CHANGED
Binary file
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: falcon
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.40.1
4
+ version: 0.42.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Samuel Williams
@@ -27,33 +27,34 @@ bindir: bin
27
27
  cert_chain:
28
28
  - |
29
29
  -----BEGIN CERTIFICATE-----
30
- MIIEhDCCAuygAwIBAgIBATANBgkqhkiG9w0BAQsFADA3MTUwMwYDVQQDDCxzYW11
31
- ZWwud2lsbGlhbXMvREM9b3Jpb250cmFuc2Zlci9EQz1jby9EQz1uejAeFw0yMTA4
32
- MTYwNjMzNDRaFw0yMjA4MTYwNjMzNDRaMDcxNTAzBgNVBAMMLHNhbXVlbC53aWxs
33
- aWFtcy9EQz1vcmlvbnRyYW5zZmVyL0RDPWNvL0RDPW56MIIBojANBgkqhkiG9w0B
34
- AQEFAAOCAY8AMIIBigKCAYEAyXLSS/cw+fXJ5e7hi+U/TeChPWeYdwJojDsFY1xr
35
- xvtqbTTL8gbLHz5LW3QD2nfwCv3qTlw0qI3Ie7a9VMJMbSvgVEGEfQirqIgJXWMj
36
- eNMDgKsMJtC7u/43abRKx7TCURW3iWyR19NRngsJJmaR51yGGGm2Kfsr+JtKKLtL
37
- L188Wm3f13KAx7QJU8qyuBnj1/gWem076hzdA7xi1DbrZrch9GCRz62xymJlrJHn
38
- 9iZEZ7AxrS7vokhMlzSr/XMUihx/8aFKtk+tMLClqxZSmBWIErWdicCGTULXCBNb
39
- E/mljo4zEVKhlTWpJklMIhr55ZRrSarKFuW7en0+tpJrfsYiAmXMJNi4XAYJH7uL
40
- rgJuJwSaa/dMz+VmUoo7VKtSfCoOI+6v5/z0sK3oT6sG6ZwyI47DBq2XqNC6tnAj
41
- w+XmCywiTQrFzMMAvcA7rPI4F0nU1rZId51rOvvfxaONp+wgTi4P8owZLw0/j0m4
42
- 8C20DYi6EYx4AHDXiLpElWh3AgMBAAGjgZowgZcwCQYDVR0TBAIwADALBgNVHQ8E
43
- BAMCBLAwHQYDVR0OBBYEFB6ZaeWKxQjGTI+pmz7cKRmMIywwMC4GA1UdEQQnMCWB
44
- I3NhbXVlbC53aWxsaWFtc0BvcmlvbnRyYW5zZmVyLmNvLm56MC4GA1UdEgQnMCWB
45
- I3NhbXVlbC53aWxsaWFtc0BvcmlvbnRyYW5zZmVyLmNvLm56MA0GCSqGSIb3DQEB
46
- CwUAA4IBgQBVoM+pu3dpdUhZM1w051iw5GfiqclAr1Psypf16Tiod/ho//4oAu6T
47
- 9fj3DPX/acWV9P/FScvqo4Qgv6g4VWO5ZU7z2JmPoTXZtYMunRAmQPFL/gSUc6aK
48
- vszMHIyhtyzRc6DnfW2AiVOjMBjaYv8xXZc9bduniRVPrLR4J7ozmGLh4o4uJp7w
49
- x9KCFaR8Lvn/r0oJWJOqb/DMAYI83YeN2Dlt3jpwrsmsONrtC5S3gOUle5afSGos
50
- bYt5ocnEpKSomR9ZtnCGljds/aeO1Xgpn2r9HHcjwnH346iNrnHmMlC7BtHUFPDg
51
- Ts92S47PTOXzwPBDsrFiq3VLbRjHSwf8rpqybQBH9MfzxGGxTaETQYOd6b4e4Ag6
52
- y92abGna0bmIEb4+Tx9rQ10Uijh1POzvr/VTH4bbIPy9FbKrRsIQ24qDbNJRtOpE
53
- RAOsIl+HOBTb252nx1kIRN5hqQx272AJCbCjKx8egcUQKffFVVCI0nye09v5CK+a
54
- HiLJ8VOFx6w=
30
+ MIIE2DCCA0CgAwIBAgIBATANBgkqhkiG9w0BAQsFADBhMRgwFgYDVQQDDA9zYW11
31
+ ZWwud2lsbGlhbXMxHTAbBgoJkiaJk/IsZAEZFg1vcmlvbnRyYW5zZmVyMRIwEAYK
32
+ CZImiZPyLGQBGRYCY28xEjAQBgoJkiaJk/IsZAEZFgJuejAeFw0yMjA4MDYwNDUz
33
+ MjRaFw0zMjA4MDMwNDUzMjRaMGExGDAWBgNVBAMMD3NhbXVlbC53aWxsaWFtczEd
34
+ MBsGCgmSJomT8ixkARkWDW9yaW9udHJhbnNmZXIxEjAQBgoJkiaJk/IsZAEZFgJj
35
+ bzESMBAGCgmSJomT8ixkARkWAm56MIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIB
36
+ igKCAYEAomvSopQXQ24+9DBB6I6jxRI2auu3VVb4nOjmmHq7XWM4u3HL+pni63X2
37
+ 9qZdoq9xt7H+RPbwL28LDpDNflYQXoOhoVhQ37Pjn9YDjl8/4/9xa9+NUpl9XDIW
38
+ sGkaOY0eqsQm1pEWkHJr3zn/fxoKPZPfaJOglovdxf7dgsHz67Xgd/ka+Wo1YqoE
39
+ e5AUKRwUuvaUaumAKgPH+4E4oiLXI4T1Ff5Q7xxv6yXvHuYtlMHhYfgNn8iiW8WN
40
+ XibYXPNP7NtieSQqwR/xM6IRSoyXKuS+ZNGDPUUGk8RoiV/xvVN4LrVm9upSc0ss
41
+ RZ6qwOQmXCo/lLcDUxJAgG95cPw//sI00tZan75VgsGzSWAOdjQpFM0l4dxvKwHn
42
+ tUeT3ZsAgt0JnGqNm2Bkz81kG4A2hSyFZTFA8vZGhp+hz+8Q573tAR89y9YJBdYM
43
+ zp0FM4zwMNEUwgfRzv1tEVVUEXmoFCyhzonUUw4nE4CFu/sE3ffhjKcXcY//qiSW
44
+ xm4erY3XAgMBAAGjgZowgZcwCQYDVR0TBAIwADALBgNVHQ8EBAMCBLAwHQYDVR0O
45
+ BBYEFO9t7XWuFf2SKLmuijgqR4sGDlRsMC4GA1UdEQQnMCWBI3NhbXVlbC53aWxs
46
+ aWFtc0BvcmlvbnRyYW5zZmVyLmNvLm56MC4GA1UdEgQnMCWBI3NhbXVlbC53aWxs
47
+ aWFtc0BvcmlvbnRyYW5zZmVyLmNvLm56MA0GCSqGSIb3DQEBCwUAA4IBgQB5sxkE
48
+ cBsSYwK6fYpM+hA5B5yZY2+L0Z+27jF1pWGgbhPH8/FjjBLVn+VFok3CDpRqwXCl
49
+ xCO40JEkKdznNy2avOMra6PFiQyOE74kCtv7P+Fdc+FhgqI5lMon6tt9rNeXmnW/
50
+ c1NaMRdxy999hmRGzUSFjozcCwxpy/LwabxtdXwXgSay4mQ32EDjqR1TixS1+smp
51
+ 8C/NCWgpIfzpHGJsjvmH2wAfKtTTqB9CVKLCWEnCHyCaRVuKkrKjqhYCdmMBqCws
52
+ JkxfQWC+jBVeG9ZtPhQgZpfhvh+6hMhraUYRQ6XGyvBqEUe+yo6DKIT3MtGE2+CP
53
+ eX9i9ZWBydWb8/rvmwmX2kkcBbX0hZS1rcR593hGc61JR6lvkGYQ2MYskBveyaxt
54
+ Q2K9NVun/S785AP05vKkXZEFYxqG6EW012U4oLcFl5MySFajYXRYbuUpH6AY+HP8
55
+ voD0MPg1DssDLKwXyt1eKD/+Fq0bFWhwVM/1XiAXL7lyYUyOq24KHgQ2Csg=
55
56
  -----END CERTIFICATE-----
56
- date: 2022-07-19 00:00:00.000000000 Z
57
+ date: 2022-08-18 00:00:00.000000000 Z
57
58
  dependencies:
58
59
  - !ruby/object:Gem::Dependency
59
60
  name: async
@@ -89,14 +90,14 @@ dependencies:
89
90
  requirements:
90
91
  - - "~>"
91
92
  - !ruby/object:Gem::Version
92
- version: 0.56.0
93
+ version: '0.57'
93
94
  type: :runtime
94
95
  prerelease: false
95
96
  version_requirements: !ruby/object:Gem::Requirement
96
97
  requirements:
97
98
  - - "~>"
98
99
  - !ruby/object:Gem::Version
99
- version: 0.56.0
100
+ version: '0.57'
100
101
  - !ruby/object:Gem::Dependency
101
102
  name: async-http-cache
102
103
  requirement: !ruby/object:Gem::Requirement
@@ -167,6 +168,20 @@ dependencies:
167
168
  - - "~>"
168
169
  - !ruby/object:Gem::Version
169
170
  version: '1.1'
171
+ - !ruby/object:Gem::Dependency
172
+ name: openssl
173
+ requirement: !ruby/object:Gem::Requirement
174
+ requirements:
175
+ - - "~>"
176
+ - !ruby/object:Gem::Version
177
+ version: '3.0'
178
+ type: :runtime
179
+ prerelease: false
180
+ version_requirements: !ruby/object:Gem::Requirement
181
+ requirements:
182
+ - - "~>"
183
+ - !ruby/object:Gem::Version
184
+ version: '3.0'
170
185
  - !ruby/object:Gem::Dependency
171
186
  name: process-metrics
172
187
  requirement: !ruby/object:Gem::Requirement
@@ -182,19 +197,19 @@ dependencies:
182
197
  - !ruby/object:Gem::Version
183
198
  version: 0.2.0
184
199
  - !ruby/object:Gem::Dependency
185
- name: rack
200
+ name: protocol-rack
186
201
  requirement: !ruby/object:Gem::Requirement
187
202
  requirements:
188
- - - ">="
203
+ - - "~>"
189
204
  - !ruby/object:Gem::Version
190
- version: '1.0'
205
+ version: 0.1.0
191
206
  type: :runtime
192
207
  prerelease: false
193
208
  version_requirements: !ruby/object:Gem::Requirement
194
209
  requirements:
195
- - - ">="
210
+ - - "~>"
196
211
  - !ruby/object:Gem::Version
197
- version: '1.0'
212
+ version: 0.1.0
198
213
  - !ruby/object:Gem::Dependency
199
214
  name: samovar
200
215
  requirement: !ruby/object:Gem::Requirement
@@ -243,14 +258,14 @@ dependencies:
243
258
  requirements:
244
259
  - - "~>"
245
260
  - !ruby/object:Gem::Version
246
- version: 0.14.0
261
+ version: 0.19.2
247
262
  type: :development
248
263
  prerelease: false
249
264
  version_requirements: !ruby/object:Gem::Requirement
250
265
  requirements:
251
266
  - - "~>"
252
267
  - !ruby/object:Gem::Version
253
- version: 0.14.0
268
+ version: 0.19.2
254
269
  - !ruby/object:Gem::Dependency
255
270
  name: bake
256
271
  requirement: !ruby/object:Gem::Requirement
@@ -304,12 +319,8 @@ files:
304
319
  - bake/falcon/supervisor.rb
305
320
  - bin/falcon
306
321
  - bin/falcon-host
322
+ - lib/.DS_Store
307
323
  - lib/falcon.rb
308
- - lib/falcon/adapters/input.rb
309
- - lib/falcon/adapters/output.rb
310
- - lib/falcon/adapters/rack.rb
311
- - lib/falcon/adapters/response.rb
312
- - lib/falcon/adapters/rewindable.rb
313
324
  - lib/falcon/command.rb
314
325
  - lib/falcon/command/host.rb
315
326
  - lib/falcon/command/paths.rb
@@ -334,7 +345,6 @@ files:
334
345
  - lib/falcon/environments/self_signed_tls.rb
335
346
  - lib/falcon/environments/supervisor.rb
336
347
  - lib/falcon/environments/tls.rb
337
- - lib/falcon/extensions/openssl.rb
338
348
  - lib/falcon/middleware/proxy.rb
339
349
  - lib/falcon/middleware/redirect.rb
340
350
  - lib/falcon/middleware/verbose.rb
metadata.gz.sig CHANGED
Binary file
@@ -1,152 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Copyright, 2018, 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 'async/io/buffer'
24
- require 'protocol/http/body/rewindable'
25
-
26
- module Falcon
27
- module Adapters
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.
33
- class Input
34
- # Initialize the input wrapper.
35
- # @parameter body [Protocol::HTTP::Body::Readable]
36
- def initialize(body)
37
- @body = body
38
-
39
- # Will hold remaining data in `#read`.
40
- @buffer = nil
41
- @finished = @body.nil?
42
- end
43
-
44
- # The input body.
45
- # @attribute [Protocol::HTTP::Body::Readable]
46
- attr :body
47
-
48
- # Enumerate chunks of the request body.
49
- # @yields {|chunk| ...}
50
- # @parameter chunk [String]
51
- def each(&block)
52
- return to_enum unless block_given?
53
-
54
- while chunk = gets
55
- yield chunk
56
- end
57
- end
58
-
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.
64
- def rewind
65
- if @body and @body.respond_to? :rewind
66
- # If the body is not rewindable, this will fail.
67
- @body.rewind
68
- @buffer = nil
69
- @finished = false
70
-
71
- return true
72
- end
73
-
74
- return false
75
- end
76
-
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
84
- def read(length = nil, buffer = nil)
85
- buffer ||= Async::IO::Buffer.new
86
- buffer.clear
87
-
88
- until buffer.bytesize == length
89
- @buffer = read_next if @buffer.nil?
90
- break if @buffer.nil?
91
-
92
- remaining_length = length - buffer.bytesize if length
93
-
94
- if remaining_length && remaining_length < @buffer.bytesize
95
- # We know that we are not going to reuse the original buffer.
96
- # But byteslice will generate a hidden copy. So let's freeze it first:
97
- @buffer.freeze
98
-
99
- buffer << @buffer.byteslice(0, remaining_length)
100
- @buffer = @buffer.byteslice(remaining_length, @buffer.bytesize)
101
- else
102
- buffer << @buffer
103
- @buffer = nil
104
- end
105
- end
106
-
107
- return nil if buffer.empty? && length && length > 0
108
-
109
- return buffer
110
- end
111
-
112
- # Has the input stream been read completely?
113
- # @returns [Boolean]
114
- def eof?
115
- @finished and @buffer.nil?
116
- end
117
-
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.
123
- def gets
124
- if @buffer.nil?
125
- return read_next
126
- else
127
- buffer = @buffer
128
- @buffer = nil
129
- return buffer
130
- end
131
- end
132
-
133
- # Close and discard the remainder of the input stream.
134
- def close
135
- @body&.close
136
- end
137
-
138
- private
139
-
140
- def read_next
141
- return nil if @finished
142
-
143
- if chunk = @body.read
144
- return chunk
145
- else
146
- @finished = true
147
- return nil
148
- end
149
- end
150
- end
151
- end
152
- end
@@ -1,120 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Copyright, 2018, 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 'protocol/http/body/readable'
24
- require 'protocol/http/body/file'
25
-
26
- module Falcon
27
- module Adapters
28
- # Wraps the rack response body.
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.
31
- class Output < ::Protocol::HTTP::Body::Readable
32
- CONTENT_LENGTH = 'content-length'.freeze
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.
38
- def self.wrap(status, headers, body)
39
- # In no circumstance do we want this header propagating out:
40
- if length = headers.delete(CONTENT_LENGTH)
41
- # We don't really trust the user to provide the right length to the transport.
42
- length = Integer(length)
43
- end
44
-
45
- if body.is_a?(::Protocol::HTTP::Body::Readable)
46
- return body
47
- elsif status == 200 and body.respond_to?(:to_path)
48
- # Don't mangle partial responsese (206)
49
- return ::Protocol::HTTP::Body::File.open(body.to_path)
50
- elsif body.is_a?(Array)
51
- length ||= body.sum(&:bytesize)
52
- return self.new(body, length)
53
- else
54
- return self.new(body, length)
55
- end
56
- end
57
-
58
- # Initialize the output wrapper.
59
- # @parameter body [Object] The rack response body.
60
- # @parameter length [Integer] The rack response length.
61
- def initialize(body, length)
62
- @length = length
63
- @body = body
64
-
65
- @chunks = nil
66
- end
67
-
68
- # The rack response body.
69
- attr :body
70
-
71
- # The content length of the rack response body.
72
- attr :length
73
-
74
- # Whether the body is empty.
75
- def empty?
76
- @length == 0 or (@body.respond_to?(:empty?) and @body.empty?)
77
- end
78
-
79
- # Whether the body can be read immediately.
80
- def ready?
81
- body.is_a?(Array) or body.respond_to?(:to_ary)
82
- end
83
-
84
- # Close the response body.
85
- def close(error = nil)
86
- if @body and @body.respond_to?(:close)
87
- @body.close
88
- end
89
-
90
- @body = nil
91
- @chunks = nil
92
-
93
- super
94
- end
95
-
96
- # Enumerate the response body.
97
- # @yields {|chunk| ...}
98
- # @parameter chunk [String]
99
- def each(&block)
100
- @body.each(&block)
101
- ensure
102
- self.close($!)
103
- end
104
-
105
- # Read the next chunk from the response body.
106
- # @returns [String | Nil]
107
- def read
108
- @chunks ||= @body.to_enum(:each)
109
-
110
- return @chunks.next
111
- rescue StopIteration
112
- return nil
113
- end
114
-
115
- def inspect
116
- "\#<#{self.class} length=#{@length.inspect} body=#{@body.class}>"
117
- end
118
- end
119
- end
120
- end
@@ -1,224 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Copyright, 2017, 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 'rack'
24
-
25
- require_relative 'input'
26
- require_relative 'response'
27
-
28
- require 'console'
29
-
30
- module Falcon
31
- module Adapters
32
- class Rack
33
- # CGI keys <https://tools.ietf.org/html/rfc3875#section-4.1>:
34
-
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'
48
-
49
- # Rack environment variables:
50
-
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'
62
-
63
- # Raised back up through the middleware when the underlying connection is hijacked.
64
- class FullHijack < StandardError
65
- end
66
-
67
- # Async::HTTP specific metadata:
68
-
69
- ASYNC_HTTP_REQUEST = "async.http.request"
70
-
71
- # Header constants:
72
-
73
- HTTP_X_FORWARDED_PROTO = 'HTTP_X_FORWARDED_PROTO'
74
-
75
- # Initialize the rack adaptor middleware.
76
- # @parameter app [Object] The rack middleware.
77
- def initialize(app)
78
- @app = app
79
-
80
- raise ArgumentError, "App must be callable!" unless @app.respond_to?(:call)
81
- end
82
-
83
- # Unwrap raw HTTP headers into the CGI-style expected by Rack middleware.
84
- #
85
- # Rack separates multiple headers with the same key, into a single field with multiple lines.
86
- #
87
- # @parameter headers [Protocol::HTTP::Headers] The raw HTTP request headers.
88
- # @parameter env [Hash] The rack request `env`.
89
- def unwrap_headers(headers, env)
90
- headers.each do |key, value|
91
- http_key = "HTTP_#{key.upcase.tr('-', '_')}"
92
-
93
- if current_value = env[http_key]
94
- env[http_key] = "#{current_value};#{value}"
95
- else
96
- env[http_key] = value
97
- end
98
- end
99
- end
100
-
101
- # Process the incoming request into a valid rack `env`.
102
- #
103
- # - Set the `env['CONTENT_TYPE']` and `env['CONTENT_LENGTH']` based on the incoming request body.
104
- # - Set the `env['HTTP_HOST']` header to the request authority.
105
- # - Set the `env['HTTP_X_FORWARDED_PROTO']` header to the request scheme.
106
- # - Set `env['REMOTE_ADDR']` to the request remote adress.
107
- #
108
- # @parameter request [Protocol::HTTP::Request] The incoming request.
109
- # @parameter env [Hash] The rack `env`.
110
- def unwrap_request(request, env)
111
- if content_type = request.headers.delete('content-type')
112
- env[CONTENT_TYPE] = content_type
113
- end
114
-
115
- # In some situations we don't know the content length, e.g. when using chunked encoding, or when decompressing the body.
116
- if body = request.body and length = body.length
117
- env[CONTENT_LENGTH] = length.to_s
118
- end
119
-
120
- self.unwrap_headers(request.headers, env)
121
-
122
- # HTTP/2 prefers `:authority` over `host`, so we do this for backwards compatibility.
123
- env[HTTP_HOST] ||= request.authority
124
-
125
- # This is the HTTP/1 header for the scheme of the request and is used by Rack.
126
- # Technically it should use the Forwarded header but this is not common yet.
127
- # https://tools.ietf.org/html/rfc7239#section-5.4
128
- # https://github.com/rack/rack/issues/1310
129
- env[HTTP_X_FORWARDED_PROTO] ||= request.scheme
130
-
131
- if remote_address = request.remote_address
132
- env[REMOTE_ADDR] = remote_address.ip_address if remote_address.ip?
133
- end
134
- end
135
-
136
- # Build a rack `env` from the incoming request and apply it to the rack middleware.
137
- #
138
- # @parameter request [Protocol::HTTP::Request] The incoming request.
139
- def call(request)
140
- request_path, query_string = request.path.split('?', 2)
141
- server_name, server_port = (request.authority || '').split(':', 2)
142
-
143
- env = {
144
- RACK_VERSION => [2, 0, 0],
145
-
146
- ASYNC_HTTP_REQUEST => request,
147
-
148
- RACK_INPUT => Input.new(request.body),
149
- RACK_ERRORS => $stderr,
150
- RACK_LOGGER => Console.logger,
151
-
152
- RACK_MULTITHREAD => true,
153
- RACK_MULTIPROCESS => true,
154
- RACK_RUNONCE => false,
155
-
156
- # The HTTP request method, such as “GET” or “POST”. This cannot ever be an empty string, and so is always required.
157
- REQUEST_METHOD => request.method,
158
-
159
- # The initial portion of the request URL's “path” that corresponds to the application object, so that the application knows its virtual “location”. This may be an empty string, if the application corresponds to the “root” of the server.
160
- SCRIPT_NAME => '',
161
-
162
- # The remainder of the request URL's “path”, designating the virtual “location” of the request's target within the application. This may be an empty string, if the request URL targets the application root and does not have a trailing slash. This value may be percent-encoded when originating from a URL.
163
- PATH_INFO => request_path,
164
- REQUEST_PATH => request_path,
165
- REQUEST_URI => request.path,
166
-
167
- # The portion of the request URL that follows the ?, if any. May be empty, but is always required!
168
- QUERY_STRING => query_string || '',
169
-
170
- # The server protocol (e.g. HTTP/1.1):
171
- SERVER_PROTOCOL => request.version,
172
-
173
- # The request scheme:
174
- RACK_URL_SCHEME => request.scheme,
175
-
176
- # I'm not sure what sane defaults should be here:
177
- SERVER_NAME => server_name,
178
- SERVER_PORT => server_port,
179
-
180
- # We support both request and response hijack.
181
- RACK_IS_HIJACK => true,
182
- }
183
-
184
- self.unwrap_request(request, env)
185
-
186
- full_hijack = false
187
-
188
- if request.hijack?
189
- env[RACK_HIJACK] = lambda do
190
- wrapper = request.hijack!
191
- full_hijack = true
192
-
193
- # We dup this as it might be taken out of the normal control flow, and the io will be closed shortly after returning from this method.
194
- io = wrapper.io.dup
195
- wrapper.close
196
-
197
- # This is implicitly returned:
198
- env[RACK_HIJACK_IO] = io
199
- end
200
- end
201
-
202
- status, headers, body = @app.call(env)
203
-
204
- # If there was a full hijack:
205
- if full_hijack
206
- raise FullHijack, "The connection was hijacked."
207
- else
208
- return Response.wrap(status, headers, body, request)
209
- end
210
- rescue => exception
211
- Console.logger.error(self) {exception}
212
-
213
- return failure_response(exception)
214
- end
215
-
216
- # Generate a suitable response for the given exception.
217
- # @parameter exception [Exception]
218
- # @returns [Protocol::HTTP::Response]
219
- def failure_response(exception)
220
- Protocol::HTTP::Response.for_exception(exception)
221
- end
222
- end
223
- end
224
- end
@@ -1,113 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Copyright, 2018, 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 'output'
24
- require_relative '../version'
25
- require_relative '../middleware/proxy'
26
-
27
- require 'async/http/body/hijack'
28
- require 'time'
29
-
30
- module Falcon
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}.
43
- class Response < ::Protocol::HTTP::Response
44
- IGNORE_HEADERS = Middleware::Proxy::HOP_HEADERS
45
-
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)]
48
- def self.wrap_headers(fields)
49
- headers = ::Protocol::HTTP::Headers.new
50
- meta = {}
51
-
52
- fields.each do |key, value|
53
- key = key.downcase
54
-
55
- if key.start_with?('rack.')
56
- meta[key] = value
57
- else
58
- value.to_s.split("\n").each do |part|
59
- headers.add(key, part)
60
- end
61
- end
62
- end
63
-
64
- return headers, meta
65
- end
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.
72
- def self.wrap(status, headers, body, request = nil)
73
- headers, meta = wrap_headers(headers)
74
-
75
- if block = meta['rack.hijack']
76
- body = Async::HTTP::Body::Hijack.wrap(request, &block)
77
- else
78
- ignored = headers.extract(IGNORE_HEADERS)
79
-
80
- unless ignored.empty?
81
- Console.logger.warn("Ignoring protocol-level headers: #{ignored.inspect}")
82
- end
83
-
84
- body = Output.wrap(status, headers, body)
85
- end
86
-
87
- if request&.head?
88
- # I thought about doing this in Output.wrap, but decided the semantics are too tricky. Specifically, the various ways a rack response body can be wrapped, and the need to invoke #close at the right point.
89
- body = ::Protocol::HTTP::Body::Head.for(body)
90
- end
91
-
92
- protocol = meta['rack.protocol']
93
-
94
- # https://tools.ietf.org/html/rfc7231#section-7.4.2
95
- # headers.add('server', "falcon/#{Falcon::VERSION}")
96
-
97
- # https://tools.ietf.org/html/rfc7231#section-7.1.1.2
98
- # headers.add('date', Time.now.httpdate)
99
-
100
- return self.new(status, headers, body, protocol)
101
- end
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.
108
- def initialize(status, headers, body, protocol = nil)
109
- super(nil, status, headers, body, protocol)
110
- end
111
- end
112
- end
113
- end
@@ -1,74 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Copyright, 2018, 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 'protocol/http/body/rewindable'
24
-
25
- module Falcon
26
- module Adapters
27
- # Content-type driven input buffering, specific to the needs of `rack`.
28
- class Rewindable < ::Protocol::HTTP::Middleware
29
- # Media types that require buffering.
30
- BUFFERED_MEDIA_TYPES = %r{
31
- application/x-www-form-urlencoded|
32
- multipart/form-data|
33
- multipart/related|
34
- multipart/mixed
35
- }x
36
-
37
- POST = 'POST'.freeze
38
-
39
- # Initialize the rewindable middleware.
40
- # @parameter app [Protocol::HTTP::Middleware] The middleware to wrap.
41
- def initialize(app)
42
- super(app)
43
- end
44
-
45
- # Determine whether the request needs a rewindable body.
46
- # @parameter request [Protocol::HTTP::Request]
47
- # @returns [Boolean]
48
- def needs_rewind?(request)
49
- content_type = request.headers['content-type']
50
-
51
- if request.method == POST and content_type.nil?
52
- return true
53
- end
54
-
55
- if BUFFERED_MEDIA_TYPES =~ content_type
56
- return true
57
- end
58
-
59
- return false
60
- end
61
-
62
- # Wrap the request body in a rewindable buffer if required.
63
- # @parameter request [Protocol::HTTP::Request]
64
- # @returns [Protocol::HTTP::Response] the response.
65
- def call(request)
66
- if body = request.body and needs_rewind?(request)
67
- request.body = Async::HTTP::Body::Rewindable.new(body)
68
- end
69
-
70
- return super
71
- end
72
- end
73
- end
74
- end
@@ -1,36 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Copyright, 2018, 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 'openssl/x509'
24
-
25
- module OpenSSL
26
- module X509
27
- CERTIFICATE_PATTERN = /-----BEGIN CERTIFICATE-----.*?-----END CERTIFICATE-----/m
28
-
29
- # An extension to load an array of certificates from a file at the given path.
30
- def self.load_certificates(path)
31
- File.read(path).scan(CERTIFICATE_PATTERN).collect do |text|
32
- Certificate.new(text)
33
- end
34
- end
35
- end
36
- end