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

#1672 Custom Default Version Policy #1673

Merged

Conversation

ibnuda
Copy link
Contributor

@ibnuda ibnuda commented Jun 30, 2023

Closes #1672

New Feature

Configurable HttpRequestMessage.VersionPolicy.

Motivation for New Feature

The following setup:

  • an MVC ASP.NET Core as frontend.
  • Ocelot gateway
  • some microservices

All components above configured to use Kestrel with the following snippet:

var builder = WebApplication.CreateBuilder(args);

builder.WebHost.ConfigureKestrel(opts =>
{
    opts.ConfigureEndpointDefaults(lopts =>
    {
        lopts.Protocols = HttpProtocols.Http2;
    });
});

All components above should only talk to each other using HTTP/2 and no TLS (plain HTTP).

User Scenario

With plain Ocelot 18.0.0, I couldn't make them talk to each other with plain HTTP from frontend to the service.
The services will print out error messages like the following:

HTTP/2 connection error (PROTOCOL_ERROR): Invalid HTTP/2 connection preface

Solution

Found out that I need to make sure that HttpRequestMessage has its VersionPolicy to be set RequestVersionOrHigher.

Changes I made

  • namespace: Ocelot.Request.Mapper
    class: RequestMapper
    method: Task<Response<HttpRequestMessage>> Map(HttpRequest request, DownstreamRoute downstreamRoute)
    changes:
    • added VersionPolicy = HttpVersionPolicy.RequestVersionOrHigher, in HttpRequestMessage instantiation.
  • namespace: Ocelot.Requester
    class: HttpClientBuilder
    method: IHttpClient Create(DownstreamRoute downstreamRoute)
    changes:
    • added DefaultRequestVersion = downstreamRoute.DownstreamHttpVersion, and DefaultVersionPolicy = HttpVersionPolicy.RequestVersionOrHigher, in HttpClient instantiation.

Then all those components above can talk to each other in HTTP/2.

And I think DefaultVersionPolicy = HttpVersionPolicy.RequestVersionOrHigher could be DefaultVersionPolicy downstreamRoute.DownstreamHttpVersionPolicy.

And I am willing to make a PR if you fine folks think this kind of changes is welcome.

Specifications

  • Version: 18.0.0
  • Platform: ASP.NET Core 6.0
  • Subsystem: ASP.NET 6.0 Docker image. Should be Debian, yes?

@ibnuda ibnuda changed the title feature written, tests passed #1672 Custom Default Version Policy Jun 30, 2023
@ibnuda ibnuda changed the title #1672 Custom Default Version Policy #1672 Custom Default Version Policy (WIP) Jun 30, 2023
@ibnuda ibnuda changed the title #1672 Custom Default Version Policy (WIP) #1672 Custom Default Version Policy Jun 30, 2023
@raman-m raman-m self-requested a review June 30, 2023 14:25
@raman-m raman-m added the conflicts Feature branch has merge conflicts label Sep 29, 2023
@raman-m raman-m added feature A new feature needs feedback Issue is waiting on feedback before acceptance and removed conflicts Feature branch has merge conflicts labels Oct 30, 2023
@raman-m
Copy link
Member

raman-m commented Oct 30, 2023

Hi Ibnu!
Thanks for being proactive!

I'll return to your PR back after merging all PRs with lower numbers... Or will find you a mentor 😉

@raman-m
Copy link
Member

raman-m commented Oct 30, 2023

@ggnaegi You are the mentor of this PR! Assigned! 😉

Copy link
Member

@ggnaegi ggnaegi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have written some comments, you should add a few more test cases and use string constants for the policies.

@ibnuda
Copy link
Contributor Author

ibnuda commented Nov 8, 2023

@ggnaegi, sorry for pinging you. i couldn't resolve this conflict. would you please resolve this conflict when you merge this pr? the difference is only whitespace and the intended change.

thanks in advance.

@raman-m
Copy link
Member

raman-m commented Nov 8, 2023

