Skip to content

Commit e70b365

Browse files
author
Paulo Gomes
authored
Merge pull request #765 from pjbgf/gitlab-dotgit-redirect
libgit2: fix gitlab redirection for HTTP
2 parents 2a52056 + b764bdb commit e70b365

File tree

2 files changed

+106
-15
lines changed

2 files changed

+106
-15
lines changed

pkg/git/libgit2/managed/http.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,13 +52,21 @@ import (
5252
"io"
5353
"net/http"
5454
"net/url"
55+
"strings"
5556
"sync"
5657

5758
pool "github.com/fluxcd/source-controller/internal/transport"
5859
"github.com/fluxcd/source-controller/pkg/git"
5960
git2go "github.com/libgit2/git2go/v33"
6061
)
6162

63+
var actionSuffixes = []string{
64+
"/info/refs?service=git-upload-pack",
65+
"/git-upload-pack",
66+
"/info/refs?service=git-receive-pack",
67+
"/git-receive-pack",
68+
}
69+
6270
// registerManagedHTTP registers a Go-native implementation of an
6371
// HTTP(S) transport that doesn't rely on any lower-level libraries
6472
// such as OpenSSL.
@@ -152,12 +160,40 @@ func (t *httpSmartSubtransport) Action(transportOptionsURL string, action git2go
152160

153161
return http.ErrUseLastResponse
154162
}
163+
164+
// Some Git servers (i.e. Gitlab) only support redirection on the GET operations.
165+
// Therefore, on the initial GET operation we update the target URL to include the
166+
// new target, so the subsequent actions include the correct target URL.
167+
// Example of this is trying to access a Git repository without the .git suffix.
168+
if req.Response != nil && req.Response.StatusCode == http.StatusMovedPermanently {
169+
if newURL, err := req.Response.Location(); err == nil && newURL != nil {
170+
if strings.EqualFold(newURL.Host, req.URL.Host) && strings.EqualFold(newURL.Port(), req.URL.Port()) {
171+
opts, _ := getTransportOptions(transportOptionsURL)
172+
if opts == nil {
173+
opts = &TransportOptions{}
174+
}
175+
176+
opts.TargetURL = trimActionSuffix(newURL.String())
177+
AddTransportOptions(transportOptionsURL, *opts)
178+
}
179+
}
180+
}
181+
155182
return nil
156183
}
157184

158185
return stream, nil
159186
}
160187

188+
func trimActionSuffix(url string) string {
189+
newUrl := url
190+
for _, s := range actionSuffixes {
191+
newUrl = strings.TrimSuffix(newUrl, s)
192+
}
193+
194+
return newUrl
195+
}
196+
161197
func createClientRequest(targetURL string, action git2go.SmartServiceAction,
162198
t *http.Transport, authOpts *git.AuthOptions) (*http.Client, *http.Request, error) {
163199
var req *http.Request

pkg/git/libgit2/managed/http_test.go

Lines changed: 70 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -200,26 +200,81 @@ func TestHTTPManagedTransport_E2E(t *testing.T) {
200200
repo.Free()
201201
}
202202

203-
func TestHTTPManagedTransport_HandleRedirect(t *testing.T) {
204-
g := NewWithT(t)
203+
func TestTrimActionSuffix(t *testing.T) {
204+
tests := []struct {
205+
name string
206+
inURL string
207+
wantURL string
208+
}{
209+
{
210+
name: "ignore other suffixes",
211+
inURL: "https://gitlab/repo/podinfo.git/somethingelse",
212+
wantURL: "https://gitlab/repo/podinfo.git/somethingelse",
213+
},
214+
{
215+
name: "trim /info/refs?service=git-upload-pack",
216+
inURL: "https://gitlab/repo/podinfo.git/info/refs?service=git-upload-pack",
217+
wantURL: "https://gitlab/repo/podinfo.git",
218+
},
219+
{
220+
name: "trim /git-upload-pack",
221+
inURL: "https://gitlab/repo/podinfo.git/git-upload-pack",
222+
wantURL: "https://gitlab/repo/podinfo.git",
223+
},
224+
{
225+
name: "trim /info/refs?service=git-receive-pack",
226+
inURL: "https://gitlab/repo/podinfo.git/info/refs?service=git-receive-pack",
227+
wantURL: "https://gitlab/repo/podinfo.git",
228+
},
229+
{
230+
name: "trim /git-receive-pack",
231+
inURL: "https://gitlab/repo/podinfo.git/git-receive-pack",
232+
wantURL: "https://gitlab/repo/podinfo.git",
233+
},
234+
}
205235

206-
tmpDir := t.TempDir()
236+
for _, tt := range tests {
237+
t.Run(tt.name, func(t *testing.T) {
238+
g := NewWithT(t)
239+
240+
gotURL := trimActionSuffix(tt.inURL)
241+
g.Expect(gotURL).To(Equal(tt.wantURL))
242+
})
243+
}
244+
}
245+
246+
func TestHTTPManagedTransport_HandleRedirect(t *testing.T) {
247+
tests := []struct {
248+
name string
249+
repoURL string
250+
}{
251+
{name: "http to https", repoURL: "http://github.com/stefanprodan/podinfo"},
252+
{name: "handle gitlab redirect", repoURL: "https://gitlab.com/stefanprodan/podinfo"},
253+
}
207254

208255
// Force managed transport to be enabled
209256
InitManagedTransport(logr.Discard())
210257

211-
id := "http://obj-id"
212-
AddTransportOptions(id, TransportOptions{
213-
TargetURL: "http://github.com/stefanprodan/podinfo",
214-
})
258+
for _, tt := range tests {
259+
t.Run(tt.name, func(t *testing.T) {
260+
g := NewWithT(t)
215261

216-
// GitHub will cause a 301 and redirect to https
217-
repo, err := git2go.Clone(id, tmpDir, &git2go.CloneOptions{
218-
CheckoutOptions: git2go.CheckoutOptions{
219-
Strategy: git2go.CheckoutForce,
220-
},
221-
})
262+
tmpDir := t.TempDir()
222263

223-
g.Expect(err).ToNot(HaveOccurred())
224-
repo.Free()
264+
id := "http://obj-id"
265+
AddTransportOptions(id, TransportOptions{
266+
TargetURL: tt.repoURL,
267+
})
268+
269+
// GitHub will cause a 301 and redirect to https
270+
repo, err := git2go.Clone(id, tmpDir, &git2go.CloneOptions{
271+
CheckoutOptions: git2go.CheckoutOptions{
272+
Strategy: git2go.CheckoutForce,
273+
},
274+
})
275+
276+
g.Expect(err).ToNot(HaveOccurred())
277+
repo.Free()
278+
})
279+
}
225280
}

0 commit comments

Comments
 (0)