http_zip 1.0.0 → 2.0.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: c5c21ac606a405c8527ce48a94f760ecd195104a4ac84a25c84bbb095a748c21
4
- data.tar.gz: 3b9c5d83fcbbbf09b858411fe10f56fc582738914bfbd5b54626571705eff72c
3
+ metadata.gz: e045495a2ce12199f65be5bf20671418ad350c81ae91fa59c574229ed20eaa22
4
+ data.tar.gz: fed79c7c440c3323204e105ba449c92bb26d8a1a8b47b258ef1215865a30d7c2
5
5
  SHA512:
6
- metadata.gz: 6770e900fbf8a657716f0426e394646ba1bcd796cce64317c9d34996bd792847d1cd147ac0c7a49a64641787519a15c4d2abcc8933f01379e75f9607359049c2
7
- data.tar.gz: 1d85ea79fd3e03302e242c4fce86a4fdf45ead85e663f3a5b4b91cf5cb735081a5d50aecd2e7931de6ef7b1064dcc6d174c2b25222689cee24ba7d26cbe8c2e9
6
+ metadata.gz: 39861993eeb2e36bbbb0e96cf620afcc94935dc11160a676ec9c65657cfca840ab98d0c57ab51fd8272ca10ac1d46c37be6f6d866598410bd13012cba3c0e262
7
+ data.tar.gz: 348b335d82a78a8b37c075981370996723b977ce3ce68c18db72a1e68f64c6ec100ac922ff7303bbafd885fc6360e7d676a980407e65048d51415bcedd26f6d2
data/CHANGELOG.md ADDED
@@ -0,0 +1,8 @@
1
+ # 2.0.0
2
+ * Drop dependency on HTTParty
3
+ * Remove `RangeRequest.server_supports_content_range?` and `RangeRequest.check_server_supports_content_range!`
4
+ * Instead of pre-checking for Range request support, we just attempt the request and abort it early if the server doesn't support Range requests. This should make it more reliable for servers that e.g. don't announce support with the Accept-Ranges header or don't support HEAD requests.
5
+ * Better separation of server error responses: When the server responds with an error, HttpZip now raises `HttpZip::RequestError`. Only when the response is successful, but not `206 Partial Content`, do we raise `HttpZip::ContentRangeError`.
6
+
7
+ # 1.0.0
8
+ * Initial release
data/README.md CHANGED
@@ -42,11 +42,7 @@ content = entry.read
42
42
  entry.write_to_file('/path/extracted.txt')
