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

[gateway] route requests by header param instead of context-path #503

Closed
Yaytay opened this issue Mar 20, 2017 · 38 comments

Comments

@Yaytay
Copy link

commented Mar 20, 2017

Hi,
Is it possible for gravitee to route by HOST?
We have multiple services, all with unique domain names. The HOST header alone is sufficient to identify the rules required for each service.
I can't work out whether the existing Multi-Tenancy support provides the ability to make an API available over a single HOST only.
Thanks.

@brasseld

This comment has been minimized.

Copy link
Member

commented Mar 20, 2017

Hi @Yaytay,

What do you mean by:

We have multiple services, all with unique domain names.

Is there an API for every service ? Or, you want to expose only one API and dispatch calls to multiple services ?

@Yaytay

This comment has been minimized.

Copy link
Author

commented Mar 20, 2017

Hi,
I'm going to leave out the term API because it's ambiguous, we have multiple domain names, each of which is serviced by a single internal HTTP endpoint (well, typically by two identical pairs for failover).
On that endpoint there could be any number of paths that do different things, and we'll want to have different permissions on some subsets of those paths.
But what we don't want is for either the external client or the internal endpoint to have to have additional path components solely for gravitee to route correctly.
Is that clearer?

I'm pretty sure we could do what we want with a graivitee instance for each domain name, but that would be overkill (we're only little).

Thanks.

@brasseld

This comment has been minimized.

Copy link
Member

commented Mar 21, 2017

Hi @Yaytay,

Yes it's perfectly clear, thanks for detailed explanations.

So, finally, to resume previous comments, you want to route HTTP requests using Host header instead of using a context-path because you don't want to have additional path components.

There is currently no way to achieve this using Gravitee.io APIM but, IMHO, it is very interesting to add such a feature.

This way, you will be able to do something like this without adding any additional path:

curl -X GET -H 'Host: service1.domain' 'https://gravitee_gateway/'
> Route to service 1

curl -X GET -H 'Host: service2.domain' 'https://gravitee_gateway/'
> Route to service 2
@brasseld

This comment has been minimized.

Copy link
Member

commented Mar 21, 2017

One more question:

Does the Host header must be part of the APIconfiguration and is just replacing the context-path mapping (we can also keep both way to map incoming HTTP requests) ?

@Yaytay

This comment has been minimized.

Copy link
Author

commented Mar 21, 2017

Hi @brasseld ,

Yes.
There might be situations in which one would want to route by both context-path and Host (our current approach does an exact match on the host and then a regex on the path, but I have to replace our current approach :)).

Thanks.

@NicolasGeraud

This comment has been minimized.

Copy link
Member

commented Mar 21, 2017

Hi @Yaytay ,
Be careful of how you design your apis inside Gravitee.io .

In your use case, do Service-1 and 2 manage the same resources types (for example products) or do they manage different resources (for example products and customers) ?

If your are in case 2, you should declare 2 different apis in Gravitee.io, an route your client requests to the good api based on the host header with your internal proxy, not with Gravitee.io

Question is : are you in case 1 or 2 ?

Thanks.

@Yaytay

This comment has been minimized.

Copy link
Author

commented Mar 21, 2017

Hi @NicolasGeraud ,

The second case, the two services are entirely independent.
I do plan to define them as different APIs in Gravitee, I just want Gravitee to know which API a request is for based on the Host rather than the path.

Do you mean we should run another load-balancing proxy inside Gravitee?
Why?

Thanks

@NicolasGeraud

This comment has been minimized.

Copy link
Member

commented Mar 21, 2017

not inside Gravitee.

you probably have a proxy in front of Gravitee.io gateways.
I don't know exactly your use case and your infrastructure, but you could rewrite urls based on host header and load balance requests to your Gravitee.io gateways.

This way, you could declare an api in Gravitee.io (which could have multiple endpoints) which manage only one type of resources.

Do you know what I mean ?

@Yaytay

This comment has been minimized.

Copy link
Author

commented Mar 21, 2017

