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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 69943db91082a470760e9f809f3cd96f05bd57c4f76869522cdb996215f93c48
4
- data.tar.gz: 4f7b62080a645c26d5a4ace80e723d707ad6fcdbeaf845f247d6ed4977d96c89
3
+ metadata.gz: e0544a70de3afab2a00c68df6923572a5533f05a0964b8c5186728019d04e55b
4
+ data.tar.gz: 3474da6a6c7a182aa02deb72139002ebf97ac98bee9c4a833de2b0413e373924
5
5
  SHA512:
6
- metadata.gz: b6a0a24ade6b6f67bf7e6c91d8aff07e3d18a7b8d67521a78ec0da2516d6518c62d45a0d1ef77176523f64d4a83862ca289aa730c05eaeb6e51e655967c396e3
7
- data.tar.gz: dafabbb4c78cd47d40cd7b74c1df15270ebc2485dccc52e754879cfe0e844edb3cd1322c7a1f4bc81075f81fd980215b8e8189d4db664537273d473d109dbd55
6
+ metadata.gz: 64d22c0a25ddf60f2dea37cb03264ec5770a8c7877fb7a843052abca4b043ba9d2d0c8eed830fc2cec97bb852338b114e2907c357aaf4b68aad26a6860459f67
7
+ data.tar.gz: 992209a7e4201cab464958bac0960ba4a6dc0344cd410b7ceaec2a02b89ebc1bd876396349204bb0bfe175a279fbdb41900c9fee85eee7e98d74eec7afa2e7a0
data/CHANGELOG.md CHANGED
@@ -1,3 +1,7 @@
1
+ ## 5.4.0 (2022-12-26)
2
+
3
+ * Add new HTTPX backend, which supports HTTP/2 protocol among other features (@HoneyryderChuck)
4
+
1
5
  ## 5.3.1 (2022-03-25)
2
6
 
3
7
  * Correctly split cookie headers on `;` instead of `,` when forwarding them on redirects (@ermolaev)
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 the backend
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
- * JRuby 9.2
521
+ * MRI 3.0
522
+ * MRI 3.1
523
+ * JRuby 9.3
499
524
 
500
525
  ## Development
501
526
 
502
- You can run tests with
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
- [kennethreitz/httpbin]: https://github.com/kennethreitz/httpbin
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
@@ -52,7 +52,7 @@ module Down
52
52
 
53
53
  tempfile.extend Down::Http::DownloadedFile
54
54
  tempfile.url = response.uri.to_s
55
- tempfile.headers = response.headers.to_h
55
+ tempfile.headers = normalize_headers(response.headers.to_h)
56
56
 
57
57
  download_result(tempfile, destination)
58
58
  rescue
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
@@ -1,5 +1,5 @@
1
1
  # frozen-string-literal: true
2
2
 
3
3
  module Down
4
- VERSION = "5.3.1"
4
+ VERSION = "5.4.0"
5
5
  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: 5.3.1
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-03-25 00:00:00.000000000 Z
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: http
70
+ name: httpx
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
73
  - - "~>"
74
74
  - !ruby/object:Gem::Version
75
- version: '5.0'
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: '5.0'
85
+ version: '0.22'
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: 0.22.2
83
89
  - !ruby/object:Gem::Dependency
84
- name: posix-spawn
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: http_parser.rb
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: docker-api
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.3.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.