# -*- coding: utf-8 -*-
module Gemirro
##
# Launch TCPServer to easily download gems.
#
# @!attribute [r] server
# @return [TCPServer]
# @!attribute [r] destination
# @return [String]
# @!attribute [r] versions_fetcher
# @return [VersionsFetcher]
# @!attribute [r] gems_fetcher
# @return [Gemirro::GemsFetcher]
#
class Server
attr_reader :server, :destination, :versions_fetcher, :gems_fetcher
##
# Initialize Server
#
def initialize
configuration.server_host = 'localhost' if configuration.server_host.nil?
configuration.server_port = '2000' if configuration.server_port.nil?
logger.info('Running server on ' \
"#{configuration.server_host}:#{configuration.server_port}")
@server = TCPServer.new(
configuration.server_host,
configuration.server_port
)
@destination = configuration.destination
end
##
# Run the server and accept all connection
#
# @return [nil]
#
def run
while (session = server.accept)
request = session.gets
logger.info(request)
trimmedrequest = request.gsub(/GET\ \//, '').gsub(/\ HTTP.*/, '').chomp
resource = "#{@destination}/#{trimmedrequest}"
# Try to download gem if file doesn't exists
fetch_gem(resource) unless File.exist?(resource)
# If not found again, return a 404
unless File.exist?(resource)
logger.warn("404 - #{trimmedrequest.gsub(/^public\//, '')}")
session.print "HTTP/1.1 404/Object Not Found\r\n\r\n"
session.close
next
end
if File.directory?(resource)
display_directory(session, resource)
else
mime_type = MIME::Types.type_for(resource)
session.print "HTTP/1.1 200/OK\r\nContent-type:#{mime_type}\r\n\r\n"
file = open(resource, 'rb')
session.puts(file.read)
end
session.close
end
end
##
# Try to fetch gem and download its if it's possible, and
# build and install indicies.
#
# @param [String] resource
# @return [Indexer]
#
def fetch_gem(resource)
name = File.basename(resource)
regexp = /^(.*)-(\d+(?:\.\d+){,4})\.gem(?:spec\.rz)?$/
gem_name, gem_version = name.match(regexp).captures
return unless gem_name && gem_version
logger.info("Try to download #{gem_name} with version #{gem_version}")
begin
gems_fetcher.source.gems.clear
gems_fetcher.source.gems.push(Gemirro::Gem.new(gem_name, gem_version))
gems_fetcher.fetch
rescue StandardError => e
logger.error(e.message)
end
generate_index
end
##
# Generate index and install indicies.
#
# @return [Indexer]
#
def generate_index
indexer = Indexer.new(configuration.destination)
indexer.ui = ::Gem::SilentUI.new
logger.info('Generating indexes')
indexer.generate_index
end
##
# Display directory on the current sesion
#
# @param [TCPSocket] session
# @param [String] resource
# @return [Array]
#
def display_directory(session, resource)
session.print "HTTP/1.1 200/OK\r\nContent-type:text/html\r\n\r\n"
base_dir = Dir.new(resource)
base_dir.entries.sort.each do |f|
dir_sign = ''
resource_path = resource.gsub(/\/$/, '') + '/' + f
dir_sign = '/' if File.directory?(resource_path)
resource_path = resource_path.gsub(/^public\//, '')
resource_path = resource_path.gsub(@destination, '')
session.print(
"#{f}#{dir_sign}
"
) unless ['.', '..'].include?(File.basename(resource_path))
end
end
##
# @see Gemirro::Configuration#logger
# @return [Logger]
#
def logger
configuration.logger
end
##
# @see Gemirro.configuration
#
def configuration
Gemirro.configuration
end
##
# @see Gemirro::VersionsFetcher.fetch
#
def versions_fetcher
@versions_fetcher ||= Gemirro::VersionsFetcher.new(configuration.source).fetch
end
##
# @return [Gemirro::GemsFetcher]
#
def gems_fetcher
@gems_fetcher = Gemirro::GemsFetcher.new(
configuration.source, versions_fetcher)
end
end
end