Files
nostrlib/khatru/grasp/info.go

102 lines
2.8 KiB
Go

package grasp
import (
"bytes"
"fmt"
"io"
"net/http"
"os"
"os/exec"
"strings"
"fiatjaf.com/nostr"
)
// handleInfoRefs handles the git info/refs endpoint
func (gs *GraspServer) handleInfoRefs(
w http.ResponseWriter,
r *http.Request,
pubkey nostr.PubKey,
repoName string,
) {
if !gs.repoExists(pubkey, repoName) {
w.Header().Set("content-type", "text/plain; charset=UTF-8")
w.WriteHeader(404)
fmt.Fprintf(w, "repository announcement event not found during info-refs\n")
return
}
repoPath := gs.getRepositoryPath(pubkey, repoName)
serviceName := r.URL.Query().Get("service")
w.Header().Set("Connection", "Keep-Alive")
w.Header().Set("Cache-Control", "no-cache, max-age=0, must-revalidate")
w.Header().Set("Content-Type", "application/x-"+serviceName+"-advertisement")
if _, err := os.Stat(repoPath); os.IsNotExist(err) {
// if the repo doesn't exist that's because it wasn't pushed yet, so return an empty response
// service advertisement header: packet-line with "# service=<service-name>\n"
serviceLine := fmt.Sprintf("# service=%s\n", serviceName)
// write packet line
length := len(serviceLine) + 4
fmt.Fprintf(w, "%04x%s", length, serviceLine)
// flush
w.Write([]byte("0000"))
// another flush packet to indicate end of refs
w.Write([]byte("0000"))
return
}
if err := gs.runInfoRefs(w, r, serviceName, repoPath); err != nil {
gs.Log("error on info-refs rpc: %s\n", err)
return
}
}
// runInfoRefs executes git-upload-pack with --http-backend-info-refs
func (gs *GraspServer) runInfoRefs(w http.ResponseWriter, r *http.Request, serviceName, repoPath string) error {
cmd := exec.Command(serviceName, "--stateless-rpc", "--http-backend-info-refs", ".")
cmd.Dir = repoPath
cmd.Env = append(os.Environ(), fmt.Sprintf("GIT_PROTOCOL=%s", r.Header.Get("Git-Protocol")))
var stderr bytes.Buffer
cmd.Stderr = &stderr
stdoutPipe, err := cmd.StdoutPipe()
if err != nil {
return fmt.Errorf("failed to create stdout pipe: %w", err)
}
// write pack line header only if not git protocol v2
if !strings.Contains(r.Header.Get("Git-Protocol"), "version=2") {
// packLine
s := "# service=" + serviceName + "\n"
if _, err := fmt.Fprintf(w, "%04x%s", len(s)+4, s); err != nil {
return fmt.Errorf("failed to write pack line: %w", err)
}
// packFlush
if _, err := fmt.Fprint(w, "0000"); err != nil {
return fmt.Errorf("failed to flush pack: %w", err)
}
}
if err := cmd.Start(); err != nil {
return fmt.Errorf("failed to start %s: %w, %s", serviceName, err, stderr.String())
}
io.Copy(gs.newWriteFlusher(w), stdoutPipe)
stdoutPipe.Close()
if err := cmd.Wait(); err != nil {
gs.Log("%s failed: %w, stderr: %s", serviceName, err, stderr.String())
return fmt.Errorf("%s failed: %w, stderr: %s", serviceName, err, stderr.String())
}
return nil
}