Skip to content

Commit 9ad3fa0

Browse files
github-actions[bot]Katya SokolovaMihaZupanCarnaViire
authored
[release/7.0] Fix compression (#79547)
* Fix compression * Apply suggestions from code review Co-authored-by: Miha Zupan <[email protected]> * Adding SendAsync to ref * fix ws deflate tests * Check bytes on server side * Remove ref assembly change, pr feedback Co-authored-by: Katya Sokolova <[email protected]> Co-authored-by: Miha Zupan <[email protected]> Co-authored-by: Natalia Kondratyeva <[email protected]>
1 parent 5d51394 commit 9ad3fa0

File tree

2 files changed

+88
-0
lines changed

2 files changed

+88
-0
lines changed

src/libraries/System.Net.WebSockets.Client/src/System/Net/WebSockets/ClientWebSocket.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,9 @@ public override Task SendAsync(ArraySegment<byte> buffer, WebSocketMessageType m
135135
public override ValueTask SendAsync(ReadOnlyMemory<byte> buffer, WebSocketMessageType messageType, bool endOfMessage, CancellationToken cancellationToken) =>
136136
ConnectedWebSocket.SendAsync(buffer, messageType, endOfMessage, cancellationToken);
137137

138+
public override ValueTask SendAsync(ReadOnlyMemory<byte> buffer, WebSocketMessageType messageType, WebSocketMessageFlags messageFlags, CancellationToken cancellationToken) =>
139+
ConnectedWebSocket.SendAsync(buffer, messageType, messageFlags, cancellationToken);
140+
138141
public override Task<WebSocketReceiveResult> ReceiveAsync(ArraySegment<byte> buffer, CancellationToken cancellationToken) =>
139142
ConnectedWebSocket.ReceiveAsync(buffer, cancellationToken);
140143

src/libraries/System.Net.WebSockets.Client/tests/DeflateTests.cs

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,91 @@ await LoopbackServer.CreateClientAndServerAsync(async uri =>
8787
}), new LoopbackServer.Options { WebSocketEndpoint = true });
8888
}
8989

90+
[ConditionalFact(nameof(WebSocketsSupported))]
91+
public async Task ThrowsWhenContinuationHasDifferentCompressionFlags()
92+
{
93+
var deflateOpt = new WebSocketDeflateOptions
94+
{
95+
ClientMaxWindowBits = 14,
96+
ClientContextTakeover = true,
97+
ServerMaxWindowBits = 14,
98+
ServerContextTakeover = true
99+
};
100+
await LoopbackServer.CreateClientAndServerAsync(async uri =>
101+
{
102+
using var cws = new ClientWebSocket();
103+
using var cts = new CancellationTokenSource(TimeOutMilliseconds);
104+
105+
cws.Options.DangerousDeflateOptions = deflateOpt;
106+
await ConnectAsync(cws, uri, cts.Token);
107+
108+
109+
await cws.SendAsync(Memory<byte>.Empty, WebSocketMessageType.Text, WebSocketMessageFlags.DisableCompression, default);
110+
Assert.Throws<ArgumentException>("messageFlags", () =>
111+
cws.SendAsync(Memory<byte>.Empty, WebSocketMessageType.Binary, WebSocketMessageFlags.EndOfMessage, default));
112+
}, server => server.AcceptConnectionAsync(async connection =>
113+
{
114+
string extensionsReply = CreateDeflateOptionsHeader(deflateOpt);
115+
await LoopbackHelper.WebSocketHandshakeAsync(connection, extensionsReply);
116+
}), new LoopbackServer.Options { WebSocketEndpoint = true });
117+
}
118+
119+
[ConditionalFact(nameof(WebSocketsSupported))]
120+
public async Task SendHelloWithDisableCompression()
121+
{
122+
byte[] bytes = "Hello"u8.ToArray();
123+
124+
int prefixLength = 2;
125+
byte[] rawPrefix = new byte[] { 0x81, 0x85 }; // fin=1, rsv=0, opcode=text; mask=1, len=5
126+
int rawRemainingBytes = 9; // mask bytes (4) + payload bytes (5)
127+
byte[] compressedPrefix = new byte[] { 0xc1, 0x87 }; // fin=1, rsv=compressed, opcode=text; mask=1, len=7
128+
int compressedRemainingBytes = 11; // mask bytes (4) + payload bytes (7)
129+
130+
var deflateOpt = new WebSocketDeflateOptions
131+
{
132+
ClientMaxWindowBits = 14,
133+
ClientContextTakeover = true,
134+
ServerMaxWindowBits = 14,
135+
ServerContextTakeover = true
136+
};
137+
138+
await LoopbackServer.CreateClientAndServerAsync(async uri =>
139+
{
140+
using var cws = new ClientWebSocket();
141+
using var cts = new CancellationTokenSource(TimeOutMilliseconds);
142+
143+
cws.Options.DangerousDeflateOptions = deflateOpt;
144+
await ConnectAsync(cws, uri, cts.Token);
145+
146+
await cws.SendAsync(bytes, WebSocketMessageType.Text, true, cts.Token);
147+
148+
WebSocketMessageFlags flags = WebSocketMessageFlags.DisableCompression | WebSocketMessageFlags.EndOfMessage;
149+
await cws.SendAsync(bytes, WebSocketMessageType.Text, flags, cts.Token);
150+
}, server => server.AcceptConnectionAsync(async connection =>
151+
{
152+
var buffer = new byte[compressedRemainingBytes];
153+
string extensionsReply = CreateDeflateOptionsHeader(deflateOpt);
154+
await LoopbackHelper.WebSocketHandshakeAsync(connection, extensionsReply);
155+
156+
// first message is compressed
157+
await ReadExactAsync(buffer, prefixLength);
158+
Assert.Equal(compressedPrefix, buffer[..prefixLength]);
159+
// read rest of the frame
160+
await ReadExactAsync(buffer, compressedRemainingBytes);
161+
162+
// second message is not compressed
163+
await ReadExactAsync(buffer, prefixLength);
164+
Assert.Equal(rawPrefix, buffer[..prefixLength]);
165+
// read rest of the frame
166+
await ReadExactAsync(buffer, rawRemainingBytes);
167+
168+
async Task ReadExactAsync(byte[] buf, int n)
169+
{
170+
await connection.Stream.ReadAtLeastAsync(buf.AsMemory(0, n), n);
171+
}
172+
}), new LoopbackServer.Options { WebSocketEndpoint = true });
173+
}
174+
90175
private static string CreateDeflateOptionsHeader(WebSocketDeflateOptions options)
91176
{
92177
var builder = new StringBuilder();

0 commit comments

Comments
 (0)