pinch 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (7) hide show
  1. data/.gemtest +0 -0
  2. data/MIT-LICENSE +19 -0
  3. data/README.rdoc +48 -0
  4. data/Rakefile +9 -0
  5. data/lib/pinch.rb +156 -0
  6. data/spec/pinch_spec.rb +24 -0
  7. metadata +73 -0
data/.gemtest ADDED
File without changes
data/MIT-LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2011 Peter Hellberg
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,48 @@
1
+ = Pinch
2
+
3
+ Retrieve a file from inside a zip file, over the network!
4
+
5
+ Pinch makes it possible to download a specific file from within a ZIP file
6
+ over HTTP/1.1 using nothing but the Ruby Standard Library (net/http and zlib)
7
+
8
+ Pinch is the Ruby implementation of an idea that my colleague Edward Patel
9
+ had a while back.
10
+
11
+ The first version was written in Objetive-C and we thought it would be cool
12
+ if we could bring that functionality to Ruby :)
13
+
14
+ I’ve tested Pitch on 1.9.2. YMMV.
15
+
16
+ == Installation
17
+
18
+ gem install pinch
19
+
20
+ == Usage example
21
+
22
+ require 'pinch'
23
+
24
+ data = Pinch.get 'URL_TO_ZIP_FILE', 'path/file.ext'
25
+
26
+ puts data
27
+
28
+ == LICENSE
29
+
30
+ Copyright (c) 2011 Peter Hellberg and Edward Patel
31
+
32
+ Permission is hereby granted, free of charge, to any person obtaining a copy
33
+ of this software and associated documentation files (the "Software"), to deal
34
+ in the Software without restriction, including without limitation the rights
35
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
36
+ copies of the Software, and to permit persons to whom the Software is
37
+ furnished to do so, subject to the following conditions:
38
+
39
+ The above copyright notice and this permission notice shall be included in
40
+ all copies or substantial portions of the Software.
41
+
42
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
43
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
44
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
45
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
46
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
47
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
48
+ THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,9 @@
1
+ require 'rake/testtask'
2
+
3
+ task :default => :test
4
+
5
+ Rake::TestTask.new(:test) do |t|
6
+ t.test_files = FileList['spec/*_spec.rb']
7
+ t.ruby_opts = ['-rubygems'] if defined? Gem
8
+ t.ruby_opts << '-I.'
9
+ end
data/lib/pinch.rb ADDED
@@ -0,0 +1,156 @@
1
+ # encoding: utf-8
2
+ require 'net/http'
3
+ require 'zlib'
4
+
5
+ class Pinch
6
+ VERSION = "0.0.1"
7
+
8
+ attr_reader :uri
9
+ attr_reader :file_name
10
+ attr_reader :content_length
11
+ attr_reader :central_directory
12
+ attr_reader :file_headers
13
+
14
+ def self.get(url, file_name)
15
+ new(url).data(file_name)
16
+ end
17
+
18
+ def initialize(url)
19
+ @uri = URI.parse(url)
20
+ @files = {}
21
+ end
22
+
23
+ def file_headers
24
+ @file_headers ||= begin
25
+ raise RuntimeError, "Couldn’t find the central directory." if central_directory.nil?
26
+
27
+ headers = {}
28
+ tmp = central_directory
29
+
30
+ begin
31
+ cd = tmp.unpack('VvvvvvvVVVvvvvvVV')
32
+ break if cd[1] == 0
33
+
34
+ length = 46+cd[10]+cd[11]+cd[12]
35
+ current_file_name = tmp[46...46+cd[10]]
36
+ tmp = tmp[length..-1]
37
+ headers[current_file_name] = cd
38
+ end while true
39
+
40
+ headers
41
+ end
42
+ end
43
+
44
+ def data(file_name)
45
+ raise Errno::ENOENT if file_headers[file_name].nil?
46
+
47
+ req = Net::HTTP::Get.new(uri.path)
48
+ req.set_range(file_headers[file_name][16],
49
+ file_headers[file_name][16] +
50
+ file_headers[file_name][8] +
51
+ file_headers[file_name][10] +
52
+ file_headers[file_name][11] +
53
+ file_headers[file_name][12] + 30)
54
+
55
+ res = Net::HTTP.start(uri.host, uri.port) do |http|
56
+ http.request(req)
57
+ end
58
+
59
+ ###########################################################################
60
+ # Local file header
61
+ ###########################################################################
62
+
63
+ #0 uint32 localFileHeaderSignature
64
+ #1 uint16 versionNeededToExtract
65
+ #2 uint16 generalPurposeBitFlag
66
+ #3 uint16 compressionMethod
67
+ #4 uint16 fileLastModificationTime
68
+ #5 uint16 fileLastModificationDate
69
+ #6 uint32 CRC32
70
+ #7 uint32 compressedSize
71
+ #8 uint32 uncompressedSize
72
+ #9 uint16 fileNameLength
73
+ #10 uint16 extraFieldLength
74
+
75
+ local_file_header = res.body.unpack('VvvvvvVVVvv')
76
+ file_data = res.body[30+local_file_header[9]+local_file_header[10]..-1]
77
+
78
+ if local_file_header[3] == 0
79
+ raise NotImplementedError, "Unable to read uncompressed ZIP files for now"
80
+ else
81
+ Zlib::Inflate.new(-Zlib::MAX_WBITS).inflate(file_data)
82
+ end
83
+ end
84
+
85
+ def central_directory
86
+ #0 uint32 centralDirectoryFileHeaderSignature
87
+ #1 uint16 versionMadeBy
88
+ #2 uint16 versionNeededToExtract
89
+ #3 uint16 generalPurposeBitFlag
90
+ #4 uint16 compressionMethod
91
+ #5 uint16 fileLastModificationTime
92
+ #6 uint16 fileLastModificationDate
93
+ #7 uint32 CRC32
94
+ #8 uint32 compressedSize
95
+ #9 uint32 uncompressedSize
96
+ #10 uint16 fileNameLength
97
+ #11 uint16 extraFieldLength
98
+ #12 uint16 fileCommentLength
99
+ #13 uint16 diskNumberWhereFileStarts
100
+ #14 uint16 internalFileAttributes
101
+ #15 uint32 externalFileAttributes
102
+ #16 uint32 relativeOffsetOfLocalFileHeader
103
+
104
+ @central_directory ||= begin
105
+ req = Net::HTTP::Get.new(uri.path)
106
+ req.set_range(end_of_central_directory_record[5],
107
+ end_of_central_directory_record[5] +
108
+ end_of_central_directory_record[4])
109
+
110
+ res = Net::HTTP.start(uri.host, uri.port) { |http|
111
+ http.request(req)
112
+ }
113
+
114
+ if [200, 206].include?(res.code)
115
+ raise RuntimeError, "Couldn’t find the ZIP file (HTTP: #{res.code})"
116
+ else
117
+ res.body
118
+ end
119
+ end
120
+ end
121
+
122
+ # Retrieve the content length of the file
123
+ def content_length
124
+ @content_length ||= Net::HTTP.start(@uri.host, @uri.port) { |http|
125
+ http.head(@uri.path)
126
+ }['Content-Length'].to_i
127
+ end
128
+
129
+ def end_of_central_directory_record
130
+ #0 uint16 numberOfThisDisk;
131
+ #1 uint16 diskWhereCentralDirectoryStarts;
132
+ #2 uint16 numberOfCentralDirectoryRecordsOnThisDisk;
133
+ #3 uint16 totalNumberOfCentralDirectoryRecords;
134
+ #4 uint32 sizeOfCentralDirectory;
135
+ #5 uint32 offsetOfStartOfCentralDirectory;
136
+ #6 uint16 ZIPfileCommentLength;
137
+
138
+ @end_of_central_directory_record ||= begin
139
+ # Retrieve a 4k of data from the end of the zip file
140
+ request = Net::HTTP::Get.new(uri.path)
141
+ offset = content_length >= 4096 ? content_length-4096 : 0
142
+
143
+ request.set_range(offset, content_length)
144
+
145
+ response = Net::HTTP.start(uri.host, uri.port) do |http|
146
+ http.request(request)
147
+ end
148
+
149
+ # Unpack the body into a hex string
150
+ hex = response.body.unpack("H*")[0]
151
+
152
+ # Split on the end record signature, and unpack the last one
153
+ [hex.split("504b0506").last].pack("H*").unpack("vvvvVVv")
154
+ end
155
+ end
156
+ end
@@ -0,0 +1,24 @@
1
+ require 'minitest/pride'
2
+ require 'minitest/autorun'
3
+ require 'minitest/spec'
4
+
5
+ require File.dirname(__FILE__) + '/../lib/pinch'
6
+
7
+ describe Pinch do
8
+ describe "when calling get" do
9
+ it "should return the contents of the file" do
10
+ url = 'http://ftp.sunet.se/pub/lang/smalltalk/Squeak/current_stable/Squeak3.8-6665-full.zip'
11
+ file = 'ReadMe.txt'
12
+ data = Pinch.get url, file
13
+ data.must_match /Morphic graphics architecture/
14
+ end
15
+
16
+ it "should not pinch from a zip file that is not deflated (for now)" do
17
+ lambda {
18
+ url = 'http://memention.com/ericjohnson-canabalt-ios-ef43b7d.zip'
19
+ file = 'ericjohnson-canabalt-ios-ef43b7d/README.TXT'
20
+ data = Pinch.get url, file
21
+ }.must_raise NotImplementedError
22
+ end
23
+ end
24
+ end
metadata ADDED
@@ -0,0 +1,73 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: pinch
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Peter Hellberg
9
+ - Edward Patel
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+ date: 2011-07-03 00:00:00.000000000 +02:00
14
+ default_executable:
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: minitest
18
+ requirement: &2156678820 !ruby/object:Gem::Requirement
19
+ none: false
20
+ requirements:
21
+ - - ! '>='
22
+ - !ruby/object:Gem::Version
23
+ version: '0'
24
+ type: :development
25
+ prerelease: false
26
+ version_requirements: *2156678820
27
+ description: Pinch makes it possible to download a specific file from within a ZIP
28
+ file over HTTP 1.1.
29
+ email: peter@c7.se
30
+ executables: []
31
+ extensions: []
32
+ extra_rdoc_files:
33
+ - README.rdoc
34
+ - MIT-LICENSE
35
+ files:
36
+ - lib/pinch.rb
37
+ - MIT-LICENSE
38
+ - README.rdoc
39
+ - Rakefile
40
+ - .gemtest
41
+ - spec/pinch_spec.rb
42
+ has_rdoc: true
43
+ homepage: https://github.com/peterhellberg/pinch
44
+ licenses:
45
+ - MIT-LICENSE
46
+ post_install_message:
47
+ rdoc_options:
48
+ - --main
49
+ - README.rdoc
50
+ - --charset=UTF-8
51
+ require_paths:
52
+ - lib
53
+ - lib
54
+ required_ruby_version: !ruby/object:Gem::Requirement
55
+ none: false
56
+ requirements:
57
+ - - ! '>='
58
+ - !ruby/object:Gem::Version
59
+ version: 1.8.7
60
+ required_rubygems_version: !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ! '>='
64
+ - !ruby/object:Gem::Version
65
+ version: '0'
66
+ requirements: []
67
+ rubyforge_project:
68
+ rubygems_version: 1.6.2
69
+ signing_key:
70
+ specification_version: 3
71
+ summary: Retrieve a file from inside a zip file, over the network!
72
+ test_files:
73
+ - spec/pinch_spec.rb