forked from TykTechnologies/tyk
-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathmiddleware_transform.go
127 lines (107 loc) · 3.54 KB
/
middleware_transform.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
package main
import (
"bytes"
"encoding/json"
"io"
"io/ioutil"
"net/http"
"github.com/TykTechnologies/logrus"
"github.com/TykTechnologies/tykcommon"
"github.com/clbanning/mxj"
"github.com/gorilla/context"
"golang.org/x/net/html/charset"
)
func WrappedCharsetReader(s string, i io.Reader) (io.Reader, error) {
return charset.NewReader(i, s)
}
// TransformMiddleware is a middleware that will apply a template to a request body to transform it's contents ready for an upstream API
type TransformMiddleware struct {
*TykMiddleware
}
func (mw *TransformMiddleware) GetName() string {
return "TransformMiddleware"
}
type TransformMiddlewareConfig struct{}
// New lets you do any initialisations for the object can be done here
func (m *TransformMiddleware) New() {}
// GetConfig retrieves the configuration from the API config - we user mapstructure for this for simplicity
func (t *TransformMiddleware) GetConfig() (interface{}, error) {
return nil, nil
}
func (t *TransformMiddleware) IsEnabledForSpec() bool {
var used bool
for _, thisVersion := range t.TykMiddleware.Spec.VersionData.Versions {
if len(thisVersion.ExtendedPaths.Transform) > 0 {
used = true
break
}
}
return used
}
// ProcessRequest will run any checks on the request on the way through the system, return an error to have the chain fail
func (t *TransformMiddleware) ProcessRequest(w http.ResponseWriter, r *http.Request, configuration interface{}) (error, int) {
// New request checker, more targetted, less likely to fail
var stat RequestStatus
var meta interface{}
var found bool
_, versionPaths, _, _ := t.TykMiddleware.Spec.GetVersionData(r)
found, meta = t.TykMiddleware.Spec.CheckSpecMatchesStatus(r.URL.Path, r.Method, versionPaths, Transformed)
if found {
stat = StatusTransform
}
if stat == StatusTransform {
thisMeta := meta.(*TransformSpec)
// Read the body:
defer r.Body.Close()
body, err := ioutil.ReadAll(r.Body)
// Put into an interface:
var bodyData interface{}
switch thisMeta.TemplateMeta.TemplateData.Input {
case tykcommon.RequestXML:
mxj.XmlCharsetReader = WrappedCharsetReader
var xErr error
bodyData, xErr = mxj.NewMapXml(body) // unmarshal
if xErr != nil {
log.WithFields(logrus.Fields{
"prefix": "inbound-transform",
"server_name": t.Spec.APIDefinition.Proxy.TargetURL,
"api_id": t.Spec.APIDefinition.APIID,
"path": r.URL.Path,
}).Error("Error unmarshalling XML: ", xErr)
}
case tykcommon.RequestJSON:
json.Unmarshal(body, &bodyData)
default:
// unset, assume an open field
bodyData = make(map[string]interface{})
}
if thisMeta.TemplateMeta.TemplateData.EnableSession {
ses := context.Get(r, SessionData).(SessionState)
switch bodyData.(type) {
case map[string]interface{}:
bodyData.(map[string]interface{})["_tyk_meta"] = ses.MetaData
}
}
if t.Spec.EnableContextVars {
contextData := context.Get(r, ContextData)
switch bodyData.(type) {
case map[string]interface{}:
bodyData.(map[string]interface{})["_tyk_context"] = contextData
}
}
// Apply to template
var bodyBuffer bytes.Buffer
err = thisMeta.Template.Execute(&bodyBuffer, bodyData)
if err != nil {
log.WithFields(logrus.Fields{
"prefix": "inbound-transform",
"server_name": t.Spec.APIDefinition.Proxy.TargetURL,
"api_id": t.Spec.APIDefinition.APIID,
"path": r.URL.Path,
}).Error("Failed to apply template to request: ", err)
}
r.Body = ioutil.NopCloser(&bodyBuffer)
r.ContentLength = int64(bodyBuffer.Len())
}
return nil, 200
}