forked from bensomers/rubygems_proxy
-
Notifications
You must be signed in to change notification settings - Fork 0
/
rubygems_proxy.rb
132 lines (107 loc) · 2.78 KB
/
rubygems_proxy.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
require "open-uri"
require "fileutils"
require "logger"
require "erb"
class RubygemsProxy
attr_reader :env
def self.call(env)
new(env).run
end
def initialize(env)
@env = env
logger.level = Logger::INFO
end
def run
logger.info "#{env["REQUEST_METHOD"]} #{env["PATH_INFO"]}"
case env["PATH_INFO"]
when "/"
[200, {"Content-Type" => "text/html"}, [erb(:index)]]
else
[200, {"Content-Type" => "application/octet-stream"}, [contents]]
end
rescue Exception
[200, {"Content-Type" => "text/html"}, [erb(404)]]
end
private
def erb(view)
ERB.new(template(view)).result(binding)
end
def server_url
env["rack.url_scheme"] + "://" + File.join(env["SERVER_NAME"], env["PATH_INFO"])
end
def rubygems_url(gemname)
"http://rubygems.org/gems/%s" % Rack::Utils.escape(gemname)
end
def gem_url(name, version)
File.join(server_url, "gems", Rack::Utils.escape("#{name}-#{version}.gem"))
end
def gem_list
Dir[File.dirname(__FILE__) + "/public/gems/**/*.gem"]
end
def grouped_gems
gem_list.inject({}) do |buffer, file|
basename = File.basename(file)
parts = basename.gsub(/\.gem/, "").split("-")
version = parts.pop
name = parts.join("-")
buffer[name] ||= []
buffer[name] << version
buffer
end
end
def template(name)
@templates ||= {}
@templates[name] ||= File.read(File.dirname(__FILE__) + "/views/#{name}.erb")
end
def root_dir
File.expand_path "..", __FILE__
end
def logger
@logger ||= Logger.new("#{root_dir}/tmp/server.log", 10, 1024000)
end
def cache_dir
"#{root_dir}/public"
end
def contents
if File.directory?(filepath)
erb(404)
elsif cached? && !specs?
logger.info "Read from cache: #{filepath}"
open(filepath).read
elsif cached? && specs? && fresh_specs?
logger.info "Specs are fresh, freshness: #{freshness}. Read specs from cache: #{filepath}"
open(filepath).read
else
logger.info "Read from interwebz: #{url}"
# pass the Host header to correctly access the rubygems site
open(url).read.tap {|content| save(content)}
end
end
def save(contents)
FileUtils.mkdir_p File.dirname(filepath)
File.open(filepath, "wb") {|handler| handler << contents}
end
def specs?
env["PATH_INFO"] =~ /specs\..+\.gz$/
end
def cached?
File.file?(filepath)
end
def fresh_specs?
freshness < 10 # 10 seconds
end
def freshness
freshness = Time.now - File.ctime(filepath)
end
def filepath
if specs?
File.join(root_dir, env["PATH_INFO"])
else
File.join(cache_dir, env["PATH_INFO"])
end
end
def url
# connect directly to the IP address
File.join("http://production.cf.rubygems.org", env["PATH_INFO"])
end
end