twitchax / AspNetCore.Proxy

ASP.NET Core Proxies made easy.
MIT License
525 stars 83 forks source link

Proxy Blazor Server Application Websocket #58

Closed georgearnall closed 4 years ago

georgearnall commented 4 years ago

I'm trying to proxy a Blazor Server Application. I have managed to proxy the initial page load and retrieve all static files. I cannot get the SignalR websocket connection to establish. Is this supported? The Websocket proxy definition is never triggered and I get 502 error codes on the _blazor?id=XXX websocket request

I have the following config:

app.UseProxies(proxies =>
{
    // SignalR Connection
    proxies.Map("ui/_blazor", proxy => proxy.UseWs((context, args) =>
        {
            var queryString = context.Request.QueryString;
            return $"{proxyUrl}/{args["endpoint"]}{queryString}";
        }));
    // Fallback
    proxies.Map("ui/{*endpoint}", proxy => proxy.UseHttp(
        (context, args) =>
        {
            var queryString = context.Request.QueryString;
            return $"{proxyUrl}/{args["endpoint"]}{queryString}";
        }, builder => builder.WithHttpClientName("proxyClient")));
});

/// services
.ConfigurePrimaryHttpMessageHandler(() => new HttpClientHandler
{
    // Disable Certificate Validation
    ServerCertificateCustomValidationCallback = (_, __, ___, ____) => true,
    UseDefaultCredentials = true
});
twitchax commented 4 years ago

Hi @georgearnall, does proxyUrl contain ws or wss?

georgearnall commented 4 years ago

Yes @twitchax, although it is not above, I've just hard coded this now and it is the same issue. As mentioned, it doesn't appear to be triggering the "ui/_blazor" proxy at all. Interestingly if I add a ui/_blazor with a UseHttp proxy configuration that does trigger... See attached log, not sure if it helps point in the right direction

