forked from taskcluster/taskcluster-proxy
/
routes.go
127 lines (102 loc) · 3.12 KB
/
routes.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
package main
import (
"fmt"
"io"
"io/ioutil"
"log"
"net/http"
"strings"
tc "github.com/lightsofapollo/taskcluster-proxy/taskcluster"
)
type Routes struct {
// Client ID used to authenticate all proxy requests.
ClientId string
// Access Token used to authenticate all proxy requests.
AccessToken string
// Scopes to use in the delegating authentication.
Scopes []string
}
var tcServices = tc.NewServices()
var httpClient = &http.Client{}
func (self Routes) signUrl(res http.ResponseWriter, req *http.Request) {
// Using ReadAll could be sketchy here since we are reading unbounded data
// into memory...
body, err := ioutil.ReadAll(req.Body)
if err != nil {
res.WriteHeader(500)
fmt.Fprintf(res, "Error reading body")
return
}
urlString := strings.TrimSpace(string(body))
bewitUrl, err := tc.Bewit(self.ClientId, self.AccessToken, urlString)
if err != nil {
res.WriteHeader(500)
fmt.Fprintf(res, "Error creating bewit url")
return
}
headers := res.Header()
headers.Set("Location", bewitUrl)
res.WriteHeader(303)
fmt.Fprintf(res, bewitUrl)
}
// Routes implements the `http.Handler` interface
func (self Routes) ServeHTTP(res http.ResponseWriter, req *http.Request) {
// A special case for the proxy is returning a bewit signed url.
if req.URL.Path[0:6] == "/bewit" {
self.signUrl(res, req)
return
}
targetPath, err := tcServices.ConvertPath(req.URL)
// Unkown service which we are trying to hit...
if err != nil {
res.WriteHeader(404)
log.Printf("Attempting to use unkown service %s", req.URL.String())
fmt.Fprintf(res, "Unkown taskcluster service: %s", err)
return
}
// Copy method and body over to the proxy request.
log.Printf("Proxying %s | %s | %s", req.URL, req.Method, targetPath)
proxyReq, err := http.NewRequest(req.Method, targetPath.String(), req.Body)
// If we fail to create a request notify the client.
if err != nil {
res.WriteHeader(500)
fmt.Fprintf(res, "Failed to generate proxy request: %s", err)
return
}
// Copy all headers over to the proxy request.
for key, _ := range req.Header {
// Do not forward connection!
if key == "Connection" || key == "Host" {
continue
}
proxyReq.Header.Set(key, req.Header.Get(key))
}
// Sign the proxy request with our credentials.
auth, err := tc.AuthorizationDelegate(
self.ClientId, self.AccessToken, self.Scopes, proxyReq,
)
if err != nil {
res.WriteHeader(500)
fmt.Fprintf(res, "Failed to sign proxy request")
return
}
proxyReq.Header.Set("Authorization", auth)
// Issue the proxy request...
proxyResp, err := httpClient.Do(proxyReq)
if err != nil {
res.WriteHeader(500)
fmt.Fprintf(res, "Failed during proxy request: %s", err)
return
}
// Map the headers from the proxy back into our proxyResponse
headersToSend := res.Header()
for key, _ := range proxyResp.Header {
headersToSend.Set(key, proxyResp.Header.Get(key))
}
headersToSend.Set("X-Taskcluster-Endpoint", targetPath.String())
// Write the proxyResponse headers and status.
res.WriteHeader(proxyResp.StatusCode)
// Proxy the proxyResponse body from the endpoint to our response.
io.Copy(res, proxyResp.Body)
proxyResp.Body.Close()
}