Kong: [request] Allow mapping arbitrary request headers to upstream targets

Created on 15 Jul 2015  路  18Comments  路  Source: Kong/kong

I would like to use Kong as a reverse proxy without the need to change the host header. Essentially I want Kong to use an arbitrary header of my choosing to "map" the request to the appropriate upstream target.

Say this was implemented and I added the API http://api.example.com/weather to Kong with the header to look for called service then I would be able to make the request:

curl http://kong:8000?location=USA -H "service: weather"

which would map to the upstream target http://api.example.com/weather

I think this may be related to #203 except it let's the user choose the header to replace host with at the time of creating the API.

proposal

Most helpful comment

Hey all,

Yes, gladly! Disclaimer: as of today, it does require a little bit of tinkering considering the API behind custom routing (from plugins) is not stable and hence, not public (nor officially documented). One could find out about it by browsing the source code (not ideal), and we also point out users to it when they ask about it. We do have plans to make this API stable and user-friendly (as in, Lua function to call for custom request rewriting) and documented.

Now, onto the relevant bits.

One of the most recent mentions of this API is in this Kong Nation message, which details the relevant variable to update the upstream URL, as well as some state exposed to plugins with regards to how a request was matched. The later is particularly relevant since it provides information to the plugin such as the host of the matched API (particularly relevant if it is set to a wildcard, like *.foo.com, which would match a Host header such as bar.foo.com. It also exposes URI regex capture groups, for URIs such as /users/(?<id>\d+)/profile, where ngx.ctx.uri_captures.id would be set). For changing other parts of the request, Kong ensures to integrate properly with the ngx_lua API, so refer to the ngx_lua documentation for this. All updates must be done in the access phase of a plugin, for obvious reasons.

To sum it up the various parts of the request you can change before a request is proxied upstream:

  • upstream scheme: ngx.var.upstream_scheme
  • URL: ngx.var.upstream_uri
  • querystring: ngx_lua's ngx.req.set_uri_args()
  • Host header: ngx.var.upstream_host
  • Other headers: ngx.req.set_header()
  • Body: ngx.req.set_body_data() or other similar APIs
  • TLS SNI: ngx.var.upstream_host as well

Once this logic is implemented in a plugin, said plugin must also be executed during a request. Either configure the plugin as a global plugin, or configure it on a particular API object. The plugin then kicks in and overwrites the values set by Kong Core and redirects the request elsewhere.

There are a lot of possibilities, pretty much everything that Kong does can be done in a plugin (even advanced load balancing, retries, etc...). The upcoming Lua API for such custom routing plugins will also expose Kong's circuit-breaker API, which can provide period healthchecks, healthy/unhealthy state of upstreams, etc... It will be easier to use once all of this gets formalized into a proper, documented API.

All 18 comments

"let's the user choose the header to replace host" is done in https://github.com/haifengkao/kong_proxy
It still lacks a lot of forward proxy features. Feel free to send pull requests.

+1
The problem I am facing is that I need to route the requests from legacy clients and I can't just force them to include the 'host' header that is required by Kong. These old clients will send some specific headers that we have no control of and that's how we identify them to be part of the legacy stack.

It is really a shame that the routing is handled by Kong's core. I suppose the addition of a plugin that exposes an arbitrary header to drive the routing would not be possible then. @haifengkao nice work though but I would endup with unsupported code that I would have to maintain myself.

@mv2a Why not? It is definitely possible and already done in various production setups.

@thibaultcha could you please point us to some example of this? Also interested in implementing custom routing logic.

To elaborate on my use case: we'd like to do custom canary releases, since we have requirements that are incompatible with the weighted canary rollout that Kong supports out-of-box. (We're used by university professors - we need to keep a prof and their class of students in the same backend version, so we want to incrementally rollout by groups of users rather than a selection of traffic. We can do this by inspecting a header, if we have some way to extend Kong with this logic.)

yeap, me too. Can you give us an example of a custom routing logic implementation that doesn't touch Kong's core?

