How to use webSocketOptions.AllowedOrigins? - c#

I have an ASP web app.
The app opens a websocket communication server. The websocket server works properly.
var webSocketOptions = new WebSocketOptions()
{
KeepAliveInterval = TimeSpan.FromSeconds(120),
};
app.UseWebSockets(webSocketOptions);
app.Use(async (context, next) =>
{
if (context.Request.Path == "/ws")
{
if (context.WebSockets.IsWebSocketRequest)
{
using (WebSocket webSocket = await context.WebSockets.AcceptWebSocketAsync())
{
//do some stuff
}
}
else
{
context.Response.StatusCode = 400;
}
}
else
{
await next();
}
});
When I open my domain example.com and go to Chrome Web Console, the following code works :
var socket = new WebSocket("wss://www.example.com/ws");
However when I add the security constraint :
webSocketOptions.AllowedOrigins.Add("https://www.example.com");
The websocket connection doesn't work anymore. I'm getting the error
VM376:1 WebSocket connection to 'wss://www.example.com/ws' failed: Error during WebSocket handshake: Unexpected response code: 403
Can anyone help please on how to use webSocketOptions.AllowedOrigins ?
I want the Websocket access be allowed only when a request is made from my website www.example.com
Thanks

You have to configure "webSocketOptions.AllowedOrigins" inside your startup middleWare
here a microsoft websocket doc:
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/websockets?view=aspnetcore-3.1

Related

Signalr connection is closing on web garden with 404 code

A signalr API (.NET 6) is hosted on IIS with "web garden" mode. Client frequently closes a connection with the following message: "System.Net.Http.HttpRequestException: Response status code does not indicate success: 404 (Not Found)."
When IIS config is changed not to use webgarden (max worker processes is set to 1) - error does not occur.
I have read that redis backplane would be a solution in such case (webgarden) but inlcuding it in my project did not help - the same error message occurs in same scenarios.
Here is my sample code from the Startup:
services.AddSignalR(options =>
{
options.EnableDetailedErrors = true;
})
.AddHubOptions<MyHub>(options =>
{
options.AddFilter<MyFilter>();
})
.AddNewtonsoftJsonProtocol()
.AddStackExchangeRedis(o =>
{
o.ConnectionFactory = async writer =>
{
var config = new ConfigurationOptions
{
AbortOnConnectFail = false
};
config.EndPoints.Add(IP_ADDR, PORT_NO);
config.DefaultDatabase = DEFAULT_DB;
var connection = await ConnectionMultiplexer.ConnectAsync(config, writer);
connection.ConnectionFailed += (_, e) =>
{
Console.WriteLine("Connection to Redis failed.");
};
if (!connection.IsConnected)
{
Console.WriteLine("Did not connect to Redis.");
}
else
{
Console.WriteLine("Connected to Redis.");
}
return connection;
};
});
Did I miss somethig in my code? Or maybe it is about IIS configuration?

How to preserve a websocket connection on ASP.NET Core?

I have an ASP.NET Core application that needs to communicate with the browser through websockets.
The server is configured as follows :
var webSocketOptions = new WebSocketOptions()
{
KeepAliveInterval = TimeSpan.FromSeconds(120),
};
app.UseWebSockets(webSocketOptions);
app.Use(async (context, next) =>
{
if (context.Request.Path == "/ws")
{
if (context.WebSockets.IsWebSocketRequest)
{
using (var WS = await context.WebSockets.AcceptWebSocketAsync())
{
// do some stuff or even nothing
} // <<<<<<<<<<<<<< connection dies here
}
else
{
context.Response.StatusCode = 400;
}
}
else
{
await next();
}
});
The connection works properly with the browser, but at the end of the following block, the connection automatically dies.
using (var WS = await context.WebSockets.AcceptWebSocketAsync())
{
// do some stuff or even nothing
} // <<<<<<<<<<<<<< connection dies here
There is no exception in my code, but still the connection dies. I guess this is normal since the variable WS is destroyed after using.
I would like to know please how can I make a websocket connection last and not destroyed immediately after the server gets a connection request.
Also, I see that with this code
WS = await context.WebSockets.AcceptWebSocketAsync()
a new web socket is created for every connection attempt, even if it's coming from the same client that had previously connected. Is possible to change that behavior please ?
Thanks,
Cheers