Hello, Ibnu!
Just do resolving conflicts in Visual Studio, because VS has a perfect merge tool. Also, for src/Ocelot/Configuration/File/FileRoute.cs conflict I would recommend to use the version from develop only, and then re-add your new property.
If no luck, let me know and I'll help you...

@ibnuda
Copy link
Contributor Author

ibnuda commented Nov 9, 2023

hello raman. thanks, the conflict is resolved.

and might be off-topic. but in ocelot will support net8, yes?

@raman-m
Copy link
Member

raman-m commented Nov 18, 2023

@ibnuda commented on Nov 9
hello raman. thanks, the conflict is resolved.

Thanks!


and might be off-topic. but in ocelot will support net8, yes?

Yes! Soon! FYI #1787
This feature will be a part of .NET8 version definitely.

@raman-m
Copy link
Member

raman-m commented Nov 18, 2023

@ggnaegi Is it ready? Or can we add this PR & feature to Nov'23 milestone? How confident are you, guys? 😃

I see only unit tests

@ggnaegi
Copy link
Member

ggnaegi commented Nov 19, 2023

@ggnaegi Is it ready? Or can we add this PR & feature to Nov'23 milestone? How confident are you, guys? 😃

I see only unit tests

Hello, I think it's safe, we can add it to the november release. Maybe, we could implement some acceptance tests...
@ibnuda do you have a bit of time for some acceptance tests?

@ibnuda
Copy link
Contributor Author

ibnuda commented Nov 20, 2023

hello folks, thanks for the answer.
yes, I'm available to write some acceptance tests. just point me where the rest of the acceptance tests are and the general direction then I will push it up.

@ggnaegi
Copy link
Member

ggnaegi commented Nov 20, 2023

@ibnuda You should add a new class in test\Ocelot.AcceptanceTests. I have created a template, you can use it if you want, you might have several combinations to test...

  • https / http
  • DownstreamHttpVersion 1.0, 2.0, 3.0
  • DownstreamVersionPolicy "exact", "downgradable", "upgradeable"

I'm just wondering if, instead of using exact, downgradable, upgradeable, you should use the terms
"RequestVersionExact", "RequestVersionOrHigher", "RequestVersionOrLower", I think it's more intuitive for the users...
and you could use nameof for the definition.

using Ocelot.Configuration.File;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Server.Kestrel.Core;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;

namespace Ocelot.AcceptanceTests;

public class DefaultVersionPolicyTests : IDisposable
{
    private readonly Steps _steps;
    private const string Body = "supercalifragilistic";

    public DefaultVersionPolicyTests()
    {
        _steps = new Steps();
    }

    [Fact]
    public async Task should_return_bad_gateway()
    {
        var port = PortFinder.GetRandomPort();

        var configuration = new FileConfiguration
        {
            Routes = new List<FileRoute>
            {
                new()
                {
                    DownstreamPathTemplate = "/",
                    DownstreamHostAndPorts = new List<FileHostAndPort>
                    {
                        new()
                        {
                            Host = "localhost",
                            Port = port,
                        },
                    },
                    DownstreamScheme = "https",
                    DownstreamHttpVersion = "2.0",
                    DownstreamVersionPolicy = "upgradeable",
                    UpstreamPathTemplate = "/",
                    UpstreamHttpMethod = new List<string> { "GET" },
                },
            },
        };

        this.Given(x => x.GivenThereIsAServiceRunningOn($"https://localhost:{port}", HttpProtocols.Http1))
            .And(x => _steps.GivenThereIsAConfiguration(configuration))
            .And(x => _steps.GivenOcelotIsRunning())
            .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
            .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.BadGateway))
            .BDDfy();
    }

    private void GivenThereIsAServiceRunningOn(string url, HttpProtocols protocols)
    {
        var builder = new WebHostBuilder()
            .UseUrls(url)
            .UseKestrel()
            .ConfigureKestrel(serverOptions =>
            {
                serverOptions.ConfigureEndpointDefaults(listenOptions => { listenOptions.Protocols = protocols; });
            })
            .UseContentRoot(Directory.GetCurrentDirectory())
            .Configure(app =>
            {
                app.Run(async context =>
                {
                    context.Response.StatusCode = (int)HttpStatusCode.OK;
                    await context.Response.WriteAsync(Body);
                });
            })
            .Build();

        builder.Start();
    }

    public void Dispose()
    {
        _steps?.Dispose();
    }
}