Verbose Log file > > info: Microsoft.AspNetCore.Hosting.Diagnostics[1] > Request starting HTTP/2 GET https://localhost:5003/ui > info: System.Net.Http.HttpClient.proxyClient.LogicalHandler[100] > Start processing HTTP request GET https://localhost:7003/ > info: System.Net.Http.HttpClient.proxyClient.ClientHandler[100] > Sending HTTP request GET https://localhost:7003/ > info: Microsoft.AspNetCore.Hosting.Diagnostics[1] > Request starting HTTP/1.1 GET https://localhost:7003/ > info: Microsoft.AspNetCore.Routing.EndpointMiddleware[0] > Executing endpoint '/_Host' > info: Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker[3] > Route matched with {page = "/_Host"}. Executing page /_Host > info: Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker[103] > Executing an implicit handler method - ModelState is Valid > info: Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker[104] > Executed an implicit handler method, returned result Microsoft.AspNetCore.Mvc.RazorPages.PageResult. > info: Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker[4] > Executed page /_Host in 277.9474ms > info: Microsoft.AspNetCore.Routing.EndpointMiddleware[1] > Executed endpoint '/_Host' > info: Microsoft.AspNetCore.Hosting.Diagnostics[2] > Request finished in 380.7956ms 200 text/html; charset=utf-8 > info: System.Net.Http.HttpClient.proxyClient.ClientHandler[101] > Received HTTP response after 500.987ms - OK > info: System.Net.Http.HttpClient.proxyClient.LogicalHandler[101] > End processing HTTP request after 513.5584ms - OK > info: Microsoft.AspNetCore.Hosting.Diagnostics[2] > Request finished in 568.3748ms 200 text/html; charset=utf-8 > info: Microsoft.AspNetCore.Hosting.Diagnostics[1] > Request starting HTTP/2 GET https://localhost:5003/ui/dist/js/main.js > info: Microsoft.AspNetCore.Hosting.Diagnostics[1] > Request starting HTTP/2 GET https://localhost:5003/ui/dist/css/main.css > info: Microsoft.AspNetCore.Hosting.Diagnostics[1] > Request starting HTTP/2 GET https://localhost:5003/ui/_framework/blazor.server.js > info: System.Net.Http.HttpClient.proxyClient.LogicalHandler[100] > Start processing HTTP request GET https://localhost:7003/_framework/blazor.server.js > info: System.Net.Http.HttpClient.proxyClient.LogicalHandler[100] > Start processing HTTP request GET https://localhost:7003/dist/js/main.js > info: System.Net.Http.HttpClient.proxyClient.LogicalHandler[100] > Start processing HTTP request GET https://localhost:7003/dist/css/main.css > info: System.Net.Http.HttpClient.proxyClient.ClientHandler[100] > Sending HTTP request GET https://localhost:7003/dist/js/main.js > info: System.Net.Http.HttpClient.proxyClient.ClientHandler[100] > Sending HTTP request GET https://localhost:7003/dist/css/main.css > info: System.Net.Http.HttpClient.proxyClient.ClientHandler[100] > Sending HTTP request GET https://localhost:7003/_framework/blazor.server.js > info: Microsoft.AspNetCore.Hosting.Diagnostics[1] > Request starting HTTP/1.1 GET https://localhost:7003/dist/css/main.css > info: Microsoft.AspNetCore.Hosting.Diagnostics[1] > Request starting HTTP/1.1 GET https://localhost:7003/_framework/blazor.server.js > info: Microsoft.AspNetCore.Hosting.Diagnostics[1] > Request starting HTTP/1.1 GET https://localhost:7003/dist/js/main.js > info: System.Net.Http.HttpClient.proxyClient.ClientHandler[101] > Received HTTP response after 76.3145ms - OK > info: System.Net.Http.HttpClient.proxyClient.LogicalHandler[101] > End processing HTTP request after 95.6325ms - OK > info: Microsoft.AspNetCore.StaticFiles.StaticFileMiddleware[2] > Sending file. Request path: '/dist/js/main.js'. Physical path: '\wwwroot\dist\js\main.js' > info: System.Net.Http.HttpClient.proxyClient.ClientHandler[101] > Received HTTP response after 95.4753ms - OK > info: System.Net.Http.HttpClient.proxyClient.LogicalHandler[101] > End processing HTTP request after 106.0832ms - OK > info: Microsoft.AspNetCore.Hosting.Diagnostics[2] > Request finished in 29.7341ms 200 application/javascript > info: System.Net.Http.HttpClient.proxyClient.ClientHandler[101] > Received HTTP response after 109.4234ms - OK > info: Microsoft.AspNetCore.Hosting.Diagnostics[2] > Request finished in 131.8885ms 200 application/javascript > info: System.Net.Http.HttpClient.proxyClient.LogicalHandler[101] > End processing HTTP request after 124.7856ms - OK > info: Microsoft.AspNetCore.StaticFiles.StaticFileMiddleware[2] > Sending file. Request path: '/dist/css/main.css'. Physical path: '\wwwroot\dist\css\main.css' > info: Microsoft.AspNetCore.Hosting.Diagnostics[2] > Request finished in 83.0475ms 200 text/css > info: Microsoft.AspNetCore.StaticFiles.StaticFileMiddleware[2] > Sending file. Request path: '/_framework/blazor.server.js'. Physical path: 'N/A' > info: Microsoft.AspNetCore.Hosting.Diagnostics[2] > Request finished in 97.4033ms 200 application/javascript > info: Microsoft.AspNetCore.Hosting.Diagnostics[2] > Request finished in 188.6684ms 200 text/css > info: Microsoft.AspNetCore.Hosting.Diagnostics[2] > Request finished in 188.7019ms 200 application/javascript > info: Microsoft.AspNetCore.Hosting.Diagnostics[1] > Request starting HTTP/2 GET https://localhost:5003/ui/dist/webfonts/fa-solid-900.woff2 > info: System.Net.Http.HttpClient.proxyClient.LogicalHandler[100] > Start processing HTTP request GET https://localhost:7003/dist/webfonts/fa-solid-900.woff2 > info: System.Net.Http.HttpClient.proxyClient.ClientHandler[100] > Sending HTTP request GET https://localhost:7003/dist/webfonts/fa-solid-900.woff2 > info: Microsoft.AspNetCore.Hosting.Diagnostics[1] > Request starting HTTP/1.1 GET https://localhost:7003/dist/webfonts/fa-solid-900.woff2 > info: System.Net.Http.HttpClient.proxyClient.ClientHandler[101] > Received HTTP response after 29.5952ms - OK > info: System.Net.Http.HttpClient.proxyClient.LogicalHandler[101] > End processing HTTP request after 35.8456ms - OK > info: Microsoft.AspNetCore.StaticFiles.StaticFileMiddleware[2] > Sending file. Request path: '/dist/webfonts/fa-solid-900.woff2'. Physical path: '\wwwroot\dist\webfonts\fa-solid-900.woff2' > infoinfo: Microsoft.AspNetCore.Hosting.Diagnostics[2] > Request finished in 26.6165ms 200 font/woff2 > : Microsoft.AspNetCore.Hosting.Diagnostics[2] > Request finished in 52.8776ms 200 font/woff2 > info: Microsoft.AspNetCore.Hosting.Diagnostics[1] > Request starting HTTP/2 POST https://localhost:5003/ui/_blazor/negotiate?negotiateVersion=1 text/plain;charset=UTF-8 0 > info: System.Net.Http.HttpClient.proxyClient.LogicalHandler[100] > Start processing HTTP request POST https://localhost:7003/_blazor/negotiate?negotiateVersion=1 > info: System.Net.Http.HttpClient.proxyClient.ClientHandler[100] > Sending HTTP request POST https://localhost:7003/_blazor/negotiate?negotiateVersion=1 > info: Microsoft.AspNetCore.Hosting.Diagnostics[1] > Request starting HTTP/1.1 POST https://localhost:7003/_blazor/negotiate?negotiateVersion=1 text/plain; charset=UTF-8 0 > info: Microsoft.AspNetCore.Routing.EndpointMiddleware[0] > Executing endpoint '/_blazor/negotiate' > info: Microsoft.AspNetCore.Routing.EndpointMiddleware[1] > Executed endpoint '/_blazor/negotiate' > info: System.Net.Http.HttpClient.proxyClient.ClientHandler[101] > Received HTTP response after 40.2669ms - OK > info: Microsoft.AspNetCore.Hosting.Diagnostics[2] > Request finished in 30.5858ms 200 application/json > info: System.Net.Http.HttpClient.proxyClient.LogicalHandler[101] > End processing HTTP request after 50.2283ms - OK > info: Microsoft.AspNetCore.Hosting.Diagnostics[2] > Request finished in 63.172ms 200 application/json > info: Microsoft.AspNetCore.Hosting.Diagnostics[1] > Request starting HTTP/2 GET https://localhost:5003/favicon.ico > info: Microsoft.AspNetCore.Hosting.Diagnostics[2] > Request finished in 5.5558ms 404 > info: Microsoft.AspNetCore.Hosting.Diagnostics[1] > Request starting HTTP/1.1 GET https://localhost:5003/ui/_blazor?id=KfqJrgcpG8oS2kD2e7uMoA > info: Microsoft.AspNetCore.Hosting.Diagnostics[2] > Request finished in 3.6766ms 502 > info: Microsoft.AspNetCore.Hosting.Diagnostics[1] > Request starting HTTP/2 POST https://localhost:5003/ui/_blazor/negotiate?negotiateVersion=1 text/plain;charset=UTF-8 0 > info: System.Net.Http.HttpClient.proxyClient.LogicalHandler[100] > Start processing HTTP request POST https://localhost:7003/_blazor/negotiate?negotiateVersion=1 > info: System.Net.Http.HttpClient.proxyClient.ClientHandler[100] > Sending HTTP request POST https://localhost:7003/_blazor/negotiate?negotiateVersion=1 > info: Microsoft.AspNetCore.Hosting.Diagnostics[1] > Request starting HTTP/1.1 POST https://localhost:7003/_blazor/negotiate?negotiateVersion=1 text/plain; charset=UTF-8 0 > info: Microsoft.AspNetCore.Routing.EndpointMiddleware[0] > Executing endpoint '/_blazor/negotiate' > infoinfo: Microsoft.AspNetCore.Routing.EndpointMiddleware[1] > Executed endpoint '/_blazor/negotiate' > : System.Net.Http.HttpClient.proxyClient.ClientHandler[101] > Received HTTP response after 18.293ms - OK > infoinfo: System.Net.Http.HttpClient.proxyClient.LogicalHandler[101] > End processing HTTP request after 28.4356ms - OK > : Microsoft.AspNetCore.Hosting.Diagnostics[2] > Request finished in 15.355ms 200 application/json > info: Microsoft.AspNetCore.Hosting.Diagnostics[2] > Request finished in 41.8808ms 200 application/json > info: Microsoft.AspNetCore.Hosting.Diagnostics[1] > Request starting HTTP/2 GET https://localhost:5003/ui/_blazor?id=g_wwqjdl3Rvz9GOXh3KIbQ&_=1593116635075 text/plain;charset=UTF-8 > info: Microsoft.AspNetCore.Hosting.Diagnostics[2] > Request finished in 3.6794ms 502 >

