down 2.4.3 → 2.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +34 -13
- data/down.gemspec +1 -1
- data/lib/down.rb +48 -13
- data/lib/down/chunked_io.rb +9 -7
- data/lib/down/version.rb +1 -1
- metadata +6 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b36995e341763d157bd7dfc5e4de2aeee8f7d42e
|
4
|
+
data.tar.gz: f4f910e36886c4c64a1f6528a8f773716e28e940
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7c01afed653ed786903043d1b745afa99ec35d5e892d37df644171aad47d2d13a4bda3d98df5ebc890684180488fff62364537c78256e71d2a52c68fd2c69898
|
7
|
+
data.tar.gz: 1b61d561a3a0088c8df2e208cd71d15b8a4314de31a1f40d881eeda067396fbc5d0c80d6f78e6796f847717abbd0da3cf813b41cf7fb47dd10cd58e4a1f22454
|
data/README.md
CHANGED
@@ -113,18 +113,6 @@ Down.download "http://example.com/image.jpg",
|
|
113
113
|
read_timeout: 5
|
114
114
|
```
|
115
115
|
|
116
|
-
### Copying to tempfile
|
117
|
-
|
118
|
-
Down has another "hidden" utility method, `#copy_to_tempfile`, which creates
|
119
|
-
a Tempfile out of the given file. The `#download` method uses it internally,
|
120
|
-
but it's also publicly available for direct use:
|
121
|
-
|
122
|
-
```rb
|
123
|
-
io # IO object that you want to copy to tempfile
|
124
|
-
tempfile = Down.copy_to_tempfile "basename.jpg", io
|
125
|
-
tempfile.path #=> "/var/folders/k7/6zx6dx6x7ys3rv3srh0nyfj00000gn/T/down20151116-77262-jgcx65.jpg"
|
126
|
-
```
|
127
|
-
|
128
116
|
## Streaming
|
129
117
|
|
130
118
|
Down has the ability to access content of the remote file *as it is being
|
@@ -157,7 +145,18 @@ end
|
|
157
145
|
remote_file.close
|
158
146
|
```
|
159
147
|
|
160
|
-
|
148
|
+
You can access the response status and headers of the HTTP request that was made:
|
149
|
+
|
150
|
+
```rb
|
151
|
+
remote_file = Down.open("http://example.com/image.jpg")
|
152
|
+
remote_file.data[:status] #=> 200
|
153
|
+
remote_file.data[:headers] #=> { ... }
|
154
|
+
```
|
155
|
+
|
156
|
+
Note that `Down::NotFound` error will automatically be raised if response
|
157
|
+
status was 4xx or 5xx.
|
158
|
+
|
159
|
+
`Down.open` accepts `:ssl_verify_mode` and `:ssl_ca_cert` options with the same
|
161
160
|
semantics as in `open-uri`, and any options with String keys will be
|
162
161
|
interpreted as request headers.
|
163
162
|
|
@@ -196,6 +195,28 @@ io = Down::ChunkedIO.new(
|
|
196
195
|
)
|
197
196
|
```
|
198
197
|
|
198
|
+
### Proxy
|
199
|
+
|
200
|
+
Both `Down.download` and `Down.open` support a `:proxy` option, where you can
|
201
|
+
specify a URL to an HTTP proxy which should be used when downloading.
|
202
|
+
|
203
|
+
```rb
|
204
|
+
Down.download("http://example.com/image.jpg", proxy: "http://proxy.org")
|
205
|
+
Down.open("http://example.com/image.jpg", proxy: "http://user:password@proxy.org")
|
206
|
+
```
|
207
|
+
|
208
|
+
### Copying to tempfile
|
209
|
+
|
210
|
+
Down has another "hidden" utility method, `#copy_to_tempfile`, which creates
|
211
|
+
a Tempfile out of the given file. The `#download` method uses it internally,
|
212
|
+
but it's also publicly available for direct use:
|
213
|
+
|
214
|
+
```rb
|
215
|
+
io # IO object that you want to copy to tempfile
|
216
|
+
tempfile = Down.copy_to_tempfile "basename.jpg", io
|
217
|
+
tempfile.path #=> "/var/folders/k7/6zx6dx6x7ys3rv3srh0nyfj00000gn/T/down20151116-77262-jgcx65.jpg"
|
218
|
+
```
|
219
|
+
|
199
220
|
## Supported Ruby versions
|
200
221
|
|
201
222
|
* MRI 1.9.3
|
data/down.gemspec
CHANGED
@@ -15,7 +15,7 @@ Gem::Specification.new do |spec|
|
|
15
15
|
|
16
16
|
spec.add_development_dependency "rake"
|
17
17
|
spec.add_development_dependency "minitest", "~> 5.8"
|
18
|
-
spec.add_development_dependency "webmock"
|
18
|
+
spec.add_development_dependency "webmock", "~> 2.3"
|
19
19
|
spec.add_development_dependency "addressable", "< 2.5"
|
20
20
|
spec.add_development_dependency "mocha"
|
21
21
|
end
|
data/lib/down.rb
CHANGED
@@ -5,7 +5,7 @@ require "open-uri"
|
|
5
5
|
require "net/http"
|
6
6
|
require "tempfile"
|
7
7
|
require "fileutils"
|
8
|
-
require "cgi
|
8
|
+
require "cgi"
|
9
9
|
|
10
10
|
module Down
|
11
11
|
class Error < StandardError; end
|
@@ -14,7 +14,7 @@ module Down
|
|
14
14
|
|
15
15
|
module_function
|
16
16
|
|
17
|
-
def download(
|
17
|
+
def download(uri, options = {})
|
18
18
|
warn "Passing :timeout option to `Down.download` is deprecated and will be removed in Down 3. You should use open-uri's :open_timeout and/or :read_timeout." if options.key?(:timeout)
|
19
19
|
warn "Passing :progress option to `Down.download` is deprecated and will be removed in Down 3. You should use open-uri's :progress_proc." if options.key?(:progress)
|
20
20
|
|
@@ -24,10 +24,24 @@ module Down
|
|
24
24
|
content_length_proc = options.delete(:content_length_proc)
|
25
25
|
timeout = options.delete(:timeout)
|
26
26
|
|
27
|
+
if options[:proxy]
|
28
|
+
proxy = URI(options[:proxy])
|
29
|
+
user = proxy.user
|
30
|
+
password = proxy.password
|
31
|
+
|
32
|
+
if user || password
|
33
|
+
proxy.user = nil
|
34
|
+
proxy.password = nil
|
35
|
+
|
36
|
+
options[:proxy_http_basic_authentication] = [proxy.to_s, user, password]
|
37
|
+
options.delete(:proxy)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
27
41
|
tries = max_redirects + 1
|
28
42
|
|
29
43
|
begin
|
30
|
-
uri = URI
|
44
|
+
uri = URI(uri)
|
31
45
|
|
32
46
|
open_uri_options = {
|
33
47
|
"User-Agent" => "Down/#{VERSION}",
|
@@ -57,12 +71,12 @@ module Down
|
|
57
71
|
|
58
72
|
downloaded_file = uri.open(open_uri_options)
|
59
73
|
rescue OpenURI::HTTPRedirect => redirect
|
60
|
-
|
74
|
+
uri = redirect.uri
|
61
75
|
retry if (tries -= 1) > 0
|
62
|
-
raise Down::NotFound, "too many redirects: #{
|
76
|
+
raise Down::NotFound, "too many redirects: #{uri.to_s}"
|
63
77
|
rescue => error
|
64
78
|
raise if error.is_a?(Down::Error)
|
65
|
-
raise Down::NotFound, "file not found: #{
|
79
|
+
raise Down::NotFound, "file not found: #{uri.to_s}"
|
66
80
|
end
|
67
81
|
|
68
82
|
# open-uri will return a StringIO instead of a Tempfile if the filesize is
|
@@ -85,9 +99,16 @@ module Down
|
|
85
99
|
io.close
|
86
100
|
end
|
87
101
|
|
88
|
-
def open(
|
89
|
-
uri = URI
|
90
|
-
|
102
|
+
def open(uri, options = {})
|
103
|
+
uri = URI(uri)
|
104
|
+
http_class = Net::HTTP
|
105
|
+
|
106
|
+
if options[:proxy]
|
107
|
+
proxy = URI.parse(options[:proxy])
|
108
|
+
http_class = Net::HTTP::Proxy(proxy.hostname, proxy.port, proxy.user, proxy.password)
|
109
|
+
end
|
110
|
+
|
111
|
+
http = http_class.new(uri.host, uri.port)
|
91
112
|
|
92
113
|
# taken from open-uri implementation
|
93
114
|
if uri.is_a?(URI::HTTPS)
|
@@ -109,7 +130,9 @@ module Down
|
|
109
130
|
|
110
131
|
request = Fiber.new do
|
111
132
|
http.start do
|
112
|
-
|
133
|
+
req = Net::HTTP::Get.new(uri.request_uri, request_headers)
|
134
|
+
req.basic_auth(uri.user, uri.password) if uri.user || uri.password
|
135
|
+
http.request(req) do |response|
|
113
136
|
Fiber.yield response
|
114
137
|
response.instance_variable_set("@read", true)
|
115
138
|
end
|
@@ -118,11 +141,13 @@ module Down
|
|
118
141
|
|
119
142
|
response = request.resume
|
120
143
|
|
144
|
+
raise Down::NotFound, "request to #{uri.to_s} returned status #{response.code} and body:\n#{response.body}" if response.code.to_i.between?(400, 599)
|
145
|
+
|
121
146
|
if response.chunked?
|
122
147
|
# Net::HTTP's implementation of reading "Transfer-Encoding: chunked"
|
123
148
|
# raises a Fiber error, so we work around it by downloading the whole
|
124
149
|
# response body without Enumerators (which internally use Fibers).
|
125
|
-
warn "Response from #{
|
150
|
+
warn "Response from #{uri.to_s} returned as \"Transfer-Encoding: chunked\", which Down cannot partially download, so the whole response body will be downloaded instead."
|
126
151
|
|
127
152
|
tempfile = Tempfile.new("down", binmode: true)
|
128
153
|
response.read_body { |chunk| tempfile << chunk }
|
@@ -130,18 +155,28 @@ module Down
|
|
130
155
|
|
131
156
|
request.resume # close HTTP connection
|
132
157
|
|
133
|
-
ChunkedIO.new(
|
158
|
+
chunked_io = ChunkedIO.new(
|
134
159
|
chunks: Enumerator.new { |y| y << tempfile.read(16*1024) until tempfile.eof? },
|
135
160
|
size: tempfile.size,
|
136
161
|
on_close: -> { tempfile.close! },
|
137
162
|
)
|
138
163
|
else
|
139
|
-
ChunkedIO.new(
|
164
|
+
chunked_io = ChunkedIO.new(
|
140
165
|
chunks: response.enum_for(:read_body),
|
141
166
|
size: response["Content-Length"] && response["Content-Length"].to_i,
|
142
167
|
on_close: -> { request.resume }, # close HTTP connnection
|
143
168
|
)
|
144
169
|
end
|
170
|
+
|
171
|
+
chunked_io.data[:status] = response.code.to_i
|
172
|
+
chunked_io.data[:headers] = {}
|
173
|
+
|
174
|
+
response.each_header do |downcased_name, value|
|
175
|
+
name = downcased_name.split("-").map(&:capitalize).join("-")
|
176
|
+
chunked_io.data[:headers].merge!(name => value)
|
177
|
+
end
|
178
|
+
|
179
|
+
chunked_io
|
145
180
|
end
|
146
181
|
|
147
182
|
def copy_to_tempfile(basename, io)
|
data/lib/down/chunked_io.rb
CHANGED
@@ -2,12 +2,13 @@ require "tempfile"
|
|
2
2
|
|
3
3
|
module Down
|
4
4
|
class ChunkedIO
|
5
|
-
attr_reader :tempfile
|
5
|
+
attr_reader :tempfile, :data
|
6
6
|
|
7
7
|
def initialize(options)
|
8
8
|
@size = options.fetch(:size)
|
9
9
|
@chunks = options.fetch(:chunks)
|
10
10
|
@on_close = options.fetch(:on_close, ->{})
|
11
|
+
@data = options.fetch(:data, {})
|
11
12
|
@tempfile = Tempfile.new("down", binmode: true)
|
12
13
|
|
13
14
|
peek_chunk
|
@@ -24,7 +25,7 @@ module Down
|
|
24
25
|
|
25
26
|
def each_chunk
|
26
27
|
return enum_for(__method__) if !block_given?
|
27
|
-
yield
|
28
|
+
yield retrieve_chunk until download_finished?
|
28
29
|
end
|
29
30
|
|
30
31
|
def eof?
|
@@ -43,8 +44,11 @@ module Down
|
|
43
44
|
private
|
44
45
|
|
45
46
|
def download_chunk
|
47
|
+
write(retrieve_chunk)
|
48
|
+
end
|
49
|
+
|
50
|
+
def retrieve_chunk
|
46
51
|
chunk = @chunks.next
|
47
|
-
write(chunk)
|
48
52
|
peek_chunk
|
49
53
|
chunk
|
50
54
|
end
|
@@ -64,10 +68,8 @@ module Down
|
|
64
68
|
end
|
65
69
|
|
66
70
|
def terminate_download
|
67
|
-
if @on_close
|
68
|
-
|
69
|
-
@on_close = nil
|
70
|
-
end
|
71
|
+
@on_close.call if @on_close
|
72
|
+
@on_close = nil
|
71
73
|
end
|
72
74
|
|
73
75
|
def write(chunk)
|
data/lib/down/version.rb
CHANGED
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: 2.
|
4
|
+
version: 2.5.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: 2017-
|
11
|
+
date: 2017-05-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -42,16 +42,16 @@ dependencies:
|
|
42
42
|
name: webmock
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
|
-
- - "
|
45
|
+
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: '
|
47
|
+
version: '2.3'
|
48
48
|
type: :development
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
|
-
- - "
|
52
|
+
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: '
|
54
|
+
version: '2.3'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: addressable
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|