I do, thanks, and I could do that as a workaround, but are you saying that this approach is better than having Gravitee be able to make that distinction without having to rewrite the path in the outer proxy?

@NicolasGeraud

This comment has been minimized.

Copy link
Member

commented Mar 21, 2017

The goal of Gravitee is not to have a single api and hundreds of paths representing all your endpoints services.

2 mains reasons:

  • manage access (apikey and memberships) and policies become very complex.
  • analytics will not be relevant because they are global for all your services.
@Yaytay

This comment has been minimized.

Copy link
Author

commented Mar 21, 2017

Ah, I don't want to have a single Gravitee API, I'm more than happy to set up a different API for each HOST.
Maybe it would be more accurate to say that I want API selection based on HOST.

@NicolasGeraud

This comment has been minimized.

Copy link
Member

commented Mar 21, 2017

Ok great. I better understand now.
I rename this issue as a feature request

@NicolasGeraud NicolasGeraud changed the title Can I route by HOST? [gateway] route requests by header param instead of context-path Mar 21, 2017
@NicolasGeraud

This comment has been minimized.

Copy link
Member

commented Mar 21, 2017

We have to allow more than one context path and/or hostheader for an api.
Use case is :

  • you have 2 apis (endpoints) : tomatoes and potatoes
  • you want to expose the following urls :
    • http://tomatoes.mycompany.com
    • http://api.mycompany.com/tomatoes
    • http://potatoes.mycompany.com
    • http://api.mycompany.com/potatoes

In Gravitee you want to create 2 apis with the folowing configuration:

  • context-path: /potatoes
  • Host: potatoes

and

  • context-path: /tomatoes
  • Host: tomatoes
@NicolasGeraud

This comment has been minimized.

Copy link
Member

commented Mar 21, 2017

With the combination of Host and context-path, we could have 2 "product" apis for 2 different services :

  • http://retail.company.com/products
  • http://bi.company.com/products

2 apis with 2 different host and the same context-path.

This means that :

  • the gateway has to check Host before context-path
  • all apis has /as default context-path
  • collision detection must covered Host and context-path by Host
@NicolasGeraud

This comment has been minimized.

Copy link
Member

commented Mar 21, 2017

@Yaytay do you have a use case where the same api (for Gravitee point of view) needs to be reached with different Host ?

@Yaytay

This comment has been minimized.

Copy link
Author

commented Mar 21, 2017

@NicolasGeraud I don't.
For me the host will identify the service, which would be a set of APIs.

I can think of two possible situations where someone (not me) might want multiple hosts with a single API:

  1. The multitenancy situation, where each tenant is on a different domain but accessing the same APIs.
    I think it would be OK to support either (not both) multi-tenancy or host-based selection for a single API.

  2. Overloaded DNS names to catch typos (like mircosoft.com).
    They ought to be fixed by redirecting outside of Gravitee.

@Yaytay

This comment has been minimized.

Copy link
Author

commented Aug 30, 2017

I've been thinking about this, and one way to handle it would be:

  • Permit multiple APIs to have the same context path (in some cases this could be "/").
  • Add Conditions to the APIs that can carry out tests on the Request.
  • Select an API by:
  1. Finding all APIs that match the context path.
  2. Filter out those that fail any conditions.
  3. Choose the longest matching context path from those that are left.

It would be possible to have two APIs with the same context path and the same conditions, but that would be stupid so the one that would be used would be indeterminate in that case.

The back end code to handle this would be quite simple, but it would require UI too.

@Yaytay

This comment has been minimized.

Copy link
Author

commented Aug 30, 2017

I do now have a use case where the same api (for Gravitee point of view) needs to be reached with different Hosts.

We have multiple systems that share microservices from the front end.
Safari is very stringent about accessing HTTP endpoints that are not on the same domain as the main page, which has caused us complications with headers and/or cookies being unusable.
The solution is to have the service available at service.system1.com for system1 and service.system2.com for system2.

@Yaytay

This comment has been minimized.

Copy link
Author

commented Aug 31, 2017

