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

#658 Change upstream Regex to account for blank placeholders #1333

Open
wants to merge 8 commits into
base: develop
Choose a base branch
from

Conversation

AndyConlisk
Copy link

@AndyConlisk AndyConlisk commented Sep 4, 2020

Fixes #658

Proposed Changes

@raman-m raman-m changed the title Change upstream regex to account for blank placeholders #658 Change upstream Regex to account for blank placeholders Jul 17, 2023
@raman-m raman-m changed the base branch from master to develop July 17, 2023 18:36
@raman-m raman-m self-requested a review July 17, 2023 18:47
@raman-m raman-m added bug Identified as a potential bug needs feedback Issue is waiting on feedback before acceptance labels Aug 5, 2023
@raman-m
Copy link
Member

raman-m commented Aug 5, 2023

@AndyConlisk
Thanks for the PR!
The feature branch (master) has been rebased onto ThreeMammals:develop!
And we have the green build.
So, we can go with code review now...


I don't see develop branch in your fork! Your fork is too old.

Could you Sync fork please? So, new develop branch will occur with all top commits!

Could you add me as collaborator to your forked repo please? I will create develop branch and make it default.

@raman-m
Copy link
Member

raman-m commented Aug 5, 2023

@skyflyer Hi Miha!
As an issue author,
Could you review the code of this solution please?

@AndyConlisk
Copy link
Author

AndyConlisk commented Aug 5, 2023

@raman-m commented on Aug 5

I added you as a collaborator to my fork. Please let me know if there is anything else you need me to do.

Thank you

@skyflyer
Copy link

skyflyer commented Aug 5, 2023

@raman-m commented on Aug 5

Hello @raman-m!

Unfortunately, I'm not really acquainted with the code base and won't be able to either reproduce the error or test the fix right now. Sorry about that.

But, I will have a look at reproducing the scenario later, if that helps.

@skyflyer
Copy link

skyflyer commented Aug 6, 2023

So, I'm not sure if my rationale is sound, but this does not seem to work, at least not for the case I reported in the issue.

First, do understand, that we're not using Ocelot anymore (the project that was the consumer of this library unfortunately died some time ago). Also understand that I'm not in a position to really review the code, as I'm not really acquainted with the codebase nor the roadmap and the design of the library. So take the following comments with that in mind. 😄

The general idea was that ocelot lib would route requests coming to /api to some services running without the /api prefix. In other words, requests coming to http://localhost:5000/api would be routed (or proxied would be a better term, I think) to http://localhost:8000/ and by that logic, requests coming to http://localhost:5000/api/foo would be routed to http://localhost:8000/api/foo.

In order to verify this behaviour, I cloned the develop branch from the main repo and "squash merged" the PR 1333 into that, so the changes are applied on top of the latest develop bits.

I then used a simple config (see below) and tried to request the resources. I've setup python's simple http server (python3 -m http.server 8000) in the parent directory where Ocelot (the repository) and OcelotDemo (the test program) were hosted.

  1. request to http://localhost/api works ✅
  2. request to http://localhost/api/ does not work ❌
    • error message: Failed to match Route configuration for upstream path: /api/, verb: GET.
  3. request to http://localhost/api/foo works ✅
  4. request to http://localhost/api/OcelotDemo/ocelot.json works ✅
Simple config
{
    "Routes": [
        {
        "DownstreamPathTemplate": "/{id}",
        "DownstreamScheme": "http",
        "DownstreamHostAndPorts": [
            {
                "Host": "localhost",
                "Port": 8000
            }
        ],
        "UpstreamPathTemplate": "/api/{id}",
        "UpstreamHttpMethod": [ "Get", "Post" ]
        }
    ],
    "GlobalConfiguration": {
        "BaseUrl": "https://localhost:5000"
    }
}

And here is the Program.cs if you wish to reproduce the complete test:

Program.cs
using Ocelot.Middleware;
using Ocelot.DependencyInjection;

var builder = WebApplication.CreateBuilder(args);

builder.Configuration.AddJsonFile("ocelot.json");

builder.Services.AddOcelot(builder.Configuration);

var app = builder.Build();

app.MapGet("/", () => "Hello World!");

app.UseOcelot().Wait();

app.Run();

@raman-m
Copy link
Member

raman-m commented Aug 14, 2023

