git_http.go 1.9 KiB raw
1
package main
2
3
import (
4
	"net/http"
5
	"net/http/cgi"
6
	"os/exec"
7
	"strings"
8
)
9
10
// handleGitHTTP serves Git smart HTTP protocol requests by proxying to git-http-backend.
11
// This enables `git clone` over HTTPS.
12
func (s *server) handleGitHTTP(w http.ResponseWriter, r *http.Request, repo *RepoInfo) {
13
	gitHTTPBackend, err := exec.LookPath("git-http-backend")
14
	if err != nil {
15
		gitExecPath, err2 := exec.Command("git", "--exec-path").Output()
16
		if err2 != nil {
17
			http.Error(w, "git-http-backend not found", http.StatusInternalServerError)
18
			return
19
		}
20
		gitHTTPBackend = strings.TrimSpace(string(gitExecPath)) + "/git-http-backend"
21
	}
22
23
	// Strip the base URL prefix and repo name to get the path info that git-http-backend expects.
24
	// e.g. /baseurl/repo/info/refs -> /repo.git/info/refs
25
	pathInfo := r.URL.Path
26
	if s.baseURL != "" {
27
		pathInfo = strings.TrimPrefix(pathInfo, s.baseURL)
28
	}
29
	// Replace /<reponame>/ with /<reponame>.git/
30
	pathInfo = strings.Replace(pathInfo, "/"+repo.Name+"/", "/"+repo.Name+".git/", 1)
31
32
	handler := &cgi.Handler{
33
		Path: gitHTTPBackend,
34
		Env: []string{
35
			"GIT_PROJECT_ROOT=" + s.scanPath,
36
			"GIT_HTTP_EXPORT_ALL=1",
37
			"GIT_CONFIG_COUNT=1",
38
			"GIT_CONFIG_KEY_0=safe.directory",
39
			"GIT_CONFIG_VALUE_0=*",
40
		},
41
		InheritEnv: []string{"PATH"},
42
	}
43
44
	// Override PATH_INFO for the CGI handler.
45
	r2 := r.Clone(r.Context())
46
	r2.URL.Path = pathInfo
47
48
	handler.ServeHTTP(w, r2)
49
}
50
51
// isGitHTTPRequest returns true if the request is a Git smart HTTP protocol request.
52
func isGitHTTPRequest(r *http.Request) bool {
53
	query := r.URL.RawQuery
54
55
	if strings.HasSuffix(r.URL.Path, "/info/refs") &&
56
		(strings.Contains(query, "service=git-upload-pack") || strings.Contains(query, "service=git-receive-pack")) {
57
		return true
58
	}
59
	if strings.HasSuffix(r.URL.Path, "/git-upload-pack") || strings.HasSuffix(r.URL.Path, "/git-receive-pack") {
60
		return true
61
	}
62
	if strings.HasSuffix(r.URL.Path, "/HEAD") || strings.Contains(r.URL.Path, "/objects/") {
63
		return true
64
	}
65
	return false
66
}