1package smarthttp
2
3import (
4	"context"
5	"fmt"
6	"io"
7
8	"github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus"
9	log "github.com/sirupsen/logrus"
10	"gitlab.com/gitlab-org/gitaly/v14/internal/git"
11	"gitlab.com/gitlab-org/gitaly/v14/internal/git/pktline"
12	"gitlab.com/gitlab-org/gitaly/v14/proto/go/gitalypb"
13	"gitlab.com/gitlab-org/gitaly/v14/streamio"
14	"google.golang.org/grpc/codes"
15	"google.golang.org/grpc/status"
16)
17
18const (
19	uploadPackSvc  = "upload-pack"
20	receivePackSvc = "receive-pack"
21)
22
23func (s *server) InfoRefsUploadPack(in *gitalypb.InfoRefsRequest, stream gitalypb.SmartHTTPService_InfoRefsUploadPackServer) error {
24	repoPath, err := s.locator.GetRepoPath(in.GetRepository())
25	if err != nil {
26		return err
27	}
28
29	w := streamio.NewWriter(func(p []byte) error {
30		return stream.Send(&gitalypb.InfoRefsResponse{Data: p})
31	})
32
33	return s.infoRefCache.tryCache(stream.Context(), in, w, func(w io.Writer) error {
34		return s.handleInfoRefs(stream.Context(), uploadPackSvc, repoPath, in, w)
35	})
36}
37
38func (s *server) InfoRefsReceivePack(in *gitalypb.InfoRefsRequest, stream gitalypb.SmartHTTPService_InfoRefsReceivePackServer) error {
39	repoPath, err := s.locator.GetRepoPath(in.GetRepository())
40	if err != nil {
41		return err
42	}
43	w := streamio.NewWriter(func(p []byte) error {
44		return stream.Send(&gitalypb.InfoRefsResponse{Data: p})
45	})
46	return s.handleInfoRefs(stream.Context(), receivePackSvc, repoPath, in, w)
47}
48
49func (s *server) handleInfoRefs(ctx context.Context, service, repoPath string, req *gitalypb.InfoRefsRequest, w io.Writer) error {
50	ctxlogrus.Extract(ctx).WithFields(log.Fields{
51		"service": service,
52	}).Debug("handleInfoRefs")
53
54	cmdOpts := []git.CmdOpt{git.WithGitProtocol(ctx, req)}
55	if service == "receive-pack" {
56		cmdOpts = append(cmdOpts, git.WithRefTxHook(ctx, req.Repository, s.cfg))
57	}
58
59	config, err := git.ConvertConfigOptions(req.GitConfigOptions)
60	if err != nil {
61		return err
62	}
63	cmdOpts = append(cmdOpts, git.WithConfig(config...))
64
65	cmd, err := s.gitCmdFactory.NewWithoutRepo(ctx, git.SubCmd{
66		Name:  service,
67		Flags: []git.Option{git.Flag{Name: "--stateless-rpc"}, git.Flag{Name: "--advertise-refs"}},
68		Args:  []string{repoPath},
69	}, cmdOpts...)
70	if err != nil {
71		if _, ok := status.FromError(err); ok {
72			return err
73		}
74		return status.Errorf(codes.Internal, "GetInfoRefs: cmd: %v", err)
75	}
76
77	if _, err := pktline.WriteString(w, fmt.Sprintf("# service=git-%s\n", service)); err != nil {
78		return status.Errorf(codes.Internal, "GetInfoRefs: pktLine: %v", err)
79	}
80
81	if err := pktline.WriteFlush(w); err != nil {
82		return status.Errorf(codes.Internal, "GetInfoRefs: pktFlush: %v", err)
83	}
84
85	if _, err := io.Copy(w, cmd); err != nil {
86		return status.Errorf(codes.Internal, "GetInfoRefs: %v", err)
87	}
88
89	if err := cmd.Wait(); err != nil {
90		return status.Errorf(codes.Internal, "GetInfoRefs: %v", err)
91	}
92
93	return nil
94}
95