package git import "codit/internal/util" import "net/http" import "os" import "path/filepath" import "strings" import "github.com/sosedoff/gitkit" type HTTPServer struct { handler http.Handler baseDir string logger *util.Logger } type AuthFunc func(username, password string) (bool, error) func NewHTTPServer(baseDir string, auth AuthFunc, logger *util.Logger) (*HTTPServer, error) { var cfg gitkit.Config var srv *gitkit.Server cfg = gitkit.Config{ Dir: baseDir, AutoCreate: false, Auth: auth != nil, } srv = gitkit.New(cfg) srv.AuthFunc = func(cred gitkit.Credential, _ *gitkit.Request) (bool, error) { if auth == nil { return true, nil } return auth(cred.Username, cred.Password) } return &HTTPServer{handler: srv, baseDir: baseDir, logger: logger}, nil } func (s *HTTPServer) ServeHTTP(w http.ResponseWriter, r *http.Request) { var repoPath string var err error var recorder *statusRecorder var userLabel string var username string var ok bool userLabel = "-" recorder = &statusRecorder{ResponseWriter: w, status: http.StatusOK} repoPath = s.repoPathFromRequest(r) if repoPath != "" { _, err = os.Stat(repoPath) if err == nil { _ = EnsureHead(repoPath) } } if r != nil { username, _, ok = r.BasicAuth() if ok && username != "" { userLabel = username } } s.handler.ServeHTTP(recorder, r) if s.logger != nil { s.logger.Write( "git", util.LOG_INFO, "method=%s path=%s remote=%s user=%s status=%d\n", r.Method, r.URL.Path, r.RemoteAddr, userLabel, recorder.status) } } func (s *HTTPServer) repoPathFromRequest(r *http.Request) string { var path string var suffixes []string var i int var suffix string path = r.URL.Path suffixes = []string{"/info/refs", "/git-upload-pack", "/git-receive-pack"} for i = 0; i < len(suffixes); i++ { suffix = suffixes[i] if strings.HasSuffix(path, suffix) { path = strings.TrimSuffix(path, suffix) break } } path = strings.Trim(path, "/") if path == "" { return "" } return filepath.Join(s.baseDir, filepath.FromSlash(path)) } type statusRecorder struct { http.ResponseWriter status int } func (r *statusRecorder) WriteHeader(code int) { r.status = code r.ResponseWriter.WriteHeader(code) } func (r *statusRecorder) Flush() { var flusher http.Flusher var ok bool flusher, ok = r.ResponseWriter.(http.Flusher) if ok { flusher.Flush() } }