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

Uri doesn't expose the original, user-provided port value #89768

Open
roji opened this issue Aug 1, 2023 · 8 comments
Open

Uri doesn't expose the original, user-provided port value #89768

roji opened this issue Aug 1, 2023 · 8 comments
Labels
api-suggestion Early API idea and discussion, it is NOT ready for implementation area-System.Net
Milestone

Comments

@roji
Copy link
Member

roji commented Aug 1, 2023

When a port isn't specified, Uri automatically infers the default port based on the scheme:

Console.WriteLine(new Uri("http://example.com").Port); // 80
Console.WriteLine(new Uri("https://example.com").Port); // 443

However, Uri doesn't provide any way to know whether that port was explicitly user-specified, or whether Uri inferred it. This presents a problem when using Uri to accept endpoint information from users for services which by default run on the non-standard port.

For example, if my custom service by default runs over HTTP on port 12345, and the user omits the port, Uri will return 80 as the HTTP default. If I assume that 80 means the user hasn't provided a port and default to 12345, it becomes impossible for the user to run on port 80 if they so wish.

Of course, it's possible to do various string matching to see whether :80 was present, but it seems like Uri should provide some way to access the user-provided port, or to know whether the port was inferred or not.

/cc @stephentoub

@ghost ghost added the untriaged New issue has not been triaged by the area owner label Aug 1, 2023
@ghost
Copy link

ghost commented Aug 1, 2023

Tagging subscribers to this area: @dotnet/ncl
See info in area-owners.md if you want to be subscribed.

Issue Details

When a port isn't specified, Uri automatically infers the default port based on the scheme:

Console.WriteLine(new Uri("http://example.com").Port); // 80
Console.WriteLine(new Uri("https://example.com").Port); // 443

However, Uri doesn't provide any way to know whether that port was explicitly user-specified, or whether Uri inferred it. This presents a problem when using Uri to accept endpoint information from users for services which by default run on the non-standard port.

For example, if my custom service by default runs over HTTP on port 12345, and the user omits the port, Uri will return 80 as the HTTP default. If I assume that 80 means the user hasn't provided a port and default to 12345, it becomes impossible for the user to run on port 80 if they so wish.

Of course, it's possible to do various string matching to see whether :80 was present, but it seems like Uri should provide some way to access the user-provided port, or to know whether the port was inferred or not.

/cc @stephentoub

Author: roji
Assignees: -
Labels:

area-System.Net

Milestone: -

@stephentoub
Copy link
Member

cc: @MihaZupan

@MihaZupan
Copy link
Member

MihaZupan commented Aug 1, 2023

I don't think there's a built-in way to distinguish whether the port was set vs. it had a default value for http Uris.
This information is something that Uri does know internally, but we don't expose an API to tell you that for known schemes.
For custom schemes that don't specify a default port, Port will actually return -1 if it wasn't explicitly set.

A workaround that makes use of that fact without manually parsing stuff:

internal static class UriHelper
{
    // "http://foo/test" => -1
    // "http://foo:80/test" => 80
    public static int GetOriginalPort(this Uri uri)
    {
        Debug.Assert(uri.Scheme is "http" or "https");
        return new Uri($"custom-{uri.OriginalString.TrimStart(' ', '\n', '\r', '\t')}", UriKind.Absolute).Port;
    }
}

@MihaZupan MihaZupan added the api-suggestion Early API idea and discussion, it is NOT ready for implementation label Aug 1, 2023
@MihaZupan
Copy link
Member

I think an API along the lines of int OriginalPort, would make sense here (same as Port, but can return -1 for known schemes as well).
Moving to future for now so we can gather feedback on how common this scenario is.

@MihaZupan MihaZupan added this to the Future milestone Aug 1, 2023
@ghost ghost removed the untriaged New issue has not been triaged by the area owner label Aug 1, 2023
@wfurt
Copy link
Member

wfurt commented Aug 1, 2023

I somewhat does not understand the argumentation. Uri has Port value and I would expect the service should respect that - regardless how the value was created. If you service is not HTTP could you use different scheme to avoid the defaults?

@roji
Copy link
Member Author

roji commented Aug 1, 2023

The service in question is a database server using gRPC over HTTP, with some arbitrary non-80 port as its default. On the client side, the .NET gRPC client has GrpcChannel.ForAddress, which accepts a Uri, and AFAIK the http scheme is expected there; it really does speak HTTP over the wire, it just does it on a different default port.

So for one thing, I'm not sure I can invent a new scheme and pass it to GrpcChannel: it may very well care about the scheme (e.g. http vs. https to know about encryption?). It's also a bit odd to require users to use the specific new scheme where in fact it's just HTTP on the wire.

Does that make sense?

@nickdavies
Copy link

I am running into the same problem. I have a URI I want to accept and for my application my default port is 38281 so I would like to know if the user did or didn't provide one and set it to 38281 if they didn't.

However If my user decides to run on port http port 80 or https port 443 (which they are allowed to do) I can't determine if I should use the default 38281 or not because 80 may be what they explicitly provided or the default.

@baronfel
Copy link
Member

Chiming in that we had the same problem in dotnet/sdk#44050 - for our scenario (falling back to default ports for http if https doesn't work) we needed to know if the user-provided domain had a port or not, but we didn't want the port-defaulting behavior of Uri to clobber our detection.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
api-suggestion Early API idea and discussion, it is NOT ready for implementation area-System.Net
Projects
None yet
Development

No branches or pull requests

6 participants