Global request headers injection doesn't work after looping

Global request headers injection doesn’t work after looping

APIDEF1:

{
  "name": "Tyk Keyless Looping API",
  "api_id": "keyless-looping-test",
  "org_id": "default",
  "definition": {
    "location": "header",
    "key": "version"
  },
  "use_keyless": true,
  "version_data": {
    "not_versioned": true,
    "versions": {
      "Default": {
        "name": "Default",
        "global_headers": {
          "X-looping1": "someValue"
        }
      }
    }
  },
  "driver": "otto",
  "disable_rate_limit": true,
  "proxy": {
    "listen_path": "/keyless-looping-test/",
    "target_url": "tyk://keyless-test",
    "strip_listen_path": true
  }
}

APIDEF2:

{
  "name": "Tyk Test Keyless API",
  "api_id": "keyless-test",
  "org_id": "default",
  "definition": {
    "location": "header",
    "key": "version"
  },
  "use_keyless": true,
  "version_data": {
    "not_versioned": true,
    "versions": {
      "Default": {
        "name": "Default",
        "global_headers": {
          "X-looping2": "ThisWillNotBeInjected"
        }
      }
    }
  },
  "driver": "otto",
  "disable_rate_limit": true,
  "proxy": {
    "listen_path": "/keyless-test/",
    "target_url": "http://httpbin.org",
    "strip_listen_path": true
  }
}

  1. API1 is looping to API2.
  2. API1 can be called using curl -s 'http://localhost:8080/keyless-looping-test/get' (tyk running on port 8080)
  3. In output we can see that header from API1 is injected but API2 failed to inject any header (is this a bug?)
{
  "args": {}, 
  "headers": {
    "Accept": "*/*", 
    "Accept-Encoding": "gzip", 
    "Host": "httpbin.org", 
    "User-Agent": "curl/7.81.0", 
    "X-Amzn-Trace-Id": "Root=1-634fa839-5451453109031d7c04505e55", 
    "X-Looping1": "someValue"
  }, 
  "origin": "172.28.0.1, externalip", 
  "url": "http://httpbin.org/get"
}

Note that header injection from API2 works fine If I remove looping and replace tyk://keyless-test in API1 to http://localhost:8080/keyless-test

Tyk version: v4.2.2

Actual issue: I am looping from keyless API1 to API2 (that is using custom authentication gRPC plugin.) I want to inject headers in API2 without writing another middleware. Note that custom Auth middleware used by API2 is already setting Metadata that I want to inject in upsteam request headers

@armujahid Thanks for spotting this.

I skimmed through sections where looping was used in the codebase and I can see it was written for URL-rewrite use cases in mind. If you try it with the URL-rewrite middleware, you should get the intended result.

{
	"name": "Tyk Keyless Looping API",
	"api_id": "keyless-looping-test",
	"org_id": "default",
	"definition": {
		"location": "header",
		"key": "version"
	},
	"use_keyless": true,
	"version_data": {
		"not_versioned": true,
		"versions": {
			"Default": {
				"name": "Default",
				"global_headers": {
					"X-looping1": "someValue"
				},
				"use_extended_paths": true,
				"extended_paths": 
				{
					"url_rewrites": [
              {
                "path": ".*",
                "method": "GET",
                "match_pattern": "(.*)/(.*)",
                "rewrite_to": "tyk://keyless-test/$2",
                "triggers": []
              }
            ]
				}
			}
		}
	},
	"disable_rate_limit": true,
	"proxy": {
		"listen_path": "/keyless-looping-test/",
		"target_url": "http://blank",
		"strip_listen_path": true
	}
}

I’ll let the engineering team know that middleware is being skipped when looping is used in the target URL.

Let us know if it works for you.

1 Like

Thanks. url_rewrites approach works but only for GET method as configured. Can I easily configure it for every HTTP verb without adding seperate objects for each verb? Just like how mw_modify_headers works.

I have created this github issue linking this post

I don’t think so. You would have to add an extra object as shown below

{
...
    "url_rewrites": [
              {
                "path": "/json",
                "method": "GET",
                "match_pattern": "/json",
                "rewrite_to": "https://<upstream-url>",
                "triggers": []
              },
              {
                "path": "/json",
                "method": "POST",
                "match_pattern": "/json",
                "rewrite_to": "https://<backend-url>",
                "triggers": []
              }
    ]
...
}

I am a but surprised about the structured of this. Can you share how this section looks in your API definition?

Thanks for creating this. I have attached the internal ticket number to it so it can be tracked.

1 Like

I am using exact structure shared by you and yes I am adding extra objects per HTTP verb like this

        "use_extended_paths": true,
        "extended_paths": {
          "url_rewrites": [
            {
              "path": ".*",
              "method": "GET",
              "match_pattern": "/([^\/]*)\/(.*)",
              "rewrite_to": "tyk://keyless-test/$2",
              "triggers": []
            },
            {
              "path": ".*",
              "method": "POST",
              "match_pattern": "/([^\/]*)\/(.*)",
              "rewrite_to": "tyk://keyless-test/$2",
              "triggers": []
            }
          ]
        }

And this works perfectly fine :slight_smile: I was referring to mw_modify_headers.go in tyk’s code in my previous reply that handles GlobalHeaders and GlobalHeadersRemove irrespective of any HTTP verb

Update: I am using a different match_pattern is different to support nested routes

Issue discovered in v4.2.2 and fixed from v4.2.4.

1 Like