async-http 0.18.0 → 0.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 283dedfc7cd41922f947682aa3f542ccd5368497a6380602de87c6e38eadd4e0
4
- data.tar.gz: cb1671a0bd33170eae0c826f081ccf5581135cc07724fd45cc9930186fc83d89
3
+ metadata.gz: dd03fb46876ed4121fa2232b18de6ec00e7176a6c224adf8d5001ec53cf89f1c
4
+ data.tar.gz: c42bf8f085ad74ee4fbc6b15c47bcb06915a41f42d3a48d58d39a43f695b267d
5
5
  SHA512:
6
- metadata.gz: 01a9422366d59623c93e7a3e17969bfb8d66b2e6213a54f4ef14bbb81d9a23de7e90f8e16dde950630ee2053bf8f7533ea47fcd404cece57de51ee3027e872cb
7
- data.tar.gz: c20d43c98c4febc062c3e94d0accb04df7a0dc8ddd08b2a83c45d1042b6229bb210f674288cca4f437d57f210255f0fd9ef720c8f2cfedd24c8886625a7193fb
6
+ metadata.gz: 7be71154d37d575a6c7b19c7d2c5b81b98e513d260a5d15c9d4c8e0a7fbaf3285d99ca7dc4030e9567af159ff73f4dc25c6144dd55e1745ae307898d0f6c8c9f
7
+ data.tar.gz: bb5748a2d385ce8528b218246a5a701d1c8fb1b45d1b40c2d9ce8e559b023c9c032c1ef396cc930c101278e56ddd2a57280e9e16d77df4eca1bfbc09c99e4caf
data/async-http.gemspec CHANGED
@@ -16,7 +16,7 @@ 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("async", "~> 1.5")
19
+ spec.add_dependency("async", "~> 1.6")
20
20
  spec.add_dependency("async-io", "~> 1.7")
21
21
 
22
22
  spec.add_dependency("http-2", "~> 0.8")
@@ -0,0 +1,65 @@
1
+ # Copyright, 2017, 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_relative 'middleware'
22
+
23
+ require_relative 'body/buffered'
24
+ require_relative 'body/inflate'
25
+
26
+ module Async
27
+ module HTTP
28
+ # Set a valid accept-encoding header and decode the response.
29
+ class AcceptEncoding < Middleware
30
+ DEFAULT_WRAPPERS = {
31
+ 'gzip' => Body::Inflate.method(:for)
32
+ }
33
+
34
+ def initialize(app, wrappers = DEFAULT_WRAPPERS)
35
+ super(app)
36
+
37
+ @accept_encoding = wrappers.keys.join(', ')
38
+ @wrappers = wrappers
39
+ end
40
+
41
+ def call(request, *)
42
+ request.headers['accept-encoding'] = @accept_encoding
43
+
44
+ response = super
45
+
46
+ if !response.body.empty? and content_encoding = response.headers['content-encoding']
47
+ encodings = content_encoding.split(/\s*,\s*/)
48
+
49
+ body = response.body
50
+
51
+ # We want to unwrap all encodings
52
+ encodings.reverse_each do |name|
53
+ if wrapper = @wrappers[name]
54
+ body = wrapper.call(body)
55
+ end
56
+ end
57
+
58
+ response.body = body
59
+ end
60
+
61
+ return response
62
+ end
63
+ end
64
+ end
65
+ end
@@ -18,247 +18,5 @@
18
18
  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
19
  # THE SOFTWARE.
20
20
 