image

twitchax commented 4 years ago

It looks like, in the log, that the web server is interpreting this request as an http request.

Request starting HTTP/1.1 GET https://localhost:5003/ui/_blazor?id=KfqJrgcpG8oS2kD2e7uMoA

Did you "turn on" websocket support by using app.UseWebSockets();?

georgearnall commented 4 years ago

Ah, I missed that! It's triggering on proxy.UseWs() now, but it's still not going through.

app.UseWebSockets();
app.UseProxies(proxies =>
{
    proxies.Map("ui/{*endpoint}", proxy => proxy.UseHttp(
        (context, args) =>
        {
            var queryString = context.Request.QueryString;
            return $"{proxyUrl}/{args["endpoint"]}{queryString}";
        }, builder => builder.WithHttpClientName("proxyClient"))
        .UseWs(
            (context, args) => {
                Console.WriteLine("WS");
                var queryString = context.Request.QueryString;
                return  $"{proxyUrl.Replace("https://", "wss://")}/{args["endpoint"]}{queryString}";
            })
        );
});

Most requests will log the connection to the other server (7003), however this one just exits immediately. Could it be related to the HTTPClient? I don't seem to be able to add the same builder.WithHttpClientName("proxyClient")) on the websocket config? I'm currently using self signed certificates.

Request starting HTTP/1.1 GET https://localhost:5003/ui/_blazor?id=TZ6JpaMsF0fDY3tgujJOQg
WS
info: Microsoft.AspNetCore.Hosting.Diagnostics[2]
      Request finished in 86.0356ms 502

Interestingly, Blazor Server has now started working with long polling but this isn't really ideal.

twitchax commented 4 years ago

Hi @georgearnall, I can investigate. Any way you can give me a toy app that repros this in a gist or something? :)

twitchax commented 4 years ago

Or, if you want to be really nice, check this repo out and add a WS test case that fails? 😬

twitchax commented 4 years ago

@georgearnall, closing this. Please let me know if you can provide a small repro. 😄

georgearnall commented 4 years ago

Apologies for not getting back to you, I've been pretty busy. This might be something I will come back to in the future. For now I have switch to WebAssembly and no longer require the proxy. Thanks for your help nonetheless.