down 5.3.1 → 5.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +4 -0
- data/README.md +38 -8
- data/down.gemspec +1 -1
- data/lib/down/http.rb +1 -1
- data/lib/down/httpx.rb +175 -0
- data/lib/down/version.rb +1 -1
- metadata +20 -13
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e0544a70de3afab2a00c68df6923572a5533f05a0964b8c5186728019d04e55b
|
4
|
+
data.tar.gz: 3474da6a6c7a182aa02deb72139002ebf97ac98bee9c4a833de2b0413e373924
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 64d22c0a25ddf60f2dea37cb03264ec5770a8c7877fb7a843052abca4b043ba9d2d0c8eed830fc2cec97bb852338b114e2907c357aaf4b68aad26a6860459f67
|
7
|
+
data.tar.gz: 992209a7e4201cab464958bac0960ba4a6dc0344cd410b7ceaec2a02b89ebc1bd876396349204bb0bfe175a279fbdb41900c9fee85eee7e98d74eec7afa2e7a0
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
# Down
|
2
2
|
|
3
3
|
Down is a utility tool for streaming, flexible and safe downloading of remote
|
4
|
-
files. It can use [open-uri] + `Net::HTTP`, [http.rb] or `wget` as
|
5
|
-
HTTP library.
|
4
|
+
files. It can use [open-uri] + `Net::HTTP`, [http.rb], [HTTPX], or `wget` as
|
5
|
+
the backend HTTP library.
|
6
6
|
|
7
7
|
## Installation
|
8
8
|
|
@@ -234,6 +234,7 @@ The following backends are available:
|
|
234
234
|
|
235
235
|
* [Down::NetHttp](#downnethttp) (default)
|
236
236
|
* [Down::Http](#downhttp)
|
237
|
+
* [Down::Httpx](#downhttpx)
|
237
238
|
* [Down::Wget](#downwget)
|
238
239
|
|
239
240
|
You can use the backend directly:
|
@@ -442,6 +443,28 @@ down = Down::Http.new(method: :post)
|
|
442
443
|
down.download("http://example.org/image.jpg")
|
443
444
|
```
|
444
445
|
|
446
|
+
### Down::Httpx
|
447
|
+
|
448
|
+
The `Down::Httpx` backend implements downloads using the [HTTPX] gem, which
|
449
|
+
supports the HTTP/2 protocol, in addition to many other features.
|
450
|
+
|
451
|
+
```rb
|
452
|
+
gem "down", "~> 5.0"
|
453
|
+
gem "httpx", "~> 0.22"
|
454
|
+
```
|
455
|
+
```rb
|
456
|
+
require "down/httpx"
|
457
|
+
|
458
|
+
tempfile = Down::Httpx.download("http://nature.com/forest.jpg")
|
459
|
+
tempfile #=> #<Tempfile:/var/folders/k7/6zx6dx6x7ys3rv3srh0nyfj00000gn/T/20150925-55456-z7vxqz.jpg>
|
460
|
+
|
461
|
+
io = Down::Httpx.open("http://nature.com/forest.jpg")
|
462
|
+
io #=> #<Down::ChunkedIO ...>
|
463
|
+
```
|
464
|
+
|
465
|
+
It's implemented in much of the same way as `Down::Http`, so be sure to check
|
466
|
+
its docs for ways to pass additional options.
|
467
|
+
|
445
468
|
### Down::Wget (experimental)
|
446
469
|
|
447
470
|
The `Down::Wget` backend implements downloads using the `wget` command line
|
@@ -495,19 +518,25 @@ wget.open("http://nature.com/forest.jpg")
|
|
495
518
|
* MRI 2.5
|
496
519
|
* MRI 2.6
|
497
520
|
* MRI 2.7
|
498
|
-
*
|
521
|
+
* MRI 3.0
|
522
|
+
* MRI 3.1
|
523
|
+
* JRuby 9.3
|
499
524
|
|
500
525
|
## Development
|
501
526
|
|
502
|
-
|
527
|
+
Tests require that a [httpbin] server is running locally, which you can do via Docker:
|
528
|
+
|
529
|
+
```sh
|
530
|
+
$ docker pull kennethreitz/httpbin
|
531
|
+
$ docker run -p 80:80 kennethreitz/httpbin
|
532
|
+
```
|
533
|
+
|
534
|
+
Then you can run tests:
|
503
535
|
|
504
536
|
```
|
505
537
|
$ bundle exec rake test
|
506
538
|
```
|
507
539
|
|
508
|
-
The test suite pulls and runs [kennethreitz/httpbin] as a Docker container, so
|
509
|
-
you'll need to have Docker installed and running.
|
510
|
-
|
511
540
|
## License
|
512
541
|
|
513
542
|
[MIT](LICENSE.txt)
|
@@ -515,5 +544,6 @@ you'll need to have Docker installed and running.
|
|
515
544
|
[open-uri]: http://ruby-doc.org/stdlib-2.3.0/libdoc/open-uri/rdoc/OpenURI.html
|
516
545
|
[Net::HTTP]: https://ruby-doc.org/stdlib-2.4.1/libdoc/net/http/rdoc/Net/HTTP.html
|
517
546
|
[http.rb]: https://github.com/httprb/http
|
547
|
+
[HTTPX]: https://github.com/HoneyryderChuck/httpx
|
518
548
|
[Addressable::URI]: https://github.com/sporkmonger/addressable
|
519
|
-
[
|
549
|
+
[httpbin]: https://github.com/postmanlabs/httpbin
|
data/down.gemspec
CHANGED
@@ -20,6 +20,7 @@ Gem::Specification.new do |spec|
|
|
20
20
|
spec.add_development_dependency "minitest", "~> 5.8"
|
21
21
|
spec.add_development_dependency "mocha", "~> 1.5"
|
22
22
|
spec.add_development_dependency "rake"
|
23
|
+
spec.add_development_dependency "httpx", "~> 0.22", ">= 0.22.2"
|
23
24
|
# http 5.0 drop support of ruby 2.3 and 2.4. We still support those versions.
|
24
25
|
if RUBY_VERSION >= "2.5"
|
25
26
|
spec.add_development_dependency "http", "~> 5.0"
|
@@ -28,6 +29,5 @@ Gem::Specification.new do |spec|
|
|
28
29
|
end
|
29
30
|
spec.add_development_dependency "posix-spawn" unless RUBY_ENGINE == "jruby"
|
30
31
|
spec.add_development_dependency "http_parser.rb" unless RUBY_ENGINE == "jruby"
|
31
|
-
spec.add_development_dependency "docker-api"
|
32
32
|
spec.add_development_dependency "warning" if RUBY_VERSION >= "2.4"
|
33
33
|
end
|
data/lib/down/http.rb
CHANGED
data/lib/down/httpx.rb
ADDED
@@ -0,0 +1,175 @@
|
|
1
|
+
# frozen-string-literal: true
|
2
|
+
|
3
|
+
require "uri"
|
4
|
+
require "tempfile"
|
5
|
+
require "httpx"
|
6
|
+
|
7
|
+
require "down/backend"
|
8
|
+
|
9
|
+
|
10
|
+
module Down
|
11
|
+
# Provides streaming downloads implemented with HTTPX.
|
12
|
+
class Httpx < Backend
|
13
|
+
# Initializes the backend
|
14
|
+
|
15
|
+
USER_AGENT = "Down/#{Down::VERSION}"
|
16
|
+
|
17
|
+
def initialize(**options, &block)
|
18
|
+
@method = options.delete(:method) || :get
|
19
|
+
headers = options.delete(:headers) || {}
|
20
|
+
@client = HTTPX
|
21
|
+
.plugin(:follow_redirects, max_redirects: 2)
|
22
|
+
.plugin(:basic_authentication)
|
23
|
+
.plugin(:stream)
|
24
|
+
.with(
|
25
|
+
headers: { "user-agent": USER_AGENT }.merge(headers),
|
26
|
+
timeout: { connect_timeout: 30, write_timeout: 30, read_timeout: 30 },
|
27
|
+
**options
|
28
|
+
)
|
29
|
+
|
30
|
+
@client = block.call(@client) if block
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
# Downlods the remote file to disk. Accepts HTTPX options via a hash or a
|
35
|
+
# block, and some additional options as well.
|
36
|
+
def download(url, max_size: nil, progress_proc: nil, content_length_proc: nil, destination: nil, extension: nil, **options, &block)
|
37
|
+
client = @client
|
38
|
+
|
39
|
+
response = request(client, url, **options, &block)
|
40
|
+
|
41
|
+
content_length = nil
|
42
|
+
|
43
|
+
if response.headers.key?("content-length")
|
44
|
+
content_length = response.headers["content-length"].to_i
|
45
|
+
|
46
|
+
content_length_proc.call(content_length) if content_length_proc
|
47
|
+
|
48
|
+
if max_size && content_length > max_size
|
49
|
+
response.close
|
50
|
+
raise Down::TooLarge, "file is too large (#{content_length/1024/1024}MB, max is #{max_size/1024/1024}MB)"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
extname = extension ? ".#{extension}" : File.extname(response.uri.path)
|
55
|
+
tempfile = Tempfile.new(["down-http", extname], binmode: true)
|
56
|
+
|
57
|
+
stream_body(response) do |chunk|
|
58
|
+
tempfile.write(chunk)
|
59
|
+
chunk.clear # deallocate string
|
60
|
+
|
61
|
+
progress_proc.call(tempfile.size) if progress_proc
|
62
|
+
|
63
|
+
if max_size && tempfile.size > max_size
|
64
|
+
raise Down::TooLarge, "file is too large (#{tempfile.size/1024/1024}MB, max is #{max_size/1024/1024}MB)"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
tempfile.open # flush written content
|
69
|
+
|
70
|
+
tempfile.extend DownloadedFile
|
71
|
+
tempfile.url = response.uri.to_s
|
72
|
+
tempfile.headers = normalize_headers(response.headers.to_h)
|
73
|
+
tempfile.content_type = response.content_type.mime_type
|
74
|
+
tempfile.charset = response.body.encoding
|
75
|
+
|
76
|
+
download_result(tempfile, destination)
|
77
|
+
rescue
|
78
|
+
tempfile.close! if tempfile
|
79
|
+
raise
|
80
|
+
end
|
81
|
+
|
82
|
+
# Starts retrieving the remote file and returns an IO-like object which
|
83
|
+
# downloads the response body on-demand. Accepts HTTP.rb options via a hash
|
84
|
+
# or a block.
|
85
|
+
def open(url, rewindable: true, **options, &block)
|
86
|
+
response = request(@client, url, stream: true, **options, &block)
|
87
|
+
size = response.headers["content-length"]
|
88
|
+
size = size.to_i if size
|
89
|
+
Down::ChunkedIO.new(
|
90
|
+
chunks: enum_for(:stream_body, response),
|
91
|
+
size: size,
|
92
|
+
encoding: response.body.encoding,
|
93
|
+
rewindable: rewindable,
|
94
|
+
data: {
|
95
|
+
status: response.status,
|
96
|
+
headers: normalize_headers(response.headers.to_h),
|
97
|
+
response: response
|
98
|
+
},
|
99
|
+
)
|
100
|
+
end
|
101
|
+
|
102
|
+
private
|
103
|
+
|
104
|
+
# Yields chunks of the response body to the block.
|
105
|
+
def stream_body(response, &block)
|
106
|
+
response.each(&block)
|
107
|
+
rescue => exception
|
108
|
+
request_error!(exception)
|
109
|
+
end
|
110
|
+
|
111
|
+
def request(client, url, method: @method, **options, &block)
|
112
|
+
response = send_request(client, method, url, **options, &block)
|
113
|
+
response.raise_for_status
|
114
|
+
response_error!(response) unless (200..299).include?(response.status)
|
115
|
+
response
|
116
|
+
rescue HTTPX::HTTPError
|
117
|
+
response_error!(response)
|
118
|
+
rescue => error
|
119
|
+
request_error!(error)
|
120
|
+
end
|
121
|
+
|
122
|
+
def send_request(client, method, url, **options, &block)
|
123
|
+
uri = URI(url)
|
124
|
+
client = @client
|
125
|
+
if uri.user || uri.password
|
126
|
+
client = client.basic_auth(uri.user, uri.password)
|
127
|
+
uri.user = uri.password = nil
|
128
|
+
end
|
129
|
+
client = block.call(client) if block
|
130
|
+
|
131
|
+
client.request(method, uri, stream: true, **options)
|
132
|
+
rescue => exception
|
133
|
+
request_error!(exception)
|
134
|
+
end
|
135
|
+
|
136
|
+
# Raises non-sucessful response as a Down::ResponseError.
|
137
|
+
def response_error!(response)
|
138
|
+
args = [response.status.to_s, response]
|
139
|
+
|
140
|
+
case response.status
|
141
|
+
when 300..399 then raise Down::TooManyRedirects, "too many redirects"
|
142
|
+
when 404 then raise Down::NotFound.new(*args)
|
143
|
+
when 400..499 then raise Down::ClientError.new(*args)
|
144
|
+
when 500..599 then raise Down::ServerError.new(*args)
|
145
|
+
else raise Down::ResponseError.new(*args)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
# Re-raise HTTP.rb exceptions as Down::Error exceptions.
|
150
|
+
def request_error!(exception)
|
151
|
+
case exception
|
152
|
+
when URI::Error, HTTPX::UnsupportedSchemeError
|
153
|
+
raise Down::InvalidUrl, exception.message
|
154
|
+
when HTTPX::ConnectionError
|
155
|
+
raise Down::ConnectionError, exception.message
|
156
|
+
when HTTPX::TimeoutError
|
157
|
+
raise Down::TimeoutError, exception.message
|
158
|
+
when OpenSSL::SSL::SSLError
|
159
|
+
raise Down::SSLError, exception.message
|
160
|
+
else
|
161
|
+
raise exception
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
# Defines some additional attributes for the returned Tempfile.
|
166
|
+
module DownloadedFile
|
167
|
+
attr_accessor :url, :headers, :charset, :content_type
|
168
|
+
|
169
|
+
def original_filename
|
170
|
+
Utils.filename_from_content_disposition(headers["Content-Disposition"]) ||
|
171
|
+
Utils.filename_from_path(URI.parse(url).path)
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
data/lib/down/version.rb
CHANGED
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: 5.
|
4
|
+
version: 5.4.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: 2022-
|
11
|
+
date: 2022-12-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: addressable
|
@@ -67,35 +67,41 @@ dependencies:
|
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '0'
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
|
-
name:
|
70
|
+
name: httpx
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
72
72
|
requirements:
|
73
73
|
- - "~>"
|
74
74
|
- !ruby/object:Gem::Version
|
75
|
-
version: '
|
75
|
+
version: '0.22'
|
76
|
+
- - ">="
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
version: 0.22.2
|
76
79
|
type: :development
|
77
80
|
prerelease: false
|
78
81
|
version_requirements: !ruby/object:Gem::Requirement
|
79
82
|
requirements:
|
80
83
|
- - "~>"
|
81
84
|
- !ruby/object:Gem::Version
|
82
|
-
version: '
|
85
|
+
version: '0.22'
|
86
|
+
- - ">="
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: 0.22.2
|
83
89
|
- !ruby/object:Gem::Dependency
|
84
|
-
name:
|
90
|
+
name: http
|
85
91
|
requirement: !ruby/object:Gem::Requirement
|
86
92
|
requirements:
|
87
|
-
- - "
|
93
|
+
- - "~>"
|
88
94
|
- !ruby/object:Gem::Version
|
89
|
-
version: '0'
|
95
|
+
version: '5.0'
|
90
96
|
type: :development
|
91
97
|
prerelease: false
|
92
98
|
version_requirements: !ruby/object:Gem::Requirement
|
93
99
|
requirements:
|
94
|
-
- - "
|
100
|
+
- - "~>"
|
95
101
|
- !ruby/object:Gem::Version
|
96
|
-
version: '0'
|
102
|
+
version: '5.0'
|
97
103
|
- !ruby/object:Gem::Dependency
|
98
|
-
name:
|
104
|
+
name: posix-spawn
|
99
105
|
requirement: !ruby/object:Gem::Requirement
|
100
106
|
requirements:
|
101
107
|
- - ">="
|
@@ -109,7 +115,7 @@ dependencies:
|
|
109
115
|
- !ruby/object:Gem::Version
|
110
116
|
version: '0'
|
111
117
|
- !ruby/object:Gem::Dependency
|
112
|
-
name:
|
118
|
+
name: http_parser.rb
|
113
119
|
requirement: !ruby/object:Gem::Requirement
|
114
120
|
requirements:
|
115
121
|
- - ">="
|
@@ -152,6 +158,7 @@ files:
|
|
152
158
|
- lib/down/chunked_io.rb
|
153
159
|
- lib/down/errors.rb
|
154
160
|
- lib/down/http.rb
|
161
|
+
- lib/down/httpx.rb
|
155
162
|
- lib/down/net_http.rb
|
156
163
|
- lib/down/utils.rb
|
157
164
|
- lib/down/version.rb
|
@@ -175,7 +182,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
175
182
|
- !ruby/object:Gem::Version
|
176
183
|
version: '0'
|
177
184
|
requirements: []
|
178
|
-
rubygems_version: 3.
|
185
|
+
rubygems_version: 3.4.1
|
179
186
|
signing_key:
|
180
187
|
specification_version: 4
|
181
188
|
summary: Robust streaming downloads using Net::HTTP, HTTP.rb or wget.
|