module Git::Webby 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(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 # HttpBackendServer end # Git::Webby