21
- require 'async/queue'
22
-
23
- module Async
24
- module HTTP
25
- class Body
26
- def initialize
27
- @queue = Async::Queue.new
28
-
29
- @finished = false
30
- @stopped = false
31
- end
32
-
33
- # Read all chunks until the stream is closed.
34
- def close
35
- BufferedBody.for(self)
36
- end
37
-
38
- # Have all chunks been read?
39
- def finished?
40
- @finished
41
- end
42
-
43
- # Enumerate all chunks until finished.
44
- def each
45
- return to_enum unless block_given?
46
-
47
- return if @finished
48
-
49
- while chunk = @queue.dequeue
50
- yield chunk
51
- end
52
- rescue
53
- # Stop the stream because the remote end is no longer reading from it. Any attempt to write to the stream will fail.
54
- @stopped = $!
55
-
56
- raise
57
- ensure
58
- @finished = true
59
- end
60
-
61
- # Read the next available chunk.
62
- def read
63
- return if @finished
64
-
65
- unless chunk = @queue.dequeue
66
- @finished = true
67
- end
68
-
69
- return chunk
70
- end
71
-
72
- # Read all remaining chunks into a single binary string.
73
- def join
74
- buffer = Async::IO::BinaryString.new
75
-
76
- self.each do |chunk|
77
- buffer << chunk
78
- end
79
-
80
- return buffer
81
- end
82
-
83
- # Write a single chunk to the body. Signal completion by calling `#finish`.
84
- def write(chunk)
85
- if @stopped
86
- raise @stopped
87
- end
88
-
89
- # TODO should this yield if the queue is full?
90
-
91
- @queue.enqueue(chunk)
92
- end
93
-
94
- # Signal that output has finished.
95
- def finish
96
- @queue.enqueue(nil)
97
- end
98
- end
99
-
100
- class BufferedBody
101
- def self.for(body)
102
- chunks = []
103
-
104
- body.each do |chunk|
105
- chunks << chunk
106
- end
107
-
108
- self.new(chunks)
109
- end
110
-
111
- def initialize(chunks)
112
- @chunks = chunks
113
- @index = 0
114
- end
115
-
116
- def close
117
- self
118
- end
119
-
120
- def each(&block)
121
- while @index < @chunks.count
122
- yield @chunks[@index]
123
- @index += 1
124
- end
125
- end
126
-
127
- def read
128
- if chunk = @chunks[@index]
129
- @index += 1
130
- end
131
-
132
- return chunk
133
- end
134
-
135
- def join
136
- buffer = Async::IO::BinaryString.new
137
-
138
- self.each do |chunk|
139
- buffer << chunk
140
- end
141
-
142
- return buffer
143
- end
144
-
145
- def rewind
146
- @index = 0
147
- end
148
-
149
- def finished?
150
- true
151
- end
152
-
153
- module Reader
154
- def read
155
- self.body ? self.body.join : nil
156
- end
157
-
158
- def finish
159
- return if self.body.nil?
160
-
161
- self.body = self.body.close
162
- end
163
- end
164
- end
165
-
166
- class ChunkedBody
167
- def initialize(protocol)
168
- @protocol = protocol
169
- @finished = false
170
- end
171
-
172
- def close
173
- BufferedBody.for(self)
174
- end
175
-
176
- def finished?
177
- @finished
178
- end
179
-
180
- def read
181
- return nil if @finished
182
-
183
- size = @protocol.read_line.to_i(16)
184
-
185
- if size == 0
186
- @protocol.read_line
187
-
188
- @finished = true
189
-
190
- return nil
191
- end
192
-
193
- chunk = @protocol.stream.read(size)
194
- @protocol.read_line # Consume the trailing CRLF
195
-
196
- return chunk
197
- end
198
-
199
- def each
200
- while chunk = self.read
201
- yield chunk
202
- end
203
- end
204
-
205
- def join
206
- buffer = Async::IO::BinaryString.new
207
-
208
- self.each do |chunk|
209
- buffer << chunk
210
- end
211
-
212
- return buffer
213
- end
214
-
215
- def finish
216
- self.each {}
217
- end
218
- end
219
-
220
- class FixedBody
221
- def initialize(length, stream)
222
- @length = length
223
- @remaining = length
224
- @stream = stream
225
- end
226
-
227
- def close
228
- BufferedBody.for(self)
229
- end
230
-
231
- def finished?
232
- @remaining == 0
233
- end
234
-
235
- def each
236
- while chunk = self.read
237
- yield chunk
238
- end
239
- end
240
-
241
- def read
242
- if @remaining > 0
243
- if chunk = @stream.read(@remaining)
244
- @remaining -= chunk.bytesize
245
-
246
- return chunk
247
- end
248
- end
249
- end
250
-
251
- def join
252
- buffer = @stream.read(@remaining)
253
-
254
- @remaining = 0
255
-
256
- return buffer
257
- end
258
-
259
- def finish
260
- read
261
- end
262
- end
263
- end
264
- end
21
+ require_relative 'body/writable'
22
+ require_relative 'body/buffered'
@@ -0,0 +1,107 @@
1
+ # Copyright, 2018, 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_relative 'readable'
22
+
23
+ module Async
24
+ module HTTP
25
+ module Body
26
+ # A body which buffers all it's contents.
27
+ class Buffered < Readable
28
+ # Wraps an array into a buffered body.
29
+ def self.wrap(body)
30
+ if body.is_a? Async::HTTP::Body::Readable
31
+ return body
32
+ elsif body.is_a? Array
33
+ return self.new(body)
34
+ else
35
+ return self.for(body)
36
+ end
37
+ end
38
+
39
+ def self.for(body)
40
+ chunks = []
41
+
42
+ body.each do |chunk|
43
+ chunks << chunk
44
+ end
45
+
46
+ self.new(chunks)
47
+ end
48
+
49
+ def initialize(chunks)
50
+ @chunks = chunks
51
+ @bytesize = nil
52
+
53
+ @index = 0
54
+ end
55
+
56
+ def bytesize
57
+ @bytesize ||= @chunks.inject(0) {|sum, chunk| sum + chunk.bytesize}
58
+ end
59
+
60
+ def empty?
61
+ @chunks.empty?
62
+ end
63
+
64
+ def close
65
+ self
66
+ end
67
+
68
+ def each
69
+ return to_enum unless block_given?
70
+
71
+ while @index < @chunks.count
72
+ yield @chunks[@index]
73
+ @index += 1
74
+ end
75
+ end
76
+
77
+ def read
78
+ if chunk = @chunks[@index]
79
+ @index += 1
80
+ end
81
+
82
+ return chunk
83
+ end
84
+
85
+ def rewind
86
+ @index = 0
87
+ end
88
+
89
+ def inspect
90
+ "\#<#{self.class} #{@chunks.count} chunks, #{self.bytesize} bytes>"
91
+ end
92
+
93
+ module Reader
94
+ def read
95
+ self.body ? self.body.join : nil
96
+ end
97
+
98
+ def finish
99
+ return if self.body.nil?
100
+
101
+ self.body = self.body.close
102
+ end
103
+ end
104
+ end
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,67 @@
1
+ # Copyright, 2018, 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_relative 'readable'
22
+
23
+ module Async
24
+ module HTTP
25
+ module Body
26
+ class Chunked < Readable
27
+ def initialize(protocol)
28
+ @protocol = protocol
29
+ @finished = false
30
+
31
+ @bytesize = 0
32
+ @count = 0
33
+ end
34
+
35
+ def empty?
36
+ @finished
37
+ end
38
+
39
+ def read
40
+ return nil if @finished
41
+
42
+ size = @protocol.read_line.to_i(16)
43
+
44
+ if size == 0
45
+ @protocol.read_line
46
+
47
+ @finished = true
48
+
49
+ return nil
50
+ end
51
+
52
+ chunk = @protocol.stream.read(size)
53
+ @protocol.read_line # Consume the trailing CRLF
54
+
55
+ @bytesize += size
56
+ @count += 1
57
+
58
+ return chunk
59
+ end
60
+
61
+ def inspect
62
+ "\#<#{self.class} #{@bytesize} bytes read in #{@count} chunks>"
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end