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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5d8f6d04def38b5f391b3cdcc17935b06612a9c95137bfaf563c58ac21cc0edc
4
- data.tar.gz: '085234503af9930b2d0a9bf7430673bb896456c2093cc627eee83fb7a6d9e1a3'
3
+ metadata.gz: 93cb18cb7157a7f267220d2d81a93d3c28bd96efddc04f799f64bdb658f9fa6b
4
+ data.tar.gz: efba9da82250f426f87705588d1601e0e6a349d1879a07b1fd18d7130f4a1091
5
5
  SHA512:
6
- metadata.gz: 41223e695feb4428ef06b260dd4b4a178c35f8889708198f5f94f4f598b6322b2c0d431e0268372debe54fd670fdb146efa2126295d8022a956a4878dd9ce095
7
- data.tar.gz: 33728e72a25355cb7025b0ae1c4f4883fc200251d82fe8d7ee1011c240ec9f49769c601a95da80801736a6cfeb3c1f78f3d499ac9c013425cc82c27c90987016
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
 
@@ -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.15"
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.20"
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
@@ -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
- # Partial hijack is not supported/tested.
188
- # if hijack = headers.delete('rack.hijack')
189
- # body = Async::HTTP::Body::Writable.new
190
- #
191
- # Task.current.async do
192
- # hijack.call(body)
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 = Set.new(Proxy::HOP_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 IGNORE_HEADERS.include? key
43
- Async.logger.warn("Ignoring protocol-level header: #{key}: #{value}!")
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
 
@@ -19,5 +19,5 @@
19
19
  # THE SOFTWARE.
20
20
 
21
21
  module Falcon
22
- VERSION = "0.27.0"
22
+ VERSION = "0.28.0"
23
23
  end
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.27.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-03-26 00:00:00.000000000 Z
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.15'
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.15'
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.20'
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.20'
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.3
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.