Solution?

Hey all,

Yes, gladly! Disclaimer: as of today, it does require a little bit of tinkering considering the API behind custom routing (from plugins) is not stable and hence, not public (nor officially documented). One could find out about it by browsing the source code (not ideal), and we also point out users to it when they ask about it. We do have plans to make this API stable and user-friendly (as in, Lua function to call for custom request rewriting) and documented.

Now, onto the relevant bits.

One of the most recent mentions of this API is in this Kong Nation message, which details the relevant variable to update the upstream URL, as well as some state exposed to plugins with regards to how a request was matched. The later is particularly relevant since it provides information to the plugin such as the host of the matched API (particularly relevant if it is set to a wildcard, like *.foo.com, which would match a Host header such as bar.foo.com. It also exposes URI regex capture groups, for URIs such as /users/(?<id>\d+)/profile, where ngx.ctx.uri_captures.id would be set). For changing other parts of the request, Kong ensures to integrate properly with the ngx_lua API, so refer to the ngx_lua documentation for this. All updates must be done in the access phase of a plugin, for obvious reasons.

To sum it up the various parts of the request you can change before a request is proxied upstream:

  • upstream scheme: ngx.var.upstream_scheme
  • URL: ngx.var.upstream_uri
  • querystring: ngx_lua's ngx.req.set_uri_args()
  • Host header: ngx.var.upstream_host
  • Other headers: ngx.req.set_header()
  • Body: ngx.req.set_body_data() or other similar APIs
  • TLS SNI: ngx.var.upstream_host as well

Once this logic is implemented in a plugin, said plugin must also be executed during a request. Either configure the plugin as a global plugin, or configure it on a particular API object. The plugin then kicks in and overwrites the values set by Kong Core and redirects the request elsewhere.

There are a lot of possibilities, pretty much everything that Kong does can be done in a plugin (even advanced load balancing, retries, etc...). The upcoming Lua API for such custom routing plugins will also expose Kong's circuit-breaker API, which can provide period healthchecks, healthy/unhealthy state of upstreams, etc... It will be easier to use once all of this gets formalized into a proper, documented API.

Oh, and reading headers from a plugin is the usual ngx.req.get_headers() or ngx.var.http_[header_name].

I think it's a necessary function to make api take custom header to decide to choose which upstream. Oftentimes, services are more likely to put stuff in cookies/headers instead in the requested url.

Now that it鈥檚 been a few months, have there been any changes in Kong to make this easier (such as making the private methods above public)? Does anyone have experiences with this they can share?

@aehlke We are actively working on what we call a Plugins SDK that formalizes a set of Lua functions to do this from plugins. Stay tuned, we should have updates in the next few weeks.

@thibaultcha thanks for the update back then; I'm not sure I can find the "Plugins SDK" now, did it end up being introduced?

"Plugins SDK" became the Plugin Development Kit (PDK) - lots of info here https://docs.konghq.com/0.14.x/pdk/

@aehlke Sorry for not following up here! Yes, thank you @coopr, as you said, the Plugins SDK became the Plugin Development Kit when it was released in 0.14.0.

It commodities operations that I described in my earlier reply, such as interacting with the downstream and upstream requests/responses. E.g. updating the URL in the upstream request, or grabbing the host header from the client鈥檚 request, etc... It also comes with other commodities for various common operations done by the gateway, which is useful for developers not entirely familiar with Lua.
Please check it out, and let us know how we can improve it :)

Thanks very much! We will be using this to control canary rollout based on user segments. We are a few months away from starting this so will try to follow up again with feedback.

Support for routing by arbitrary headers has been merged in Kong next and will be available in the next release!

PR for reference: #4758

Was this page helpful?
0 / 5 - 0 ratings

Related issues

daviesf1 picture daviesf1  路  39Comments

sonicaghi picture sonicaghi  路  39Comments

throrin19 picture throrin19  路  39Comments

SunshineYang picture SunshineYang  路  39Comments

sonicaghi picture sonicaghi  路  47Comments