@AndyConlisk commented on Aug 5

Thanks!
All that's left to do is making it default. 👇

image

@AndyConlisk
Copy link
Author

@raman-m I have made the develop branch to be the default branch.

@raman-m
Copy link
Member

raman-m commented Aug 14, 2023

@skyflyer commented on Aug 6:

  1. request to http://localhost/api/ does not work ❌
    * error message: Failed to match Route configuration for upstream path: /api/, verb: GET.

Thanks for user scenarios.
Yeah, we will test this scenario too.
And, on my opinion, all user's scenarios from your issue should be tested, but they are not, based on current source code.

@raman-m
Copy link
Member

raman-m commented Aug 14, 2023

@RaynaldM approved these changes on Aug 7

Ray, why did you approve the PR?
There are no unit tests at all! 🤣
Even a beautiful code without tests should not be approved. 😃

Copy link
Member

@raman-m raman-m left a comment

Choose a reason for hiding this comment

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

@AndyConlisk
Although the code looks solid, but it can be improved. 👇


${\color{orange}There\ are\ no\ new\ tests!}$

The new code blocks are not covered by unit tests.
There are no unit tests for user scenarios from the linked issue.
Also, there are no acceptance tests to make sure Ocelot pipeline will handle all user scenarios correctly.

@@ -9,6 +9,7 @@ namespace Ocelot.Configuration.Creator
public class UpstreamTemplatePatternCreator : IUpstreamTemplatePatternCreator
{
private const string RegExMatchOneOrMoreOfEverything = ".+";
private const string RegExMatchLastPlaceHolderZeroOrMoreOfEverything = "(|/.+|/[\\?&#].+)";
Copy link
Member

Choose a reason for hiding this comment

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

  1. What about URL encoded queries including everything case?
    Should we add % to the set of chars [\\?&#]?

  2. The 2nd case of having something after last slash char (everything-case) is defined by expression which matches by .+ quantifier. Can we replace all reg-expression like that: (/[\\?&#].*) ?

@@ -52,7 +53,15 @@ public UpstreamPathTemplate Create(IRoute route)
var indexOfNextForwardSlash = upstreamTemplate.IndexOf("/", indexOfPlaceholder, StringComparison.Ordinal);
if (indexOfNextForwardSlash < indexOfPlaceholder || (containsQueryString && upstreamTemplate.IndexOf('?', StringComparison.Ordinal) < upstreamTemplate.IndexOf(placeholders[i], StringComparison.Ordinal)))
{
upstreamTemplate = upstreamTemplate.Replace(placeholders[i], RegExMatchOneOrMoreOfEverything);
if (upstreamTemplate[indexOfPlaceholder - 1] == '/')
Copy link
Member

Choose a reason for hiding this comment

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

Could you look at line 77 please?
Is not the same case when the string ends with slash?

Comment on lines +78 to +79
delimiter = '&';
nextDelimiter = '&';
Copy link
Member

Choose a reason for hiding this comment

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

Can be one line assignment

Suggested change
delimiter = '&';
nextDelimiter = '&';
delimiter = nextDelimiter = '&';

Comment on lines +85 to +86
delimiter = '?';
nextDelimiter = '?';
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
delimiter = '?';
nextDelimiter = '?';
delimiter = nextDelimiter = '?';

Comment on lines +73 to +74
else if (TemplateDoesNotEndInForwardSlash(pathTemplate) && IsLastPlaceholder(path, counterForPath, pathTemplate))
{
Copy link
Member

Choose a reason for hiding this comment

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

The code for this if-block was copied from lines 26-46.
Based on the function of code snippet, why not to define private helper-method to be reused in both places?

@raman-m
Copy link
Member

raman-m commented Aug 14, 2023

@raman-m
Copy link
Member

raman-m commented Oct 26, 2023

@AndyConlisk Do you have time to fix code review issues?
Do you have an intention to write unit tests at all?

@AndyConlisk
Copy link
Author

I don't know when I will have time to write the new unit tests. I can try in the near future, but I can't give a solid commitment at this time.

@raman-m
Copy link
Member

raman-m commented Nov 8, 2023

@AndyConlisk commented on Nov 7

Lazy bones! 😉

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Identified as a potential bug needs feedback Issue is waiting on feedback before acceptance
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Routing question: placeholders vs slash
4 participants