Search code examples
asp.net.netowinkatana

OWIN use SignalR and prevent 'HTTP Error 403.14 - Forbidden'


I'm hosting SignalR in an OWIN selfhosted app - in Azure, like so:

public class Startup
{
    public void Configuration(IAppBuilder app)
    {
        app.MapSignalR();
    }
...

Easy enough, works fine. Exposes /signalr/hubs as expected.

What I would like to do is prevent an HTTP 403.14 when someone hits the / url. Requesting / returns an HTTP 403.14:

HTTP Error 403.14 - Forbidden

I think it would be nice to hide OS and hosting details by returning an empty response in case the 'root' url would be hit. So I would like to do this:

app.Map("/", builder => builder.Use((context, func) => context.Response.WriteAsync(string.Empty)));

However - the mapPath argument "/" is not allowed - it cannot end with a slash.

app.Use((context, func) => context.Response.WriteAsync(string.Empty));

This also overwrites the SignalR virtual folder (/signal) and thus makes the hubs unreachable.

This is the equivalent of having emtpy index.htm pages in web app folders in the old days.


Solution

  • You can write your own Middleware (thats the beauty of OWIN) to handle these requests. Ive made an example where it will return statuscode 200 and return empty:

    public partial class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            app.Use<OnlySignalRMiddleware>();
            app.MapSignalR();
        }
    }
    
    public class OnlySignalRMiddleware : OwinMiddleware
    {
        public OnlySignalRMiddleware(OwinMiddleware next) : base(next) { }
    
        public override Task Invoke(IOwinContext context)
        {
            if (!context.Request.Path.HasValue || context.Request.Path.Value.StartsWith("/signalr"))
                return Next.Invoke(context); //continue the pipeline
    
            context.Response.StatusCode = 200;
            return context.Response.WriteAsync(""); //terminate the pipeline
        }
    }
    

    If you want you can also remove some headers by doing:

            context.Response.Headers.Remove("Server");