@raman-m
Copy link
Member

raman-m commented Nov 21, 2023

@ggnaegi commented on Nov 20

namespace Ocelot.AcceptanceTests;
public class DefaultVersionPolicyTests : IDisposable
{ }

If we will create all test classes in the root folder then we will make long list of them.
Too many files in root folder! Why not to organize files/namespaces in Acceptance Tests project?
What Ocelot feature will this small feature belong to?

@ggnaegi
Copy link
Member

ggnaegi commented Nov 21, 2023

@ggnaegi commented on Nov 20

namespace Ocelot.AcceptanceTests;
public class DefaultVersionPolicyTests : IDisposable
{ }

If we will create all test classes in the root folder then we will make long list of them. Too many files in root folder! Why not to organize files/namespaces in Acceptance Tests project? What Ocelot feature will this small feature belong to?

Yes, you are right, we should reorganize the tests structure, maybe a new PR?

@ibnuda
Copy link
Contributor Author

ibnuda commented Nov 22, 2023

hello folks, seems like there's a problem when testing with http2 and https. the test pass just fine in local, but it seems the circle ci setup needs to trust dotnet issued cert.

i made change in the .circleci/config.yaml by adding dotnet dev-certs https but it seems circleci doesn't pick it up.

what can i do to pass the ci?

thanks in advance.

@ggnaegi
Copy link
Member

ggnaegi commented Nov 22, 2023

@ibnuda uuupppss... Let me check that
maybe you could try with "DangerousAcceptAnyServerCertificateValidator": true
It might work

@ggnaegi
Copy link
Member

ggnaegi commented Nov 22, 2023

Ok, the problem is on ocelot side too... Ok, will have a look later, sorry.

@ibnuda
Copy link
Contributor Author

ibnuda commented Nov 22, 2023

Thank, ggnaegi. but please don't feel pressured. please do it in your own pace and.

@raman-m
Copy link
Member

raman-m commented Nov 22, 2023

Guys, we have the new commits in develop!
Please, rebase feature branch onto develop! ...with conflicts resolving for sure 😉

@raman-m
Copy link
Member

raman-m commented Nov 22, 2023

@ibnuda commented on Nov 22

What the issue with .circleci/config.yaml and certificates? Some builds have failed?
Show the links to the CircleCI logs plz!
But first, could you rebase the feature branch please?

@raman-m raman-m force-pushed the feat/custom-defaultversionpolicy branch from 1145c54 to 201f296 Compare April 18, 2024 18:11
@raman-m
Copy link
Member

raman-m commented Apr 19, 2024

@ggnaegi commented:

Yes, it makes sense, you're right

Renamed! See commits 1585061 and 891bb16

@ggnaegi ggnaegi merged commit 140f9b5 into ThreeMammals:release/24.0 Apr 19, 2024
1 check passed
@raman-m
Copy link
Member

raman-m commented Apr 19, 2024

@ibnuda Congrats! 🥳

@raman-m raman-m added Spring'24 Spring 2024 release and removed 2023 Annual 2023 release labels May 22, 2024
@raman-m raman-m modified the milestones: Annual 2023, Spring'24 May 22, 2024
raman-m added a commit that referenced this pull request May 23, 2024
* feature written, tests passed

* actualy passes almost all the test.

* resolve conflict, hopefully.

* please.

* let it cook.

* uses constants instead of string for version policies.

* conflict res

* swapped downstream method and version.

* #1731 Read the Docs configuration file v2 (#1733)

* fixing the documentation, using Release/20.0 as base branch

* using latest conf.py, created with sphinx-quickstart, fixing the warnings during documentation generation

* Update .readthedocs.yaml

* switching to threemammals.org for copyright

* adding requirements file, updating readthedocs.yaml, adding formats pdf / epub and config for requirements file

