top of page

WebSocket Payload Optimization: WebSocket Compression in ASP.NET Core

WebSocket communication is a powerful tool for real-time interactions in web applications. However, as data flows back and forth between the server and clients, payload size can become a critical factor affecting performance. In this article, we’ll explore WebSocket compression—a technique that reduces the amount of data transmitted over WebSocket connections, resulting in improved network efficiency and faster communication.

WebSocket Payload Optimization: WebSocket Compression in ASP.NET Core

What is WebSocket Compression?

WebSocket enables full-duplex communication between a client (usually a web browser) and a server over a single, long-lived connection. Unlike traditional HTTP, which follows a request-response model, WebSocket allows real-time, bidirectional data exchange. Here are some key points:

  • Persistent Connection: WebSockets establish a persistent connection, eliminating the need to repeatedly open and close connections for each interaction.

  • Low Latency: Real-time applications (such as chat, live updates, and online gaming) benefit from minimal latency provided by WebSockets.

  • Efficient Data Transfer: WebSockets transmit data in a compact binary format, reducing overhead compared to text-based protocols.


WebSocket compression enhances performance by minimizing the size of data payloads exchanged between the client and server.

  1. Reduced Bandwidth Usage: Compressed data consumes less network bandwidth. It is important for mobile devices and low-bandwidth connections.

  2. Faster Transmission: Smaller payloads travel faster over the network. Improves responsiveness in real-time applications.

  3. Resource Efficiency: Compression reduces server load and client processing time. Efficient resource utilization benefits both ends of the connection.



What is Content Security Policy (CSP)?

CSP is a security feature that helps prevent cross-site scripting (XSS) attacks and other code injection vulnerabilities. It allows you to specify the sources of content (such as scripts, styles, and images) that are allowed to be loaded by a web page. By defining a policy, you control what external resources can be executed or displayed within your application.


The frame-ancestors directive is part of CSP and specifically controls the embedding of your web application in <iframe> elements. It allows to embed your app using frames or iframes. The default value for frame-ancestors is 'self', which means your app can only be embedded in a <iframe> from the same origin (same domain, protocol, and port).


When using Interactive Server components (such as Blazor), the default CSP policy is set to 'self'. This ensures that your app can only be embedded in a <iframe> from the same origin from which it is served. While restrictive, this policy enhances security by preventing unauthorized embedding.


Enable WebSocket Compression in ASP.NET Core

WebSocket (RFC 6455) is a protocol that enables two-way persistent communication channels over TCP connections. It’s commonly used in applications that require fast, real-time communication, such as chat, dashboards, and games.


When you use WebSockets over HTTP/2, you take advantage of new features:

  1. Header Compression: HTTP/2 includes header compression mechanisms. Headers (such as request/response headers) are compressed before transmission, reducing network overhead. Smaller headers mean less data to transfer, improving efficiency.

  2. Multiplexing: HTTP/2 allows multiple requests and responses to share a single connection. WebSocket communication can coexist with other HTTP/2 traffic on the same connection. Efficiently utilizing a single connection optimizes resource utilization.


Kestrel, the web server used by ASP.NET Core, fully supports HTTP/2. These features are available out of the box on all HTTP/2-enabled platforms without requiring additional APIs or configuration.


SignalR vs. Raw WebSockets:

Aspect

SignalR

Raw WebSockets

Abstraction Level

High-level abstraction for real-time features

Low-level protocol for bidirectional communication

Use Case

Ideal for real-time applications (chat, live updates, etc.)

Suitable for custom protocols and low-level control

Transport Fallback

Provides fallback to other transports (e.g., long polling) if WebSockets are not available

No built-in fallback; relies solely on WebSockets

RPC Model

Offers a basic Remote Procedure Call (RPC) app model

No built-in RPC model; developers handle message processing

Performance

Comparable performance to raw WebSockets

Direct access to WebSockets may be slightly faster

Ease of Use

Easier to implement and manage

Requires manual handling of WebSocket connections

Security

Built-in security features (authentication, authorization)

Developers must handle security aspects themselves

Scalability

Scales well with multiple clients

Scalability depends on application design and server infrastructure

Browser Support

  • Chrome and Edge have HTTP/2 WebSockets enabled by default.

  • You can enable HTTP/2 WebSockets in Firefox by navigating to the about:config page and setting the network.http.spdy.websockets flag.


Below are the steps to enable WebSocket compression in ASP.NET Core:


