diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.IO.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.IO.cs index a29fbfa920062..4b876aee9e8ba 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.IO.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.IO.cs @@ -469,7 +469,7 @@ private ProtocolToken ProcessTlsFrame(int frameSize) { TlsFrameHeader nextHeader = default; - if (!TlsFrameHelper.TryGetFrameHeader(_buffer.EncryptedReadOnlySpan.Slice(chunkSize), ref nextHeader)) + if (!TlsFrameHelper.TryGetFrameHeader(availableData.Slice(chunkSize), ref nextHeader)) { break; } @@ -478,7 +478,7 @@ private ProtocolToken ProcessTlsFrame(int frameSize) // Can process more handshake frames in single step or during TLS1.3 post-handshake auth, but we should // avoid processing too much so as to preserve API boundary between handshake and I/O. - if ((nextHeader.Type != TlsContentType.Handshake && nextHeader.Type != TlsContentType.ChangeCipherSpec) && !_isRenego || frameSize > _buffer.EncryptedLength) + if ((nextHeader.Type != TlsContentType.Handshake && nextHeader.Type != TlsContentType.ChangeCipherSpec) && !_isRenego || frameSize > availableData.Length - chunkSize) { // We don't have full frame left or we already have app data which needs to be processed by decrypt. break; diff --git a/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamFramingTest.cs b/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamFramingTest.cs index efc405da00eb7..f83e1b7027ebb 100644 --- a/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamFramingTest.cs +++ b/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamFramingTest.cs @@ -39,7 +39,10 @@ public enum FramingType // 1 byte reads ByteByByte, - // coalesce reads to biggest chunks possible + // Receive data at chunks, not necessarily respecting frame boundaries + Chunked, + + // Coalesce reads to biggest chunks possible Coalescing } @@ -129,7 +132,6 @@ public async Task Handshake_Success(FramingType framingType, SslProtocols sslPro Assert.True(serverStream.ReadCalled, "Mocked read method was not used"); await TestHelper.PingPong(client, server); - } internal class ConfigurableReadStream : Stream @@ -168,6 +170,7 @@ public override async ValueTask ReadAsync(Memory buffer, Cancellation { case FramingType.ByteByByte: return await _stream.ReadAsync(buffer.Length > 0 ? buffer.Slice(0, 1) : buffer, cancellationToken); + case FramingType.Coalescing: { if (buffer.Length > 0) @@ -178,6 +181,24 @@ public override async ValueTask ReadAsync(Memory buffer, Cancellation } return await _stream.ReadAsync(buffer, cancellationToken); } + case FramingType.Chunked: + { + if (buffer.Length > 0) + { + // wait 10ms, this should be enough for the other side to write as much data + // as it will ever write before receiving something back. + await Task.Delay(10); + + const int maxRead = 1519; // arbitrarily chosen chunk size + + if (buffer.Length > maxRead) + { + buffer = buffer.Slice(0, maxRead); + } + } + return await _stream.ReadAsync(buffer, cancellationToken); + } + default: throw new NotImplementedException(); }