From 9d4f37f51fb2ffdef5e318afb3cb81516dcba4f7 Mon Sep 17 00:00:00 2001 From: drbrain Date: Tue, 17 Jun 2008 22:04:18 +0000 Subject: Update RubyGems to 1.1.1 r1778 (almost 1.2) git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@17392 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- lib/rubygems/server.rb | 347 +++++++++++++++++++++++++++++++++---------------- 1 file changed, 238 insertions(+), 109 deletions(-) (limited to 'lib/rubygems/server.rb') diff --git a/lib/rubygems/server.rb b/lib/rubygems/server.rb index b4f58f9706..2c617ff144 100644 --- a/lib/rubygems/server.rb +++ b/lib/rubygems/server.rb @@ -4,6 +4,7 @@ require 'zlib' require 'erb' require 'rubygems' +require 'rubygems/doc_manager' ## # Gem::Server and allows users to serve gems for consumption by @@ -11,18 +12,24 @@ require 'rubygems' # # gem_server starts an HTTP server on the given port and serves the following: # * "/" - Browsing of gem spec files for installed gems -# * "/Marshal" - Full SourceIndex dump of metadata for installed gems -# * "/yaml" - YAML dump of metadata for installed gems - deprecated +# * "/specs.#{Gem.marshal_version}.gz" - specs name/version/platform index +# * "/latest_specs.#{Gem.marshal_version}.gz" - latest specs +# name/version/platform index +# * "/quick/" - Individual gemspecs # * "/gems" - Direct access to download the installable gems +# * legacy indexes: +# * "/Marshal.#{Gem.marshal_version}" - Full SourceIndex dump of metadata +# for installed gems +# * "/yaml" - YAML dump of metadata for installed gems - deprecated # # == Usage # -# gem server [-p portnum] [-d gem_path] +# gem_server = Gem::Server.new Gem.dir, 8089, false +# gem_server.run # -# port_num:: The TCP port the HTTP server will bind to -# gem_path:: -# Root gem directory containing both "cache" and "specifications" -# subdirectories. +#-- +# TODO Refactor into a real WEBrick servlet to remove code duplication. + class Gem::Server include Gem::UserInteraction @@ -36,7 +43,6 @@ class Gem::Server RubyGems Documentation Index - @@ -325,32 +331,99 @@ div.method-source-code pre { color: #ffdead; overflow: hidden; } new(options[:gemdir], options[:port], options[:daemon]).run end - def initialize(gemdir, port, daemon) + def initialize(gem_dir, port, daemon) Socket.do_not_reverse_lookup = true - @gemdir = gemdir + @gem_dir = gem_dir @port = port @daemon = daemon logger = WEBrick::Log.new nil, WEBrick::BasicLog::FATAL @server = WEBrick::HTTPServer.new :DoNotListen => true, :Logger => logger - @spec_dir = File.join @gemdir, "specifications" + @spec_dir = File.join @gem_dir, 'specifications' + + unless File.directory? @spec_dir then + raise ArgumentError, "#{@gem_dir} does not appear to be a gem repository" + end + @source_index = Gem::SourceIndex.from_gems_in @spec_dir end + def Marshal(req, res) + @source_index.refresh! + + res['date'] = File.stat(@spec_dir).mtime + + index = Marshal.dump @source_index + + if req.request_method == 'HEAD' then + res['content-length'] = index.length + return + end + + if req.path =~ /Z$/ then + res['content-type'] = 'application/x-deflate' + index = Gem.deflate index + else + res['content-type'] = 'application/octet-stream' + end + + res.body << index + end + + def latest_specs(req, res) + @source_index.refresh! + + res['content-type'] = 'application/x-gzip' + + res['date'] = File.stat(@spec_dir).mtime + + specs = @source_index.latest_specs.sort.map do |spec| + platform = spec.original_platform + platform = Gem::Platform::RUBY if platform.nil? + [spec.name, spec.version, platform] + end + + specs = Marshal.dump specs + + if req.path =~ /\.gz$/ then + specs = Gem.gzip specs + res['content-type'] = 'application/x-gzip' + else + res['content-type'] = 'application/octet-stream' + end + + if req.request_method == 'HEAD' then + res['content-length'] = specs.length + else + res.body << specs + end + end + def quick(req, res) + @source_index.refresh! + res['content-type'] = 'text/plain' res['date'] = File.stat(@spec_dir).mtime - case req.request_uri.request_uri + case req.request_uri.path when '/quick/index' then - res.body << @source_index.map { |name,_| name }.join("\n") + res.body << @source_index.map { |name,| name }.sort.join("\n") when '/quick/index.rz' then - index = @source_index.map { |name,_| name }.join("\n") - res.body << Zlib::Deflate.deflate(index) + index = @source_index.map { |name,| name }.sort.join("\n") + res['content-type'] = 'application/x-deflate' + res.body << Gem.deflate(index) + when '/quick/latest_index' then + index = @source_index.latest_specs.map { |spec| spec.full_name } + res.body << index.sort.join("\n") + when '/quick/latest_index.rz' then + index = @source_index.latest_specs.map { |spec| spec.full_name } + res['content-type'] = 'application/x-deflate' + res.body << Gem.deflate(index.sort.join("\n")) when %r|^/quick/(Marshal.#{Regexp.escape Gem.marshal_version}/)?(.*?)-([0-9.]+)(-.*?)?\.gemspec\.rz$| then dep = Gem::Dependency.new $2, $3 specs = @source_index.search dep + marshal_format = $1 selector = [$2, $3, $4].map { |s| s.inspect }.join ' ' @@ -368,17 +441,98 @@ div.method-source-code pre { color: #ffdead; overflow: hidden; } elsif specs.length > 1 then res.status = 500 res.body = "Multiple gems found matching #{selector}" - elsif $1 then # marshal quickindex instead of YAML - res.body << Zlib::Deflate.deflate(Marshal.dump(specs.first)) + elsif marshal_format then + res['content-type'] = 'application/x-deflate' + res.body << Gem.deflate(Marshal.dump(specs.first)) else # deprecated YAML format - res.body << Zlib::Deflate.deflate(specs.first.to_yaml) + res['content-type'] = 'application/x-deflate' + res.body << Gem.deflate(specs.first.to_yaml) end else - res.status = 404 - res.body = "#{req.request_uri} not found" + raise WEBrick::HTTPStatus::NotFound, "`#{req.path}' not found." end end + def root(req, res) + @source_index.refresh! + res['date'] = File.stat(@spec_dir).mtime + + raise WEBrick::HTTPStatus::NotFound, "`#{req.path}' not found." unless + req.path == '/' + + specs = [] + total_file_count = 0 + + @source_index.each do |path, spec| + total_file_count += spec.files.size + deps = spec.dependencies.map do |dep| + { "name" => dep.name, + "type" => dep.type, + "version" => dep.version_requirements.to_s, } + end + + deps = deps.sort_by { |dep| [dep["name"].downcase, dep["version"]] } + deps.last["is_last"] = true unless deps.empty? + + # executables + executables = spec.executables.sort.collect { |exec| {"executable" => exec} } + executables = nil if executables.empty? + executables.last["is_last"] = true if executables + + specs << { + "authors" => spec.authors.sort.join(", "), + "date" => spec.date.to_s, + "dependencies" => deps, + "doc_path" => "/doc_root/#{spec.full_name}/rdoc/index.html", + "executables" => executables, + "only_one_executable" => (executables && executables.size == 1), + "full_name" => spec.full_name, + "has_deps" => !deps.empty?, + "homepage" => spec.homepage, + "name" => spec.name, + "rdoc_installed" => Gem::DocManager.new(spec).rdoc_installed?, + "summary" => spec.summary, + "version" => spec.version.to_s, + } + end + + specs << { + "authors" => "Chad Fowler, Rich Kilmer, Jim Weirich, Eric Hodel and others", + "dependencies" => [], + "doc_path" => "/doc_root/rubygems-#{Gem::RubyGemsVersion}/rdoc/index.html", + "executables" => [{"executable" => 'gem', "is_last" => true}], + "only_one_executable" => true, + "full_name" => "rubygems-#{Gem::RubyGemsVersion}", + "has_deps" => false, + "homepage" => "http://rubygems.org/", + "name" => 'rubygems', + "rdoc_installed" => true, + "summary" => "RubyGems itself", + "version" => Gem::RubyGemsVersion, + } + + specs = specs.sort_by { |spec| [spec["name"].downcase, spec["version"]] } + specs.last["is_last"] = true + + # tag all specs with first_name_entry + last_spec = nil + specs.each do |spec| + is_first = last_spec.nil? || (last_spec["name"].downcase != spec["name"].downcase) + spec["first_name_entry"] = is_first + last_spec = spec + end + + # create page from template + template = ERB.new(DOC_TEMPLATE) + res['content-type'] = 'text/html' + + values = { "gem_count" => specs.size.to_s, "specs" => specs, + "total_file_count" => total_file_count.to_s } + + result = template.result binding + res.body = result + end + def run @server.listen nil, @port @@ -386,27 +540,21 @@ div.method-source-code pre { color: #ffdead; overflow: hidden; } WEBrick::Daemon.start if @daemon - @server.mount_proc("/yaml") do |req, res| - res['content-type'] = 'text/plain' - res['date'] = File.stat(@spec_dir).mtime - if req.request_method == 'HEAD' then - res['content-length'] = @source_index.to_yaml.length - else - res.body << @source_index.to_yaml - end - end + @server.mount_proc "/yaml", method(:yaml) + @server.mount_proc "/yaml.Z", method(:yaml) - @server.mount_proc("/Marshal") do |req, res| - res['content-type'] = 'text/plain' - res['date'] = File.stat(@spec_dir).mtime - if req.request_method == 'HEAD' then - res['content-length'] = Marshal.dump(@source_index).length - else - res.body << Marshal.dump(@source_index) - end - end + @server.mount_proc "/Marshal.#{Gem.marshal_version}", method(:Marshal) + @server.mount_proc "/Marshal.#{Gem.marshal_version}.Z", method(:Marshal) + + @server.mount_proc "/specs.#{Gem.marshal_version}", method(:specs) + @server.mount_proc "/specs.#{Gem.marshal_version}.gz", method(:specs) + + @server.mount_proc "/latest_specs.#{Gem.marshal_version}", + method(:latest_specs) + @server.mount_proc "/latest_specs.#{Gem.marshal_version}.gz", + method(:latest_specs) - @server.mount_proc("/quick/", &method(:quick)) + @server.mount_proc "/quick/", method(:quick) @server.mount_proc("/gem-server-rdoc-style.css") do |req, res| res['content-type'] = 'text/css' @@ -414,80 +562,12 @@ div.method-source-code pre { color: #ffdead; overflow: hidden; } res.body << RDOC_CSS end - @server.mount_proc("/") do |req, res| - specs = [] - total_file_count = 0 - - @source_index.each do |path, spec| - total_file_count += spec.files.size - deps = spec.dependencies.collect { |dep| - { "name" => dep.name, - "version" => dep.version_requirements.to_s, } - } - deps = deps.sort_by { |dep| [dep["name"].downcase, dep["version"]] } - deps.last["is_last"] = true unless deps.empty? - - # executables - executables = spec.executables.sort.collect { |exec| {"executable" => exec} } - executables = nil if executables.empty? - executables.last["is_last"] = true if executables - - specs << { - "authors" => spec.authors.sort.join(", "), - "date" => spec.date.to_s, - "dependencies" => deps, - "doc_path" => ('/doc_root/' + spec.full_name + '/rdoc/index.html'), - "executables" => executables, - "only_one_executable" => (executables && executables.size==1), - "full_name" => spec.full_name, - "has_deps" => !deps.empty?, - "homepage" => spec.homepage, - "name" => spec.name, - "rdoc_installed" => Gem::DocManager.new(spec).rdoc_installed?, - "summary" => spec.summary, - "version" => spec.version.to_s, - } - end - - specs << { - "authors" => "Chad Fowler, Rich Kilmer, Jim Weirich, Eric Hodel and others", - "dependencies" => [], - "doc_path" => "/doc_root/rubygems-#{Gem::RubyGemsVersion}/rdoc/index.html", - "executables" => [{"executable" => 'gem', "is_last" => true}], - "only_one_executable" => true, - "full_name" => "rubygems-#{Gem::RubyGemsVersion}", - "has_deps" => false, - "homepage" => "http://rubygems.org/", - "name" => 'rubygems', - "rdoc_installed" => true, - "summary" => "RubyGems itself", - "version" => Gem::RubyGemsVersion, - } - - specs = specs.sort_by { |spec| [spec["name"].downcase, spec["version"]] } - specs.last["is_last"] = true - - # tag all specs with first_name_entry - last_spec = nil - specs.each do |spec| - is_first = last_spec.nil? || (last_spec["name"].downcase != spec["name"].downcase) - spec["first_name_entry"] = is_first - last_spec = spec - end - - # create page from template - template = ERB.new(DOC_TEMPLATE) - res['content-type'] = 'text/html' - values = { "gem_count" => specs.size.to_s, "specs" => specs, - "total_file_count" => total_file_count.to_s } - result = template.result binding - res.body = result - end + @server.mount_proc "/", method(:root) paths = { "/gems" => "/cache/", "/doc_root" => "/doc/" } paths.each do |mount_point, mount_dir| @server.mount(mount_point, WEBrick::HTTPServlet::FileHandler, - File.join(@gemdir, mount_dir), true) + File.join(@gem_dir, mount_dir), true) end trap("INT") { @server.shutdown; exit! } @@ -496,5 +576,54 @@ div.method-source-code pre { color: #ffdead; overflow: hidden; } @server.start end + def specs(req, res) + @source_index.refresh! + + res['date'] = File.stat(@spec_dir).mtime + + specs = @source_index.sort.map do |_, spec| + platform = spec.original_platform + platform = Gem::Platform::RUBY if platform.nil? + [spec.name, spec.version, platform] + end + + specs = Marshal.dump specs + + if req.path =~ /\.gz$/ then + specs = Gem.gzip specs + res['content-type'] = 'application/x-gzip' + else + res['content-type'] = 'application/octet-stream' + end + + if req.request_method == 'HEAD' then + res['content-length'] = specs.length + else + res.body << specs + end + end + + def yaml(req, res) + @source_index.refresh! + + res['date'] = File.stat(@spec_dir).mtime + + index = @source_index.to_yaml + + if req.path =~ /Z$/ then + res['content-type'] = 'application/x-deflate' + index = Gem.deflate index + else + res['content-type'] = 'text/plain' + end + + if req.request_method == 'HEAD' then + res['content-length'] = index.length + return + end + + res.body << index + end + end -- cgit v1.2.3