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

Support running server with URL which contains custom service suffix #17

Open
Sascha-L opened this issue Dec 13, 2024 · 3 comments
Open

Comments

@Sascha-L
Copy link

To run your server on Windows you have to call, e.g.:

netsh http add urlacl url=http://localhost:1234/ user="username"

However, since I want users to be able to freely change the port and hostname and I execute the netsh command from my application (with admin rights), more and more URLs would be registered over time. Since I cannot determine with certainty which URLs (i.e. which port or hostname) were last used, I also do not know which entries I can delete again (netsh http delete urlacl...)

Since my server only provides its services at: http://hostname:port/MyService/ , it would be easy if I registered the following URL, for example:

http://localhost:1234/MyService/

This way I can then delete all registered old URLs that contain "MyService" and thus not accidentally delete other important URL registrations from other services.

The problem now is that the Sisk server does not take "MyService" into account in the HttpListener.

See HttpServer.cs (line 285-301):

`for (int i = 0; i < this.ServerConfiguration.ListeningHosts.Count; i++)
{
ListeningHost listeningHost = this.ServerConfiguration.ListeningHosts[i];
listeningHost.EnsureReady();

            for (int j = 0; j < listeningHost.Ports.Count; j++)
            {
                var port = listeningHost.Ports[j];

                safelisteningPrefixes.Add(port.ToString(false));
                this.listeningPrefixes.Add(port.ToString(true));
            }
        }

        this.httpListener.Prefixes.Clear();
        foreach (string prefix in safelisteningPrefixes)
            this.httpListener.Prefixes.Add(prefix);`

The prefix does not take the path ("/MyService/") into account, so the listener can rightly throw an Access Denied Exception when this.httpListener.Start() is called because it wants to listen on "http://localhost:1234/", but only "http://localhost:1234/MyService/" was registered via netsh.

Is there any reason why the path is explicitly removed from the HttpListener instead of being used? If the listener used "http://localhost:1234/MyService/" as the prefix, there would be no problem.

@CypherPotato
Copy link
Member

Hello. Thank you for your issue.

That's not true. Sisk does consider the /MyService you provided into your ListeningPort object. I don't blame you. I realized I didn't properly documented this.

To achieve this behavior, you need to add the suffix in the constructor of your target ListeningPort that the server will listen to, such as new ListeningPort("http://localhost:5555/MyService"). In this case, the property ListeningPort.Path will be set to /MyService, and the server will be able to listen on this path.

You should assign the prefix of this path in your router to avoid having to prefix all your routes with /MyService. You can do this through the property Router.Prefix. By setting a prefix in this property, all subsequent routes defined in the router will be automatically prefixed.

All of this is automatic with the HTTP builder syntax. Here’s a functional example of this:

class Program
{
    static async Task Main(string[] args)
    {
        using var app = HttpServer.CreateBuilder("http://localhost:5555/MyService")
            .UseRouter(router =>
            {
                router.SetObject(new WeatherApi());
            })
            .Build();

        await app.StartAsync();
    }
}


public class WeatherApi
{
    [RouteGet("/api/<query>")]
    public HttpResponse ServeWeatherData(HttpRequest request)
    {
        string query = request.RouteParameters["query"].GetString();

        return new HttpResponse()
        {
            Content = JsonContent.Create(new
            {
                temperature = new
                {
                    // fictional data
                    current = 24.5f,
                    min = 12.5f,
                    max = 28.6f
                }
            })
        };
    }
}

Now, try to access http://localhost:5555/MyService/api/Paris and see the results.

@Sascha-L
Copy link
Author

Sascha-L commented Dec 16, 2024

Hi,

this is only working with "localhost". Sorry if my example with "localhost" was misleading.

Use your code you posted in your comment and replace "localhost" with your DNS Host Name (PC Device Name). Then you will get an "Access Denied" exception, because you have to run this code with admin rights before:

netsh http add urlacl url=http://HOSTNAME:5555/ user="username"

where "HOSTNAME" is your DNS Host Name / PC Device Name and user your Windows user name.

Now it is running, because you have registered "http://HOSTNAME:5555/" and it is part of "http://HOSTNAME:5555/MyService" .

But that is not nice and the best way to handle this. You should register your full service URL, so you should call:

netsh http add urlacl url=http://HOSTNAME:5555/MyService/ user="username"

(of course the previous registered url has to be deleted!)

Now your code from your comment is not working anymore, because you will get an Access Denied exception. The reason is the code I showed you in my first post:

This line:
safelisteningPrefixes.Add(port.ToString(false));

This code line will add "http://HOSTNAME:5555/" to "safelisteningPrefixes" instead of "http://HOSTNAME:5555/MyService/" (which was given with HttpServer.CreateBuilder()).

So when you add this prefix to your HttpListener instance and start the HttpListener, you will get the Access Denied exception, because the HttpListener wants to listen to "http://HOSTNAME:5555/" but this URL is not registered (only "http://HOSTNAME:5555/MyService/" and all sub pathes of this URL are allowed).

But if you set "http://HOSTNAME:5555/MyService/" as Prefix to the HttpListener instance, it is working.

So that's why I'm asking if there is any reason why you explicit remove the path "/MyService/" which was given in the Constructor, when setting the Prefixes to HttpListener?

CypherPotato added a commit that referenced this issue Dec 17, 2024
@CypherPotato
Copy link
Member

The fact that HttpListener requires permissions in Windows to register where it can listen is very annoying and creates a repetitive and unnecessary process in development, but we can't control this until we create our own implementation of an HTTP listener, which I was already building.

I reproduced your problem now after switching from 'localhost' to anything else and understood the point of the issue. What I don't remember is why I separated the safelisteningPrefixes from the listeningPrefixes. There was a reason, but looking at the code, I see that it is not so necessary. Maybe I used HttpServer.ListeningPrefixes for something else which I don't remember exactly what.

I need to test if this commit will have side effects on the applications already running with Sisk. All this hassle with HttpListener can be ignored if development is made through Sisk.SslProxy or any other reverse proxy.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants