http_zip 2.0.0 → 2.1.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 +4 -4
- data/.gitignore +3 -1
- data/Gemfile +1 -1
- data/Rakefile +5 -5
- data/http_zip.gemspec +21 -19
- data/lib/http_zip/compression/stored.rb +2 -1
- data/lib/http_zip/entry.rb +8 -7
- data/lib/http_zip/errors.rb +3 -0
- data/lib/http_zip/file.rb +2 -1
- data/lib/http_zip/parser/central_directory.rb +9 -9
- data/lib/http_zip/parser/central_directory_file_header.rb +7 -7
- data/lib/http_zip/range_request.rb +6 -6
- data/lib/http_zip/version.rb +1 -1
- data/lib/http_zip.rb +9 -9
- metadata +31 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 19b6d04b94e6807748d28eaa6efd8fea800ffa3954483f6054d9a72ffc33c5d6
|
4
|
+
data.tar.gz: 43616195f5b74a786b384757a5cad9ee0928dc7b9b13ec7f43294abe036a1f33
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6b4f9b7fccbefa2a04bb61a7127ac70fad27129f58b0bbe0a7081e4e175359272de6f4db2ec98fc73887adfe34b339823b13606a3f9dc9afe9df7cba932fe4d4
|
7
|
+
data.tar.gz: f280e3fcb4fef1c70a83e80ed09916eea48a6918a3055b41b099b88365dcd2a569e516a1cb86b9b56a9e35ee414b02aece303796f3c4da47e3eef476a03673d0
|
data/.gitignore
CHANGED
data/Gemfile
CHANGED
data/Rakefile
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
3
|
+
require "bundler/gem_tasks"
|
4
|
+
require "rake/testtask"
|
5
5
|
|
6
6
|
Rake::TestTask.new(:test) do |t|
|
7
|
-
t.libs <<
|
8
|
-
t.libs <<
|
9
|
-
t.test_files = FileList[
|
7
|
+
t.libs << "test"
|
8
|
+
t.libs << "lib"
|
9
|
+
t.test_files = FileList["test/**/*_test.rb"]
|
10
10
|
end
|
11
11
|
|
12
12
|
task default: :test
|
data/http_zip.gemspec
CHANGED
@@ -1,34 +1,36 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
lib = File.expand_path(
|
3
|
+
lib = File.expand_path("lib", __dir__)
|
4
4
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
-
require
|
5
|
+
require "http_zip/version"
|
6
6
|
|
7
7
|
Gem::Specification.new do |spec|
|
8
|
-
spec.name
|
9
|
-
spec.version
|
10
|
-
spec.authors
|
11
|
-
spec.email
|
8
|
+
spec.name = "http_zip"
|
9
|
+
spec.version = HttpZip::VERSION
|
10
|
+
spec.authors = ["Marvin Killing", "Peter Retzlaff"]
|
11
|
+
spec.email = ["pe.retzlaff@gmail.com"]
|
12
12
|
|
13
|
-
spec.summary
|
14
|
-
spec.homepage
|
15
|
-
spec.license
|
13
|
+
spec.summary = "HttpZip is a gem to extract individual files from a remote ZIP archive, without the need to download the entire file."
|
14
|
+
spec.homepage = "https://github.com/peret/http_zip"
|
15
|
+
spec.license = "MIT"
|
16
16
|
|
17
|
-
spec.metadata[
|
18
|
-
spec.metadata[
|
17
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
18
|
+
spec.metadata["source_code_uri"] = "https://github.com/peret/http_zip"
|
19
19
|
|
20
20
|
# Specify which files should be added to the gem when it is released.
|
21
21
|
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
22
22
|
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
23
23
|
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
24
24
|
end
|
25
|
-
spec.bindir
|
26
|
-
spec.executables
|
27
|
-
spec.require_paths = [
|
25
|
+
spec.bindir = "exe"
|
26
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
27
|
+
spec.require_paths = ["lib"]
|
28
28
|
|
29
|
-
spec.add_development_dependency
|
30
|
-
spec.add_development_dependency
|
31
|
-
spec.add_development_dependency
|
32
|
-
spec.add_development_dependency
|
33
|
-
spec.add_development_dependency
|
29
|
+
spec.add_development_dependency "bundler", "~> 2.0"
|
30
|
+
spec.add_development_dependency "minitest", "~> 5.15"
|
31
|
+
spec.add_development_dependency "rake", "~> 12.3", ">= 12.3.3"
|
32
|
+
spec.add_development_dependency "simplecov", "~> 0.21"
|
33
|
+
spec.add_development_dependency "webmock", "~> 3.14"
|
34
|
+
spec.add_development_dependency "solargraph", "~> 0.50"
|
35
|
+
spec.add_development_dependency "standard", "~> 1.31.0"
|
34
36
|
end
|
data/lib/http_zip/entry.rb
CHANGED
@@ -4,13 +4,14 @@ 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, :compressed_size
|
7
|
+
attr_reader :name, :compressed_size, :uncompressed_size
|
8
8
|
|
9
|
-
def initialize(url, name, header_offset, central_directory_file_compressed_size)
|
9
|
+
def initialize(url, name, header_offset, central_directory_file_compressed_size, central_directory_file_uncompressed_size)
|
10
10
|
@range_request = HttpZip::RangeRequest.new(url)
|
11
11
|
@name = name
|
12
12
|
@header_offset = header_offset
|
13
13
|
@compressed_size = central_directory_file_compressed_size
|
14
|
+
@uncompressed_size = central_directory_file_uncompressed_size
|
14
15
|
end
|
15
16
|
|
16
17
|
# Get the decompressed content of the file entry
|
@@ -32,7 +33,7 @@ module HttpZip
|
|
32
33
|
to = @header_offset + header_size + @compressed_size
|
33
34
|
|
34
35
|
decompressor = compression_method
|
35
|
-
::File.open(filename,
|
36
|
+
::File.open(filename, "wb") do |out_file|
|
36
37
|
@range_request.get(from, to) do |chunk|
|
37
38
|
decompressed = decompressor.decompress(chunk)
|
38
39
|
out_file.write(decompressed)
|
@@ -50,14 +51,14 @@ module HttpZip
|
|
50
51
|
|
51
52
|
def header_size
|
52
53
|
# find out where the file contents start and how large the file is
|
53
|
-
file_name_length = header[26...28].unpack1(
|
54
|
-
extra_field_length = header[28...30].unpack1(
|
54
|
+
file_name_length = header[26...28].unpack1("v")
|
55
|
+
extra_field_length = header[28...30].unpack1("v")
|
55
56
|
30 + file_name_length + extra_field_length
|
56
57
|
end
|
57
58
|
|
58
59
|
def compression_method
|
59
60
|
# which compression method is used?
|
60
|
-
algorithm = header[8...10].unpack1(
|
61
|
+
algorithm = header[8...10].unpack1("v")
|
61
62
|
|
62
63
|
case algorithm
|
63
64
|
when 0
|
@@ -66,7 +67,7 @@ module HttpZip
|
|
66
67
|
HttpZip::Compression::Deflate.new
|
67
68
|
else
|
68
69
|
raise HttpZip::ZipError,
|
69
|
-
|
70
|
+
"Unsupported compression method #{algorithm}. HttpZip only supports compression methods 0 (STORED) and 8 (DEFLATE)."
|
70
71
|
end
|
71
72
|
end
|
72
73
|
end
|
data/lib/http_zip/errors.rb
CHANGED
data/lib/http_zip/file.rb
CHANGED
@@ -25,10 +25,10 @@ module HttpZip
|
|
25
25
|
# @raise [ZipError] if the byte stream does not contain a valid EOCD64 block
|
26
26
|
def parse_eocd64!(eocd64_block)
|
27
27
|
unless eocd64_block.start_with?(EOCD64_BLOCK_IDENTIFER)
|
28
|
-
raise ZipError,
|
28
|
+
raise ZipError, "EOCD64 record not found"
|
29
29
|
end
|
30
30
|
|
31
|
-
@size, @offset = eocd64_block[40..-1].unpack(
|
31
|
+
@size, @offset = eocd64_block[40..-1].unpack("Q<Q<")
|
32
32
|
end
|
33
33
|
|
34
34
|
private
|
@@ -40,20 +40,20 @@ module HttpZip
|
|
40
40
|
def parse!
|
41
41
|
eocd_block_index = get_eocd_block_index(@bytes)
|
42
42
|
eocd_block = @bytes[eocd_block_index..-1]
|
43
|
-
@size, @offset = eocd_block[12...20].unpack(
|
43
|
+
@size, @offset = eocd_block[12...20].unpack("VV")
|
44
44
|
return if @size != 0xFFFFFFFF && @offset != 0xFFFFFFFF
|
45
45
|
|
46
46
|
# there will be a zip64 EOCD locator block before the EOCD block
|
47
47
|
# parse the EOCD locator to find out where the EOCD64 block starts
|
48
48
|
eocd64_locator_block = @bytes[(eocd_block_index - 20)..eocd_block_index]
|
49
49
|
unless eocd64_locator_block.start_with?(EOCD64_LOCATOR_BLOCK_IDENTIFER)
|
50
|
-
raise ZipError,
|
50
|
+
raise ZipError, "Could not locate the EOCD64 locator block"
|
51
51
|
end
|
52
52
|
|
53
|
-
@eocd64_offset, total_num_disks = eocd64_locator_block[8..-1].unpack(
|
53
|
+
@eocd64_offset, total_num_disks = eocd64_locator_block[8..-1].unpack("Q<V")
|
54
54
|
return if total_num_disks == 1
|
55
55
|
|
56
|
-
raise ZipError,
|
56
|
+
raise ZipError, "Multi-disk archives are not supported"
|
57
57
|
end
|
58
58
|
|
59
59
|
# In order to find the central directory, we have to first find the EOCD block.
|
@@ -70,13 +70,13 @@ module HttpZip
|
|
70
70
|
search_end_position = candidate_eocd_block.length
|
71
71
|
loop do
|
72
72
|
eocd_block_start_index = candidate_eocd_block.rindex(EOCD_BLOCK_IDENTIFIER,
|
73
|
-
|
73
|
+
search_end_position)
|
74
74
|
|
75
|
-
raise ZipError,
|
75
|
+
raise ZipError, "Could not locate valid EOCD block" if eocd_block_start_index.nil?
|
76
76
|
|
77
77
|
# we have a candidate, verify that we found the actual eocd block start by
|
78
78
|
# checking whether its position + length matches the end of the file
|
79
|
-
comment_length = candidate_eocd_block[(eocd_block_start_index + 20)...(eocd_block_start_index + 22)].unpack1(
|
79
|
+
comment_length = candidate_eocd_block[(eocd_block_start_index + 20)...(eocd_block_start_index + 22)].unpack1("v")
|
80
80
|
if (eocd_block_start_index + 22 + comment_length) == candidate_eocd_block.length
|
81
81
|
# we found it
|
82
82
|
break
|
@@ -28,7 +28,7 @@ module HttpZip
|
|
28
28
|
def initialize(file_header_bytes)
|
29
29
|
@bytes = file_header_bytes
|
30
30
|
unless @bytes.start_with?(CENTRAL_DIRECTORY_FILE_HEADER_IDENTIFIER)
|
31
|
-
raise ZipError,
|
31
|
+
raise ZipError, "Central Directory File Header seems to be corrupt"
|
32
32
|
end
|
33
33
|
|
34
34
|
parse!
|
@@ -47,7 +47,7 @@ module HttpZip
|
|
47
47
|
@disk_number,
|
48
48
|
@internal_file_attributes,
|
49
49
|
@external_file_attributes,
|
50
|
-
@header_offset = @bytes[20...46].unpack(
|
50
|
+
@header_offset = @bytes[20...46].unpack("VVvvvvvVV")
|
51
51
|
|
52
52
|
file_name_end = 46 + file_name_length
|
53
53
|
@file_name = @bytes[46...file_name_end]
|
@@ -72,7 +72,7 @@ module HttpZip
|
|
72
72
|
# so we need to abort if there’s nothing of value in the extra fields
|
73
73
|
break if remaining_extra_field_bytes.delete("\0").empty?
|
74
74
|
|
75
|
-
record_length = remaining_extra_field_bytes[2...4].unpack1(
|
75
|
+
record_length = remaining_extra_field_bytes[2...4].unpack1("v")
|
76
76
|
|
77
77
|
# did we find the Zip64 extra field?
|
78
78
|
if remaining_extra_field_bytes.start_with?(ZIP64_EXTRA_FIELD_HEADER_ID)
|
@@ -95,19 +95,19 @@ module HttpZip
|
|
95
95
|
# so only the values too large for the non-zip64 file header will be stored here
|
96
96
|
ptr = 2 # ignore the size field, since it seems to be incorrect in some cases
|
97
97
|
if @uncompressed_size == 0xFFFFFFFF
|
98
|
-
@uncompressed_size = extra_field_bytes[ptr...(ptr + 8)].unpack1(
|
98
|
+
@uncompressed_size = extra_field_bytes[ptr...(ptr + 8)].unpack1("Q<")
|
99
99
|
ptr += 8
|
100
100
|
end
|
101
101
|
if @compressed_size == 0xFFFFFFFF
|
102
|
-
@compressed_size = extra_field_bytes[ptr...(ptr + 8)].unpack1(
|
102
|
+
@compressed_size = extra_field_bytes[ptr...(ptr + 8)].unpack1("Q<")
|
103
103
|
ptr += 8
|
104
104
|
end
|
105
105
|
if @header_offset == 0xFFFFFFFF
|
106
|
-
@header_offset = extra_field_bytes[ptr...(ptr + 8)].unpack1(
|
106
|
+
@header_offset = extra_field_bytes[ptr...(ptr + 8)].unpack1("Q<")
|
107
107
|
ptr += 8
|
108
108
|
end
|
109
109
|
if @disk_number == 0xFFFF
|
110
|
-
@disk_number = extra_field_bytes[ptr...(ptr + 4)].unpack1(
|
110
|
+
@disk_number = extra_field_bytes[ptr...(ptr + 4)].unpack1("V")
|
111
111
|
end
|
112
112
|
end
|
113
113
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
3
|
+
require "net/http"
|
4
4
|
|
5
5
|
module HttpZip
|
6
6
|
# Class to make Range requests to a HTTP server
|
@@ -11,7 +11,7 @@ module HttpZip
|
|
11
11
|
def initialize(url)
|
12
12
|
@uri = URI(url)
|
13
13
|
@connection = Net::HTTP.new(@uri.host, @uri.port)
|
14
|
-
@connection.use_ssl = true if @uri.scheme ==
|
14
|
+
@connection.use_ssl = true if @uri.scheme == "https"
|
15
15
|
end
|
16
16
|
|
17
17
|
# Request a partial object via HTTP. If a block is given, yields the response body in chunks.
|
@@ -22,7 +22,7 @@ module HttpZip
|
|
22
22
|
# @raise [ContentRangeError] if the server responds with anything other than 206 Partial Content
|
23
23
|
def get(from, to, &block)
|
24
24
|
request = Net::HTTP::Get.new(@uri)
|
25
|
-
request[
|
25
|
+
request["Range"] = "bytes=#{from}-#{to - 1}"
|
26
26
|
make_request(request, &block)
|
27
27
|
end
|
28
28
|
|
@@ -32,7 +32,7 @@ module HttpZip
|
|
32
32
|
# @raise [ContentRangeError] if the server responds with anything other than 206 Partial Content
|
33
33
|
def last(num_bytes)
|
34
34
|
request = Net::HTTP::Get.new(@uri)
|
35
|
-
request[
|
35
|
+
request["Range"] = "bytes=-#{num_bytes}"
|
36
36
|
make_request(request)
|
37
37
|
end
|
38
38
|
|
@@ -45,7 +45,7 @@ module HttpZip
|
|
45
45
|
res.read_body(&block)
|
46
46
|
end
|
47
47
|
|
48
|
-
response.body unless
|
48
|
+
response.body unless block
|
49
49
|
end
|
50
50
|
end
|
51
51
|
|
@@ -55,7 +55,7 @@ module HttpZip
|
|
55
55
|
end
|
56
56
|
return if response.is_a?(Net::HTTPPartialContent)
|
57
57
|
|
58
|
-
raise ContentRangeError,
|
58
|
+
raise ContentRangeError, "Server does not support the Range header"
|
59
59
|
end
|
60
60
|
end
|
61
61
|
end
|
data/lib/http_zip/version.rb
CHANGED
data/lib/http_zip.rb
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
6
|
-
require
|
7
|
-
require
|
8
|
-
require
|
9
|
-
require
|
10
|
-
require
|
11
|
-
require
|
3
|
+
require "http_zip/version"
|
4
|
+
require "http_zip/errors"
|
5
|
+
require "http_zip/range_request"
|
6
|
+
require "http_zip/entry"
|
7
|
+
require "http_zip/file"
|
8
|
+
require "http_zip/parser/central_directory_file_header"
|
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: 2.
|
4
|
+
version: 2.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Marvin Killing
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: exe
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 1980-01-01 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bundler
|
@@ -87,6 +87,34 @@ dependencies:
|
|
87
87
|
- - "~>"
|
88
88
|
- !ruby/object:Gem::Version
|
89
89
|
version: '3.14'
|
90
|
+
- !ruby/object:Gem::Dependency
|
91
|
+
name: solargraph
|
92
|
+
requirement: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0.50'
|
97
|
+
type: :development
|
98
|
+
prerelease: false
|
99
|
+
version_requirements: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0.50'
|
104
|
+
- !ruby/object:Gem::Dependency
|
105
|
+
name: standard
|
106
|
+
requirement: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: 1.31.0
|
111
|
+
type: :development
|
112
|
+
prerelease: false
|
113
|
+
version_requirements: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - "~>"
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: 1.31.0
|
90
118
|
description:
|
91
119
|
email:
|
92
120
|
- pe.retzlaff@gmail.com
|
@@ -135,7 +163,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
135
163
|
- !ruby/object:Gem::Version
|
136
164
|
version: '0'
|
137
165
|
requirements: []
|
138
|
-
rubygems_version: 3.
|
166
|
+
rubygems_version: 3.5.6
|
139
167
|
signing_key:
|
140
168
|
specification_version: 4
|
141
169
|
summary: HttpZip is a gem to extract individual files from a remote ZIP archive, without
|