-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathhttp-proxy.go
More file actions
116 lines (94 loc) · 2.92 KB
/
http-proxy.go
File metadata and controls
116 lines (94 loc) · 2.92 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
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
// Copyright 2020 Blues Inc. All rights reserved.
// Use of this source code is governed by licenses granted by the
// copyright holder including that found in the LICENSE file.
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"strings"
"time"
)
// Rate limiting because of Balena's proxy
const throttleMs = 250
var throttleTime int64
// Proxy handler so that we may make external references from local pages without CORS issues. Note that
// this ONLY is supported for JSON queries.
func inboundWebProxyHandler(httpRsp http.ResponseWriter, httpReq *http.Request) {
// Throttle because of Balena's rate limit
msSinceLastTransaction := (time.Now().UnixNano() - throttleTime) / 1000000
if msSinceLastTransaction < throttleMs {
time.Sleep(time.Duration(throttleMs-msSinceLastTransaction) * time.Millisecond)
}
throttleTime = time.Now().UnixNano()
// Get the body if supplied
reqBody, err := io.ReadAll(httpReq.Body)
if err != nil {
reqBody = []byte("")
}
// Get the target
_, args := HTTPArgs(httpReq, "")
var proxyURL string
proxyURL, err = url.QueryUnescape(args["url"])
if err != nil {
httpRsp.Write([]byte(fmt.Sprintf("{\"err\":\"%s\"}", err)))
return
}
fmt.Printf("proxy: %s\n", proxyURL)
// Perform the transaction several times to cover the Balena problem that yields
// a strange web page on a semi-random basis.
var rspbuf []byte
var resp *http.Response
var maxRetries = 5
for i := 0; ; i++ {
var req *http.Request
req, err = http.NewRequest(httpReq.Method, proxyURL, bytes.NewBuffer(reqBody))
if err != nil {
fmt.Printf("proxy NR err: %s\n", err)
httpRsp.Write([]byte(fmt.Sprintf("{\"err\":\"%s\"}", err)))
return
}
httpclient := &http.Client{Timeout: time.Second * 15}
resp, err = httpclient.Do(req)
if err != nil {
fmt.Printf("proxy DO err: %s\n", err)
httpRsp.Write([]byte(fmt.Sprintf("{\"err\":\"%s\"}", err)))
return
}
rspbuf, err = io.ReadAll(resp.Body)
if err != nil {
fmt.Printf("proxy RD err: %s\n", err)
httpRsp.Write([]byte(fmt.Sprintf("{\"err\":\"%s\"}", err)))
return
}
// Validate that it's compliant JSON
var jobj map[string]interface{}
err = json.Unmarshal(rspbuf, &jobj)
if err == nil {
// See if there was an I/O error to the card, and retry if so
if !strings.Contains(string(rspbuf), "{io}") {
break
}
if i > maxRetries {
httpRsp.Write([]byte("{\"err\":\"proxy: cannot communicate with notecard {io}\"}"))
return
}
fmt.Printf("proxy: I/O error on try #%d/%d\n", i+1, maxRetries)
} else {
fmt.Printf("proxy: received non-JSON on try #%d/%d\n", i+1, maxRetries)
if i > maxRetries {
fmt.Printf("%s\n", string(rspbuf))
fmt.Printf("%s\n", err)
httpRsp.Write([]byte(fmt.Sprintf("{\"err\":\"%s\"}", err)))
return
}
}
// Essential to coming out of Balena's penalty box
time.Sleep(2 * time.Second)
}
httpRsp.WriteHeader(resp.StatusCode)
httpRsp.Write(rspbuf)
}