Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(router) new API router with multiple hosts/paths and HTTP methods support #1970

Merged
merged 39 commits into from
Jan 12, 2017

Conversation

thibaultcha
Copy link
Member

@thibaultcha thibaultcha commented Jan 11, 2017

Summary

Implement a new router (previously called "resolver", but renamed to avoid confusion with the new DNS resolver integrated in Kong in 0.9). This new router allows for an API to be defined by multiple Host headers and/or URI prefixes, as well as making routing decisions from the request's HTTP method. This new router should also perform better than the previous implementation and offer better maintainability for future features such as regex matching or path segments extraction in URIs.

The new router module can be reviewed at kong/core/router.lua.

Additionally, this removes the ssl plugin and incorporates the dynamic SSL certificates serving into the core. The reason behind this reasoning is that in Kong, plugins are applied to APIs, and thus, the router first needs to determine which API a given request targets, before knowing what plugins apply to this request. However, this new router offers routing capabilities based on URIs and HTTP methods, that are not available to us at the time of the SSL handshake with the client. This was already the case with the previous router (resolver), but this flaw is not taken care of. Dynamic certificates serving is now ensured via SNI and the appropriate entities (certificates and SNIs) are configurable via the Admin API.

How it works

APIs no longer have request_host and request_path properties, and the strip_request_path property was renamed to strip_uri. Instead, one could register an API with the following schema:

{
  "name": "api-1",
  "upstream_url": "http://httpbin.org",
  "hosts": ["example.com", "service.com"],
  "uris": ["/foo", "/bar"],
  "methods": ["GET"]
}

We notice the hosts, uris and methods fields. For an incoming request to be routed to a given API, all three of these conditions must be met. When a field has multiple values, any of those values makes the condition pass.

Examples of matching requests:

GET /foo HTTP/1.1
Host: example.com

GET /bar HTTP/1.1
Host: service.com

GET /foo HTTP/1.1
Host: example.com

Example of non-matching requests:

GET / HTTP/1.1
Host: example.com

POST /foo HTTP/1.1
Host: service.com

GET /foo HTTP/1.1
Host: foo.com

Routing priorities

The rule of thumb when registering multiple APIs with various properties is that the API with the most matching rules will be chosen over one with less matching rules. Example:

API 1:

{
  "name": "api-1",
  "upstream_url": "http://foo.com",
  "hosts": ["foo.com"]
}

API 2:

{
  "name": "api-2",
  "upstream_url": "http://bar.com",
  "hosts": ["foo.com"],
  "methods": ["POST"]
}

This request will match api-1:

GET / HTTP/1.1
Host: foo.com

This one will match api-2, as more rules are matching api-2 (host + method) than api-1:

POST / HTTP/1.1
Host: foo.com

Routing by method only

Starting with this router, one can also route an API like so:

$ curl -X POST http://localhost:8001/apis \
  -d "name=my-api" \
  -d "upstream_url=http://httpbin.org" \
  -d "methods=GET,HEAD"
HTTP/1.1 201 Created
...
# make request
$ curl -X HEAD http://localhost:8000/anything \
  -H "Host: anything.com" 

This assumes that another API does not supersedes this one in a given request, in case such another API would have more matching rules compared to ours, as previously described in the priorities section.

Implementing a fallback API

In the continuity of the previous example, one can now easily implement fallback APIs for various cases (all POST methods that didn't match any other API, for example).

We can also imagine an API that would match any incoming request (that is, requests that didn't match another API with more rules):

{
  "name": "root-fallback",
  "upstream_url": "http://httpbin.org",
  "uris": ["/"]
}

It should be clear that all incoming requests will match the / URI prefix. Hence, this API can act as a fallback to all requests not matching any other API.

How dynamic SSL now works

APIs also have new fields:

  • https_only: incoming matching requests will not be proxied if not made over TLS (aka, on the 8000 or 80 port). Kong will return HTTP 426 Upgrade.
  • http_if_terminated: will allow the request over plain HTTP if the request was previously terminated (X-Forwarded-Proto: https).

Assuming the following API:

{
  "name": "my-ssl-api",
  "upstream_url": "http://httpbin.org",
  "hosts": ["ssl-example.com", "other-ssl-example.com"],
  "https_only": true
}

We should now configure a certificate (in PEM format) and an SNI of value "ssl-example.com", which will indicate that SSL handshake with the given SNI should serve our configured certificate:

$ curl -X POST http://localhost:8001/certificates \
  -d "cert=@/path/to/cert.pem" \
  -d "key=@/path/to/cert.key" \
  -d "snis=ssl-example.com,other-ssl-example.com"

The snis form parameter is a sugar parameter, directly inserting an SNI and linking it to the provided certificate. The endpoint to manage SNIs is available on the Admin API as /snis.

We can now make an HTTPS request to proxy this API through Kong (your client needs to support the SNI extension):

$ curl https://localhost:8443/ \
  -H "Host: ssl-example.com"

Issues resolved

Implement #369
Addresses #845
Addresses #847
Partially addresses #505

@thibaultcha thibaultcha added this to the 0.10 RC milestone Jan 11, 2017
@thibaultcha thibaultcha changed the title feat(router) new API router with multiple hosts/paths and HTTP method s support feat(router) new API router with multiple hosts/paths and HTTP methods support Jan 11, 2017
This allows shorter URIs to be evaluated last, solving the "/" URI used
as a fallback (it is now evaluated after URIs containing a path such as
"/foo"). Same for nested path: "/foo/bar" is evaluated before "/foo",
etc.
Also disable tests relying on 'ALL_APIS_BY_DICT' cache
thibaultcha and others added 15 commits January 11, 2017 16:27
* adds loadbalancing on specified targets
* adds service registry
* implements #157
* adds entities: upstreams and targets
* modifies timestamps to millisecond precision (except for the non-related tables when using postgres)
* adds collecting health-data on a per-request basis (unused for now)
We also remember why the router was not created after an invalidation
event so further failed routings will show the reason in the error logs.
@thibaultcha thibaultcha merged commit 43dc39c into next Jan 12, 2017
@thibaultcha thibaultcha deleted the feat/new-router branch January 12, 2017 00:34
@ghost
Copy link

ghost commented Jan 13, 2017

great job

amal-v added a commit to amal-v/kong-vagrant that referenced this pull request Feb 15, 2017
Updating the request params as the names changed in newer version of Kong (^0.10.0). 
Reference : Kong/kong#1970
@Vad1mo Vad1mo mentioned this pull request Apr 3, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants