down 5.3.1 → 5.4.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: 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.