down 4.4.0 → 4.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/CHANGELOG.md +10 -0
- data/README.md +9 -8
- data/down.gemspec +1 -1
- data/lib/down/backend.rb +1 -0
- data/lib/down/chunked_io.rb +10 -12
- data/lib/down/http.rb +37 -55
- data/lib/down/net_http.rb +2 -16
- data/lib/down/utils.rb +21 -0
- data/lib/down/version.rb +1 -1
- data/lib/down/wget.rb +5 -19
- metadata +8 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 82abe7a56a0950e4743ebba457ceb8cadb113303d2942ab2a2cfd34bc065d06a
|
4
|
+
data.tar.gz: bf7a15be5a3f4129f1ad6b85e7dba6757ac43f0877b3da4b7e18abc1703bb861
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ae99273f99ef4ac8a3a23512482f1e5fa2dbaea5697f197681ad99543b7f07b9665c0532ebdf3d87521abf1d45f34ac1a52c21e314c1408e70e5299ed44ca9b7
|
7
|
+
data.tar.gz: 946eb4bda59b214c2aea5b885fd2d0ee9f85ac035cce635454a91e859d6d4dbf2e2a6de79ac6d9e631089aa12a7aa0fc7f2b96c73e6711890f3234eabe158489
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,13 @@
|
|
1
|
+
## 4.5.0 (2018-05-11)
|
2
|
+
|
3
|
+
* Deprecate passing an `HTTP::Client` object to `Down::Http#initialize` (@janko-m)
|
4
|
+
|
5
|
+
* Add ability to pass a block to `Down::Http#initialize` for extending default options (@janko-m)
|
6
|
+
|
7
|
+
* Return empty string when length is zero in `ChunkedIO#read` and `ChunkedIO#readpartial` (@janko-m)
|
8
|
+
|
9
|
+
* Make `posix-spawn` optional (@janko-m)
|
10
|
+
|
1
11
|
## 4.4.0 (2018-04-12)
|
2
12
|
|
3
13
|
* Add `:method` option to `Down::Http` for specifying the request method (@janko-m)
|
data/README.md
CHANGED
@@ -7,7 +7,7 @@ HTTP library.
|
|
7
7
|
## Installation
|
8
8
|
|
9
9
|
```rb
|
10
|
-
gem "down"
|
10
|
+
gem "down", "~> 4.4"
|
11
11
|
```
|
12
12
|
|
13
13
|
## Downloading
|
@@ -235,7 +235,7 @@ Down.open("...")
|
|
235
235
|
### open-uri + Net::HTTP
|
236
236
|
|
237
237
|
```rb
|
238
|
-
gem "down", "
|
238
|
+
gem "down", "~> 4.4"
|
239
239
|
```
|
240
240
|
```rb
|
241
241
|
require "down/net_http"
|
@@ -327,8 +327,8 @@ net_http.open("http://example.com/image.jpg")
|
|
327
327
|
### HTTP.rb
|
328
328
|
|
329
329
|
```rb
|
330
|
-
gem "down", "
|
331
|
-
gem "http", "~> 2
|
330
|
+
gem "down", "~> 4.4"
|
331
|
+
gem "http", "~> 3.2"
|
332
332
|
```
|
333
333
|
```rb
|
334
334
|
require "down/http"
|
@@ -340,9 +340,10 @@ io = Down::Http.open("http://nature.com/forest.jpg")
|
|
340
340
|
io #=> #<Down::ChunkedIO ...>
|
341
341
|
```
|
342
342
|
|
343
|
-
Some features that give the HTTP.rb backend an advantage over open-uri +
|
344
|
-
Net::HTTP include:
|
343
|
+
Some features that give the HTTP.rb backend an advantage over `open-uri` +
|
344
|
+
`Net::HTTP` include:
|
345
345
|
|
346
|
+
* Low memory usage (**10x less** than `open-uri`/`Net::HTTP`)
|
346
347
|
* Correct URI parsing with [Addressable::URI]
|
347
348
|
* Proper support for streaming downloads (`#download` and now reuse `#open`)
|
348
349
|
* Proper support for SSL
|
@@ -371,7 +372,7 @@ You can also initialize the backend with default options:
|
|
371
372
|
```rb
|
372
373
|
http = Down::Http.new(headers: { "Foo" => "Bar" })
|
373
374
|
# or
|
374
|
-
http = Down::Http.new
|
375
|
+
http = Down::Http.new { |client| client.timeout(connect: 3) }
|
375
376
|
|
376
377
|
http.download("http://example.com/image.jpg")
|
377
378
|
http.open("http://example.com/image.jpg")
|
@@ -393,7 +394,7 @@ down.download("http://example.org/image.jpg")
|
|
393
394
|
### Wget (experimental)
|
394
395
|
|
395
396
|
```rb
|
396
|
-
gem "down", "
|
397
|
+
gem "down", "~> 4.4"
|
397
398
|
gem "posix-spawn" # omit if on JRuby
|
398
399
|
gem "http_parser.rb"
|
399
400
|
```
|
data/down.gemspec
CHANGED
@@ -16,7 +16,7 @@ Gem::Specification.new do |spec|
|
|
16
16
|
spec.require_path = "lib"
|
17
17
|
|
18
18
|
spec.add_development_dependency "minitest", "~> 5.8"
|
19
|
-
spec.add_development_dependency "mocha"
|
19
|
+
spec.add_development_dependency "mocha", "~> 1.5"
|
20
20
|
spec.add_development_dependency "rake"
|
21
21
|
spec.add_development_dependency "http", "~> 3.0"
|
22
22
|
spec.add_development_dependency "posix-spawn" unless RUBY_ENGINE == "jruby"
|
data/lib/down/backend.rb
CHANGED
data/lib/down/chunked_io.rb
CHANGED
@@ -77,7 +77,7 @@ module Down
|
|
77
77
|
remaining_length = length - data.bytesize if length
|
78
78
|
end
|
79
79
|
|
80
|
-
data.to_s unless length && (data.nil? || data.empty?)
|
80
|
+
data.to_s unless length && length > 0 && (data.nil? || data.empty?)
|
81
81
|
end
|
82
82
|
|
83
83
|
# Implements IO#gets semantics. Without arguments it retrieves lines of
|
@@ -150,6 +150,8 @@ module Down
|
|
150
150
|
|
151
151
|
data = outbuf.clear.force_encoding(@encoding) if outbuf
|
152
152
|
|
153
|
+
return data.to_s if length == 0
|
154
|
+
|
153
155
|
if cache && !cache.eof?
|
154
156
|
data = cache.read(length, outbuf)
|
155
157
|
data.force_encoding(@encoding)
|
@@ -163,11 +165,13 @@ module Down
|
|
163
165
|
remaining_length = data && length ? length - data.bytesize : length
|
164
166
|
|
165
167
|
unless @buffer.nil? || remaining_length == 0
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
168
|
+
if remaining_length && remaining_length < @buffer.bytesize
|
169
|
+
buffered_data = @buffer.byteslice(0, remaining_length)
|
170
|
+
@buffer = @buffer.byteslice(remaining_length..-1)
|
171
|
+
else
|
172
|
+
buffered_data = @buffer
|
173
|
+
@buffer = nil
|
174
|
+
end
|
171
175
|
|
172
176
|
if data
|
173
177
|
data << buffered_data
|
@@ -177,12 +181,6 @@ module Down
|
|
177
181
|
|
178
182
|
cache.write(buffered_data) if cache
|
179
183
|
|
180
|
-
if buffered_data.bytesize < @buffer.bytesize
|
181
|
-
@buffer = @buffer.byteslice(buffered_data.bytesize..-1)
|
182
|
-
else
|
183
|
-
@buffer = nil
|
184
|
-
end
|
185
|
-
|
186
184
|
buffered_data.clear unless buffered_data.equal?(data)
|
187
185
|
end
|
188
186
|
|
data/lib/down/http.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen-string-literal: true
|
2
2
|
|
3
|
+
gem "http", ">= 2.1.0", "< 4"
|
4
|
+
|
3
5
|
require "http"
|
4
6
|
|
5
7
|
require "down/backend"
|
@@ -8,40 +10,39 @@ require "tempfile"
|
|
8
10
|
require "cgi"
|
9
11
|
require "base64"
|
10
12
|
|
11
|
-
if Gem::Version.new(HTTP::VERSION) < Gem::Version.new("2.1.0")
|
12
|
-
fail "Down::Http requires HTTP.rb version 2.1.0 or higher"
|
13
|
-
end
|
14
|
-
|
15
13
|
module Down
|
16
14
|
class Http < Backend
|
17
|
-
def initialize(
|
18
|
-
options
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
15
|
+
def initialize(options = {}, &block)
|
16
|
+
if options.is_a?(HTTP::Client)
|
17
|
+
warn "[Down] Passing an HTTP::Client object to Down::Http#initialize is deprecated and won't be supported in Down 5. Use the block initialization instead."
|
18
|
+
options = options.default_options.to_hash
|
19
|
+
end
|
20
|
+
|
21
|
+
@method = options.delete(:method) || :get
|
22
|
+
@client = HTTP
|
23
|
+
.headers("User-Agent" => "Down/#{Down::VERSION}")
|
24
|
+
.follow(max_hops: 2)
|
25
|
+
.timeout(connect: 30, write: 30, read: 30)
|
26
|
+
|
27
|
+
@client = HTTP::Client.new(@client.default_options.merge(options)) if options.any?
|
28
|
+
@client = block.call(@client) if block
|
27
29
|
end
|
28
30
|
|
29
31
|
def download(url, max_size: nil, progress_proc: nil, content_length_proc: nil, destination: nil, **options, &block)
|
30
|
-
|
32
|
+
response = request(url, **options, &block)
|
31
33
|
|
32
|
-
content_length_proc.call(
|
34
|
+
content_length_proc.call(response.content_length) if content_length_proc && response.content_length
|
33
35
|
|
34
|
-
if max_size &&
|
36
|
+
if max_size && response.content_length && response.content_length > max_size
|
35
37
|
raise Down::TooLarge, "file is too large (max is #{max_size/1024/1024}MB)"
|
36
38
|
end
|
37
39
|
|
38
|
-
extname = File.extname(
|
40
|
+
extname = File.extname(response.uri.path)
|
39
41
|
tempfile = Tempfile.new(["down-http", extname], binmode: true)
|
40
42
|
|
41
|
-
|
42
|
-
chunk = io.readpartial(nil, buffer ||= String.new)
|
43
|
-
|
43
|
+
stream_body(response) do |chunk|
|
44
44
|
tempfile.write(chunk)
|
45
|
+
chunk.clear # deallocate string
|
45
46
|
|
46
47
|
progress_proc.call(tempfile.size) if progress_proc
|
47
48
|
|
@@ -53,45 +54,42 @@ module Down
|
|
53
54
|
tempfile.open # flush written content
|
54
55
|
|
55
56
|
tempfile.extend Down::Http::DownloadedFile
|
56
|
-
tempfile.url =
|
57
|
-
tempfile.headers =
|
57
|
+
tempfile.url = response.uri.to_s
|
58
|
+
tempfile.headers = response.headers.to_h
|
58
59
|
|
59
60
|
download_result(tempfile, destination)
|
60
61
|
rescue
|
61
62
|
tempfile.close! if tempfile
|
62
63
|
raise
|
63
|
-
ensure
|
64
|
-
io.close if io
|
65
64
|
end
|
66
65
|
|
67
66
|
def open(url, rewindable: true, **options, &block)
|
68
67
|
response = request(url, **options, &block)
|
69
68
|
|
70
|
-
response_error!(response) unless response.status.success?
|
71
|
-
|
72
69
|
Down::ChunkedIO.new(
|
73
70
|
chunks: enum_for(:stream_body, response),
|
74
71
|
size: response.content_length,
|
75
72
|
encoding: response.content_type.charset,
|
76
73
|
rewindable: rewindable,
|
77
|
-
on_close: (-> { response.connection.close } unless default_client.persistent?),
|
78
74
|
data: { status: response.code, headers: response.headers.to_h, response: response },
|
79
75
|
)
|
80
76
|
end
|
81
77
|
|
82
78
|
private
|
83
79
|
|
84
|
-
def
|
85
|
-
|
80
|
+
def request(url, method: @method, **options, &block)
|
81
|
+
response = send_request(method, url, **options, &block)
|
82
|
+
response_error!(response) unless response.status.success?
|
83
|
+
response
|
86
84
|
end
|
87
85
|
|
88
|
-
def
|
86
|
+
def send_request(method, url, **options, &block)
|
89
87
|
url = process_url(url, options)
|
90
88
|
|
91
|
-
client =
|
89
|
+
client = @client
|
92
90
|
client = block.call(client) if block
|
93
91
|
|
94
|
-
client.
|
92
|
+
client.request(method, url, options)
|
95
93
|
rescue => exception
|
96
94
|
request_error!(exception)
|
97
95
|
end
|
@@ -100,6 +98,8 @@ module Down
|
|
100
98
|
response.body.each(&block)
|
101
99
|
rescue => exception
|
102
100
|
request_error!(exception)
|
101
|
+
ensure
|
102
|
+
response.connection.close unless @client.persistent?
|
103
103
|
end
|
104
104
|
|
105
105
|
def process_url(url, options)
|
@@ -147,34 +147,16 @@ module Down
|
|
147
147
|
attr_accessor :url, :headers
|
148
148
|
|
149
149
|
def original_filename
|
150
|
-
filename_from_content_disposition ||
|
150
|
+
Utils.filename_from_content_disposition(headers["Content-Disposition"]) ||
|
151
|
+
Utils.filename_from_path(HTTP::URI.parse(url).path)
|
151
152
|
end
|
152
153
|
|
153
154
|
def content_type
|
154
|
-
|
155
|
+
HTTP::ContentType.parse(headers["Content-Type"]).mime_type
|
155
156
|
end
|
156
157
|
|
157
158
|
def charset
|
158
|
-
|
159
|
-
end
|
160
|
-
|
161
|
-
private
|
162
|
-
|
163
|
-
def content_type_header
|
164
|
-
HTTP::ContentType.parse(headers["Content-Type"])
|
165
|
-
end
|
166
|
-
|
167
|
-
def filename_from_content_disposition
|
168
|
-
content_disposition = headers["Content-Disposition"].to_s
|
169
|
-
filename = content_disposition[/filename="([^"]*)"/, 1] || content_disposition[/filename=(.+)/, 1]
|
170
|
-
filename = CGI.unescape(filename.to_s.strip)
|
171
|
-
filename unless filename.empty?
|
172
|
-
end
|
173
|
-
|
174
|
-
def filename_from_url
|
175
|
-
path = HTTP::URI.parse(url).path
|
176
|
-
filename = path.split("/").last
|
177
|
-
CGI.unescape(filename) if filename
|
159
|
+
HTTP::ContentType.parse(headers["Content-Type"]).charset
|
178
160
|
end
|
179
161
|
end
|
180
162
|
end
|
data/lib/down/net_http.rb
CHANGED
@@ -266,27 +266,13 @@ module Down
|
|
266
266
|
|
267
267
|
module DownloadedFile
|
268
268
|
def original_filename
|
269
|
-
filename_from_content_disposition ||
|
269
|
+
Utils.filename_from_content_disposition(meta["content-disposition"]) ||
|
270
|
+
Utils.filename_from_path(base_uri.path)
|
270
271
|
end
|
271
272
|
|
272
273
|
def content_type
|
273
274
|
super unless meta["content-type"].to_s.empty?
|
274
275
|
end
|
275
|
-
|
276
|
-
private
|
277
|
-
|
278
|
-
def filename_from_content_disposition
|
279
|
-
content_disposition = meta["content-disposition"].to_s
|
280
|
-
filename = content_disposition[/filename="([^"]*)"/, 1] || content_disposition[/filename=(.+)/, 1]
|
281
|
-
filename = CGI.unescape(filename.to_s.strip)
|
282
|
-
filename unless filename.empty?
|
283
|
-
end
|
284
|
-
|
285
|
-
def filename_from_uri
|
286
|
-
path = base_uri.path
|
287
|
-
filename = path.split("/").last
|
288
|
-
CGI.unescape(filename) if filename
|
289
|
-
end
|
290
276
|
end
|
291
277
|
end
|
292
278
|
end
|
data/lib/down/utils.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require "cgi"
|
2
|
+
|
3
|
+
module Down
|
4
|
+
module Utils
|
5
|
+
module_function
|
6
|
+
|
7
|
+
def filename_from_content_disposition(content_disposition)
|
8
|
+
content_disposition = content_disposition.to_s
|
9
|
+
|
10
|
+
filename = content_disposition[/filename="([^"]*)"/, 1] || content_disposition[/filename=(.+)/, 1]
|
11
|
+
filename = CGI.unescape(filename.to_s.strip)
|
12
|
+
|
13
|
+
filename unless filename.empty?
|
14
|
+
end
|
15
|
+
|
16
|
+
def filename_from_path(path)
|
17
|
+
filename = path.split("/").last
|
18
|
+
CGI.unescape(filename) if filename
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
data/lib/down/version.rb
CHANGED
data/lib/down/wget.rb
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
# frozen-string-literal: true
|
2
2
|
|
3
|
-
|
4
|
-
require "open3"
|
5
|
-
else
|
3
|
+
begin
|
6
4
|
require "posix-spawn"
|
5
|
+
rescue LoadError
|
6
|
+
require "open3"
|
7
7
|
end
|
8
8
|
require "http_parser"
|
9
9
|
|
@@ -209,7 +209,8 @@ module Down
|
|
209
209
|
attr_accessor :url, :headers
|
210
210
|
|
211
211
|
def original_filename
|
212
|
-
filename_from_content_disposition ||
|
212
|
+
Utils.filename_from_content_disposition(headers["Content-Disposition"]) ||
|
213
|
+
Utils.filename_from_path(URI.parse(url).path)
|
213
214
|
end
|
214
215
|
|
215
216
|
def content_type
|
@@ -219,21 +220,6 @@ module Down
|
|
219
220
|
def charset
|
220
221
|
headers["Content-Type"].to_s[/;\s*charset=([^;]+)/i, 1]
|
221
222
|
end
|
222
|
-
|
223
|
-
private
|
224
|
-
|
225
|
-
def filename_from_content_disposition
|
226
|
-
content_disposition = headers["Content-Disposition"].to_s
|
227
|
-
filename = content_disposition[/filename="([^"]*)"/, 1] || content_disposition[/filename=(.+)/, 1]
|
228
|
-
filename = CGI.unescape(filename.to_s.strip)
|
229
|
-
filename unless filename.empty?
|
230
|
-
end
|
231
|
-
|
232
|
-
def filename_from_url
|
233
|
-
path = URI(url).path
|
234
|
-
filename = path.split("/").last
|
235
|
-
CGI.unescape(filename) if filename
|
236
|
-
end
|
237
223
|
end
|
238
224
|
end
|
239
225
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: down
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 4.
|
4
|
+
version: 4.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Janko Marohnić
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-
|
11
|
+
date: 2018-05-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: minitest
|
@@ -28,16 +28,16 @@ dependencies:
|
|
28
28
|
name: mocha
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- - "
|
31
|
+
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '
|
33
|
+
version: '1.5'
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- - "
|
38
|
+
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: '
|
40
|
+
version: '1.5'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: rake
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -125,6 +125,7 @@ files:
|
|
125
125
|
- lib/down/errors.rb
|
126
126
|
- lib/down/http.rb
|
127
127
|
- lib/down/net_http.rb
|
128
|
+
- lib/down/utils.rb
|
128
129
|
- lib/down/version.rb
|
129
130
|
- lib/down/wget.rb
|
130
131
|
homepage: https://github.com/janko-m/down
|
@@ -147,7 +148,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
147
148
|
version: '0'
|
148
149
|
requirements: []
|
149
150
|
rubyforge_project:
|
150
|
-
rubygems_version: 2.6
|
151
|
+
rubygems_version: 2.7.6
|
151
152
|
signing_key:
|
152
153
|
specification_version: 4
|
153
154
|
summary: Robust streaming downloads using Net::HTTP, HTTP.rb or wget.
|