github.com/Tyktechnologies/tyk@v2.9.5+incompatible/gateway/mw_transform.go (about) 1 package gateway 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "fmt" 7 "io" 8 "io/ioutil" 9 "net/http" 10 11 "github.com/clbanning/mxj" 12 "golang.org/x/net/html/charset" 13 14 "github.com/TykTechnologies/tyk/apidef" 15 ) 16 17 func WrappedCharsetReader(s string, i io.Reader) (io.Reader, error) { 18 return charset.NewReader(i, s) 19 } 20 21 // TransformMiddleware is a middleware that will apply a template to a request body to transform it's contents ready for an upstream API 22 type TransformMiddleware struct { 23 BaseMiddleware 24 } 25 26 func (t *TransformMiddleware) Name() string { 27 return "TransformMiddleware" 28 } 29 30 func (t *TransformMiddleware) EnabledForSpec() bool { 31 for _, version := range t.Spec.VersionData.Versions { 32 if len(version.ExtendedPaths.Transform) > 0 { 33 return true 34 } 35 } 36 return false 37 } 38 39 // ProcessRequest will run any checks on the request on the way through the system, return an error to have the chain fail 40 func (t *TransformMiddleware) ProcessRequest(w http.ResponseWriter, r *http.Request, _ interface{}) (error, int) { 41 _, versionPaths, _, _ := t.Spec.Version(r) 42 found, meta := t.Spec.CheckSpecMatchesStatus(r, versionPaths, Transformed) 43 if !found { 44 return nil, http.StatusOK 45 } 46 err := transformBody(r, meta.(*TransformSpec), t.Spec.EnableContextVars) 47 if err != nil { 48 t.Logger().WithError(err).Error("Body transform failure") 49 } 50 return nil, http.StatusOK 51 } 52 53 func transformBody(r *http.Request, tmeta *TransformSpec, contextVars bool) error { 54 body, _ := ioutil.ReadAll(r.Body) 55 defer r.Body.Close() 56 57 // Put into an interface: 58 bodyData := make(map[string]interface{}) 59 60 switch tmeta.TemplateData.Input { 61 case apidef.RequestXML: 62 if len(body) == 0 { 63 body = []byte("<_/>") 64 } 65 mxj.XmlCharsetReader = WrappedCharsetReader 66 var err error 67 bodyData, err = mxj.NewMapXml(body) // unmarshal 68 if err != nil { 69 return fmt.Errorf("error unmarshalling XML: %v", err) 70 } 71 case apidef.RequestJSON: 72 if len(body) == 0 { 73 body = []byte("{}") 74 } 75 76 var tempBody interface{} 77 if err := json.Unmarshal(body, &tempBody); err != nil { 78 return err 79 } 80 81 switch tempBody.(type) { 82 case []interface{}: 83 bodyData["array"] = tempBody 84 case map[string]interface{}: 85 bodyData = tempBody.(map[string]interface{}) 86 } 87 default: 88 return fmt.Errorf("unsupported request input type: %v", tmeta.TemplateData.Input) 89 } 90 91 if tmeta.TemplateData.EnableSession { 92 if session := ctxGetSession(r); session != nil { 93 bodyData["_tyk_meta"] = session.GetMetaData() 94 } else { 95 log.Error("Session context was enabled but not found.") 96 } 97 } 98 99 if contextVars { 100 bodyData["_tyk_context"] = ctxGetData(r) 101 } 102 103 // Apply to template 104 var bodyBuffer bytes.Buffer 105 if err := tmeta.Template.Execute(&bodyBuffer, bodyData); err != nil { 106 return fmt.Errorf("failed to apply template to request: %v", err) 107 } 108 r.Body = ioutil.NopCloser(&bodyBuffer) 109 r.ContentLength = int64(bodyBuffer.Len()) 110 nopCloseRequestBody(r) 111 112 return nil 113 }