package main import ( "net/http" "net/http/cgi" "os/exec" "strings" ) // handleGitHTTP serves Git smart HTTP protocol requests by proxying to git-http-backend. // This enables `git clone` over HTTPS. func (s *server) handleGitHTTP(w http.ResponseWriter, r *http.Request, repo *RepoInfo) { gitHTTPBackend, err := exec.LookPath("git-http-backend") if err != nil { gitExecPath, err2 := exec.Command("git", "--exec-path").Output() if err2 != nil { http.Error(w, "git-http-backend not found", http.StatusInternalServerError) return } gitHTTPBackend = strings.TrimSpace(string(gitExecPath)) + "/git-http-backend" } // Strip the base URL prefix and repo name to get the path info that git-http-backend expects. // e.g. /baseurl/repo/info/refs -> /repo.git/info/refs pathInfo := r.URL.Path if s.baseURL != "" { pathInfo = strings.TrimPrefix(pathInfo, s.baseURL) } // Replace // with /.git/ pathInfo = strings.Replace(pathInfo, "/"+repo.Name+"/", "/"+repo.Name+".git/", 1) handler := &cgi.Handler{ Path: gitHTTPBackend, Env: []string{ "GIT_PROJECT_ROOT=" + s.scanPath, "GIT_HTTP_EXPORT_ALL=1", "GIT_CONFIG_COUNT=1", "GIT_CONFIG_KEY_0=safe.directory", "GIT_CONFIG_VALUE_0=*", }, InheritEnv: []string{"PATH"}, } // Override PATH_INFO for the CGI handler. r2 := r.Clone(r.Context()) r2.URL.Path = pathInfo handler.ServeHTTP(w, r2) } // isGitHTTPRequest returns true if the request is a Git smart HTTP protocol request. func isGitHTTPRequest(r *http.Request) bool { query := r.URL.RawQuery if strings.HasSuffix(r.URL.Path, "/info/refs") && (strings.Contains(query, "service=git-upload-pack") || strings.Contains(query, "service=git-receive-pack")) { return true } if strings.HasSuffix(r.URL.Path, "/git-upload-pack") || strings.HasSuffix(r.URL.Path, "/git-receive-pack") { return true } if strings.HasSuffix(r.URL.Path, "/HEAD") || strings.Contains(r.URL.Path, "/objects/") { return true } return false }