-
Notifications
You must be signed in to change notification settings - Fork 0
/
morganfield.go
208 lines (177 loc) · 4.67 KB
/
morganfield.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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
package morganfield
import (
"bytes"
"crypto/tls"
"crypto/x509"
"fmt"
"github.com/blackjack/syslog"
"io/ioutil"
"net"
"net/http"
"net/http/cookiejar"
"time"
)
var (
Services []Service_Definition = []Service_Definition{}
)
// Get the service by comparing request with service definitions
func get_service(r *http.Request) (Service_Definition, error) {
x := Service_Definition{}
found := false
for _, s := range Services {
if s.Matches_Request(r) {
x = s
found = true
}
}
if !found {
return x, fmt.Errorf("get_service: no service found for request")
}
return x, nil
}
// Initializes http client for forwarding the request
func get_http_client(s Service_Definition) *http.Client {
jaropts := cookiejar.Options{
PublicSuffixList: get_suffix_list(s),
}
jar, err := cookiejar.New(&jaropts)
if err != nil {
panic(err)
}
host, _, err := net.SplitHostPort(s.External_Host)
if err != nil {
panic(err)
}
var client *http.Client
if s.External_Protocol == "https" {
client = &http.Client{
Timeout: 60 * time.Second,
Jar: jar,
Transport: get_transport(host),
}
} else {
client = &http.Client{
Timeout: 60 * time.Second,
Jar: jar,
}
}
return client
}
// Get transport for https client
func get_transport(server_name string) *http.Transport {
// Load client key and certificate (optional)
cert, clerr := tls.LoadX509KeyPair("/opt/morganfield/etc/morganfield.crt", "/opt/morganfield/etc/morganfield.key")
// Load the CA certificate for server certificate validation (mandatory)
capool := x509.NewCertPool()
cacert, err := ioutil.ReadFile("/opt/morganfield/etc/extca.crt")
if err != nil {
// proceeding without CA certificate checking would be bad
syslog.Errf("%v", err)
panic(err)
}
capool.AppendCertsFromPEM(cacert)
// Prepare config and transport
var config tls.Config
if clerr != nil {
// We did NOT have client certificate
config = tls.Config{RootCAs: capool}
} else {
// We DID have client certificate so offer it
config = tls.Config{
Certificates: []tls.Certificate{cert},
RootCAs: capool,
ServerName: server_name}
}
tr := &http.Transport{
TLSClientConfig: &config,
}
return tr
}
// Handler for all requests
func Handler(w http.ResponseWriter, r *http.Request) {
// Syslog connection should be ok for every request
syslog.Openlog("morganfield", syslog.LOG_PID, syslog.LOG_DAEMON)
// Initialize object for logging request
reqlog := RequestLog_from_request(r)
defer reqlog.Log()
// Detect service
s, err := get_service(r)
if err != nil {
reqlog.Status = http.StatusBadGateway
w.WriteHeader(http.StatusBadGateway)
panic(err)
}
// filter input JSON
injson, err := filter_input_json(r, s)
if err != nil {
// The JSON will be empty string - no worries just log it
syslog.Errf("%v", err)
reqlog.InJson = fmt.Sprintf("%v", err)
} else {
reqlog.InJson = injson
}
// Build a new client & request
client := get_http_client(s)
request, err := http.NewRequest(s.Method, s.External_Protocol+"://"+s.External_Host+r.URL.Path, bytes.NewBufferString(injson))
if err != nil {
reqlog.Status = http.StatusInternalServerError
w.WriteHeader(http.StatusInternalServerError)
panic(err)
}
request.Header.Add("Content-Type", "application/json")
host, _, err := net.SplitHostPort(s.External_Host)
if err != nil {
panic(err)
}
// Add cookies
if s.SetCookies {
origc := r.Cookies()
for _, c := range origc {
// When outgoing, cookie domain must match External_Host
c.Domain = host
reqlog.InCookies = append(reqlog.InCookies, fmt.Sprintf("%v", c))
request.AddCookie(c)
}
}
// Copy the user agent string
if ua, ok := r.Header["User-Agent"]; ok {
request.Header.Add("User-Agent", ua[0])
}
// Forward the request to target
response, err := client.Do(request)
if err != nil {
reqlog.Status = http.StatusNotImplemented
w.WriteHeader(http.StatusNotImplemented)
panic(err)
}
// filter output JSON
outjson, err := filter_output_json(response, s)
if err != nil {
// The JSON will be empty string - no worries just log it
syslog.Errf("%v", err)
reqlog.OutJson = fmt.Sprintf("%v", err)
} else {
reqlog.OutJson = outjson
}
inthost, _, err := net.SplitHostPort(s.Internal_Host)
if err != nil {
panic(err)
}
// Cookies back
if s.SetCookies {
newc := response.Cookies()
for _, c := range newc {
// When incoming, cookie domain must match Internal_Host
if len(c.Domain) > 0 {
c.Domain = inthost
}
w.Header().Add("Set-Cookie", c.String())
reqlog.OutCookies = append(reqlog.OutCookies, fmt.Sprintf("%v", c))
}
}
// Status number
w.WriteHeader(response.StatusCode)
reqlog.Status = response.StatusCode
// Return the JSON
fmt.Fprintf(w, "%s", outjson)
}