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,58 @@ 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 rewriteGitlabLinkEntry (linkEntry , endpointAPIURL , proxyAPIURL string ) string {
126
+ linkAndRel := strings .Split (strings .TrimSpace (linkEntry ), ";" )
127
+ if len (linkAndRel ) != 2 {
128
+ return linkEntry
129
+ }
130
+
131
+ linkMatch := gitlabLinkRegex .FindStringSubmatch (linkAndRel [0 ])
132
+ if len (linkMatch ) < 2 {
133
+ return linkEntry
134
+ }
135
+
136
+ relMatch := gitlabLinkRelRegex .FindStringSubmatch (linkAndRel [1 ])
137
+ if len (relMatch ) < 2 {
138
+ return linkEntry
139
+ }
140
+
141
+ proxiedLink := proxyAPIURL + strings .TrimPrefix (linkMatch [1 ], endpointAPIURL )
142
+ rel := relMatch [1 ]
143
+ return "<" + proxiedLink + ">; rel=\" " + rel + "\" "
144
+ }
145
+
146
+ func rewriteGitlabLinks (linkHeader , endpointAPIURL , proxyAPIURL string ) string {
147
+ linkEntries := strings .Split (linkHeader , "," )
148
+ finalLinkEntries := make ([]string , len (linkEntries ), len (linkEntries ))
149
+ for i , linkEntry := range linkEntries {
150
+ finalLinkEntries [i ] = rewriteGitlabLinkEntry (linkEntry , endpointAPIURL , proxyAPIURL )
151
+ }
152
+ finalLinkHeader := strings .Join (finalLinkEntries , "," )
153
+ return finalLinkHeader
154
+ }
155
+
113
156
type GitLabTransport struct {}
114
157
115
158
func (t * GitLabTransport ) RoundTrip (r * http.Request ) (* http.Response , error ) {
159
+ ctx := r .Context ()
160
+ config := getConfig (ctx )
116
161
resp , err := http .DefaultTransport .RoundTrip (r )
117
162
if err == nil {
118
- // remove CORS headers from GitHub and use our own
163
+ // remove CORS headers from GitLab and use our own
119
164
resp .Header .Del ("Access-Control-Allow-Origin" )
165
+ linkHeader := resp .Header .Get ("Link" )
166
+ if linkHeader != "" {
167
+ endpoint := config .GitLab .Endpoint
168
+ repo := url .PathEscape (config .GitLab .Repo )
169
+ apiURL := singleJoiningSlash (endpoint , "/projects/" + repo )
170
+ newLinkHeader := rewriteGitlabLinks (linkHeader , apiURL , "" )
171
+ resp .Header .Set ("Link" , newLinkHeader )
172
+ }
120
173
}
174
+
121
175
return resp , err
122
176
}
0 commit comments