Skip to content

Commit

Permalink
fix plugins (TykTechnologies#2923)
Browse files Browse the repository at this point in the history
Fixes TykTechnologies#2922 

In 2.8.x URL Rewrite/Method transform middleware directly modified http.Request object.
This behavior was changed in 2.9.x(https://github.com/TykTechnologies/tyk/pull/2301/files) 
http.Request is now transformed at the end of middleware chain. Because of this Post hook did not received modified values.

Fixed the code by passing URLRewrite/Method Transform info to Post hook of the plugin.
  • Loading branch information
komalsukhani committed Mar 12, 2020
1 parent 34cf3a9 commit b4166dd
Show file tree
Hide file tree
Showing 3 changed files with 120 additions and 4 deletions.
78 changes: 78 additions & 0 deletions coprocess/python/coprocess_python_test.go
Expand Up @@ -11,6 +11,7 @@ import (
"testing"
"time"

"github.com/TykTechnologies/tyk/apidef"
"github.com/TykTechnologies/tyk/config"
"github.com/TykTechnologies/tyk/gateway"
"github.com/TykTechnologies/tyk/test"
Expand Down Expand Up @@ -98,6 +99,41 @@ def MyPostHook(request, session, spec):
`,
}

var pythonPostRequestTransform = map[string]string{
"manifest.json": `
{
"file_list": [
"middleware.py"
],
"custom_middleware": {
"driver": "python",
"post": [{
"name": "MyPostHook"
}]
}
}
`,
"middleware.py": `
from tyk.decorators import *
from gateway import TykGateway as tyk
import json
@Hook
def MyPostHook(request, session, spec):
if request.object.url == "/test2":
if request.object.method != "POST":
request.object.return_overrides.response_code = 500
request.object.return_overrides.response_error = "'invalid method type'"
return request, session
request.object.url = "tyk://test-api-2/newpath"
request.object.method = "GET"
return request , session
`,
}

var pythonBundleWithPreHook = map[string]string{
"manifest.json": `
{
Expand Down Expand Up @@ -182,6 +218,7 @@ func TestPythonBundles(t *testing.T) {
postHookBundle := gateway.RegisterBundle("python_with_post_hook", pythonBundleWithPostHook)
preHookBundle := gateway.RegisterBundle("python_with_pre_hook", pythonBundleWithPreHook)
responseHookBundle := gateway.RegisterBundle("python_with_response_hook", pythonBundleWithResponseHook)
postRequestTransformHookBundle := gateway.RegisterBundle("python_post_with_request_transform_hook", pythonPostRequestTransform)

t.Run("Single-file bundle with authentication hook", func(t *testing.T) {
gateway.BuildAndLoadAPI(func(spec *gateway.APISpec) {
Expand Down Expand Up @@ -295,4 +332,45 @@ func TestPythonBundles(t *testing.T) {
{Path: "/test-api-2/", Code: http.StatusOK, Data: "{}", Headers: map[string]string{"Content-Type": "application/json"}},
}...)
})

t.Run("python post hook with url rewrite and method transform", func(t *testing.T) {
gateway.BuildAndLoadAPI(func(spec *gateway.APISpec) {
spec.Proxy.ListenPath = "/test-api-1/"
spec.UseKeylessAccess = true
spec.EnableCoProcessAuth = false
spec.CustomMiddlewareBundle = postRequestTransformHookBundle

v1 := spec.VersionData.Versions["v1"]
v1.UseExtendedPaths = true
v1.ExtendedPaths.URLRewrite = []apidef.URLRewriteMeta{{
Path: "/get",
Method: http.MethodGet,
MatchPattern: "/get",
RewriteTo: "/test2",
}}

v1.ExtendedPaths.MethodTransforms = []apidef.MethodTransformMeta{{
Path: "/get",
Method: http.MethodGet,
ToMethod: http.MethodPost,
}}

spec.VersionData.Versions["v1"] = v1

}, func(spec *gateway.APISpec) {
spec.Name = "test-api-2"
spec.Proxy.ListenPath = "/test-api-2/"
spec.UseKeylessAccess = true
spec.EnableCoProcessAuth = false
spec.UseKeylessAccess = true
})

time.Sleep(1 * time.Second)

ts.Run(t, []test.TestCase{
{Path: "/test-api-1/get", Code: http.StatusOK, BodyMatch: "newpath"},
{Path: "/test-api-1/get", Code: http.StatusOK, BodyMatch: "GET"},
{Path: "/test-api-1/post", Code: http.StatusOK, BodyNotMatch: "newpath"},
}...)
})
}
44 changes: 41 additions & 3 deletions gateway/coprocess.go
Expand Up @@ -172,11 +172,13 @@ func (c *CoProcessor) BuildObject(req *http.Request, res *http.Response) (*copro
}

// ObjectPostProcess does CoProcessObject post-processing (adding/removing headers or params, etc.).
func (c *CoProcessor) ObjectPostProcess(object *coprocess.Object, r *http.Request) (err error) {
func (c *CoProcessor) ObjectPostProcess(object *coprocess.Object, r *http.Request, origURL string, origMethod string) (err error) {
r.ContentLength = int64(len(object.Request.RawBody))
r.Body = ioutil.NopCloser(bytes.NewReader(object.Request.RawBody))
nopCloseRequestBody(r)

logger := c.Middleware.Logger()

for _, dh := range object.Request.DeleteHeaders {
r.Header.Del(dh)
}
Expand All @@ -194,11 +196,34 @@ func (c *CoProcessor) ObjectPostProcess(object *coprocess.Object, r *http.Reques
values.Set(p, v)
}

r.URL, err = url.ParseRequestURI(object.Request.Url)
parsedURL, err := url.ParseRequestURI(object.Request.Url)
if err != nil {
logger.Error(err)
return
}

rewriteURL := ctxGetURLRewriteTarget(r)
if rewriteURL != nil {
ctxSetURLRewriteTarget(r, parsedURL)
r.URL, err = url.ParseRequestURI(origURL)
if err != nil {
logger.Error(err)
return
}
} else {
r.URL = parsedURL
}

transformMethod := ctxGetTransformRequestMethod(r)
if transformMethod != "" {
ctxSetTransformRequestMethod(r, object.Request.Method)
r.Method = origMethod
} else {
r.Method = object.Request.Method
}

r.URL.RawQuery = values.Encode()

return
}

Expand Down Expand Up @@ -300,6 +325,19 @@ func (m *CoProcessMiddleware) ProcessRequest(w http.ResponseWriter, r *http.Requ
return errors.New("Middleware error"), 500
}

var origURL string
if rewriteUrl := ctxGetURLRewriteTarget(r); rewriteUrl != nil {
origURL = object.Request.Url
object.Request.Url = rewriteUrl.String()
object.Request.RequestUri = rewriteUrl.RequestURI()
}

var origMethod string
if transformMethod := ctxGetTransformRequestMethod(r); transformMethod != "" {
origMethod = r.Method
object.Request.Method = transformMethod
}

t1 := time.Now()
returnObject, err := coProcessor.Dispatch(object)
ms := DurationToMillisecond(time.Since(t1))
Expand All @@ -315,7 +353,7 @@ func (m *CoProcessMiddleware) ProcessRequest(w http.ResponseWriter, r *http.Requ

m.logger.WithField("ms", ms).Debug("gRPC request processing took")

err = coProcessor.ObjectPostProcess(returnObject, r)
err = coProcessor.ObjectPostProcess(returnObject, r, origURL, origMethod)
if err != nil {
// Restore original URL object so that it can be used by ErrorHandler:
r.URL = originalURL
Expand Down
2 changes: 1 addition & 1 deletion gateway/server.go
Expand Up @@ -151,7 +151,7 @@ func getApisForOauthClientId(oauthClientId string) []string {

//generate a copy only with ids so we do not attempt to lock twice
apisMu.RLock()
for apiId, _ := range apisByID {
for apiId := range apisByID {
apisIdsCopy = append(apisIdsCopy, apiId)
}
apisMu.RUnlock()
Expand Down

0 comments on commit b4166dd

Please sign in to comment.