async-http 0.50.2 → 0.50.3

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: 41ac80694fcbf70c6db39bf8eb7c623b488f7e532fee830cbfee9e7be0886fed
4
- data.tar.gz: 649b951f1a870b651ff0f792c392644683ea00f99ccac95fc3fb6d90f992bdef
3
+ metadata.gz: c153c0c4ab78ac5723d7e92c0210913fd89009503149a33142df4d262dc7e647
4
+ data.tar.gz: 8922a6fe85a00d84735b7d8bb68a876d6465a57a5c76cfc3b943c82fa8f69c64
5
5
  SHA512:
6
- metadata.gz: 037607a4e52539fa49d7c4977151c0287af88054b3fbfc743b76ef464a8318d63013b3d24065fdf481281fd8387b528038e2a2b1e832bca945fd4bf0d6a006f5
7
- data.tar.gz: 22dd139aa493c0212a12a130878c6b90873398abc92fa9729b01418105e0e17978b338959ba6c0fbe75e3222567352eb07415ad89d64c246524887b6ea28944c
6
+ metadata.gz: b022b86258ed5322e6e5f5ad14f78d0eeabde46a4afa11bd2307b6b00c20995aee9795fa72d58743bbdb29b36009d73c75e782b42962f87d59b5dc4eef9c71d3
7
+ data.tar.gz: a652c97a5e238bf77effee3d8206807a03139674e328863d614dec53bf4ebd7be72946b9854a9ac84dafb9691e88ac3394d97fe3ce4d28bbc8852c6e917fcd6a
@@ -21,7 +21,7 @@ Gem::Specification.new do |spec|
21
21
  spec.add_dependency("async-io", "~> 1.27.0")
22
22
  spec.add_dependency("async-pool", "~> 0.2")
23
23
 
24
- spec.add_dependency("protocol-http", "~> 0.13.0")
24
+ spec.add_dependency("protocol-http", "~> 0.14.0")
25
25
  spec.add_dependency("protocol-http1", "~> 0.10.0")
26
26
  spec.add_dependency("protocol-http2", "~> 0.10.0")
27
27
 
@@ -32,6 +32,7 @@ Gem::Specification.new do |spec|
32
32
 
33
33
  spec.add_development_dependency "rack-test"
34
34
 
35
+ spec.add_development_dependency "bake"
35
36
  spec.add_development_dependency "covered"
36
37
  spec.add_development_dependency "bundler"
37
38
  spec.add_development_dependency "rspec", "~> 3.6"
@@ -0,0 +1,83 @@
1
+
2
+ # Fetch the specified URL and print the response.
3
+ # @param url [String] the URL to parse and fetch.
4
+ # @param method [String] the HTTP method to use.
5
+ def fetch(url, method:)
6
+ require 'async/http/internet'
7
+ require 'kernel/sync'
8
+
9
+ terminal = Console::Terminal.for($stdout)
10
+ terminal[:request] = terminal.style(:blue, nil, :bold)
11
+ terminal[:response] = terminal.style(:green, nil, :bold)
12
+ terminal[:length] = terminal.style(nil, nil, :bold)
13
+
14
+ terminal[:key] = terminal.style(nil, nil, :bold)
15
+
16
+ terminal[:chunk_0] = terminal.style(:blue)
17
+ terminal[:chunk_1] = terminal.style(:cyan)
18
+
19
+ align = 20
20
+
21
+ format_body = proc do |body, terminal|
22
+ if body
23
+ if length = body.length
24
+ terminal.print(:body, "body with length", :length, length, "B")
25
+ else
26
+ terminal.print(:body, "body without length")
27
+ end
28
+ else
29
+ terminal.print(:body, "no body")
30
+ end
31
+ end.curry
32
+
33
+ Sync do
34
+ internet = Async::HTTP::Internet.new
35
+
36
+ response = internet.send(method.downcase.to_sym, url)
37
+
38
+ terminal.print_line(
39
+ :request, method.rjust(align), :reset, ": ", url
40
+ )
41
+
42
+ terminal.print_line(
43
+ :response, "version".rjust(align), :reset, ": ", response.version
44
+ )
45
+
46
+ terminal.print_line(
47
+ :response, "status".rjust(align), :reset, ": ", response.status,
48
+ )
49
+
50
+ terminal.print_line(
51
+ :response, "body".rjust(align), :reset, ": ", format_body[response.body],
52
+ )
53
+
54
+ response.headers.each do |key, value|
55
+ terminal.print_line(
56
+ :key, key.rjust(align), :reset, ": ", :value, value.inspect
57
+ )
58
+ end
59
+
60
+ if body = response.body
61
+ index = 0
62
+ style = [:chunk_0, :chunk_1]
63
+ response.body.each do |chunk|
64
+ terminal.print(style[index % 2], chunk)
65
+ index += 1
66
+ end
67
+ end
68
+
69
+ response.finish
70
+ internet.close
71
+ end
72
+ end
73
+
74
+ # GET the specified URL and print the response.
75
+ def get(url)
76
+ self.fetch(url, method: "GET")
77
+ end
78
+
79
+ # HEAD the specified URL and print the response.
80
+ def head(url)
81
+ self.fetch(url, method: "HEAD")
82
+ end
83
+
@@ -0,0 +1,153 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright, 2020, 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/http/client'
24
+ require 'protocol/http/headers'
25
+ require 'protocol/http/middleware'
26
+ require 'async/clock'
27
+
28
+ require 'protocol/http/body/cacheable'
29
+
30
+ module Async
31
+ module HTTP
32
+ class Cache < ::Protocol::HTTP::Middleware
33
+ CACHE_CONTROL = 'cache-control'
34
+
35
+ class Response < ::Protocol::HTTP::Response
36
+ def initialize(response, body)
37
+ @generated_at = Async::Clock.now
38
+
39
+ super(
40
+ response.version,
41
+ response.status,
42
+ response.headers.dup,
43
+ body,
44
+ response.protocol
45
+ )
46
+
47
+ @max_age = @headers[CACHE_CONTROL]&.max_age
48
+ end
49
+
50
+ def cachable?
51
+ if cache_control = @headers[CACHE_CONTROL]
52
+ if cache_control.private?
53
+ return false
54
+ end
55
+
56
+ if cache_control.public?
57
+ return true
58
+ end
59
+ end
60
+ end
61
+
62
+ attr :generated_at
63
+
64
+ def age
65
+ Async::Clock.now - @generated_at
66
+ end
67
+
68
+ def expired?
69
+ self.age > @max_age
70
+ end
71
+
72
+ def dup
73
+ dup = super
74
+
75
+ dup.body = @body.dup
76
+ dup.headers = @headers.dup
77
+
78
+ return dup
79
+ end
80
+ end
81
+
82
+ def initialize(app, responses = {})
83
+ super(app)
84
+
85
+ @count = 0
86
+
87
+ @responses = {}
88
+ end
89
+
90
+ attr :count
91
+
92
+ def key(request)
93
+ [request.authority, request.method, request.path]
94
+ end
95
+
96
+ def cachable?(request)
97
+ # We don't support caching requests which have a body:
98
+ if request.body
99
+ return false
100
+ end
101
+
102
+ # We can't cache upgraded requests:
103
+ if request.protocol
104
+ return false
105
+ end
106
+
107
+ # We only support caching GET and HEAD requests:
108
+ if request.method == 'GET' || request.method == 'HEAD'
109
+ return true
110
+ end
111
+
112
+ # Otherwise, we can't cache it:
113
+ return false
114
+ end
115
+
116
+ def wrap(request, key, response)
117
+ Body::Cacheable.wrap(response) do |response, body|
118
+ response = Response.new(response, body)
119
+
120
+ if response.cachable?
121
+ @responses[key] = response
122
+ end
123
+ end
124
+
125
+ return response
126
+ end
127
+
128
+ def call(request)
129
+ key = self.key(request)
130
+
131
+ if response = @responses[key]
132
+ Async.logger.debug(self) {"Cache hit for #{key}..."}
133
+ @count += 1
134
+
135
+ if response.expired?
136
+ @responses.delete(key)
137
+ Async.logger.debug(self) {"Cache expired for #{key}..."}
138
+ else
139
+ # Create a dup of the response:
140
+ return response.dup
141
+ end
142
+ end
143
+
144
+ if cachable?(request)
145
+ Async.logger.debug(self) {"Updating cache for #{key}..."}
146
+ return wrap(request, key, super)
147
+ else
148
+ return super
149
+ end
150
+ end
151
+ end
152
+ end
153
+ end
@@ -32,8 +32,9 @@ module Async
32
32
  # A client wrapper which transparently handles both relative and absolute redirects to a given maximum number of hops.
