Skip to content

Commit 028470e

Browse files
Add WebSocket compression support (#32600)
1 parent 65b3e76 commit 028470e

19 files changed

+1035
-29
lines changed

src/Http/Headers/src/HeaderNames.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,9 @@ public static class HeaderNames
225225
/// <summary>Gets the <c>Sec-WebSocket-Version</c> HTTP header name.</summary>
226226
public static readonly string SecWebSocketVersion = "Sec-WebSocket-Version";
227227

228+
/// <summary>Gets the <c>Sec-WebSocket-Extensions</c> HTTP header name.</summary>
229+
public static readonly string SecWebSocketExtensions = "Sec-WebSocket-Extensions";
230+
228231
/// <summary>Gets the <c>Server</c> HTTP header name.</summary>
229232
public static readonly string Server = "Server";
230233

src/Http/Headers/src/PublicAPI.Unshipped.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ Microsoft.Net.Http.Headers.RangeConditionHeaderValue.RangeConditionHeaderValue(M
55
static readonly Microsoft.Net.Http.Headers.HeaderNames.Baggage -> string!
66
static readonly Microsoft.Net.Http.Headers.HeaderNames.Link -> string!
77
static readonly Microsoft.Net.Http.Headers.HeaderNames.ProxyConnection -> string!
8+
static readonly Microsoft.Net.Http.Headers.HeaderNames.SecWebSocketExtensions -> string!
89
static readonly Microsoft.Net.Http.Headers.HeaderNames.XContentTypeOptions -> string!
910
static readonly Microsoft.Net.Http.Headers.HeaderNames.XPoweredBy -> string!
1011
static readonly Microsoft.Net.Http.Headers.HeaderNames.XUACompatible -> string!

src/Http/Http.Abstractions/src/PublicAPI.Unshipped.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,4 @@ abstract Microsoft.AspNetCore.Http.HttpRequest.ContentType.get -> string?
2323
static Microsoft.AspNetCore.Builder.UseExtensions.Use(this Microsoft.AspNetCore.Builder.IApplicationBuilder! app, System.Func<Microsoft.AspNetCore.Http.HttpContext!, Microsoft.AspNetCore.Http.RequestDelegate!, System.Threading.Tasks.Task!>! middleware) -> Microsoft.AspNetCore.Builder.IApplicationBuilder!
2424
static Microsoft.AspNetCore.Builder.UseMiddlewareExtensions.UseMiddleware(this Microsoft.AspNetCore.Builder.IApplicationBuilder! app, System.Type! middleware, params object?[]! args) -> Microsoft.AspNetCore.Builder.IApplicationBuilder!
2525
static Microsoft.AspNetCore.Builder.UseMiddlewareExtensions.UseMiddleware<TMiddleware>(this Microsoft.AspNetCore.Builder.IApplicationBuilder! app, params object?[]! args) -> Microsoft.AspNetCore.Builder.IApplicationBuilder!
26+
virtual Microsoft.AspNetCore.Http.WebSocketManager.AcceptWebSocketAsync(Microsoft.AspNetCore.Http.WebSocketAcceptContext! acceptContext) -> System.Threading.Tasks.Task<System.Net.WebSockets.WebSocket!>!

src/Http/Http.Abstractions/src/WebSocketManager.cs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
// Copyright (c) .NET Foundation. All rights reserved.
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

4+
using System;
45
using System.Collections.Generic;
56
using System.Net.WebSockets;
67
using System.Threading.Tasks;
78

89
namespace Microsoft.AspNetCore.Http
910
{
1011
/// <summary>
11-
/// Manages the establishment of WebSocket connections for a specific HTTP request.
12+
/// Manages the establishment of WebSocket connections for a specific HTTP request.
1213
/// </summary>
1314
public abstract class WebSocketManager
1415
{
@@ -37,5 +38,12 @@ public virtual Task<WebSocket> AcceptWebSocketAsync()
3738
/// <param name="subProtocol">The sub-protocol to use.</param>
3839
/// <returns>A task representing the completion of the transition.</returns>
3940
public abstract Task<WebSocket> AcceptWebSocketAsync(string? subProtocol);
41+
42+
/// <summary>
43+
///
44+
/// </summary>
45+
/// <param name="acceptContext"></param>
46+
/// <returns></returns>
47+
public virtual Task<WebSocket> AcceptWebSocketAsync(WebSocketAcceptContext acceptContext) => throw new NotImplementedException();
4048
}
4149
}

src/Http/Http.Features/src/IHeaderDictionary.Keyed.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,9 @@ public partial interface IHeaderDictionary
202202
/// <summary>Gets or sets the <c>Sec-WebSocket-Version</c> HTTP header.</summary>
203203
StringValues SecWebSocketVersion { get => this[HeaderNames.SecWebSocketVersion]; set => this[HeaderNames.SecWebSocketVersion] = value; }
204204

205+
/// <summary>Gets or sets the <c>Sec-WebSocket-Extensions</c> HTTP header.</summary>
206+
StringValues SecWebSocketExtensions { get => this[HeaderNames.SecWebSocketExtensions]; set => this[HeaderNames.SecWebSocketExtensions] = value; }
207+
205208
/// <summary>Gets or sets the <c>Server</c> HTTP header.</summary>
206209
StringValues Server { get => this[HeaderNames.Server]; set => this[HeaderNames.Server] = value; }
207210

src/Http/Http.Features/src/PublicAPI.Unshipped.txt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,8 @@ Microsoft.AspNetCore.Http.IHeaderDictionary.RetryAfter.get -> Microsoft.Extensio
161161
Microsoft.AspNetCore.Http.IHeaderDictionary.RetryAfter.set -> void
162162
Microsoft.AspNetCore.Http.IHeaderDictionary.SecWebSocketAccept.get -> Microsoft.Extensions.Primitives.StringValues
163163
Microsoft.AspNetCore.Http.IHeaderDictionary.SecWebSocketAccept.set -> void
164+
Microsoft.AspNetCore.Http.IHeaderDictionary.SecWebSocketExtensions.get -> Microsoft.Extensions.Primitives.StringValues
165+
Microsoft.AspNetCore.Http.IHeaderDictionary.SecWebSocketExtensions.set -> void
164166
Microsoft.AspNetCore.Http.IHeaderDictionary.SecWebSocketKey.get -> Microsoft.Extensions.Primitives.StringValues
165167
Microsoft.AspNetCore.Http.IHeaderDictionary.SecWebSocketKey.set -> void
166168
Microsoft.AspNetCore.Http.IHeaderDictionary.SecWebSocketProtocol.get -> Microsoft.Extensions.Primitives.StringValues
@@ -232,6 +234,14 @@ Microsoft.AspNetCore.Http.Features.FeatureCollection.IsReadOnly.get -> bool (for
232234
Microsoft.AspNetCore.Http.Features.FeatureCollection.Set<TFeature>(TFeature? instance) -> void (forwarded, contained in Microsoft.Extensions.Features)
233235
Microsoft.AspNetCore.Http.Features.FeatureCollection.this[System.Type! key].get -> object? (forwarded, contained in Microsoft.Extensions.Features)
234236
Microsoft.AspNetCore.Http.Features.FeatureCollection.this[System.Type! key].set -> void (forwarded, contained in Microsoft.Extensions.Features)
237+
Microsoft.AspNetCore.Http.WebSocketAcceptContext.DangerousEnableCompression.get -> bool
238+
Microsoft.AspNetCore.Http.WebSocketAcceptContext.DangerousEnableCompression.set -> void
239+
Microsoft.AspNetCore.Http.WebSocketAcceptContext.DisableServerContextTakeover.get -> bool
240+
Microsoft.AspNetCore.Http.WebSocketAcceptContext.DisableServerContextTakeover.set -> void
241+
Microsoft.AspNetCore.Http.WebSocketAcceptContext.ServerMaxWindowBits.get -> int
242+
Microsoft.AspNetCore.Http.WebSocketAcceptContext.ServerMaxWindowBits.set -> void
235243
virtual Microsoft.AspNetCore.Http.Features.FeatureCollection.Revision.get -> int (forwarded, contained in Microsoft.Extensions.Features)
244+
virtual Microsoft.AspNetCore.Http.WebSocketAcceptContext.KeepAliveInterval.get -> System.TimeSpan?
245+
virtual Microsoft.AspNetCore.Http.WebSocketAcceptContext.KeepAliveInterval.set -> void
236246
~Microsoft.AspNetCore.Http.Features.FeatureReference<> (forwarded, contained in Microsoft.Extensions.Features)
237247
~Microsoft.AspNetCore.Http.Features.FeatureReferences<> (forwarded, contained in Microsoft.Extensions.Features)
Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
// Copyright (c) .NET Foundation. All rights reserved.
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

4-
using Microsoft.AspNetCore.Http.Features;
4+
using System;
5+
using System.Net.WebSockets;
56

67
namespace Microsoft.AspNetCore.Http
78
{
@@ -10,9 +11,63 @@ namespace Microsoft.AspNetCore.Http
1011
/// </summary>
1112
public class WebSocketAcceptContext
1213
{
14+
private int _serverMaxWindowBits = 15;
15+
1316
/// <summary>
1417
/// Gets or sets the subprotocol being negotiated.
1518
/// </summary>
1619
public virtual string? SubProtocol { get; set; }
20+
21+
/// <summary>
22+
/// The interval to send pong frames. This is a heart-beat that keeps the connection alive.
23+
/// </summary>
24+
public virtual TimeSpan? KeepAliveInterval { get; set; }
25+
26+
/// <summary>
27+
/// Enables support for the 'permessage-deflate' WebSocket extension.<para />
28+
/// Be aware that enabling compression over encrypted connections makes the application subject to CRIME/BREACH type attacks.
29+
/// It is strongly advised to turn off compression when sending data containing secrets by
30+
/// specifying <see cref="WebSocketMessageFlags.DisableCompression"/> when sending such messages.
31+
/// </summary>
32+
public bool DangerousEnableCompression { get; set; }
33+
34+
/// <summary>
35+
/// Disables server context takeover when using compression.
36+
/// This setting reduces the memory overhead of compression at the cost of a potentially worse compresson ratio.
37+
/// </summary>
38+
/// <remarks>
39+
/// This property does nothing when <see cref="DangerousEnableCompression"/> is false,
40+
/// or when the client does not use compression.
41+
/// </remarks>
42+
/// <value>
43+
/// false
44+
/// </value>
45+
public bool DisableServerContextTakeover { get; set; }
46+
47+
/// <summary>
48+
/// Sets the maximum base-2 logarithm of the LZ77 sliding window size that can be used for compression.
49+
/// This setting reduces the memory overhead of compression at the cost of a potentially worse compresson ratio.
50+
/// </summary>
51+
/// <remarks>
52+
/// This property does nothing when <see cref="DangerousEnableCompression"/> is false,
53+
/// or when the client does not use compression.
54+
/// Valid values are 9 through 15.
55+
/// </remarks>
56+
/// <value>
57+
/// 15
58+
/// </value>
59+
public int ServerMaxWindowBits
60+
{
61+
get => _serverMaxWindowBits;
62+
set
63+
{
64+
if (value < 9 || value > 15)
65+
{
66+
throw new ArgumentOutOfRangeException(nameof(ServerMaxWindowBits),
67+
"The argument must be a value from 9 to 15.");
68+
}
69+
_serverMaxWindowBits = value;
70+
}
71+
}
1772
}
1873
}

src/Http/Http/src/Internal/DefaultWebSocketManager.cs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ internal sealed class DefaultWebSocketManager : WebSocketManager
1717
private readonly static Func<IFeatureCollection, IHttpWebSocketFeature?> _nullWebSocketFeature = f => null;
1818

1919
private FeatureReferences<FeatureInterfaces> _features;
20+
private readonly static WebSocketAcceptContext _defaultWebSocketAcceptContext = new WebSocketAcceptContext();
2021

2122
public DefaultWebSocketManager(IFeatureCollection features)
2223
{
@@ -61,12 +62,19 @@ public override IList<string> WebSocketRequestedProtocols
6162
}
6263

6364
public override Task<WebSocket> AcceptWebSocketAsync(string? subProtocol)
65+
{
66+
var acceptContext = subProtocol is null ? _defaultWebSocketAcceptContext :
67+
new WebSocketAcceptContext() { SubProtocol = subProtocol };
68+
return AcceptWebSocketAsync(acceptContext);
69+
}
70+
71+
public override Task<WebSocket> AcceptWebSocketAsync(WebSocketAcceptContext acceptContext)
6472
{
6573
if (WebSocketFeature == null)
6674
{
6775
throw new NotSupportedException("WebSockets are not supported");
6876
}
69-
return WebSocketFeature.AcceptAsync(new WebSocketAcceptContext() { SubProtocol = subProtocol });
77+
return WebSocketFeature.AcceptAsync(acceptContext);
7078
}
7179

7280
struct FeatureInterfaces

src/Middleware/WebSockets/src/ExtendedWebSocketAcceptContext.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ namespace Microsoft.AspNetCore.WebSockets
99
/// <summary>
1010
/// Extends the <see cref="WebSocketAcceptContext"/> class with additional properties.
1111
/// </summary>
12+
[Obsolete("This type is obsolete and will be removed in a future version. The recommended alternative is Microsoft.AspNetCore.Http.WebSocketAcceptContext.")]
1213
public class ExtendedWebSocketAcceptContext : WebSocketAcceptContext
1314
{
1415
/// <inheritdoc />
@@ -23,6 +24,6 @@ public class ExtendedWebSocketAcceptContext : WebSocketAcceptContext
2324
/// <summary>
2425
/// The interval to send pong frames. This is a heart-beat that keeps the connection alive.
2526
/// </summary>
26-
public TimeSpan? KeepAliveInterval { get; set; }
27+
public new TimeSpan? KeepAliveInterval { get; set; }
2728
}
2829
}

0 commit comments

Comments
 (0)