This repository has been archived by the owner on Sep 25, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 29
/
rubygems_proxy.rb
138 lines (114 loc) · 3.01 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
132
133
134
135
136
137
138
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"]}"
return update_specs if env["REQUEST_METHOD"] == "DELETE"
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?
logger.info "Read from cache: #{filepath}"
open(filepath).read
else
logger.info "Read from interwebz: #{url}"
open(url).read.tap {|content| save(content)}
end
rescue Exception => error
# Just try to load from file if something goes wrong.
# This includes HTTP timeout, or something.
# If it fails again, we won't have any files anyway!
logger.error "Error: #{error.class} => #{error.message}"
open(filepath).read
end
def save(contents)
FileUtils.mkdir_p File.dirname(filepath)
File.open(filepath, "wb") {|handler| handler << contents}
end
def cached?
case File.basename(filepath)
when /^specs\./
File.exist?(filepath) && (Time.now - File.mtime(filepath)).to_i < 84600
when /\.gz$/
false
else
File.file?(filepath)
end
end
def specs?
env["PATH_INFO"] =~ /specs\..+\.gz$/
end
def filepath
if specs?
File.join(root_dir, env["PATH_INFO"])
else
File.join(cache_dir, env["PATH_INFO"])
end
end
def url
File.join("http://rubygems.org", env["PATH_INFO"])
end
def update_specs
Dir[File.dirname(__FILE__) + "/*.gz"].each {|file| File.unlink(file)}
[200, {"Content-Type" => "text/plain"}, [""]]
end
end