diff options
Diffstat (limited to 'internal/unsorted/http_handle_repo_info.go')
-rw-r--r-- | internal/unsorted/http_handle_repo_info.go | 107 |
1 files changed, 107 insertions, 0 deletions
diff --git a/internal/unsorted/http_handle_repo_info.go b/internal/unsorted/http_handle_repo_info.go new file mode 100644 index 0000000..e23b1d2 --- /dev/null +++ b/internal/unsorted/http_handle_repo_info.go @@ -0,0 +1,107 @@ +// SPDX-License-Identifier: AGPL-3.0-only +// SPDX-FileCopyrightText: Copyright (c) 2025 Runxi Yu <https://runxiyu.org> + +package unsorted + +import ( + "fmt" + "io" + "net/http" + "os/exec" + + "github.com/jackc/pgx/v5/pgtype" +) + +// httpHandleRepoInfo provides advertised refs of a repo for use in Git's Smart +// HTTP protocol. +// +// TODO: Reject access from web browsers. +func (s *Server) httpHandleRepoInfo(writer http.ResponseWriter, request *http.Request, params map[string]any) (err error) { + groupPath := params["group_path"].([]string) + repoName := params["repo_name"].(string) + var repoPath string + + if err := s.database.QueryRow(request.Context(), ` + WITH RECURSIVE group_path_cte AS ( + -- Start: match the first name in the path where parent_group IS NULL + SELECT + id, + parent_group, + name, + 1 AS depth + FROM groups + WHERE name = ($1::text[])[1] + AND parent_group IS NULL + + UNION ALL + + -- Recurse: jion next segment of the path + SELECT + g.id, + g.parent_group, + g.name, + group_path_cte.depth + 1 + FROM groups g + JOIN group_path_cte ON g.parent_group = group_path_cte.id + WHERE g.name = ($1::text[])[group_path_cte.depth + 1] + AND group_path_cte.depth + 1 <= cardinality($1::text[]) + ) + SELECT r.filesystem_path + FROM group_path_cte c + JOIN repos r ON r.group_id = c.id + WHERE c.depth = cardinality($1::text[]) + AND r.name = $2 + `, + pgtype.FlatArray[string](groupPath), + repoName, + ).Scan(&repoPath); err != nil { + return err + } + + writer.Header().Set("Content-Type", "application/x-git-upload-pack-advertisement") + writer.WriteHeader(http.StatusOK) + + cmd := exec.Command("git", "upload-pack", "--stateless-rpc", "--advertise-refs", repoPath) + stdout, err := cmd.StdoutPipe() + if err != nil { + return err + } + defer func() { + _ = stdout.Close() + }() + cmd.Stderr = cmd.Stdout + + if err = cmd.Start(); err != nil { + return err + } + + if err = packLine(writer, "# service=git-upload-pack\n"); err != nil { + return err + } + + if err = packFlush(writer); err != nil { + return + } + + if _, err = io.Copy(writer, stdout); err != nil { + return err + } + + if err = cmd.Wait(); err != nil { + return err + } + + return nil +} + +// Taken from https://github.com/icyphox/legit, MIT license. +func packLine(w io.Writer, s string) error { + _, err := fmt.Fprintf(w, "%04x%s", len(s)+4, s) + return err +} + +// Taken from https://github.com/icyphox/legit, MIT license. +func packFlush(w io.Writer) error { + _, err := fmt.Fprint(w, "0000") + return err +} |