43
43
  ```
44
44
 
45
- If the server that the zip file is hosted on doesn't support Range requests, HttpZip will throw `HttpZip::ContentRangeError`. If you want you can check this beforehand by calling:
46
-
47
- ```ruby
48
- HttpZip::RangeRequest.server_supports_content_range?(url)
49
- ```
45
+ If the server that the zip file is hosted on doesn't support Range requests, HttpZip will throw `HttpZip::ContentRangeError`.
50
46
 
51
47
  ## Contributing
52
48
 
data/http_zip.gemspec CHANGED
@@ -26,11 +26,9 @@ Gem::Specification.new do |spec|
26
26
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
27
27
  spec.require_paths = ['lib']
28
28
 
29
- spec.add_runtime_dependency 'httparty', '~> 0.20'
30
-
31
29
  spec.add_development_dependency 'bundler', '~> 2.0'
32
30
  spec.add_development_dependency 'minitest', '~> 5.15'
33
- spec.add_development_dependency 'rake', '~> 10.0'
31
+ spec.add_development_dependency 'rake', '~> 12.3', '>= 12.3.3'
34
32
  spec.add_development_dependency 'simplecov', '~> 0.21'
35
33
  spec.add_development_dependency 'webmock', '~> 3.14'
36
34
  end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module HttpZip
4
+ module Compression
5
+ class Deflate
6
+ def initialize
7
+ @inflater = Zlib::Inflate.new(-Zlib::MAX_WBITS)
8
+ end
9
+
10
+ def decompress(input)
11
+ @inflater.inflate(input)
12
+ end
13
+
14
+ def finish
15
+ @inflater.finish
16
+ @inflater.close
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module HttpZip
4
+ module Compression
5
+ class Stored
6
+ def decompress(input)
7
+ input
8
+ end
9
+
10
+ def finish; end
11
+ end
12
+ end
13
+ end
@@ -4,7 +4,7 @@ module HttpZip
4
4
  # Describes one entry in an HTTP zip archive
5
5
  # @attr_reader [String] name filename of the entry
6
6
  class Entry
7
- attr_reader :name
7
+ attr_reader :name, :compressed_size
8
8
 
9
9
  def initialize(url, name, header_offset, central_directory_file_compressed_size)
10
10
  @range_request = HttpZip::RangeRequest.new(url)
@@ -20,10 +20,9 @@ module HttpZip
20
20
  from = @header_offset + header_size
21
21
  to = @header_offset + header_size + @compressed_size
22
22
 
23
- decompress, _finish = decompress_funcs
24
-
23
+ decompressor = compression_method
25
24
  compressed_contents = @range_request.get(from, to)
26
- decompress.call(compressed_contents)
25
+ decompressor.decompress(compressed_contents)
27
26
  end
28
27
 
29
28
  # Get the decompressed content of the file entry
@@ -32,14 +31,13 @@ module HttpZip
32
31
  from = @header_offset + header_size
33
32
  to = @header_offset + header_size + @compressed_size
34
33
 
35
- decompress, finish = decompress_funcs
36
-
34
+ decompressor = compression_method
37
35
  ::File.open(filename, 'wb') do |out_file|
38
36
  @range_request.get(from, to) do |chunk|
39
- decompressed = decompress.call(chunk)
37
+ decompressed = decompressor.decompress(chunk)
40
38
  out_file.write(decompressed)
41
39
  end
42
- finish.call
40
+ decompressor.finish
43
41
  end
44
42
  end
45
43
 
@@ -57,33 +55,19 @@ module HttpZip
57
55
  30 + file_name_length + extra_field_length
58
56
  end
59
57
 
60
- def decompress_funcs
58
+ def compression_method
61
59
  # which compression method is used?
62
- compression_method = header[8...10].unpack1('v')
60
+ algorithm = header[8...10].unpack1('v')
63
61
 
64
- case compression_method
62
+ case algorithm
65
63
  when 0
66
- # STORED content, doesn't require decompression
67
- decompress = lambda { |input|
68
- input
69
- }
70
- finish = -> {}
64
+ HttpZip::Compression::Stored.new
71
65
  when 8
72
- inflater = Zlib::Inflate.new(-Zlib::MAX_WBITS)
73
- # DEFLATED content, inflate it
74
- decompress = lambda { |input|
75
- inflater.inflate(input)
76
- }
77
- finish = lambda do
78
- inflater.finish
79
- inflater.close
80
- end
66
+ HttpZip::Compression::Deflate.new
81
67
  else
82
68
  raise HttpZip::ZipError,
83
- "Unsupported compression method #{compression_method}. HttpZip only supports compression methods 0 (STORED) and 8 (DEFLATE)."
69
+ "Unsupported compression method #{algorithm}. HttpZip only supports compression methods 0 (STORED) and 8 (DEFLATE)."
84
70
  end
85
-
86
- [decompress, finish]
87
71
  end
88
72
  end
89
73
  end
@@ -2,6 +2,7 @@
2
2
 
3
3
  module HttpZip
4
4
  class Error < StandardError; end
5
- class ContentRangeError < Error; end
5
+ class RequestError < Error; end
6
+ class ContentRangeError < RequestError; end
6
7
  class ZipError < Error; end
7
8
  end
data/lib/http_zip/file.rb CHANGED
@@ -23,7 +23,6 @@ module HttpZip
23
23
  @url = url
24
24
  @entries = nil
25
25
  @range_request = RangeRequest.new(url)
26
- @range_request.check_server_supports_content_range!
27
26
  end
28
27
 
29
28
  # Get all entries in the zip archive as an array of HttpZip::Entry.
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'httparty'
3
+ require 'net/http'
4
4
 
5
5
  module HttpZip
6
6
  # Class to make Range requests to a HTTP server
@@ -9,7 +9,9 @@ module HttpZip
9
9
  #
10
10
  # @param [String] url remote file URL
11
11
  def initialize(url)
12
- @url = url
12
+ @uri = URI(url)
13
+ @connection = Net::HTTP.new(@uri.host, @uri.port)
14
+ @connection.use_ssl = true if @uri.scheme == 'https'
13
15
  end
14
16
 
15
17
  # Request a partial object via HTTP. If a block is given, yields the response body in chunks.
@@ -18,20 +20,10 @@ module HttpZip
18
20
  # @param [Integer] to end byte of the range to request. Exclusive.
19
21
  # @yield [chunk] yields a chunk of data to the block
20
22
  # @raise [ContentRangeError] if the server responds with anything other than 206 Partial Content
21
- def get(from, to)
22
- options = { headers: { 'Range' => "bytes=#{from}-#{to - 1}" } }
23
- options[:stream_body] = true if block_given?
24
-
25
- response = HTTParty.get(@url, options) do |chunk|
26
- yield chunk if block_given?
27
- end
28
-
29
- if response.code != 206
30
- # oops, we downloaded the whole file
31
- raise ContentRangeError, 'Server does not support the Range header'
32
- end
33
-
34
- response.body
23
+ def get(from, to, &block)
24
+ request = Net::HTTP::Get.new(@uri)
25
+ request['Range'] = "bytes=#{from}-#{to - 1}"
26
+ make_request(request, &block)
35
27
  end
36
28
 
37
29
  # Request the last `num_bytes` bytes of the remote file via HTTP.
@@ -39,32 +31,31 @@ module HttpZip
39
31
  # @param [Integer] num_bytes number of bytes to request
40
32
  # @raise [ContentRangeError] if the server responds with anything other than 206 Partial Content
41
33
  def last(num_bytes)
42
- response = HTTParty.get(@url, headers: { 'Range' => "bytes=-#{num_bytes}" })
43
- if response.code != 206
44
- # oops, we downloaded the whole file
45
- raise ContentRangeError, 'Server does not support the Range header'
46
- end
47
-
48
- response.body
34
+ request = Net::HTTP::Get.new(@uri)
35
+ request['Range'] = "bytes=-#{num_bytes}"
36
+ make_request(request)
49
37
  end
50
38
 
51
- # Tests if the server supports the Range header by checking the "Accept-Ranges" header,
52
- # otherwise raises an exception.
53
- #
54
- # @raise [ContentRangeError] if the server does not support the Range header
55
- def check_server_supports_content_range!
56
- return if self.class.server_supports_content_range?(@url)
39
+ private
57
40
 
58
- raise ContentRangeError, 'Server does not support the Range header'
41
+ def make_request(request, &block)
42
+ @connection.start do |http|
43
+ response = http.request(request) do |res|
44
+ handle_response_code!(res)
45
+ res.read_body(&block)
46
+ end
47
+
48
+ response.body unless block_given?
49
+ end
59
50
  end
60
51
 
61
- # Tests if the server supports the Range header by checking the "Accept-Ranges" header.
62
- #
63
- # @param [String] url remote file URL
64
- # @return [Boolean] true if the server supports the Range header
65
- def self.server_supports_content_range?(url)
66
- response = HTTParty.head(url)
67
- response.headers['Accept-Ranges'] && response.headers['Accept-Ranges'].downcase != 'none'
52
+ def handle_response_code!(response)
53
+ unless response.is_a?(Net::HTTPSuccess)
54
+ raise RequestError, "Server responded with #{response.code} #{response.message}"
55
+ end
56
+ return if response.is_a?(Net::HTTPPartialContent)
57
+
58
+ raise ContentRangeError, 'Server does not support the Range header'
68
59
  end
69
60
  end
70
61
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module HttpZip
4
- VERSION = '1.0.0'
4
+ VERSION = '2.0.0'
5
5
  end
data/lib/http_zip.rb CHANGED
@@ -7,3 +7,5 @@ require 'http_zip/entry'
7
7
  require 'http_zip/file'
8
8
  require 'http_zip/parser/central_directory_file_header'
9
9
  require 'http_zip/parser/central_directory'
10
+ require 'http_zip/compression/stored'
11
+ require 'http_zip/compression/deflate'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: http_zip
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Marvin Killing
@@ -9,22 +9,8 @@ authors:
9
9
  autorequire:
10
10
  bindir: exe
11
11
  cert_chain: []
12
- date: 2022-04-07 00:00:00.000000000 Z
12
+ date: 2022-04-13 00:00:00.000000000 Z
13
13
  dependencies:
14
- - !ruby/object:Gem::Dependency
15
- name: httparty
16
- requirement: !ruby/object:Gem::Requirement
17
- requirements:
18
- - - "~>"
19
- - !ruby/object:Gem::Version
20
- version: '0.20'
21
- type: :runtime
22
- prerelease: false
23
- version_requirements: !ruby/object:Gem::Requirement
24
- requirements:
25
- - - "~>"
26
- - !ruby/object:Gem::Version
27
- version: '0.20'
28
14
  - !ruby/object:Gem::Dependency
29
15
  name: bundler
30
16
  requirement: !ruby/object:Gem::Requirement
@@ -59,14 +45,20 @@ dependencies:
59
45
  requirements:
60
46
  - - "~>"
61
47
  - !ruby/object:Gem::Version
62
- version: '10.0'
48
+ version: '12.3'
49
+ - - ">="
50
+ - !ruby/object:Gem::Version
51
+ version: 12.3.3
63
52
  type: :development
64
53
  prerelease: false
65
54
  version_requirements: !ruby/object:Gem::Requirement
66
55
  requirements:
67
56
  - - "~>"
68
57
  - !ruby/object:Gem::Version
69
- version: '10.0'
58
+ version: '12.3'
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: 12.3.3
70
62
  - !ruby/object:Gem::Dependency
71
63
  name: simplecov
72
64
  requirement: !ruby/object:Gem::Requirement
@@ -104,6 +96,7 @@ extra_rdoc_files: []
104
96
  files:
105
97
  - ".gitignore"
106
98
  - ".travis.yml"
99
+ - CHANGELOG.md
107
100
  - Gemfile
108
101
  - LICENSE.txt
109
102
  - README.md
@@ -112,6 +105,8 @@ files:
112
105
  - bin/setup
113
106
  - http_zip.gemspec
114
107
  - lib/http_zip.rb
108
+ - lib/http_zip/compression/deflate.rb
109
+ - lib/http_zip/compression/stored.rb
115
110
  - lib/http_zip/entry.rb
116
111
  - lib/http_zip/errors.rb
117
112
  - lib/http_zip/file.rb