module Git::Lighttp module HttpBackendHelpers #:nodoc: include GitHelpers def service_request? not params[:service].nil? end # select_service feature def service @service = params[:service] return false if @service.nil? return false if @service[0, 4] != 'git-' @service = @service.gsub('git-', '') end # pkt_write feature def packet_write(line) (line.size + 4).to_s(base=16).rjust(4, '0') + line end # pkt_flush feature def packet_flush '0000' end # hdr_nocache feature def header_nocache headers 'Expires' => 'Fri, 01 Jan 1980 00:00:00 GMT', 'Pragma' => 'no-cache', 'Cache-Control' => 'no-cache, max-age=0, must-revalidate' end # hdr_cache_forever feature def header_cache_forever now = Time.now headers 'Date' => now.to_s, 'Expires' => (now + 31536000).to_s, 'Cache-Control' => 'public, max-age=31536000' end # select_getanyfile feature def read_any_file unless settings.get_any_file halt 403, 'Unsupported service: getanyfile' end end # get_text_file feature def read_text_file(*file) read_any_file header_nocache content_type 'text/plain' repository.read_file(*file) end # get_loose_object feature def send_loose_object(prefix, suffix) read_any_file header_cache_forever content_type_for_git :loose, :object send_file(repository.loose_object_path(prefix, suffix)) end # get_pack_file and get_idx_file def send_pack_idx_file(pack, idx = false) read_any_file header_cache_forever content_type_for_git :packed, :objects, (idx ? :toc : nil) send_file(repository.pack_idx_path(pack)) end def send_info_packs read_any_file header_nocache content_type 'text/plain; charset=utf-8' send_file(repository.info_packs_path) end # run_service feature def run_advertisement(service) header_nocache content_type_for_git service, :advertisement response.body.clear response.body << packet_write("# service=git-#{service}\n") response.body << packet_flush response.body << repository.run(service, '--stateless-rpc --advertise-refs .') response.finish end def run_process(service) content_type_for_git service, :result input = request.body.read command = repository.cli(service, "--stateless-rpc #{git.repository}") # This source has extracted from Grack written by Scott Chacon. IO.popen(command, File::RDWR) do |pipe| pipe.write(input) while !pipe.eof? block = pipe.read(8192) # 8M at a time response.write block # steam it to the client end end # IO response.finish end end # HttpBackendHelpers # The Smart HTTP handler server. This is the main Web application which respond to following requests: # # /HEAD :: HEAD contents # /info/refs :: Text file that contains references. # /objects/info/* :: Text file that contains all list of packets, alternates or http-alternates. # /objects/*/* :: Git objects, packets or indexes. # /upload-pack :: Post an upload packets. # /receive-pack :: Post a receive packets. # # See ::configure for more details. class HttpBackend < Application set :authenticate, true set :get_any_file, true set :upload_pack, true set :receive_pack, false helpers HttpBackendHelpers before do authenticate! if settings.authenticate end # implements the get_text_file function get '/:repository/HEAD' do read_text_file('HEAD') end # implements the get_info_refs function get '/:repository/info/refs' do if service_request? # by URL query parameters run_advertisement service else read_text_file(:info, :refs) end end # implements the get_text_file and get_info_packs functions get %r{/(.*?)/objects/info/(packs|alternates|http-alternates)} do |repository, file| if file == 'packs' send_info_packs else read_text_file(:objects, :info, file) end end # implements the get_loose_object function get %r{/(.*?)/objects/([0-9a-f]{2})/([0-9a-f]{38})} do |repository, prefix, suffix| send_loose_object(prefix, suffix) end # implements the get_pack_file and get_idx_file functions get %r{/(.*?)/objects/pack/(pack-[0-9a-f]{40}.(pack|idx))} do |repository, pack, ext| send_pack_idx_file(pack, ext == 'idx') end # implements the service_rpc function post '/:repository/:service' do run_process service end private helpers AuthenticationHelpers end # HttpBackend end # Git::Lighttp