Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add WebSocket compression support #32600

Merged
merged 14 commits into from
May 27, 2021
3 changes: 3 additions & 0 deletions src/Http/Headers/src/HeaderNames.cs
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,9 @@ public static class HeaderNames
/// <summary>Gets the <c>Sec-WebSocket-Version</c> HTTP header name.</summary>
public static readonly string SecWebSocketVersion = "Sec-WebSocket-Version";

/// <summary>Gets the <c>Sec-WebSocket-Extensions</c> HTTP header name.</summary>
public static readonly string SecWebSocketExtensions = "Sec-WebSocket-Extensions";

/// <summary>Gets the <c>Server</c> HTTP header name.</summary>
public static readonly string Server = "Server";

Expand Down
1 change: 1 addition & 0 deletions src/Http/Headers/src/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ Microsoft.Net.Http.Headers.RangeConditionHeaderValue.RangeConditionHeaderValue(M
static readonly Microsoft.Net.Http.Headers.HeaderNames.Baggage -> string!
static readonly Microsoft.Net.Http.Headers.HeaderNames.Link -> string!
static readonly Microsoft.Net.Http.Headers.HeaderNames.ProxyConnection -> string!
static readonly Microsoft.Net.Http.Headers.HeaderNames.SecWebSocketExtensions -> string!
static readonly Microsoft.Net.Http.Headers.HeaderNames.XContentTypeOptions -> string!
static readonly Microsoft.Net.Http.Headers.HeaderNames.XPoweredBy -> string!
static readonly Microsoft.Net.Http.Headers.HeaderNames.XUACompatible -> string!
Expand Down
1 change: 1 addition & 0 deletions src/Http/Http.Abstractions/src/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,4 @@ abstract Microsoft.AspNetCore.Http.HttpRequest.ContentType.get -> string?
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!
static Microsoft.AspNetCore.Builder.UseMiddlewareExtensions.UseMiddleware(this Microsoft.AspNetCore.Builder.IApplicationBuilder! app, System.Type! middleware, params object?[]! args) -> Microsoft.AspNetCore.Builder.IApplicationBuilder!
static Microsoft.AspNetCore.Builder.UseMiddlewareExtensions.UseMiddleware<TMiddleware>(this Microsoft.AspNetCore.Builder.IApplicationBuilder! app, params object?[]! args) -> Microsoft.AspNetCore.Builder.IApplicationBuilder!
virtual Microsoft.AspNetCore.Http.WebSocketManager.AcceptWebSocketAsync(Microsoft.AspNetCore.Http.WebSocketAcceptContext! acceptContext) -> System.Threading.Tasks.Task<System.Net.WebSockets.WebSocket!>!
10 changes: 9 additions & 1 deletion src/Http/Http.Abstractions/src/WebSocketManager.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Collections.Generic;
using System.Net.WebSockets;
using System.Threading.Tasks;

namespace Microsoft.AspNetCore.Http
{
/// <summary>
/// Manages the establishment of WebSocket connections for a specific HTTP request.
/// Manages the establishment of WebSocket connections for a specific HTTP request.
/// </summary>
public abstract class WebSocketManager
{
Expand Down Expand Up @@ -37,5 +38,12 @@ public virtual Task<WebSocket> AcceptWebSocketAsync()
/// <param name="subProtocol">The sub-protocol to use.</param>
/// <returns>A task representing the completion of the transition.</returns>
public abstract Task<WebSocket> AcceptWebSocketAsync(string? subProtocol);

/// <summary>
///
/// </summary>
/// <param name="acceptContext"></param>
/// <returns></returns>
public virtual Task<WebSocket> AcceptWebSocketAsync(WebSocketAcceptContext acceptContext) => throw new NotImplementedException();
}
}
3 changes: 3 additions & 0 deletions src/Http/Http.Features/src/IHeaderDictionary.Keyed.cs
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,9 @@ public partial interface IHeaderDictionary
/// <summary>Gets or sets the <c>Sec-WebSocket-Version</c> HTTP header.</summary>
StringValues SecWebSocketVersion { get => this[HeaderNames.SecWebSocketVersion]; set => this[HeaderNames.SecWebSocketVersion] = value; }

/// <summary>Gets or sets the <c>Sec-WebSocket-Extensions</c> HTTP header.</summary>
StringValues SecWebSocketExtensions { get => this[HeaderNames.SecWebSocketExtensions]; set => this[HeaderNames.SecWebSocketExtensions] = value; }

/// <summary>Gets or sets the <c>Server</c> HTTP header.</summary>
StringValues Server { get => this[HeaderNames.Server]; set => this[HeaderNames.Server] = value; }

Expand Down
10 changes: 10 additions & 0 deletions src/Http/Http.Features/src/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,8 @@ Microsoft.AspNetCore.Http.IHeaderDictionary.RetryAfter.get -> Microsoft.Extensio
Microsoft.AspNetCore.Http.IHeaderDictionary.RetryAfter.set -> void
Microsoft.AspNetCore.Http.IHeaderDictionary.SecWebSocketAccept.get -> Microsoft.Extensions.Primitives.StringValues
Microsoft.AspNetCore.Http.IHeaderDictionary.SecWebSocketAccept.set -> void
Microsoft.AspNetCore.Http.IHeaderDictionary.SecWebSocketExtensions.get -> Microsoft.Extensions.Primitives.StringValues
Microsoft.AspNetCore.Http.IHeaderDictionary.SecWebSocketExtensions.set -> void
Microsoft.AspNetCore.Http.IHeaderDictionary.SecWebSocketKey.get -> Microsoft.Extensions.Primitives.StringValues
Microsoft.AspNetCore.Http.IHeaderDictionary.SecWebSocketKey.set -> void
Microsoft.AspNetCore.Http.IHeaderDictionary.SecWebSocketProtocol.get -> Microsoft.Extensions.Primitives.StringValues
Expand Down Expand Up @@ -232,6 +234,14 @@ Microsoft.AspNetCore.Http.Features.FeatureCollection.IsReadOnly.get -> bool (for
Microsoft.AspNetCore.Http.Features.FeatureCollection.Set<TFeature>(TFeature? instance) -> void (forwarded, contained in Microsoft.Extensions.Features)
Microsoft.AspNetCore.Http.Features.FeatureCollection.this[System.Type! key].get -> object? (forwarded, contained in Microsoft.Extensions.Features)
Microsoft.AspNetCore.Http.Features.FeatureCollection.this[System.Type! key].set -> void (forwarded, contained in Microsoft.Extensions.Features)
Microsoft.AspNetCore.Http.WebSocketAcceptContext.DangerousEnableCompression.get -> bool
Microsoft.AspNetCore.Http.WebSocketAcceptContext.DangerousEnableCompression.set -> void
Microsoft.AspNetCore.Http.WebSocketAcceptContext.DisableServerContextTakeover.get -> bool
Microsoft.AspNetCore.Http.WebSocketAcceptContext.DisableServerContextTakeover.set -> void
Microsoft.AspNetCore.Http.WebSocketAcceptContext.ServerMaxWindowBits.get -> int
Microsoft.AspNetCore.Http.WebSocketAcceptContext.ServerMaxWindowBits.set -> void
virtual Microsoft.AspNetCore.Http.Features.FeatureCollection.Revision.get -> int (forwarded, contained in Microsoft.Extensions.Features)
virtual Microsoft.AspNetCore.Http.WebSocketAcceptContext.KeepAliveInterval.get -> System.TimeSpan?
virtual Microsoft.AspNetCore.Http.WebSocketAcceptContext.KeepAliveInterval.set -> void
~Microsoft.AspNetCore.Http.Features.FeatureReference<> (forwarded, contained in Microsoft.Extensions.Features)
~Microsoft.AspNetCore.Http.Features.FeatureReferences<> (forwarded, contained in Microsoft.Extensions.Features)
57 changes: 56 additions & 1 deletion src/Http/Http.Features/src/WebSocketAcceptContext.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using Microsoft.AspNetCore.Http.Features;
using System;
using System.Net.WebSockets;

namespace Microsoft.AspNetCore.Http
{
Expand All @@ -10,9 +11,63 @@ namespace Microsoft.AspNetCore.Http
/// </summary>
public class WebSocketAcceptContext
{
private int _serverMaxWindowBits = 15;

/// <summary>
/// Gets or sets the subprotocol being negotiated.
/// </summary>
public virtual string? SubProtocol { get; set; }

/// <summary>
/// The interval to send pong frames. This is a heart-beat that keeps the connection alive.
/// </summary>
public virtual TimeSpan? KeepAliveInterval { get; set; }

/// <summary>
/// Enables support for the 'permessage-deflate' WebSocket extension.<para />
/// Be aware that enabling compression over encrypted connections makes the application subject to CRIME/BREACH type attacks.
/// It is strongly advised to turn off compression when sending data containing secrets by
/// specifying <see cref="WebSocketMessageFlags.DisableCompression"/> when sending such messages.
/// </summary>
public bool DangerousEnableCompression { get; set; }

/// <summary>
/// Disables server context takeover when using compression.
BrennanConroy marked this conversation as resolved.
Show resolved Hide resolved
/// This setting reduces the memory overhead of compression at the cost of a potentially worse compresson ratio.
/// </summary>
/// <remarks>
/// This property does nothing when <see cref="DangerousEnableCompression"/> is false,
/// or when the client does not use compression.
/// </remarks>
/// <value>
/// false
/// </value>
public bool DisableServerContextTakeover { get; set; }

/// <summary>
/// Sets the maximum base-2 logarithm of the LZ77 sliding window size that can be used for compression.
BrennanConroy marked this conversation as resolved.
Show resolved Hide resolved
/// This setting reduces the memory overhead of compression at the cost of a potentially worse compresson ratio.
/// </summary>
/// <remarks>
/// This property does nothing when <see cref="DangerousEnableCompression"/> is false,
/// or when the client does not use compression.
/// Valid values are 9 through 15.
/// </remarks>
/// <value>
/// 15
/// </value>
public int ServerMaxWindowBits
{
get => _serverMaxWindowBits;
set
{
if (value < 9 || value > 15)
{
throw new ArgumentOutOfRangeException(nameof(ServerMaxWindowBits),
"The argument must be a value from 9 to 15.");
}
_serverMaxWindowBits = value;
}
}
}
}
10 changes: 9 additions & 1 deletion src/Http/Http/src/Internal/DefaultWebSocketManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ internal sealed class DefaultWebSocketManager : WebSocketManager
private readonly static Func<IFeatureCollection, IHttpWebSocketFeature?> _nullWebSocketFeature = f => null;

private FeatureReferences<FeatureInterfaces> _features;
private readonly static WebSocketAcceptContext _defaultWebSocketAcceptContext = new WebSocketAcceptContext();

public DefaultWebSocketManager(IFeatureCollection features)
{
Expand Down Expand Up @@ -61,12 +62,19 @@ public override IList<string> WebSocketRequestedProtocols
}

public override Task<WebSocket> AcceptWebSocketAsync(string? subProtocol)
{
var acceptContext = subProtocol is null ? _defaultWebSocketAcceptContext :
new WebSocketAcceptContext() { SubProtocol = subProtocol };
return AcceptWebSocketAsync(acceptContext);
}

public override Task<WebSocket> AcceptWebSocketAsync(WebSocketAcceptContext acceptContext)
{
if (WebSocketFeature == null)
{
throw new NotSupportedException("WebSockets are not supported");
}
return WebSocketFeature.AcceptAsync(new WebSocketAcceptContext() { SubProtocol = subProtocol });
return WebSocketFeature.AcceptAsync(acceptContext);
}

struct FeatureInterfaces
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ namespace Microsoft.AspNetCore.WebSockets
/// <summary>
/// Extends the <see cref="WebSocketAcceptContext"/> class with additional properties.
/// </summary>
[Obsolete("This type is obsolete and will be removed in a future version. The recommended alternative is Microsoft.AspNetCore.Http.WebSocketAcceptContext.")]
public class ExtendedWebSocketAcceptContext : WebSocketAcceptContext
{
/// <inheritdoc />
Expand All @@ -23,6 +24,6 @@ public class ExtendedWebSocketAcceptContext : WebSocketAcceptContext
/// <summary>
/// The interval to send pong frames. This is a heart-beat that keeps the connection alive.
/// </summary>
public TimeSpan? KeepAliveInterval { get; set; }
public new TimeSpan? KeepAliveInterval { get; set; }
}
}
Loading