forked from SeleniumHQ/selenium
-
Notifications
You must be signed in to change notification settings - Fork 0
/
downloader.rb
143 lines (111 loc) · 3.2 KB
/
downloader.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
require 'uri'
require 'net/http'
require 'net/ftp'
require 'digest/md5'
class Downloader
class Error < StandardError; end
CL_RESET = Platform.windows? ? "\n" : "\r\e[0K"
def self.fetch(source_url, destination_path = nil)
if destination_path
destination_dir = File.dirname destination_path
FileUtils.mkdir_p(destination_dir) unless File.exist?(destination_dir)
else
tmpdir = Dir.mktmpdir("crazy-fun-download")
destination_path = File.join(tmpdir, File.basename(source_url))
end
File.open(destination_path, "wb") do |io|
new(source_url, io).download!
end
destination_path
end
def initialize(source, destination)
raise ArgumentError, "destination must be an IO" unless destination.kind_of?(IO)
@url = URI.parse source
@destination = destination
end
def download!
case @url.scheme
when 'ftp'
ftp_download!
when 'http', 'https'
http_download!
else
raise Error, "unknown scheme: #{@url.scheme}"
end
complete_progress
end
private
def net_http
http_proxy = ENV['http_proxy'] || ENV['HTTP_PROXY']
if http_proxy
http_proxy = "http://#{http_proxy}" unless http_proxy.start_with?("http://")
uri = URI.parse(http_proxy)
Net::HTTP::Proxy(uri.host, uri.port)
else
Net::HTTP
end
end
def http_download!
resp = net_http.get_response(@url) do |response|
total = response.content_length
progress = 0
segment_count = 0
response.read_body do |segment|
progress += segment.length
segment_count += 1
if segment_count % 15 == 0
report_progress(progress, total)
segment_count = 0
end
@destination.write(segment)
end
end
unless resp.kind_of? Net::HTTPSuccess
raise Error, "#{resp.code} for #{@url}"
end
end
def ftp_download!
raise Error, "no #path for #{@destination.inspect}" unless @destination.respond_to?(:path)
Net::FTP.open(@url.host) do |ftp|
ftp.login
filename = @url.path
total = ftp.size(filename)
progress = 0
segment_count = 0
ftp.getbinaryfile(filename, @destination.path, 2048) do |segment|
progress += segment.length
segment_count += 1
if segment_count % 15 == 0
report_progress(progress, total)
segment_count = 0
end
end
end
end
def report_progress(progress, total)
percent = (progress.to_f / total.to_f) * 100
print "#{CL_RESET}Downloading #{basename}: #{percent.to_i}% (#{progress} / #{total})"
$stdout.flush
end
def complete_progress
puts CL_RESET
end
def basename
@basename ||= File.basename @url.to_s
end
end
class CachingDownloader < Downloader
CACHE_DIR = File.expand_path("~/.crazyfun")
def self.fetch(url, hash)
cached_path = File.join(CACHE_DIR, url.gsub(/[^\w.]/, '_'))
if File.exist?(cached_path)
cached_hash = Digest::MD5.file(cached_path).hexdigest
if cached_hash == hash
$stderr.puts "Using cached Gecko SDK: #{cached_path}"
return cached_path
end
$stderr.puts "MD5 mismatch: #{cached_path.inspect}, overwriting.."
end
super(url, cached_path)
end
end