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

Dynamic Routing Error: Using dynamic routing with services that use HTTP and HTTPS #2253

Open
viktoralfa19 opened this issue Jan 18, 2025 · 10 comments
Labels
Consul Service discovery by Consul Routing Ocelot feature: Routing Service Discovery Ocelot feature: Service Discovery

Comments

@viktoralfa19
Copy link

viktoralfa19 commented Jan 18, 2025

Please I have a simple .Net 8 web api application where Ocelot version 23.2.2 is used and I have this configuration. I am also using CONSUL as service discovery.
When running in a container with docker-compose and trying to use dynamic routing I want to use internal APIs with HTTP and external APIs with HTTPS so I configure OCELOT like this:

{
  "Routes": [],
  "DynamicRoutes": [
    {
      "ServiceName": "openloyalti",
      "UseServiceDiscovery": true
    }
  ],
  "GlobalConfiguration": {
    "ServiceDiscoveryProvider": {
      "RequestIdKey": "OcRequestId",
      "Scheme": "http",
      "Host": "consul-server",
      "Port": 8500,
      "Type": "Consul"
    },
    "RateLimitOptions": {
      "ClientWhitelist": [],
      "EnableRateLimiting": false,
      "HttpStatusCode": 429,
      "QuotaExceededMessage": "Too many requests, please try again later."
    },
    "LoadBalancerOptions": {
      "Type": "LeastConnection"
    },
    "HttpHandlerOptions": {
      "UseTracing": true
    },
    "DownstreamScheme": "http"
  }
}

When doing so I always get this exception:

Image

Basically I want to know how to solve my problem of using dynamic routing with services that use HTTP and other services that use HTTPS.

@viktoralfa19
Copy link
Author

Something I forgot to mention is that the routes with HTTP are resolved successfully and if I want to make requests to the routes with HTTPS it returns an error and it is because the route resolution is HTTP and not HTTPS.

@raman-m
Copy link
Member

raman-m commented Jan 19, 2025

Could you make the title shorter please?
It is very long!
Please focus our attention to the problem with short title.

Also, I'm confused in recognizing this issue as a bug or an question?

@raman-m raman-m added Service Discovery Ocelot feature: Service Discovery Routing Ocelot feature: Routing Consul Service discovery by Consul labels Jan 19, 2025
@raman-m
Copy link
Member

raman-m commented Jan 21, 2025

@viktoralfa19 Are you online?

@raman-m
Copy link
Member

raman-m commented Jan 21, 2025

When running in a container with docker-compose and trying to use dynamic routing I want to use internal APIs with HTTP and external APIs with HTTPS so I configure OCELOT like this:

Something I forgot to mention is that the routes with HTTP are resolved successfully and if I want to make requests to the routes with HTTPS it returns an error and it is because the route resolution is HTTP and not HTTPS.

Victor,
Based on your description you want to have the following DownstreamScheme property overriding in route config 👇

{
  "DynamicRoutes": [
    {
      "ServiceName": "openloyalti",
      "DownstreamScheme": "https" // important! It is HTTPS route
    }
  ],
  "GlobalConfiguration": {
    "ServiceDiscoveryProvider": {
      // ...
    },
    "RateLimitOptions": {
      // ...
    },
    // ...
    "DownstreamScheme": "http" // important! ALL routes are HTTP
  }
}

Am I correct?

🆗 This feature is not currently implemented in Ocelot! All dynamic routes should be HTTP or HTTPS globally, as defined in the GlobalConfiguration.DownstreamScheme option. Thus, the option is global and cannot be overridden at the route level within the DynamicRoutes collection.

Explanation of current design:

File-models

Finally, global properties from FileGlobalConfiguration are utilized in all dynamic routes. However, the properties from FileDynamicRoute override those in FileGlobalConfiguration. Consequently, it is currently impossible to override the DownstreamScheme at the route level. This feature needs to be developed!
Are you going to contribute?

@raman-m
Copy link
Member

raman-m commented Jan 21, 2025

Please I have a simple .Net 8 web api application where Ocelot version 23.2.2 is used and I have this configuration. I am also using CONSUL as service discovery.

Please upgrade to the latest stable version 23.4.3

Basically I want to know how to solve my problem of using dynamic routing with services that use HTTP and other services that use HTTPS.

As I have explained above, it is now impossible to override the scheme in route JSON via ocelot.json. If you really need this feature, a custom solution must be developed by writing C# code. Since the Consul provider is used, we can utilize either the Custom Provider or the Consul Service Builder features. The best option is the latter one due to the Consul provider.

🆗 To draft a solution, we need to override the GetServiceHostAndPort method:

protected virtual ServiceHostAndPort GetServiceHostAndPort(ServiceEntry entry, Node node)
=> new(
GetDownstreamHost(entry, node),
entry.Service.Port);

This method currently calls the constructor with two arguments:

public ServiceHostAndPort(string downstreamHost, int downstreamPort)
{
DownstreamHost = downstreamHost?.Trim('/');
DownstreamPort = downstreamPort;
}

To reapply the actual scheme, we must call the constructor with three arguments, including the scheme:

public ServiceHostAndPort(string downstreamHost, int downstreamPort, string scheme)
: this(downstreamHost, downstreamPort) => Scheme = scheme;

The remaining task is to attach the new behavior class utilizing the AddConsul<T> method.

Which approach do you prefer: JSON or C#?

@raman-m raman-m added needs validation Issue has not been replicated or verified yet needs feedback Issue is waiting on feedback before acceptance labels Jan 21, 2025
@viktoralfa19 viktoralfa19 changed the title Dynamic Routing Error: Exception caught in global error handler, exception message: Object reference not set to an instance of an object., exception stack: at Ocelot.Configuration.RateLimitOptions.get_ClientWhitelist() Dynamic Routing Error: Using dynamic routing with services that use HTTP and HTTPS Jan 22, 2025
@viktoralfa19
Copy link
Author

viktoralfa19 commented Jan 22, 2025

Finally, global properties from FileGlobalConfiguration are utilized in all dynamic routes. However, the properties from FileDynamicRoute override those in FileGlobalConfiguration. Consequently, it is currently impossible to override the DownstreamScheme at the route level. This feature needs to be developed! Are you going to contribute?

Thank you very much for answering quickly.

Of course nice to collaborate

@viktoralfa19
Copy link
Author

viktoralfa19 commented Jan 22, 2025

I can't update to the current version 23.4.3 because I have another bug that I'm going to upload with a description

@viktoralfa19
Copy link
Author

public ServiceHostAndPort(string downstreamHost, int downstreamPort, string scheme)
: this(downstreamHost, downstreamPort) => Scheme = scheme;
The remaining task is to attach the new behavior class utilizing the AddConsul method.

Which approach do you prefer: JSON or C#?

I would like to implement the modification made with C# code but it cannot be implemented by extending the functionality of the ocelot.consul library, or should it be changed in its source code?

@viktoralfa19
Copy link
Author

viktoralfa19 commented Jan 23, 2025

@raman-m
Thank you very much, I actually updated to the latest version because this DefaultConsulServiceBuilder class does not exist with the version I was using and by modifying a couple of virtual methods I was able to use http and https with dynamic routing and the issue I had was still resolved. What I added was this class:

public class SchemaConsulServiceBuilder(
    IHttpContextAccessor contextAccessor,
    IConsulClientFactory clientFactory,
    IOcelotLoggerFactory loggerFactory)
    : DefaultConsulServiceBuilder(contextAccessor, clientFactory, loggerFactory)
{
    protected override string GetDownstreamHost(ServiceEntry entry, Node node)  => entry.Service.Address;
    
    protected override ServiceHostAndPort GetServiceHostAndPort(ServiceEntry entry, Node node)
    {
        if (entry.Service.Meta?.TryGetValue("Scheme", out var schema) ?? false)
            return new ServiceHostAndPort(GetDownstreamHost(entry, node), entry.Service.Port,
                schema);

        return new ServiceHostAndPort(GetDownstreamHost(entry, node), entry.Service.Port);
    }
}

And I used it here:

webApplicationBuilder.Services.AddOcelot(configuration).AddConsul<SchemaConsulServiceBuilder>();

I hope this helps someone else.

@raman-m
Copy link
Member

raman-m commented Jan 23, 2025

I have a simple .Net 8 web api application where Ocelot version 23.2.2 is used
I actually updated to the latest version because this DefaultConsulServiceBuilder class does not exist with the version I was using

Indeed, the DefaultConsulServiceBuilder class has been available since version 23.3.0

What I added was this class:

    protected override ServiceHostAndPort GetServiceHostAndPort(ServiceEntry entry, Node node)
    {
        if (entry.Service.Meta?.TryGetValue("Scheme", out var schema) ?? false)
            return new(GetDownstreamHost(entry, node), entry.Service.Port,
                schema); // !!!

        return new(GetDownstreamHost(entry, node), entry.Service.Port);
    }

What is notable in your code is the extraction of the scheme value:

entry.Service.Meta?.TryGetValue("Scheme", out var schema)

This is the cornerstone of the logic. The agent service, known entry.Service.Address, lacks information about the scheme (protocol). Therefore, you read the scheme from Consul Meta-data.

Could you describe this use case in detail, please?
What are the ways to define scheme for service entry: metadata, tags?

I have an idea to work more on this case and possibly create a PR together with you.
It seems user scenario will be very useful for Consul & Ocelot fans.

@raman-m raman-m removed needs validation Issue has not been replicated or verified yet needs feedback Issue is waiting on feedback before acceptance labels Jan 23, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Consul Service discovery by Consul Routing Ocelot feature: Routing Service Discovery Ocelot feature: Service Discovery
Projects
None yet
Development

No branches or pull requests

2 participants