STEP 1: Configure the Middleware:

Open your Program.cs file. Add the WebSockets middleware using app.UseWebSockets();.

public class Program 
{ 
	public static void Main(string[] args) 
	{ // Other configuration code... 
	app.UseWebSockets(); 
	
	// Add this line 
	// More configuration code... 
	} 
}

STEP 2: Customize Settings:

You can configure the following settings:

  • KeepAliveInterval: Determines how frequently to send “ping” frames to the client to ensure proxies keep the connection open. The default is two minutes.

  • AllowedOrigins: A list of allowed Origin header values for WebSocket requests. By default, all origins are allowed.

app.UseWebSockets(new WebSocketOptions 
{ 
	KeepAliveInterval = TimeSpan.FromMinutes(2), 
	AllowedOrigins = new[] { "https://example.com" } 
	// Customize as needed 
});

STEP 3: Accept WebSocket Connections:

In your controller or middleware, handle WebSocket requests. To enable compression, set DangerousEnableCompression to true in the WebSocketAcceptContext.

public async Task 
HandleWebSocket(HttpContext context) 
{ 
	if (context.WebSockets.IsWebSocketRequest) 
	{ 
		using var webSocket = await context.WebSockets.AcceptWebSocketAsync(new WebSocketAcceptContext 
		{ 
			DangerousEnableCompression = true 
			// Enable compression 
		}); 
			// Handle WebSocket communication... 
		} 
		else 
		{ 
			context.Response.StatusCode = 400; // Bad request 
	} 
}

STEP 4: Send and Receive Messages:

Once you have a WebSocket connection, you can send and receive messages. Use webSocket.SendAsync() and webSocket.ReceiveAsync() methods.


Customizing WebSocket Compression and CSP

1. Stricter CSP with compression:

To allow WebSocket compression but prevent embedding in any <iframe>, set ContentSecurityFrameAncestorsPolicy to 'none'.

builder.MapRazorComponents<App>()
    .AddInteractiveServerRenderMode(o => o.ContentSecurityFrameAncestorsPolicy = "'none'");

2. Removing CSP (centralized configuration):

If managing the CSP centrally, remove the frame-ancestors CSP by setting ContentSecurityFrameAncestorsPolicy to null.

builder.MapRazorComponents<App>()
    .AddInteractiveServerRenderMode(o => o.ContentSecurityFrameAncestorsPolicy = null);

Important Considerations and Best Practices

The important considerations and best practices related to Content Security Policy (CSP) and the frame-ancestors directive:


Browsers’ Handling of Multiple CSP Headers:

  • When a browser receives multiple CSP headers (e.g., from different sources or middleware), it combines them into a single policy.

  • The strictest policy directive value is enforced across all headers.

  • Therefore, if you have conflicting policies, the most restrictive one prevails.

  • Always ensure consistency and avoid conflicting directives.


Proper Usage of Single Quotes for CSP Values:

  • In CSP, single quotes (') are required around certain values.

  • Specifically, use single quotes for:

  • 'none': Indicates that no sources are allowed for a particular directive.

  • 'self': Refers to the same origin as the document.

  • Example:

  • ContentSecurityFrameAncestorsPolicy = "'none'"


Supported Values for frame-ancestors Directive:

  • The frame-ancestors directive controls embedding in <iframe> elements.

  • Supported values:

  • 'none': Prevents embedding in any <iframe>.

  • 'self': Allows embedding only in an <iframe> from the same origin.

  • Choose the appropriate value based on your security requirements.


Threat Mitigation Guidance:

  • Refer to threat mitigation guidance specific to your application and technology stack.

  • Understand potential risks related to CSP and WebSocket communication.

  • Implement additional security measures as needed.


Conclusion

In this article, we explored the benefits of using WebSockets over HTTP/2 and how to enable WebSocket compression in ASP.NET Core. By leveraging HTTP/2 features such as header compression and multiplexing, you can enhance performance and reduce latency in real-time communication applications.


Remember that ASP.NET Core SignalR simplifies WebSocket integration, providing transport fallback and a convenient app model. However, if you prefer working directly with raw WebSockets, enabling compression for messages is straightforward.


Stay informed about browser support for HTTP/2 WebSockets, and keep your routes and controllers up to date. With these practices, you’ll optimize WebSocket payloads and deliver efficient, responsive experiences to your users.

Σχόλια


bottom of page