Skip to content

Commit

Permalink
Fix multiple plugins when request method transformed (TykTechnologies…
Browse files Browse the repository at this point in the history
…#2070)

Our URL matching code depends on `r.Method`, but if the method gets transformed, some plugins which run in the chain after Method transform plugin, stop working.

To fix we remember the original request method, and use it instead.

Fix TykTechnologies#2069
  • Loading branch information
buger authored Jan 17, 2019
1 parent efa657a commit fd28e12
Show file tree
Hide file tree
Showing 5 changed files with 52 additions and 61 deletions.
13 changes: 13 additions & 0 deletions api.go
Original file line number Diff line number Diff line change
Expand Up @@ -1739,6 +1739,19 @@ func ctxGetUrlRewritePath(r *http.Request) string {
return ""
}

func ctxSetRequestMethod(r *http.Request, path string) {
setCtxValue(r, RequestMethod, path)
}

func ctxGetRequestMethod(r *http.Request) string {
if v := r.Context().Value(RequestMethod); v != nil {
if strVal, ok := v.(string); ok {
return strVal
}
}
return r.Method
}

func ctxGetDefaultVersion(r *http.Request) bool {
return r.Context().Value(VersionDefault) != nil
}
Expand Down
60 changes: 21 additions & 39 deletions api_definition.go
Original file line number Diff line number Diff line change
Expand Up @@ -973,10 +973,12 @@ func (a *APISpec) URLAllowedAndIgnored(r *http.Request, rxPaths []URLSpec, white

// CheckSpecMatchesStatus checks if a url spec has a specific status
func (a *APISpec) CheckSpecMatchesStatus(r *http.Request, rxPaths []URLSpec, mode URLStatus) (bool, interface{}) {
matchPath := r.URL.Path
if origURL := ctxGetOrigRequestURL(r); origURL != nil {
matchPath = origURL.Path
matchPath := ctxGetUrlRewritePath(r)
method := ctxGetRequestMethod(r)
if matchPath == "" {
matchPath = r.URL.Path
}

if !strings.HasPrefix(matchPath, "/") {
matchPath = "/" + matchPath
}
Expand All @@ -989,91 +991,71 @@ func (a *APISpec) CheckSpecMatchesStatus(r *http.Request, rxPaths []URLSpec, mod
if mode != v.Status {
continue
}
match := v.Spec.MatchString(matchPath)

// only return it it's what we are looking for
if !match {
// check for special case when using url_rewrites with transform_response
// and specifying the same "path" expression

if mode == TransformedResponse {
if v.TransformResponseAction.Path != ctxGetUrlRewritePath(r) {
continue
}
} else if mode == HeaderInjectedResponse {
if v.InjectHeadersResponse.Path != ctxGetUrlRewritePath(r) {
continue
}
} else if mode == TransformedJQResponse {
if v.TransformJQResponseAction.Path != ctxGetUrlRewritePath(r) {
continue
}
} else {
continue
}
if !v.Spec.MatchString(matchPath) {
continue
}

switch v.Status {
case Ignored, BlackList, WhiteList, Cached:
return true, nil
case Transformed:
if r.Method == v.TransformAction.Method {
if method == v.TransformAction.Method {
return true, &v.TransformAction
}
case TransformedJQ:
if r.Method == v.TransformJQAction.Method {
if method == v.TransformJQAction.Method {
return true, &v.TransformJQAction
}
case HeaderInjected:
if r.Method == v.InjectHeaders.Method {
if method == v.InjectHeaders.Method {
return true, &v.InjectHeaders
}
case HeaderInjectedResponse:
if r.Method == v.InjectHeadersResponse.Method {
if method == v.InjectHeadersResponse.Method {
return true, &v.InjectHeadersResponse
}
case TransformedResponse:
if r.Method == v.TransformResponseAction.Method {
if method == v.TransformResponseAction.Method {
return true, &v.TransformResponseAction
}
case TransformedJQResponse:
if r.Method == v.TransformJQResponseAction.Method {
if method == v.TransformJQResponseAction.Method {
return true, &v.TransformJQResponseAction
}
case HardTimeout:
if r.Method == v.HardTimeout.Method {
return true, &v.HardTimeout.TimeOut
}
case CircuitBreaker:
if r.Method == v.CircuitBreaker.Method {
if method == v.CircuitBreaker.Method {
return true, &v.CircuitBreaker
}
case URLRewrite:
if r.Method == v.URLRewrite.Method {
if method == v.URLRewrite.Method {
return true, v.URLRewrite
}
case VirtualPath:
if r.Method == v.VirtualPathSpec.Method {
if method == v.VirtualPathSpec.Method {
return true, &v.VirtualPathSpec
}
case RequestSizeLimit:
if r.Method == v.RequestSize.Method {
if method == v.RequestSize.Method {
return true, &v.RequestSize
}
case MethodTransformed:
if r.Method == v.MethodTransform.Method {
if method == v.MethodTransform.Method {
return true, &v.MethodTransform
}
case RequestTracked:
if r.Method == v.TrackEndpoint.Method {
if method == v.TrackEndpoint.Method {
return true, &v.TrackEndpoint
}
case RequestNotTracked:
if r.Method == v.DoNotTrackEndpoint.Method {
if method == v.DoNotTrackEndpoint.Method {
return true, &v.DoNotTrackEndpoint
}
case ValidateJSONRequest:
if r.Method == v.ValidatePathMeta.Method {
if method == v.ValidatePathMeta.Method {
return true, &v.ValidatePathMeta
}
}
Expand Down
1 change: 1 addition & 0 deletions handler_success.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ const (
TrackThisEndpoint
DoNotTrackThisEndpoint
UrlRewritePath
RequestMethod
OrigRequestURL
LoopLevel
LoopLevelLimit
Expand Down
18 changes: 6 additions & 12 deletions mw_method_transform.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,19 +34,13 @@ func (t *TransformMethod) ProcessRequest(w http.ResponseWriter, r *http.Request,
return nil, http.StatusOK
}
mmeta := meta.(*apidef.MethodTransformMeta)
toMethod := strings.ToUpper(mmeta.ToMethod)

ctxSetRequestMethod(r, r.Method)

switch strings.ToUpper(mmeta.ToMethod) {
case "GET":
r.Method = "GET"
case "POST":
r.Method = "POST"
case "PUT":
r.Method = "PUT"
case "DELETE":
r.Method = "DELETE"
case "OPTIONS":
r.Method = "OPTIONS"
case "PATCH":
r.Method = "PATCH"
case "GET", "POST", "PUT", "DELETE", "OPTIONS", "PATCH":
r.Method = toMethod
default:
return errors.New("Method not allowed"), http.StatusMethodNotAllowed
}
Expand Down
21 changes: 11 additions & 10 deletions res_handler_transform.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,36 +75,37 @@ func (h *ResponseTransformMiddleware) HandleResponse(rw http.ResponseWriter, res

_, versionPaths, _, _ := h.Spec.Version(req)
found, meta := h.Spec.CheckSpecMatchesStatus(req, versionPaths, TransformedResponse)

if !found {
return nil
}
tmeta := meta.(*TransformSpec)

respBody := respBodyReader(req, res)
body, _ := ioutil.ReadAll(respBody)
defer respBody.Close()

// Put into an interface:
bodyData := make(map[string]interface{})
switch tmeta.TemplateData.Input {
case apidef.RequestXML:
if len(body) == 0 {
body = []byte("<_/>")
}

mxj.XmlCharsetReader = WrappedCharsetReader
var err error

bodyBytes, err := ioutil.ReadAll(respBody)
if err != nil {
logger.WithError(err).Error("Error reading XML body")
// not returning error, just break out of switch.
break
}

bodyData, err = mxj.NewMapXml(bodyBytes) // unmarshal
bodyData, err = mxj.NewMapXml(body) // unmarshal
if err != nil {
logger.WithError(err).Error("Error unmarshalling XML")
}
default: // apidef.RequestJSON
if len(body) == 0 {
body = []byte("{}")
}

var tempBody interface{}
if err := json.NewDecoder(respBody).Decode(&tempBody); err != nil {
if err := json.Unmarshal(body, &tempBody); err != nil {
logger.WithError(err).Error("Error unmarshalling JSON")
}

Expand Down

0 comments on commit fd28e12

Please sign in to comment.