down 5.6.1 → 5.6.2

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: 04d70935026de1e61cbda54578c360164e4ab252658c2b7a5531701e8195f55d
4
- data.tar.gz: 047bbd998f10b26ad7865f4dc445a5ddc4ad811c4639ba51b8691eb1436cb6af
3
+ metadata.gz: dafd791660ca13ac117b8db2da6a31a38b5c6008735039dcbf4263b60438cf6a
4
+ data.tar.gz: 1ce7f027452f7c21421f169be6f967d3a180c3e34940d07c0d41be35fb1015cf
5
5
  SHA512:
6
- metadata.gz: '0924393fcdee5795751393b9965e3cb2221da29baa5797d30bdd19115edb63972e8c6119310e1f65f77feeae7aae61ef5f03452394c77b57a57aaa1d8663a0da'
7
- data.tar.gz: 808f8ac45f19f7a54e708b9b51ca4e659474eebe2bd14d894157a9454d4a2726d1260d323c61445218d8cc00f4b25497fb1c680c527e0fbc51a9cf1f374d605c
6
+ metadata.gz: 32d796dfff756b332fbf688d2560a08820f3636b1e64b7e5545bb896f2955a5c5def44c9866207a7a9898eba0bbb45bee8d8d663e7c1060a43157556624cd176
7
+ data.tar.gz: 13fedb0b5618d6c7ff9dda385835e6fb15a9a3d6bdad30db201c4ee216ddb6b508cbad374e7dd48d08df5ff26b39b2ca8e837789f44a6e59df2beab164c705c0
data/CHANGELOG.md CHANGED
@@ -1,3 +1,9 @@
1
+ ## 5.6.2 (2026-11-01)
2
+
3
+ * Return `nil` `Down::ChunkedIO#size` from `Down::NetHttp.open` when net/http decompresses the body (@wtn)
4
+
5
+ * Set `Down::ResponseError#response` to the response object on invalid redirect URIs in `Down::NetHttp` (@wtn)
6
+
1
7
  ## 5.6.1 (2026-10-18)
2
8
 
3
9
  * Avoid warnings on requiring `cgi` on Ruby 4.0+ (@janko)
data/README.md CHANGED
@@ -109,7 +109,7 @@ internally downloads chunks of the remote file, but only how much is needed.
109
109
 
110
110
  ```rb
111
111
  remote_file = Down.open("http://example.com/image.jpg")
112
- remote_file.size # read from the "Content-Length" header
112
+ remote_file.size # read from the "Content-Length" header, nil if unknown
113
113
 
114
114
  remote_file.read(1024) # downloads and returns first 1 KB
115
115
  remote_file.read(1024) # downloads and returns next 1 KB
@@ -121,6 +121,17 @@ remote_file.eof? #=> true
121
121
  remote_file.close # closes the HTTP connection and deletes the internal Tempfile
122
122
  ```
123
123
 
124
+ The size is `nil` when the server doesn't send a `Content-Length`. With the
125
+ Net::HTTP backend it's also `nil` when the response body is compressed, because
126
+ Net::HTTP decompresses it on the fly and the header holds the compressed size. If
127
+ you need the size, you can set your own `Accept-Encoding`, which turns off
128
+ decompression; the body may then need to be decompressed by hand if the server
129
+ sends it compressed anyway:
130
+
131
+ ```rb
132
+ remote_file = Down.open("http://example.com/file.json", headers: { "Accept-Encoding" => "identity" })
133
+ ```
134
+
124
135
  The following IO methods are implemented:
125
136
 
126
137
  * `#read` & `#readpartial`
data/lib/down/net_http.rb CHANGED
@@ -17,6 +17,10 @@ module Down
17
17
  addressable_uri.normalize.to_s
18
18
  end
19
19
 
20
+ # Content encodings that Net::HTTP transparently decodes.
21
+ DECODED_ENCODINGS = %w[gzip x-gzip deflate].freeze
22
+ private_constant :DECODED_ENCODINGS
23
+
20
24
  DEFAULT_OPTIONS = {
21
25
  headers: { "User-Agent" => "Down/#{Down::VERSION}" }.freeze,
22
26
  max_redirects: 2,
@@ -127,7 +131,7 @@ module Down
127
131
  # Build an IO-like object that will retrieve response body on-demand.
128
132
  Down::ChunkedIO.new(
129
133
  chunks: enum_for(:stream_body, response),
130
- size: response["Content-Length"]&.to_i,
134
+ size: content_length(response),
131
135
  encoding: response.type_params["charset"],
132
136
  rewindable: options.fetch(:rewindable, true),
133
137
  on_close: -> { request.resume }, # close HTTP connnection
@@ -153,7 +157,7 @@ module Down
153
157
  rescue Down::InvalidUrl
154
158
  response = rebuild_response_from_open_uri_exception(exception)
155
159
 
156
- raise ResponseError.new("Invalid Redirect URI: #{exception.uri}", response:)
160
+ raise ResponseError.new("Invalid Redirect URI: #{exception.uri}", response)
157
161
  end
158
162
 
159
163
  # do not leak credentials on redirect
@@ -176,7 +180,7 @@ module Down
176
180
 
177
181
  # open-uri attempts to parse the redirect URI, so we re-raise that exception
178
182
  if exception.message.include?("(Invalid Location URI)")
179
- raise ResponseError.new("Invalid Redirect URI: #{response["Location"]}", response:)
183
+ raise ResponseError.new("Invalid Redirect URI: #{response["Location"]}", response)
180
184
  end
181
185
 
182
186
  response_error!(response)
@@ -233,7 +237,7 @@ module Down
233
237
  begin
234
238
  location = ensure_uri(response["Location"], allow_relative: true)
235
239
  rescue Down::InvalidUrl
236
- raise ResponseError.new("Invalid Redirect URI: #{response["Location"]}", response:)
240
+ raise ResponseError.new("Invalid Redirect URI: #{response["Location"]}", response)
237
241
  end
238
242
 
239
243
  # do not leak credentials on redirect
@@ -297,6 +301,20 @@ module Down
297
301
  request_error!(exception)
298
302
  end
299
303
 
304
+ # Returns the Content-Length, unless a Transfer-Encoding is present or
305
+ # Net::HTTP will decode the body, in which case it doesn't reflect the
306
+ # number of bytes read. Decoding conditions mirror Net::HTTPResponse#inflater.
307
+ def content_length(response)
308
+ return if response["Transfer-Encoding"]
309
+
310
+ length = response.content_length
311
+ return length if length.nil? || length.zero?
312
+ return if response.decode_content && !response["Content-Range"] &&
313
+ DECODED_ENCODINGS.include?(response["Content-Encoding"]&.downcase)
314
+
315
+ length
316
+ end
317
+
300
318
  # Checks that the url is a valid URI and that its scheme is http or https.
301
319
  def ensure_uri(url, allow_relative: false)
302
320
  begin
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.6.1"
4
+ VERSION = "5.6.2"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: down
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.6.1
4
+ version: 5.6.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Janko Marohnić
@@ -179,7 +179,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
179
179
  - !ruby/object:Gem::Version
180
180
  version: '0'
181
181
  requirements: []
182
- rubygems_version: 4.0.13
182
+ rubygems_version: 4.0.20
183
183
  specification_version: 4
184
184
  summary: Robust streaming downloads using Net::HTTP, http.rb or HTTPX.
185
185
  test_files: []