Looking into this a bit further I think there is a fundamental problem with doing it - I think you auto generate an ApiReactorHandler per API (per context path).
Any implementation that solves this issue would need a single "ReactorHandler" that would then pass on the call to the appropriate API handler.

So quite a key architectural change and I don't know what the impact of that would be (could certainly make the dynamic config more awkward).

@brasseld

This comment has been minimized.

Copy link
Member

commented Jan 23, 2018

Hi @Yaytay,

Yes, you are right, we have one ApiReactorHandler per API, but it shouldn't be an issue at all.
We also have a ReactorHandlerResolver which is used to resolve to an APIHandler according to a given incoming Request. So I'm sure the logic would be part of this class.

@Yaytay

This comment has been minimized.

Copy link
Author

commented Jan 24, 2018

Hi @brasseld,
Ah yes, if you were to modify ReactorHandler so it had:
boolean canHandleReqest(Request request, String path) // Just because you've potentially modified the path
You could then have different logic for each API depending on whether it was using HOST or Path.
And it would be easy to make either of those use a regex too - so you could have four options:

  • Path startsWith
  • Header exact match
  • Path regex
  • Header regex

That would be nice.

@Yaytay

This comment has been minimized.

Copy link
Author

commented Jan 24, 2018

I'd be happy to do a PR that did the backend for this, but if you've ever seen my attempts at UI you won't want me going near that bit (and obviously the backend won't actually work without the front).

@brasseld

This comment has been minimized.

Copy link
Member

commented Jan 24, 2018

Haha ! You know what: it's exactly the same for me...

Anyway, I don't understand why you need to pass the path in canHandleReqest(Request request, String path).

From my point of view, we only have to apply changes in ReactorHandlerResolver in order to have multiple algorithms to resolve an API according to a given request.

Also options could be at least:

  • Path startsWith (current solution)
  • Host header resolution

Why would you need also regex approach for them ?

@Yaytay

This comment has been minimized.

Copy link
Author

commented Jan 24, 2018

Why pass in path?
The DefaultReactorHandlerResolver (possibly) modifies the path to ensure that it ends in a "/", if you didn't pass in the path you'd have to do that for each ApiReactor.

Why move the logic to ApiReactor?
Just seems tidier - in order to keep the call to a single line with a lambda you need to implement a new function (to my mind the new logic is too much to squeeze into the lambda), that function could be in ApiReactor or DefaultReactorHandlerResolver.
If it's in ApiReactor it gives more opportunity for the decision to be made based on other criteria later (send all requests that GeoIp determines come from France to the "special API" :) )

Why regex?
Comes back to my microservices needing to be available through multiple domains (in order to be transparently accessible from Safari).
Now I have apiA.site1.com and apiA.site2.com, and I'd like to match on Host ~ /apiA..*/
For my use it would also work to have Host.startsWith("apiA."), but once you accept that you could have multiple values match it might as well be a regex.

@brasseld

This comment has been minimized.

Copy link
Member

commented Jan 24, 2018

Why pass in path?
I'm not sure it's a good idea to ensure that path ends with a trailing '/' because, as a gateway acts as a 'proxy', we are trying to not modify the request unless it's done explicitly (by using policies for example).

Why move the logic to ApiReactor?
I would prefer to put logic in DefaultReactorHandlerResolver in a first time. Then, we will see if other options will be required in the future... we are working incrementally and only if there is a real need.

Why regex?
Ok, I got you... but as far as I understand, it is only required to work on the Host header. No reason to apply such logic to other headers, right ?

@Yaytay

This comment has been minimized.

Copy link
Author

commented Jan 24, 2018

Right now, I don't have a reason to apply it to other headers.

Someone might want to have an API for XML and a different one for JSON, with the selection being based on Content-Type, but I don't.

@Yaytay

This comment has been minimized.

Copy link
Author

commented Jan 24, 2018

Incidentally - not forcing the matching part of the path to be a full path segment (i.e. not making it end with "/") is potentially really useful for me.
In the same way as I would like to match against the first part of the Host header I could match against the first part of the path.
Whilst it would still be a bit confusing for new admins on my gravitee just making that change on its own would actually enable me to achieve what I need.

