class Gem::Server

def self.run(options)

def self.run(options)
  new(options[:gemdir], options[:port], options[:daemon],
      options[:launch], options[:addresses]).run
end

def add_date(res)

def add_date(res)
  res['date'] = @spec_dirs.map do |spec_dir|
    File.stat(spec_dir).mtime
  end.max
end

def doc_root(gem_name)

def doc_root(gem_name)
  if have_rdoc_4_plus?
    "/doc_root/#{u gem_name}/"
  else
    "/doc_root/#{u gem_name}/rdoc/index.html"
  end
end

def have_rdoc_4_plus?

def have_rdoc_4_plus?
  @have_rdoc_4_plus ||=
    Gem::Requirement.new('>= 4.0.0.preview2').satisfied_by? Gem::RDoc.rdoc_version
end

def initialize(gem_dirs, port, daemon, launch = nil, addresses = nil)

def initialize(gem_dirs, port, daemon, launch = nil, addresses = nil)
  begin
    require 'webrick'
  rescue LoadError
    abort "webrick is not found. You may need to `gem install webrick` to install webrick."
  end
  Gem::RDoc.load_rdoc
  Socket.do_not_reverse_lookup = true
  @gem_dirs  = Array gem_dirs
  @port      = port
  @daemon    = daemon
  @launch    = launch
  @addresses = addresses
  logger  = WEBrick::Log.new nil, WEBrick::BasicLog::FATAL
  @server = WEBrick::HTTPServer.new :DoNotListen => true, :Logger => logger
  @spec_dirs = @gem_dirs.map {|gem_dir| File.join gem_dir, 'specifications' }
  @spec_dirs.reject! {|spec_dir| !File.directory? spec_dir }
  reset_gems
  @have_rdoc_4_plus = nil
end

def latest_specs(req, res)

def latest_specs(req, res)
  reset_gems
  res['content-type'] = 'application/x-gzip'
  add_date res
  latest_specs = Gem::Specification.latest_specs
  specs = latest_specs.sort.map do |spec|
    platform = spec.original_platform || Gem::Platform::RUBY
    [spec.name, spec.version, platform]
  end
  specs = Marshal.dump specs
  if req.path =~ /\.gz$/
    specs = Gem::Util.gzip specs
    res['content-type'] = 'application/x-gzip'
  else
    res['content-type'] = 'application/octet-stream'
  end
  if req.request_method == 'HEAD'
    res['content-length'] = specs.length
  else
    res.body << specs
  end
end

def launch

def launch
  listeners = @server.listeners.map{|l| l.addr[2] }
  # TODO: 0.0.0.0 == any, not localhost.
  host = listeners.any?{|l| l == '0.0.0.0' } ? 'localhost' : listeners.first
  say "Launching browser to http://#{host}:#{@port}"
  system("#{@launch} http://#{host}:#{@port}")
end

def listen(addresses = @addresses)

def listen(addresses = @addresses)
  addresses = [nil] unless addresses
  listeners = 0
  addresses.each do |address|
    begin
      @server.listen address, @port
      @server.listeners[listeners..-1].each do |listener|
        host, port = listener.addr.values_at 2, 1
        host = "[#{host}]" if host =~ /:/ # we don't reverse lookup
        say "Server started at http://#{host}:#{port}"
      end
      listeners = @server.listeners.length
    rescue SystemCallError
      next
    end
  end
  if @server.listeners.empty?
    say "Unable to start a server."
    say "Check for running servers or your --bind and --port arguments"
    terminate_interaction 1
  end
end

def prerelease_specs(req, res)

def prerelease_specs(req, res)
  reset_gems
  res['content-type'] = 'application/x-gzip'
  add_date res
  specs = Gem::Specification.select do |spec|
    spec.version.prerelease?
  end.sort.map do |spec|
    platform = spec.original_platform || Gem::Platform::RUBY
    [spec.name, spec.version, platform]
  end
  specs = Marshal.dump specs
  if req.path =~ /\.gz$/
    specs = Gem::Util.gzip specs
    res['content-type'] = 'application/x-gzip'
  else
    res['content-type'] = 'application/octet-stream'
  end
  if req.request_method == 'HEAD'
    res['content-length'] = specs.length
  else
    res.body << specs
  end
end

def quick(req, res)

