-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathhookEngine.go
112 lines (93 loc) · 2.91 KB
/
hookEngine.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
package captainhook
import (
"bytes"
"errors"
"fmt"
"log"
"net/http"
"text/template"
)
// Main routine that processes received hooks, obtaining endpoints and processing rules.
// Various error checking and validation happens at this stage, i.e mapping required secrets to
// dataBag. data bag is a map of input parameters passed to each rules function.
func Hook(w http.ResponseWriter, r *http.Request, endpoint *Endpoint, secrets SecretEngine, log *log.Logger, dataBag *map[string]interface{}) {
var bag = *dataBag
var secretMap = make(map[string]string)
// Attach secrets to secrets map/
for _, secret := range endpoint.Secrets {
v, err := secrets.GetTextSecret(secret)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
log.Println("unable to get secret from engine", secret)
return
}
secretMap[secret] = v
}
// Store secrets on data bag.
bag["_secrets"] = secretMap
var request bytes.Buffer
rules, err := endpoint.GetRules()
if err != nil {
log.Println("unable to enumerate rules, endpoint", endpoint.Name)
w.WriteHeader(http.StatusInternalServerError)
return
}
for _, r := range rules {
// For each rule, assign related function then execute via the interface method execute passing in
// dataBag and the http request.
AssignFunction(&r)
err = r.Execute(&request, *dataBag)
if err != nil {
log.Println(r, "failed to execute template.", err)
continue
}
// For rules with destinations specified, move to forward function result.
if r.Destination != "" {
// Template url and create http request.
client := &http.Client{}
dest, err := templateString(r.Destination, dataBag)
if err != nil {
log.Println(err)
continue
}
log.Println("forwarding to", dest)
req, err := http.NewRequest("POST", dest, &request)
if err != nil {
log.Println("Failed to create new request", err)
continue
}
// Render headers and attach to request
for k, v := range r.Headers {
value, err := templateString(v, dataBag)
if err != nil {
log.Println(err)
continue
}
req.Header.Add(k, value)
}
// POST request
resp, err := client.Do(req)
if err != nil {
log.Println("post request to", r.Destination, "failed.")
continue
}
if resp.StatusCode > 299 || resp.StatusCode < 200 {
log.Println("Request returned non 200 status code:", resp.StatusCode)
log.Println("HTTP Status:", resp.Status)
}
}
request.Reset()
}
}
// Helper function to quickly render a string template and return the value.
// Useful for URI and Header rendering.
func templateString(templ string, data *map[string]interface{}) (string, error) {
tmpl, err := template.New("tmpl").Parse(templ)
if err != nil {
return "", errors.New(fmt.Sprint("Unable to create template for header from: ", templ))
}
buf := make([]byte, 0, 1)
var tpl *bytes.Buffer = bytes.NewBuffer(buf)
err = tmpl.Execute(tpl, &data)
return tpl.String(), nil
}