2

I have a chat application that ran fine while debugging locally with IIS express under visual studio 2017. After I depolyed it into production (Windows server 2012R2 IIS 8.5.9600), the javascript console is giving me message:

"WebSocket connection to 'wss://[myServer]/[myHubName]?id=[weirdString]' failed: Error during WebSocket handshake: Unexpected response code: 200"

Altough The client uses https to access the website, the ssl decryption is performed at the firewall, so incoming https requests reach the server as http on port 8088.

Also tried to put my website url on teh "hostname" option on the site bindings on IIS, but then the site started to return error 400.

I have already installed WebSockets support on IIS as described on microsoft documentation and made sure my site had it enabled.

I also browsed the website locally on the server (http), and the error did not appear, so It looks like it`s some issue with IIS reverse proxy configuration.

Here are some IIS logs, with some private information ommited, from the same request both from a client on the WAN and the other with a local client, on the same machine as IIS is running.

IIS log when access was performed from wan:

[lan server ip address] POST /[myHubName]/negotiate - 8088 - https://[public website url]/ 200 0 0 0
[lan server ip address] GET /[myHubName] id=zHiunn5_ynV2jO5812KpDg 8088 -  https://[public website url]/ - 200 0 0 15
[lan server ip address] POST /[myHubName]/negotiate - 8088 -  https://[public website url]/ 200 0 0 78
[lan server ip address] POST /[myHubName] id=T7M5A-o-qqTyd13dSMB64A 8088 - https://[public website url]/ 200 0 0 0

IIS log when access was performed locally on the IIS machine:
::1 POST /[myHubName]/negotiate - 8088 - ::1  http://localhost:8088/ 200 0 0 14
::1 GET /[myHubName] id=QY4do7yqF3EKRbS1Y3usuw 8088 - ::1  - 101 0 64 6356

Fields reference for both scenarios are:

#Fields: s-ip cs-method cs-uri-stem cs-uri-query s-port c-ip  sc-status sc-substatus sc-win32-status time-taken

Any Ideas on how to fix that?

The snippets from my startup code are:

Startup.cs:

public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
        //ommited configuration: authorization, database connection, etc.

        services.AddMvc(options =>
            {
                //some options;

            }).SetCompatibilityVersion(CompatibilityVersion.Version_2_1);

        //adds signalR
        services.AddSignalR();

    }


    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        else
        {
            app.UseExceptionHandler("/Home/Error");
            app.UseHsts();
        }


        app.UseHttpsRedirection();
        app.UseCookiePolicy();
        app.UseSession();
        app.UseAuthentication();

        app.UseSignalR(routes =>
        {
            routes.MapHub<MyHubName>("/MyHubName");
        });

        app.UseStaticFiles();
        app.UseMvc(routes =>
        {
            routes.MapRoute(
                name: "default",
                template: "{controller=Home}/{action=Index}/{id?}");
        });


    }
}

Program.cs :

public class Program
{
    public static void Main(string[] args)
    {
        CreateWebHostBuilder(args).Build().Run();
    }

    public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .UseStartup<Startup>()
            .UseKestrel()
            .UseIISIntegration();

}
Simon_Weaver
  • 140,023
  • 84
  • 646
  • 689
Mudo
  • 207
  • 3
  • 8
  • Did you resolve this? Could you post the launchsetttings.json? –  Mar 20 '19 at 19:19
  • Hello Jesse I Managed to isolate the problem: The reverse proxy (apache) was messing with the websocket headers. I will eventually post how I managed to isolate the problem. But the short story is: I used the IIS URL rewrite module to capture the negotiation request that was arriving without the "Upgrade: websocket" "Connection: Upgrade" headers at IIS, and injected those headers on the request before passing it to the server. Then I noticed the error changed, because the server responded with the 101 desired result, but with an error that those same headers were missing. – Mudo Mar 27 '19 at 12:36
  • Interesting. From an overall architecture, it's probably better to manage all the headers in Apache or IIS, but not both. We always make the proxy guy fix it :) –  Mar 27 '19 at 12:48

1 Answers1

1

Now that I understand your question more, make sure your IIS bindings are set to the listen on all ipaddresses, Type = http, hostname = the url you used from WAN, ipaddress = *, Port = 8088

Note: you said in your comment that 8088 is coming from the firewall to your server to access the application. So, you're not using https on the server, but http, and the incoming traffic is already encrypted by the firewall. You don't want to encrypt the traffic twice in route.

You also said that it works locally on the server, so one of two things are happening:

  1. Your server is only listening on the 127.0.0.1 IP address. Changing the * in the IIS bindings and using the correct port as above should fix that.

  2. The traffic is never making it your server properly from the network, or isn’t properly going back to the client.

Since 2 is happening, in part, based on your update, you need a URL rewrite somewhere, perhaps this will help:

Websockets reverse proxy in IIS 8

Maybe not exactly like that, but rewrite the traffic you are sending back to the client to remove the port to match the url the user used to connect from the client. The client doesn’t know about the 8088 port from the WAN.

  • Ok, so that sort of makes sense. You shouldn't expect to be able to route to https from localhost then, because the https encryption is happening upstream. So, check the IIS bindings to make sure it is listening on all ip addresses, not just localhost. –  Mar 06 '19 at 12:56
  • Updated answer, since http is working on the server, and your ssl is happening at the firewall, or elsewhere, you might not need to do anything with kestrel if the application is running directly in IIS. If the app is running in kestel, outside of IIS, then you'll have to do some url rewriting. –  Mar 06 '19 at 13:25
  • -Option 2 is not happening. The request makes to the server, but the ssl decryption somehow "corrupted" the request, and the server responds with 200 "ok" instead of 101. – Mudo Mar 06 '19 at 20:01
  • -Option 1: Bindings are Type http, ipaddress=*, Port = 8088. If I put anything, be it "localhost" or my website url, It starts responding with error 400. I edited my question including some logs – Mudo Mar 07 '19 at 01:29
  • I’m updating my answer as we go along, the hostname will be what the client uses. If the user were going to google.com, that would be in the hostname. The client will never see the internal IP from the WAN, they will see the the proxy. In your example, hostname is public website url like google.com. –  Mar 07 '19 at 01:33
  • Also, httpsredirection seems funky, since you are not really forcing https in the app itself. It’s going http, not https from the proxy to IIS most likely and that https work is happening by someone else. –  Mar 07 '19 at 01:53