I would keep my front-end load balancer changing the incoming path to put the Host head in it and then have "/apiA" as the path, despite the actual path being "/apiA.site1.com/some/thing/else".
I'd still want the whole of the first path segment removed, so the path that reaches the API is "/some/thing/else" not "site1.com/some/thing/else".

@Yaytay

This comment has been minimized.

Copy link
Author

commented Apr 4, 2018

Thinking about the simplest way to solve this for me (because the script I'm running on the load balancer is getting unmanageable, and I have to go through our ops team for every change to it).
If the contextPath in the ReactorHandler was a regex it would solve nearly all my problems nicely (I'd still map host to path in the load balancer, but with one simple script), without requiring big changes in gravitee.

You'd need to have a flag somewhere to identify whether the path was a regex or not (otherwise existing paths would cause problems with dots).

Which would mean a UI change :(

@brasseld

This comment has been minimized.

Copy link
Member

commented Apr 4, 2018

I was also thinking about this issue these last days.

I really think that we must not constraint on the context-path and that context-path is only a subset of possible routing rules.

So it means that, eventually, some API may share the same context-path but with different host / virtual host.

So finally, routing rules may be defined using:

  • Path startsWith /my-api
  • Host Header exact match company.com
  • Path regex / '/api/[product|store]`
  • Host Header regex *.company.com
@Yaytay

This comment has been minimized.

Copy link
Author

commented Apr 4, 2018

Well that sounds great. v1.15? :)

@brasseld

This comment has been minimized.

Copy link
Member

commented Apr 4, 2018

Haha, you can get that out of your head :)

Will have a look for 1.16, at least for path startsWith and exact host header.

@Yaytay

This comment has been minimized.

Copy link
Author

commented Apr 4, 2018

Cool.
Happy to help if I can.

@briankrug

This comment has been minimized.

Copy link

commented May 22, 2018

What about routing by API Key? Could that use a similar approach? (An altered ReactorHandlerResolver?)

@brasseld

This comment has been minimized.

Copy link
Member

commented May 23, 2018

Hi @briankrug

I think that routing by API_key should be part of a new dynamic routing policy and not part of this ticket.

The ReactorHandlerResolver responsibility is to handle the request to a given API. No more rules must be applied according to the IP of the consumer, or its api-key, ...

The best you can do would be to create a dedicated issue to create an improved version of the routing policy and list all the rules which can be implemented.
IMHO, rules should be based on our internal EL (expression language) to implement such scenario (route by api-key).

@brasseld

This comment has been minimized.

Copy link
Member

commented May 23, 2018

I forgot to say that I'm thinking about changing the context-path property used to search for the API to dispatch to.

I think it would be preferable to set a complete URL, with a host and with a path (possible a regex).
This way, there will be no more constraint on the context-path unicity / duplication.

For example, one API may be set with:
http://api1.mycompany.com/v1/products

And an other with:
http://api2.mycompany.com/v1/products

Here we have the same context-path, but used for two different APIs.

@briankrug

This comment has been minimized.

Copy link

commented May 23, 2018

I submitted issue #1252 to capture my idea of Routing by API Key

@brasseld

This comment has been minimized.

Copy link
Member

commented Oct 19, 2018

Here we are, it's time to work on this feature!
I'm starting to think about API routing rules.

For the first step, I'm looking to support the host header and path to route HTTP requests to APIs.

At the time of writing, there is no reason to support more HTTP headers than the Host header.

IMHO, we should support some kind of virtual-hosting as its done in Nginx / http / ...

There should be no more restriction on the context-path, meaning that multiple APIs may share the same path, but also may use different host to distinguish them.

@brasseld brasseld self-assigned this Aug 29, 2019
@brasseld brasseld added this to the APIM - 1.29.0 milestone Aug 29, 2019
@brasseld

This comment has been minimized.

Copy link
Member

commented Aug 29, 2019

Closed by #1594

@brasseld brasseld closed this Aug 29, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
API Management
Awaiting triage
4 participants
You can’t perform that action at this time.