Can't connect secured Asp.Net Core Web Socket hosted on Azure Web App (using TLS)

I spent a whole day searching for a solution but I didn't solve my problem. As you can see from the title, I implemented a basic Web Socket using Asp.Net Core (3.1) framework and I deployed it on Azure (on a Web App service). I successfully make it works without the TLS protocol (so I think the ws has been configured in a good manner) but when I try to connect using wss I receive this errors on the client side:
System.Net.WebSockets.WebSocketException : Unable to connect to the remote server
System.Net.WebException: The underlying connection was closed: An unexpected error occurred on a send.
System.IO.IOException: Unable to read data from the transport connection: An existing connection was forcibly closed by the remote host.
System.Net.Sockets.SocketException: An existing connection was forcibly closed by the remote host
I tried to switch the "HTTPS Only" trigger on azure portal but it keep refusing any client to connect.
Do you have any idea how to let wss works with Azure Web App? Do I need to configure a certificate? I read that azure provide a certificate if the user didn't have one. Thanks and regards
UPDATE: the code has been "copied" from this great Les Jackson's tutorial and is available on git hub at this precise address. The important part of the code is here:
/* THIS IS THE SERVER PART WHERE THE CONNECTION IS ACCEPTED */
namespace WebSocketServer.Middleware
{
public class WebSocketServerMiddleware
{
private readonly RequestDelegate _next;
private WebSocketServerConnectionManager _manager;
public WebSocketServerMiddleware(RequestDelegate next, WebSocketServerConnectionManager manager)
{
_next = next;
_manager = manager;
}
public async Task InvokeAsync(HttpContext context)
{
if (context.WebSockets.IsWebSocketRequest)
{
WebSocket webSocket = await context.WebSockets.AcceptWebSocketAsync();
await Receive(webSocket, async (result, buffer) =>
{
if (result.MessageType == WebSocketMessageType.Text)
{
Console.WriteLine($"Receive->Text");
return;
}
else if (result.MessageType == WebSocketMessageType.Close)
{
await sock.CloseAsync(result.CloseStatus.Value, result.CloseStatusDescription, CancellationToken.None);
return;
}
});
}
else
{
await _next(context);
}
}
}
}
/* THIS IS THE STARTUP FILE*/
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddWebSocketServerConnectionManager();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseWebSockets();
app.UseWebSocketServer();
}
}
/* THIS IS THE CLIENT (WRITTEN IN NET FULLFRAMEWORK) */
Console.Write("Connecting....");
var cts = new CancellationTokenSource();
var socket = new ClientWebSocket();
string wsUri = "wss://testingwebsocket123123.azurewebsites.net";
await socket.ConnectAsync(new Uri(wsUri), cts.Token);
Console.WriteLine(socket.State);
await Task.Factory.StartNew(
async () =>
{
var rcvBytes = new byte[1024 * 1024];
var rcvBuffer = new ArraySegment<byte>(rcvBytes);
while (true)
{
WebSocketReceiveResult rcvResult = await socket.ReceiveAsync(rcvBuffer, cts.Token);
byte[] msgBytes = rcvBuffer.Skip(rcvBuffer.Offset).Take(rcvResult.Count).ToArray();
string rcvMsg = Encoding.UTF8.GetString(msgBytes);
Console.WriteLine("Received: {0}", rcvMsg);
}
}, cts.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default);
Thank you for reading
As disussed over the comments, it works fine with the provided javascript client in the sample. The error in .net client happens because of TLS version when you connect from c# client with full framework. The screenshot of your Azure web app enforces min TLS 1.2. Set that in the .net client like below:
System.Net.ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls12;

Calling WebApi from Console Application is Failing