* fixing code block in websockets.rst

* ok, now it should be fine...

* Update kubernetes.rst: Review and fix markup code

* Update websockets.rst: Review and fix markup

* Update conf.py: Update release, author and copyright

---------

Co-authored-by: Raman Maksimchuk <dotnet044@gmail.com>

* * When using the QoS option "ExceptionsAllowedBeforeBreaking" the circuit breaker never opens the circuit.

* merge issue, PortFinder

* some code improvements, using httpresponsemessage status codes as a base for circuit breaker

* Adding more unit tests, and trying to mitigate the test issues with the method "GivenThereIsAPossiblyBrokenServiceRunningOn"

* fixing some test issues

* setting timeout value to 5000 to avoid side effects

* again timing issues

* timing issues again

* ok, first one ok

* Revert "ok, first one ok"

This reverts commit 2e4a673.

* inline method

* putting back logging for http request exception

* removing logger configuration, back to default

* adding a bit more tests to check the policy wrap

* Removing TimeoutStrategy from parameters, it's set by default to pessimistic, at least one policy will be returned, so using First() in circuit breaker and removing the branch Policy == null from delegating handler.

* Fix StyleCop warnings

* Format parameters

* Sort usings

* since we might have two policies wrapped,  timeout and circuit breaker, we can't use the name CircuitBreaker for polly qos provider, it's not right. Using PollyPolicyWrapper and AsnycPollyPolicy instead.

* modifying circuit breaker delegating handler name, usin Polly policies instead

* renaming CircuitBreakerFactory to PolicyWrapperFactory in tests

* DRY for FileConfiguration, using FileConfigurationFactory

* Add copy constructor

* Refactor setup

* Use expression body for method

* Fix acceptance test

* IDE1006 Naming rule violation: These words must begin with upper case characters

* CA1816 Change ReturnsErrorTests.Dispose() to call GC.SuppressFinalize(object)

* Sort usings

* Use expression body for method

* Return back named arguments

---------

Co-authored-by: raman-m <dotnet044@gmail.com>

* feature written, tests passed

* actualy passes almost all the test.

* resolve conflict, hopefully.

* missed this one.

* please.

* come on...

* let it build.

* let it cook.

* copied from main branch.

* conflict res

* resolving conflicts.

* another attempt.

* lf

* re-incorporate downstream version policy.

* renamed the version policies and added acceptance tests.

* trust the dotnet dev cert.

* accepts cert from dotnet.

* Fix compiling errors

* Refactor tests

* a bit of code cleanup, removing some usings

* a bit more cleanup in fileroute

* try and error with the tests

* "Yahoo!...", said @ibnuda :)

* FileRoute: let it go...
Binary copy! :LoL:

* FileRoute: let it cook...
Re-add sweet props

* `dotnet dev-certs` for the `build` job

* Recover `kubernetes.rst`

* docs/make.bat original version

* OcelotBuilderExtensions

* original src/Ocelot.Provider.Polly/v7/PollyPolicyWrapper.cs

* `IVersionPolicyCreator` XML docs

* Code review by @raman-m (part 1)

* RequestMapper : care about diff

* Code review by @raman-m (part 2)

* Fix Should_return_OK_status_and_multiline_indented_json_response_with_json_options_for_custom_builder

* Update configuration.rst

Add DownstreamVersionPolicy section

* Update docs

* Rename `DownstreamVersionPolicy` to `DownstreamHttpVersionPolicy`

* update docs after prop renaming

* Sort props

---------

Co-authored-by: Guillaume Gnaegi <58469901+ggnaegi@users.noreply.github.com>
Co-authored-by: Raman Maksimchuk <dotnet044@gmail.com>
@raman-m
Copy link
Member

raman-m commented May 23, 2024

Feature commit in develop → 176a4c8

@raman-m raman-m added the Configuration Ocelot feature: Configuration label Jun 6, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Configuration Ocelot feature: Configuration feature A new feature Spring'24 Spring 2024 release
Projects
None yet
Development

Successfully merging this pull request may close these issues.

More Flexible VersionPolicy
4 participants