falcon 0.27.0 → 0.28.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/falcon.gemspec +2 -2
- data/lib/falcon/adapters/hijack.rb +90 -0
- data/lib/falcon/adapters/rack.rb +14 -26
- data/lib/falcon/adapters/response.rb +20 -11
- data/lib/falcon/version.rb +1 -1
- metadata +8 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 93cb18cb7157a7f267220d2d81a93d3c28bd96efddc04f799f64bdb658f9fa6b
|
4
|
+
data.tar.gz: efba9da82250f426f87705588d1601e0e6a349d1879a07b1fd18d7130f4a1091
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f076c28cf4e3012624f56e775f27403bcd36aca3bf6c2f30120ce3c3fe60476a2cc1f68d2e64f235b66b51e9d08219d5242ec993b53a32e65e6e187dce991187
|
7
|
+
data.tar.gz: ae523db0420b3b8408ed84d9d6f99ae4ab6cabd0672699ed81c0d89a866aeed157d6b14984ce72ce8bd2bf930a074d5e702b4f4dfc74c73113eb9d2e2fb408b7
|
data/README.md
CHANGED
@@ -42,7 +42,7 @@ Alternatively, install in terminal:
|
|
42
42
|
|
43
43
|
## Usage
|
44
44
|
|
45
|
-
You can run `falcon serve` directly. It will load the `config.ru` and start serving on https://localhost:9292.
|
45
|
+
You can run `falcon serve` directly. It will load the `config.ru` and start serving on https://localhost:9292. Please [try the interactive online tutorial](https://katacoda.com/ioquatix/scenarios/falcon-introduction).
|
46
46
|
|
47
47
|
The `falcon serve` command has the following options for you to use:
|
48
48
|
|
data/falcon.gemspec
CHANGED
@@ -16,10 +16,10 @@ Gem::Specification.new do |spec|
|
|
16
16
|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
17
17
|
spec.require_paths = ["lib"]
|
18
18
|
|
19
|
-
spec.add_dependency "http-protocol", "~> 0.
|
19
|
+
spec.add_dependency "http-protocol", "~> 0.17"
|
20
20
|
|
21
21
|
spec.add_dependency "async", "~> 1.13"
|
22
|
-
spec.add_dependency "async-io", "~> 1.
|
22
|
+
spec.add_dependency "async-io", "~> 1.22"
|
23
23
|
spec.add_dependency "async-http", "~> 0.38.0"
|
24
24
|
spec.add_dependency "async-container", "~> 0.10.0"
|
25
25
|
|
@@ -0,0 +1,90 @@
|
|
1
|
+
# Copyright, 2019, by Samuel G. D. Williams. <http://www.codeotaku.com>
|
2
|
+
#
|
3
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
# of this software and associated documentation files (the "Software"), to deal
|
5
|
+
# in the Software without restriction, including without limitation the rights
|
6
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
# copies of the Software, and to permit persons to whom the Software is
|
8
|
+
# furnished to do so, subject to the following conditions:
|
9
|
+
#
|
10
|
+
# The above copyright notice and this permission notice shall be included in
|
11
|
+
# all copies or substantial portions of the Software.
|
12
|
+
#
|
13
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
# THE SOFTWARE.
|
20
|
+
|
21
|
+
require 'async/http/middleware'
|
22
|
+
require 'async/io/buffer'
|
23
|
+
|
24
|
+
module Falcon
|
25
|
+
module Adapters
|
26
|
+
# This is used for implementing partial hijack.
|
27
|
+
class Hijack
|
28
|
+
def self.for(env, block, socket = nil, task: Async::Task.current)
|
29
|
+
input = env[Rack::RACK_INPUT]
|
30
|
+
output = Async::HTTP::Body::Writable.new
|
31
|
+
|
32
|
+
stream = Hijack.new(input, output, socket)
|
33
|
+
|
34
|
+
task.async do
|
35
|
+
begin
|
36
|
+
block.call(stream)
|
37
|
+
ensure
|
38
|
+
stream.close
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
return output
|
43
|
+
end
|
44
|
+
|
45
|
+
def initialize(input, output, socket)
|
46
|
+
@input = input
|
47
|
+
@output = output
|
48
|
+
@socket = socket
|
49
|
+
@closed = false
|
50
|
+
end
|
51
|
+
|
52
|
+
def read(length = nil, buffer = nil)
|
53
|
+
@input.read(length, buffer)
|
54
|
+
end
|
55
|
+
|
56
|
+
def read_nonblock(length, buffer = nil)
|
57
|
+
@input.read(length, buffer)
|
58
|
+
end
|
59
|
+
|
60
|
+
def write(buffer)
|
61
|
+
@output.write(buffer)
|
62
|
+
end
|
63
|
+
|
64
|
+
alias write_nonblock write
|
65
|
+
|
66
|
+
def flush
|
67
|
+
end
|
68
|
+
|
69
|
+
def close
|
70
|
+
return if @closed
|
71
|
+
|
72
|
+
@input.close
|
73
|
+
@output.close
|
74
|
+
@closed = true
|
75
|
+
end
|
76
|
+
|
77
|
+
def close_read
|
78
|
+
@input.close
|
79
|
+
end
|
80
|
+
|
81
|
+
def close_write
|
82
|
+
@output.close
|
83
|
+
end
|
84
|
+
|
85
|
+
def closed?
|
86
|
+
@closed
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
data/lib/falcon/adapters/rack.rb
CHANGED
@@ -23,6 +23,7 @@ require 'rack'
|
|
23
23
|
require_relative 'input'
|
24
24
|
require_relative 'response'
|
25
25
|
require_relative 'early_hints'
|
26
|
+
require_relative 'hijack'
|
26
27
|
|
27
28
|
require 'async/logger'
|
28
29
|
|
@@ -111,12 +112,6 @@ module Falcon
|
|
111
112
|
end
|
112
113
|
end
|
113
114
|
|
114
|
-
def make_response(request, status, headers, body)
|
115
|
-
# @logger.debug(request) {"Rack response: #{status} #{headers.inspect} #{body.class}"}
|
116
|
-
|
117
|
-
return Response.wrap(status, headers, body)
|
118
|
-
end
|
119
|
-
|
120
115
|
def call(request)
|
121
116
|
request_path, query_string = request.path.split('?', 2)
|
122
117
|
server_name, server_port = (request.authority || '').split(':', 2)
|
@@ -157,6 +152,9 @@ module Falcon
|
|
157
152
|
# I'm not sure what sane defaults should be here:
|
158
153
|
SERVER_NAME => server_name || '',
|
159
154
|
SERVER_PORT => server_port || '',
|
155
|
+
|
156
|
+
# We support both request and response hijack.
|
157
|
+
RACK_IS_HIJACK => true,
|
160
158
|
}
|
161
159
|
|
162
160
|
self.unwrap_request(request, env)
|
@@ -165,11 +163,12 @@ module Falcon
|
|
165
163
|
env[RACK_EARLY_HINTS] = EarlyHints.new(request)
|
166
164
|
end
|
167
165
|
|
166
|
+
full_hijack = false
|
167
|
+
|
168
168
|
if request.hijack?
|
169
|
-
env[RACK_IS_HIJACK] = true
|
170
|
-
|
171
169
|
env[RACK_HIJACK] = lambda do
|
172
|
-
wrapper = request.hijack
|
170
|
+
wrapper = request.hijack!
|
171
|
+
full_hijack = true
|
173
172
|
|
174
173
|
# 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.
|
175
174
|
io = wrapper.io.dup
|
@@ -178,27 +177,16 @@ module Falcon
|
|
178
177
|
# This is implicitly returned:
|
179
178
|
env[RACK_HIJACK_IO] = io
|
180
179
|
end
|
181
|
-
else
|
182
|
-
env[RACK_IS_HIJACK] = false
|
183
180
|
end
|
184
181
|
|
185
182
|
status, headers, body = @app.call(env)
|
186
183
|
|
187
|
-
#
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
# end
|
194
|
-
# return nil
|
195
|
-
# end
|
196
|
-
|
197
|
-
# if env['rack.hijack_io']
|
198
|
-
# return nil
|
199
|
-
# end
|
200
|
-
|
201
|
-
return make_response(request, status, headers, body)
|
184
|
+
# If there was a full hijack:
|
185
|
+
if full_hijack
|
186
|
+
return nil
|
187
|
+
else
|
188
|
+
return Response.wrap(status, headers, body, request, env)
|
189
|
+
end
|
202
190
|
rescue => exception
|
203
191
|
@logger.error(self) {exception}
|
204
192
|
|
@@ -19,6 +19,7 @@
|
|
19
19
|
# THE SOFTWARE.
|
20
20
|
|
21
21
|
require_relative 'output'
|
22
|
+
require_relative 'hijack'
|
22
23
|
require_relative '../version'
|
23
24
|
require_relative '../proxy'
|
24
25
|
|
@@ -27,20 +28,18 @@ require 'time'
|
|
27
28
|
module Falcon
|
28
29
|
module Adapters
|
29
30
|
class Response < Async::HTTP::Response
|
30
|
-
IGNORE_HEADERS =
|
31
|
+
IGNORE_HEADERS = Proxy::HOP_HEADERS
|
31
32
|
|
32
33
|
# Append a list of newline encoded headers.
|
33
34
|
def self.wrap_headers(fields)
|
34
35
|
headers = ::HTTP::Protocol::Headers.new
|
36
|
+
meta = {}
|
35
37
|
|
36
38
|
fields.each do |key, value|
|
37
|
-
next if key.start_with? 'rack.'
|
38
|
-
|
39
|
-
# This is a quick fix, but perhaps it should be part of the protocol, because it IS valid for HTTP/1.
|
40
39
|
key = key.downcase
|
41
40
|
|
42
|
-
if
|
43
|
-
|
41
|
+
if key.start_with?('rack.')
|
42
|
+
meta[key] = value
|
44
43
|
else
|
45
44
|
value.to_s.split("\n").each do |part|
|
46
45
|
headers.add(key, part)
|
@@ -48,11 +47,23 @@ module Falcon
|
|
48
47
|
end
|
49
48
|
end
|
50
49
|
|
51
|
-
return headers
|
50
|
+
return headers, meta
|
52
51
|
end
|
53
52
|
|
54
|
-
def self.wrap(status, headers, body)
|
55
|
-
headers = wrap_headers(headers)
|
53
|
+
def self.wrap(status, headers, body, request = nil, env = nil)
|
54
|
+
headers, meta = wrap_headers(headers)
|
55
|
+
|
56
|
+
if block = meta['rack.hijack'] and request and env
|
57
|
+
body = Hijack.for(env, block, request.hijack? ? request.hijack! : nil)
|
58
|
+
else
|
59
|
+
sliced = headers.slice!(IGNORE_HEADERS)
|
60
|
+
|
61
|
+
unless sliced.empty?
|
62
|
+
Async.logger.warn("Ignoring protocol-level headers: #{sliced.inspect}")
|
63
|
+
end
|
64
|
+
|
65
|
+
body = Output.wrap(status, headers, body)
|
66
|
+
end
|
56
67
|
|
57
68
|
# https://tools.ietf.org/html/rfc7231#section-7.4.2
|
58
69
|
# headers.add('server', "falcon/#{Falcon::VERSION}")
|
@@ -60,8 +71,6 @@ module Falcon
|
|
60
71
|
# https://tools.ietf.org/html/rfc7231#section-7.1.1.2
|
61
72
|
# headers.add('date', Time.now.httpdate)
|
62
73
|
|
63
|
-
body = Output.wrap(status, headers, body)
|
64
|
-
|
65
74
|
return self.new(status, headers, body)
|
66
75
|
end
|
67
76
|
|
data/lib/falcon/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: falcon
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.28.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Samuel Williams
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-
|
11
|
+
date: 2019-05-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: http-protocol
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '0.
|
19
|
+
version: '0.17'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '0.
|
26
|
+
version: '0.17'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: async
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -44,14 +44,14 @@ dependencies:
|
|
44
44
|
requirements:
|
45
45
|
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: '1.
|
47
|
+
version: '1.22'
|
48
48
|
type: :runtime
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: '1.
|
54
|
+
version: '1.22'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: async-http
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -282,6 +282,7 @@ files:
|
|
282
282
|
- gems/rack3.gemfile
|
283
283
|
- lib/falcon.rb
|
284
284
|
- lib/falcon/adapters/early_hints.rb
|
285
|
+
- lib/falcon/adapters/hijack.rb
|
285
286
|
- lib/falcon/adapters/input.rb
|
286
287
|
- lib/falcon/adapters/output.rb
|
287
288
|
- lib/falcon/adapters/rack.rb
|
@@ -321,7 +322,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
321
322
|
- !ruby/object:Gem::Version
|
322
323
|
version: '0'
|
323
324
|
requirements: []
|
324
|
-
rubygems_version: 3.0.
|
325
|
+
rubygems_version: 3.0.2
|
325
326
|
signing_key:
|
326
327
|
specification_version: 4
|
327
328
|
summary: A fast, asynchronous, rack-compatible web server.
|