module Repobrowse::GitHTTPBackend

provides smart HTTP cloning for git repos

Public Instance Methods

git_http_backend_routes(r, repo) click to toggle source
# File lib/repobrowse/git_http_backend.rb, line 80
def git_http_backend_routes(r, repo)
  r.is(path = 'git-upload-pack') { r.post { serve_smart(r, repo, path) } }
  r.is(path = 'info/refs') {
    # QUERY_STRING has exactly one parameter
    # See https://80x24.org/git/src/v2.16.1:Documentation/technical/http-protocol.txt
    if /\Aservice=git-\w+-pack\z/ =~ r.env['QUERY_STRING']
      r.get { serve_smart(r, repo, path) }
    else
      static(r, -"#{repo.path}/#{path}", 'text/plain', nil)
    end
  }
  %w(HEAD cloneurl description
     objects/info/http-alternates
     objects/info/alternates
     objects/info/packs).each { |txt|
    r.is(txt) { static(r, -"#{repo.path}/#{txt}", 'text/plain', nil) }
  }
  r.is('objects', :git_x2, :git_x38) { |x2, x38|
    static(r, -"#{repo.path}/objects/#{x2}/#{x38}",
           'application/x-git-loose-object')
  }
  [ :git_pack, 'application/x-git-packed-objects',
    :git_pack_idx, 'application/x-git-packed-objects-toc'
  ].each_slice(2) { |sym, type|
    r.is('objects/pack', :sym) { |o|
      static(r, -"#{repo.path}/objects/pack/#{o}", type)
    }
  }
end
input_to_file(env) click to toggle source
# File lib/repobrowse/git_http_backend.rb, line 32
def input_to_file(env)
  tmp = Tempfile.new('git-http-backend-in')
  tmp.unlink
  tmp.sync = true
  IO.copy_stream(env['rack.input'], tmp.to_io)
  tmp.rewind
  tmp
end
run_http_backend(r, repo, path) click to toggle source
# File lib/repobrowse/git_http_backend.rb, line 41
def run_http_backend(r, repo, path)
  penv = {
    'GIT_HTTP_EXPORT_ALL' => '1',
    'PATH_TRANSLATED' => -"#{repo.path}/#{path}",
  }
  env = r.env
  # GIT_COMMITTER_NAME, GIT_COMMITTER_EMAIL
  # may be set in the server-process and are passed as-is
  %w(QUERY_STRING REMOTE_USER REMOTE_ADDR HTTP_CONTENT_ENCODING
     CONTENT_TYPE SERVER_PROTOCOL REQUEST_METHOD).each do |k|
    v = env[k] and penv[k] = v
  end
  IO.popen(penv, %W(git http-backend), in: input_to_file(env))
rescue => e
  r_err(r, "E: #{e.message} (#{e.class}) on #{repo.path}")
end
serve_smart(r, repo, path) click to toggle source

returns undef if 403 so it falls back to dumb HTTP

# File lib/repobrowse/git_http_backend.rb, line 59
def serve_smart(r, repo, path)
  rd = run_http_backend(r, repo, path)
  code = 200
  h = {}

  # parse CGI response headers
  case rd.gets
  when %r{\AStatus:\s+(\d+)}i
    code = $1.to_i
  when %r{\A([^:]+):\s*(.*)\r\n\z}
    h[-$1] = -$2
  when "\r\n"
    break # headers done onto the body
  when nil
    rd.close
    r_err(r, "unexpected EOF on git http-backend in #{repo.path}")
  end while true

  r.halt [ code, h, EachWrap.new(rd) ]
end