6
6
"net/http/httputil"
7
7
"net/url"
8
8
"regexp"
9
+ "strings"
9
10
)
10
11
11
12
// GitLabGateway acts as a proxy to Gitlab
@@ -34,7 +35,11 @@ func gitlabDirector(r *http.Request) {
34
35
r .Host = target .Host
35
36
r .URL .Scheme = target .Scheme
36
37
r .URL .Host = target .Host
37
- r .URL .Path = singleJoiningSlash (target .Path , gitlabPathRegexp .ReplaceAllString (r .URL .Path , "/" ))
38
+ // We need to set URL.Opaque using the target and r.URL EscapedPath
39
+ // methods, because the Go stdlib URL parsing automatically converts
40
+ // %2F to / in URL paths, and GitLab requires %2F to be preserved
41
+ // as-is.
42
+ r .URL .Opaque = "//" + target .Host + singleJoiningSlash (target .EscapedPath (), gitlabPathRegexp .ReplaceAllString (r .URL .EscapedPath (), "/" ))
38
43
if targetQuery == "" || r .URL .RawQuery == "" {
39
44
r .URL .RawQuery = targetQuery + r .URL .RawQuery
40
45
} else {
@@ -44,8 +49,9 @@ func gitlabDirector(r *http.Request) {
44
49
// explicitly disable User-Agent so it's not set to default value
45
50
r .Header .Set ("User-Agent" , "" )
46
51
}
52
+ r .Header .Del ("Authorization" )
47
53
if r .Method != http .MethodOptions {
48
- r .Header .Set ("Authorization " , "Bearer " + accessToken )
54
+ r .Header .Set ("Private-Token " , accessToken )
49
55
}
50
56
51
57
log := getLogEntry (r )
@@ -66,7 +72,10 @@ func (gl *GitLabGateway) ServeHTTP(w http.ResponseWriter, r *http.Request) {
66
72
}
67
73
68
74
endpoint := config .GitLab .Endpoint
69
- apiURL := singleJoiningSlash (endpoint , "/repos/" + config .GitLab .Repo )
75
+ // repos in the form of userName/repoName must be encoded as
76
+ // userName%2FrepoName
77
+ repo := url .PathEscape (config .GitLab .Repo )
78
+ apiURL := singleJoiningSlash (endpoint , "/projects/" + repo )
70
79
target , err := url .Parse (apiURL )
71
80
if err != nil {
72
81
handleError (internalServerError ("Unable to process GitLab endpoint" ), w , r )
@@ -110,13 +119,40 @@ func (gl *GitLabGateway) authenticate(w http.ResponseWriter, r *http.Request) er
110
119
return errors .New ("Access to endpoint not allowed: your role doesn't allow access" )
111
120
}
112
121
122
+ var gitlabLinkRegex = regexp .MustCompile ("<(.*?)>" )
123
+ var gitlabLinkRelRegex = regexp .MustCompile ("rel=\" (.*?)\" " )
124
+
125
+ func rewriteGitlabLinks (linkHeader , endpointAPIURL , proxyAPIURL string ) string {
126
+ linkEntries := strings .Split (linkHeader , "," )
127
+ finalLinkEntries := []string {}
128
+ for _ , linkEntry := range linkEntries {
129
+ linkAndRel := strings .Split (strings .TrimSpace (linkEntry ), ";" )
130
+ link := proxyAPIURL + strings .TrimPrefix (gitlabLinkRegex .FindStringSubmatch (linkAndRel [0 ])[1 ], endpointAPIURL )
131
+ rel := gitlabLinkRelRegex .FindStringSubmatch (linkAndRel [1 ])[1 ]
132
+ finalLinkEntries = append (finalLinkEntries , "<" + link + ">; rel=\" " + rel + "\" " )
133
+ }
134
+ finalLinkHeader := strings .Join (finalLinkEntries , "," )
135
+ return finalLinkHeader
136
+ }
137
+
113
138
type GitLabTransport struct {}
114
139
115
140
func (t * GitLabTransport ) RoundTrip (r * http.Request ) (* http.Response , error ) {
141
+ ctx := r .Context ()
142
+ config := getConfig (ctx )
116
143
resp , err := http .DefaultTransport .RoundTrip (r )
117
144
if err == nil {
118
- // remove CORS headers from GitHub and use our own
145
+ // remove CORS headers from GitLab and use our own
119
146
resp .Header .Del ("Access-Control-Allow-Origin" )
147
+ linkHeader := resp .Header .Get ("Link" )
148
+ if linkHeader != "" {
149
+ endpoint := config .GitLab .Endpoint
150
+ repo := url .PathEscape (config .GitLab .Repo )
151
+ apiURL := singleJoiningSlash (endpoint , "/projects/" + repo )
152
+ newLinkHeader := rewriteGitlabLinks (linkHeader , apiURL , "" )
153
+ resp .Header .Set ("Link" , newLinkHeader )
154
+ }
120
155
}
156
+
121
157
return resp , err
122
158
}
0 commit comments