Reputation: 405
I'm currently running a .Net Core application with SignalR on an Ubuntu Apache web server. This .Net Core app is reachable by the example.com/api path.
I also have a Vue JS app running on the webserver as well. When I attempt to establish a websocket connection to the the SignalR application, the following error occurs:
WebSocket connection to 'wss://example.com/api/mainHub?id=V3XZrhdsQDTTMIZvQ-8cbQ' failed: Error during WebSocket handshake: Unexpected response code: 200
Error: Failed to start the transport 'WebSockets': null
The Apache config for the .Net Core app is as follows:
#Main/Desired Path - https://example.com
<VirtualHost *:443>
DocumentRoot /var/www/example.com/dist/spa
ServerName example.com
#SSL Certs
SSLEngine on
SSLProtocol all -SSLv2
SSLCipherSuite ALL:!ADH:!EXPORT:!SSLv2:!RC4+RSA:+HIGH:+MEDIUM:!LOW:!RC4
SSLCertificateFile /etc/apache2/ssl/example.crt
SSLCertificateKeyFile /etc/apache2/ssl/server.key
SSLCertificateChainFile /etc/apache2/ssl/example.ca-bundle
#Upgrade Websockets
RewriteEngine on
RewriteCond %{HTTP:UPGRADE} ^WebSocket$ [NC]
RewriteCond %{HTTP:CONNECTION} Upgrade$ [NC]
RewriteRule /(.*) ws://localhost:5000/$1 [P]
#Other specific directories
ProxyPreserveHost On
ProxyPass "/api" http://localhost:5000
ProxyPassReverse "/api" http://localhost:5000
</VirtualHost>
I've added the RewriteEngine to upgrade the websockets, I'm not sure if the issue is with the configuration on Apache, or in the .Net Core app itself.
The .Net Core app has the following in the startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(options => options.AddPolicy("CorsPolicy", builder =>
{
builder
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials()
.WithOrigins("*");
}));
services.AddSignalR();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
//first handle any websocket requests
app.UseWebSockets();
app.UseCors("CorsPolicy");
app.UseSignalR(routes =>
{
routes.MapHub<mainHub>("/mainHub");
routes.MapHub<accounthub>("/accountHub");
});
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.Run(async (context) =>
{
await context.Response.WriteAsync("Hello World!");
});
}
It seems that either something is incorrect in the startup.cs
that is sending a 200 response back to the client when establishing the websocket connection or the Apache virtual host is not properly upgrading the websocket.
Upvotes: 3
Views: 2315
Reputation: 51
I have found this on a site ( http://shyammakwana.me/server/websockets-with-apache-reverse-proxy-with-ssl.html ) which seems to work also for me. The apache config for the given site should be like this
<IfModule mod_ssl.c>
<VirtualHost *:443>
RewriteEngine On
ProxyPreserveHost On
ProxyRequests Off
# allow for upgrading to websockets
RewriteEngine On
RewriteCond %{HTTP:Upgrade} =websocket [NC]
RewriteRule /(.*) ws://localhost:5000/$1 [P,L]
RewriteCond %{HTTP:Upgrade} !=websocket [NC]
RewriteRule /(.*) http://localhost:5000/$1 [P,L]
ProxyPass "/" "http://localhost:5000/"
ProxyPassReverse "/" "http://localhost:5000/"
ProxyPass "/chatHub" "ws://localhost:5000/chatHub"
ProxyPassReverse "/chatHub" "ws://localhost:5000/chatHub"
ServerName site.com
SSLCertificateFile /etc/letsencrypt/live/site.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/site.com/privkey.pem
Include /etc/letsencrypt/options-ssl-apache.conf
</VirtualHost>
</IfModule>
Also you should have these modules enabled:
a2enmod proxy
a2enmod proxy_wstunnel
Upvotes: 5
Reputation: 63
I am also getting this error while deploying a ASP.NET Core 3.1 Blazor Server-side app to an Ubuntu 18.04 server running Apache2.
In combination with this error, I am also getting a 404 error trying to access static resources (.css files, images and scripts) from the Linux server, but everything works perfectly in an IIS environment. I've also noted that the contents of the WWWRoot folder in my solution do not appear to be copied to the linux server when I publish the app. I am using the FolderProfile method of publishing, then copying the contents of the publish folder to the linux server via SFTP.
The only "real" change from the boilerplate startup.Configure class (from the New Blazor-Server Web App template in VS2019) I have made is to add the UseForwardHeaders directive as described at https://learn.microsoft.com/en-us/aspnet/core/host-and-deploy/linux-apache?view=aspnetcore-3.1
I've also added Authentication and Authorisation to the app, but once again, the whole thing runs perfectly on IIS, but it needs to be installed on a production linux server once testing is complete.
My Startup.Configure looks like:
public static void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseForwardedHeaders(new ForwardedHeadersOptions
{
ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
});
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
}
else
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
// Enable SyncFusion Community Edition License
Syncfusion.Licensing.SyncfusionLicenseProvider
.RegisterLicense("Key-Removed");
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
// Use Authentication and Authorisation providers in the application
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.MapBlazorHub();
endpoints.MapFallbackToPage("/_Host");
});
}
Also, on the linux side, the virtual host configuration file looks like this:
#<VirtualHost *:*>
# RequestHeader set "X-Forwarded-Proto" expr=%{REQUEST_SCHEME}
#</VirtualHost>
<VirtualHost *:80>
ProxyPreserveHost On
ProxyPass / http://127.0.0.1:5000/
ProxyPassReverse / http://127.0.0.1:5000/
ServerName www.grafton.internal
ErrorLog ${APACHE_LOG_DIR}labapp_error.log
CustomLog ${APACHE_LOG_DIR}labapp_access.log common
</VirtualHost>
Note I have commented out the top 3 lines because I could not get this expression to work correctly - Apache failed to start with every variation of the 2nd line I tried. I believe this might be related to my problem, but I am not sure what to do about it.
I also want to stress that this app is for internal use only, and the web server is only visible on our Intranet - there are no external / internet facing servers or sites and access is limited to a very few people via physical access to the servers, and restricted fixed IP addresses, so I am not at all concerned with using SSL.
Any assistance with this one would be appreciated. I've been trying to get this running for a week, and only have a week left before it MUST be in the linux test environment.
Upvotes: -1