33
33
  class RelativeLocation < ::Protocol::HTTP::Middleware
34
34
  DEFAULT_METHOD = GET
35
-
36
- def initialize(app, maximum_hops = 4)
35
+
36
+ # maximum_hops is the max number of redirects. Set to 0 to allow 1 request with no redirects.
37
+ def initialize(app, maximum_hops = 3)
37
38
  super(app)
38
39
 
39
40
  @maximum_hops = maximum_hops
@@ -48,11 +49,11 @@ module Async
48
49
  # We need to cache the body as it might be submitted multiple times.
49
50
  request.finish
50
51
 
51
- while hops < @maximum_hops
52
+ while hops <= @maximum_hops
52
53
  response = super(request)
53
- hops += 1
54
-
54
+
55
55
  if response.redirection?
56
+ hops += 1
56
57
  response.finish
57
58
 
58
59
  location = response.headers['location']
@@ -22,6 +22,6 @@
22
22
 
23
23
  module Async
24
24
  module HTTP
25
- VERSION = "0.50.2"
25
+ VERSION = "0.50.3"
26
26
  end
27
27
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: async-http
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.50.2
4
+ version: 0.50.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Samuel Williams
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-01-31 00:00:00.000000000 Z
11
+ date: 2020-03-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: async
@@ -58,14 +58,14 @@ dependencies:
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: 0.13.0
61
+ version: 0.14.0
62
62
  type: :runtime
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: 0.13.0
68
+ version: 0.14.0
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: protocol-http1
71
71
  requirement: !ruby/object:Gem::Requirement
@@ -136,6 +136,20 @@ dependencies:
136
136
  - - ">="
137
137
  - !ruby/object:Gem::Version
138
138
  version: '0'
139
+ - !ruby/object:Gem::Dependency
140
+ name: bake
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
139
153
  - !ruby/object:Gem::Dependency
140
154
  name: covered
141
155
  requirement: !ruby/object:Gem::Requirement
@@ -207,6 +221,7 @@ files:
207
221
  - README.md
208
222
  - Rakefile
209
223
  - async-http.gemspec
224
+ - bake/async/http.rb
210
225
  - examples/fetch/Gemfile
211
226
  - examples/fetch/Gemfile.lock
212
227
  - examples/fetch/README.md
@@ -229,6 +244,7 @@ files:
229
244
  - lib/async/http/body/slowloris.rb
230
245
  - lib/async/http/body/stream.rb
231
246
  - lib/async/http/body/writable.rb
247
+ - lib/async/http/cache.rb
232
248
  - lib/async/http/client.rb
233
249
  - lib/async/http/endpoint.rb
234
250
  - lib/async/http/internet.rb