I have a WebApi that is running on Azure and fully tested via Postman
This API takes 2 headers
Content-Type: application/json
AppToken: {{AppToken}}
I want to call this API from a console application and I thought it is a pretty straight forward process. Here is my Main program
static void Main(string[] args)
{
HttpClient client = new HttpClient();
client.BaseAddress = new Uri("BASE_ADDRESS/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Add("AppToken", "MY_APP_TOKEN_VALUE");
// Call API to create the registration
CreateRegistration(client).Wait();
}
public static async Task CreateRegistration(HttpClient client)
{
using (client)
{
try
{
Registration reg1 = new Registration { email = "test#ssdafsds.com", clientId = 2342342, registrationId = 23423, ProgramId = 13 };
HttpResponseMessage responseMessage = await client.PostAsJsonAsync("api/auth/register", reg1);
responseMessage.EnsureSuccessStatusCode();
// Handle success
}
catch (Exception ex)
{
// Handle failure
Console.WriteLine(ex.StackTrace);
}
}
}
I am getting an internal server error as the "Reason Phrase" with status code 500. I am not sure why I am getting this error. I should be getting 201 Created like postman
Any idea how to solve this issue?
I made a mistake by passing a program id that I don't actually have in my database which failed the FK_Reference and caused this issue. Thank you all for your help.

Websockets connection failing only with Chrome accessing ASP Core Server using subprotocol

I have a MQTT client / broker implementation, client is browser websockets based, server is ASPNetCore 1.1 with Kestrel & Websockets (0.1.0).
As part of the MQTT spec, I am initiating a websockets connection using "MQTT" as the subprotocol, which the Server looks for when establishing the websockets connection.
JavaScript connection established using the following:
var wsMQTT = new WebSocket(g.protocol + "//" + g.serverName + ":" + net.WSPORT, "MQTT");
wsMQTT.binaryType = "arraybuffer";
Here is the Chrome error (basically timeout as nothing coming back from the server):
Error during WebSocket handshake: Sent non-empty 'Sec-WebSocket-Protocol' header but no response was received.
Here is the ASPNetCore middleware for handling the websocket connection:
public async Task Invoke(HttpContext context)
{
if (context.WebSockets.IsWebSocketRequest)
{
CancellationToken ct = context.RequestAborted;
WebSocket webSocket = await context.WebSockets.AcceptWebSocketAsync();
MQTT.Broker MQTTClient = new MQTT.Broker(_host, webSocket, context.Connection.RemoteIpAddress.ToString(), context.Connection.RemotePort.ToString(), _MQTTUser, _MQTTPass);
MQTTClient.TimeoutEvent += CloseWSSess; // When MQTT stack sees ping/resp timeout, shut down WS
if (context.WebSockets.WebSocketRequestedProtocols.Count == 1 &&
context.WebSockets.WebSocketRequestedProtocols[0].Substring(0, 4) == "MQTT")
{
_host.WriteLog(LOGTYPES.INFORMATION, "Received new MQTT WebSocket connection from " + context.Connection.RemoteIpAddress);
// receive loop
while (!ct.IsCancellationRequested && webSocket.State == WebSocketState.Open)
{
var response = await ReceiveBin(MQTTClient, ct);
}
}
else
{
CloseWSSess(MQTTClient, "Invalid WebSockets subprotocol requested from client [" + context.Connection.RemoteIpAddress.ToString() + "]. Session aborted.");
}
}
else
{
await _next.Invoke(context); // Not a web socket request
}
}
Here is the error stack from ASP Core (which isn't getting trapped inside the websockets library):
Connection id "0HL5GB806MGNU": An unhandled exception was thrown by the application.
System.IO.IOException: Unexpected end of stream at Microsoft.AspNetCore.WebSockets.Protocol.CommonWebSocket.<EnsureDataAvailableOrReadAsync>d__38.MoveNext()
Works fine with IE, Edge and Firefox, and doesn't crash ASP Core if I remove the subprotocol from the client connection. So I suspect Chrome is establishing the connection a little differently to the other browsers which isn't being handled properly by ASP Core Websockets.
Any ideas how to solve this?
Fix is to specify the subprotocol in AcceptWebSocketAsync (ie. AcceptWebSocketAsync("MQTT").

Categories