Repository navigation
Expand file tree
/
Copy pathproxy.go
More file actions
73 lines (69 loc) · 2.43 KB
/
Copy pathproxy.go
File metadata and controls
73 lines (69 loc) · 2.43 KB
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
package daemonapi
import (
"bytes"
"context"
"errors"
"io"
"net/http"
"net/http/httputil"
"time"
"github.com/korioinc/multica-runtime-controller/internal/workspace"
)
// The backend accepts files up to 100 MiB. Leave room for multipart framing
// without interpreting business payloads or buffering downloaded files.
const maxProxyPayload = 128 << 20
func (g *Gateway) proxy(w http.ResponseWriter, r *http.Request, grant workspace.TaskGrant, token string) {
ctx, cancel := context.WithTimeout(r.Context(), time.Minute)
defer cancel()
if len(r.URL.RawQuery) > 4096 {
reject(w, 400)
return
}
body, err := io.ReadAll(http.MaxBytesReader(w, r.Body, maxProxyPayload))
if err != nil {
reject(w, 400)
return
}
upstream, err := g.Client.newRequest(ctx, r.Method, r.URL.RequestURI(), token, grant.WorkspaceID, nil)
if err != nil {
reject(w, 400)
return
}
// Only transport and native client metadata cross this boundary. Backend
// authentication derives actor identity from the original task token.
upstream.Header.Del("Content-Type")
for _, name := range []string{
"Content-Type", "Content-Encoding", "Accept", "Accept-Encoding", "Accept-Language",
"If-Match", "If-None-Match", "If-Modified-Since", "If-Unmodified-Since", "Range", "If-Range",
"Idempotency-Key", "X-Client-Capabilities", "X-Client-Version", "X-Client-Platform", "X-Client-OS",
} {
key := http.CanonicalHeaderKey(name)
if values, exists := r.Header[key]; exists {
upstream.Header[key] = append([]string(nil), values...)
}
}
r = r.WithContext(ctx)
r.Body = io.NopCloser(bytes.NewReader(body))
r.ContentLength = int64(len(body))
proxy := httputil.ReverseProxy{
Transport: g.Client.http.Transport,
Rewrite: func(p *httputil.ProxyRequest) {
p.Out.URL = upstream.URL
p.Out.Host = upstream.URL.Host
p.Out.Header = upstream.Header
},
ModifyResponse: func(response *http.Response) error {
if response.StatusCode >= 300 && response.StatusCode < 400 && response.StatusCode != http.StatusNotModified {
return errors.New("backend redirect refused")
}
if r.Method != http.MethodHead && response.ContentLength > maxProxyPayload {
return errors.New("backend response exceeds transport limit")
}
response.Body = http.MaxBytesReader(nil, response.Body, maxProxyPayload)
response.Header.Del("Set-Cookie")
return nil
},
ErrorHandler: func(w http.ResponseWriter, _ *http.Request, _ error) { reject(w, 502) },
}
proxy.ServeHTTP(w, r)
}