-
Notifications
You must be signed in to change notification settings - Fork 13
/
Copy pathproxy.go
130 lines (107 loc) · 3.05 KB
/
proxy.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
128
129
130
package main
import (
"net/http"
"net/url"
"io/ioutil"
"bytes"
"io"
"strings"
"strconv"
"math"
)
type Proxy struct {
Host string
Port int
Scheme string
Servers []Server
}
func (proxy Proxy) origin() string {
return (proxy.Scheme + "://" + proxy.Host + ":" + strconv.Itoa(proxy.Port));
}
// TODO: This crashes if we define no servers in our config
func (proxy Proxy)chooseServer(ignoreList []string) *Server {
var min = -1
var minIndex = 0
for index,server := range proxy.Servers {
var skip = false
for _, ignore := range ignoreList {
if(ignore == server.Name){
skip = true
break
}
}
if skip {
continue
}
var conn = server.Connections
if min == -1 {
min = conn
minIndex = index
}else if(conn < min){
min = conn
minIndex = index
}
}
return &proxy.Servers[minIndex]
}
func (proxy Proxy)ReverseProxy(w http.ResponseWriter, r *http.Request, server Server) (int, error){
u, err := url.Parse(server.Url() + r.RequestURI)
if err != nil {
LogErrAndCrash(err.Error())
}
r.URL = u
r.Header.Set("X-Forwarded-Host", r.Host)
r.Header.Set("Origin", proxy.origin())
r.Host = server.Url()
r.RequestURI = ""
client := &http.Client{
CheckRedirect: func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
},
}
// TODO: If the server doesn't respond, try a new web server
// We could return a status code from this function and let the handler try passing the request to a new server.
resp, err := client.Do(r)
if err != nil {
// For now, this is a fatal error
// When we can fail to another webserver, this should only be a warning.
LogErr("connection refused")
return 0, err
}
LogInfo("Recieved response: " + strconv.Itoa(resp.StatusCode))
bodyBytes, err := ioutil.ReadAll(resp.Body)
if err != nil {
LogErr("Proxy: Failed to read response body")
http.NotFound(w, r)
return 0, err
}
buffer := bytes.NewBuffer(bodyBytes)
for k, v := range resp.Header {
w.Header().Set(k, strings.Join(v, ";"))
}
w.WriteHeader(resp.StatusCode)
io.Copy(w, buffer)
return resp.StatusCode, nil
}
func (proxy Proxy)attemptServers(w http.ResponseWriter, r *http.Request, ignoreList []string) {
if float64(len(ignoreList)) >= math.Min(float64(3), float64(len(proxy.Servers))) {
LogErr("Failed to find server for request")
http.NotFound(w, r)
return
}
var server = proxy.chooseServer(ignoreList)
LogInfo("Got request: " + r.RequestURI)
LogInfo("Sending to server: " + server.Name)
server.Connections += 1
_, err := proxy.ReverseProxy(w, r, *server)
server.Connections -= 1
if err != nil && strings.Contains(err.Error(), "connection refused") {
LogWarn("Server did not respond: " + server.Name)
proxy.attemptServers(w, r, append(ignoreList, server.Name))
return
}
LogInfo("Responded to request successfuly")
}
func (proxy Proxy)handler(w http.ResponseWriter, r *http.Request) {
proxy.attemptServers(w, r, []string{})
}