def quick(req, res)
  reset_gems
  res['content-type'] = 'text/plain'
  add_date res
  case req.request_uri.path
  when %r{^/quick/(Marshal.#{Regexp.escape Gem.marshal_version}/)?(.*?)\.gemspec\.rz$} then
    marshal_format, full_name = $1, $2
    specs = Gem::Specification.find_all_by_full_name(full_name)
    selector = full_name.inspect
    if specs.empty?
      res.status = 404
      res.body = "No gems found matching #{selector}"
    elsif specs.length > 1
      res.status = 500
      res.body = "Multiple gems found matching #{selector}"
    elsif marshal_format
      res['content-type'] = 'application/x-deflate'
      res.body << Gem.deflate(Marshal.dump(specs.first))
    end
  else
    raise WEBrick::HTTPStatus::NotFound, "`#{req.path}' not found."
  end
end

def rdoc(req, res)

def rdoc(req, res)
  query = req.query['q']
  show_rdoc_for_pattern("#{query}*", res) && return
  show_rdoc_for_pattern("*#{query}*", res) && return
  template = ERB.new RDOC_NO_DOCUMENTATION
  res['content-type'] = 'text/html'
  res.body = template.result binding
end

def reset_gems # :nodoc:

:nodoc:
def reset_gems # :nodoc:
  Gem::Specification.dirs = @gem_dirs
end

def root(req, res)

def root(req, res)
  reset_gems
  add_date res
  raise WEBrick::HTTPStatus::NotFound, "`#{req.path}' not found." unless
    req.path == '/'
  specs = []
  total_file_count = 0
  Gem::Specification.each do |spec|
    total_file_count += spec.files.size
    deps = spec.dependencies.map do |dep|
      {
        "name"    => dep.name,
        "type"    => dep.type,
        "version" => dep.requirement.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
    # Pre-process spec homepage for safety reasons
    begin
      homepage_uri = URI.parse(spec.homepage)
      if [URI::HTTP, URI::HTTPS].member? homepage_uri.class
        homepage_uri = spec.homepage
      else
        homepage_uri = "."
      end
    rescue URI::InvalidURIError
      homepage_uri = "."
    end
    specs << {
      "authors"             => spec.authors.sort.join(", "),
      "date"                => spec.date.to_s,
      "dependencies"        => deps,
      "doc_path"            => doc_root(spec.full_name),
      "executables"         => executables,
      "only_one_executable" => (executables && executables.size == 1),
      "full_name"           => spec.full_name,
      "has_deps"            => !deps.empty?,
      "homepage"            => homepage_uri,
      "name"                => spec.name,
      "rdoc_installed"      => Gem::RDoc.new(spec).rdoc_installed?,
      "ri_installed"        => Gem::RDoc.new(spec).ri_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::VERSION}"),
    "executables" => [{"executable" => 'gem', "is_last" => true}],
    "only_one_executable" => true,
    "full_name" => "rubygems-#{Gem::VERSION}",
    "has_deps" => false,
    "homepage" => "https://guides.rubygems.org/",
    "name" => 'rubygems',
    "ri_installed" => true,
    "summary" => "RubyGems itself",
    "version" => Gem::VERSION,
  }
  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 }
  # suppress 1.9.3dev warning about unused variable
  values = values
  result = template.result binding
  res.body = result
end

def run

def run
  listen
  WEBrick::Daemon.start if @daemon
  @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 "/prerelease_specs.#{Gem.marshal_version}",
                     method(:prerelease_specs)
  @server.mount_proc "/prerelease_specs.#{Gem.marshal_version}.gz",
                     method(:prerelease_specs)
  @server.mount_proc "/quick/", method(:quick)
  @server.mount_proc("/gem-server-rdoc-style.css") do |req, res|
    res['content-type'] = 'text/css'
    add_date res
    res.body << RDOC_CSS
  end
  @server.mount_proc "/", method(:root)
  @server.mount_proc "/rdoc", method(:rdoc)
  file_handlers = {
    '/gems' => '/cache/',
  }
  if have_rdoc_4_plus?
    @server.mount '/doc_root', RDoc::Servlet, '/doc_root'
  else
    file_handlers['/doc_root'] = '/doc/'
  end
  @gem_dirs.each do |gem_dir|
    file_handlers.each do |mount_point, mount_dir|
      @server.mount(mount_point, WEBrick::HTTPServlet::FileHandler,
                    File.join(gem_dir, mount_dir), true)
    end
  end
  trap("INT") { @server.shutdown; exit! }
  trap("TERM") { @server.shutdown; exit! }
  launch if @launch
  @server.start
end

def show_rdoc_for_pattern(pattern, res)

def show_rdoc_for_pattern(pattern, res)
  found_gems = Dir.glob("{#{@gem_dirs.join ','}}/doc/#{pattern}").select do |path|
    File.exist? File.join(path, 'rdoc/index.html')
  end
  case found_gems.length
  when 0
    return false
  when 1
    new_path = File.basename(found_gems[0])
    res.status = 302
    res['Location'] = doc_root new_path
    return true
  else
    doc_items = []
    found_gems.each do |file_name|
      base_name = File.basename(file_name)
      doc_items << {
        :name    => base_name,
        :url     => doc_root(new_path),
        :summary => '',
      }
    end
    template = ERB.new(RDOC_SEARCH_TEMPLATE)
    res['content-type'] = 'text/html'
    result = template.result binding
    res.body = result
    return true
  end
end

def specs(req, res)

def specs(req, res)
  reset_gems
  add_date res
  specs = Gem::Specification.sort_by(&:sort_obj).map do |spec|
    platform = spec.original_platform || Gem::Platform::RUBY
    [spec.name, spec.version, platform]
  end
  specs = Marshal.dump specs
  if req.path =~ /\.gz$/
    specs = Gem::Util.gzip specs
    res['content-type'] = 'application/x-gzip'
  else
    res['content-type'] = 'application/octet-stream'
  end
  if req.request_method == 'HEAD'
    res['content-length'] = specs.length
  else
    res.body << specs
  end
end

def uri_encode(str)

def uri_encode(str)
  str.gsub(URI::UNSAFE) do |match|
    match.each_byte.map {|c| sprintf('%%%02X', c.ord) }.join
  end
end