diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj
index b602857563..59dd058406 100644
--- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj
+++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj
@@ -261,6 +261,7 @@
+
@@ -289,6 +290,7 @@
+
diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SslOverTdsStream.NetCoreApp.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SslOverTdsStream.NetCoreApp.cs
new file mode 100644
index 0000000000..cb634cb6af
--- /dev/null
+++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SslOverTdsStream.NetCoreApp.cs
@@ -0,0 +1,305 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Buffers;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Microsoft.Data.SqlClient.SNI
+{
+ internal sealed partial class SslOverTdsStream
+ {
+ public override int Read(byte[] buffer, int offset, int count)
+ {
+ return Read(buffer.AsSpan(offset, count));
+ }
+
+ public override void Write(byte[] buffer, int offset, int count)
+ {
+ Write(buffer.AsSpan(offset, count));
+ }
+
+ public override Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
+ {
+ return ReadAsync(new Memory(buffer, offset, count), cancellationToken).AsTask();
+ }
+
+ public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
+ {
+ return WriteAsync(new ReadOnlyMemory(buffer, offset, count), cancellationToken).AsTask();
+ }
+
+ public override int Read(Span buffer)
+ {
+ if (!_encapsulate)
+ {
+ return _stream.Read(buffer);
+ }
+
+ using (SNIEventScope.Create(" reading encapsulated bytes"))
+ {
+ if (_packetBytes > 0)
+ {
+ // there are queued bytes from a previous packet available
+ // work out how many of the remaining bytes we can consume
+ int wantedCount = Math.Min(buffer.Length, _packetBytes);
+ int readCount = _stream.Read(buffer.Slice(0, wantedCount));
+ if (readCount == 0)
+ {
+ // 0 means the connection was closed, tell the caller
+ return 0;
+ }
+ _packetBytes -= readCount;
+ return readCount;
+ }
+ else
+ {
+ Span headerBytes = stackalloc byte[TdsEnums.HEADER_LEN];
+
+ // fetch the packet header to determine how long the packet is
+ int headerBytesRead = 0;
+ do
+ {
+ int headerBytesReadIteration = _stream.Read(headerBytes.Slice(headerBytesRead, TdsEnums.HEADER_LEN - headerBytesRead));
+ if (headerBytesReadIteration == 0)
+ {
+ // 0 means the connection was closed, tell the caller
+ return 0;
+ }
+ headerBytesRead += headerBytesReadIteration;
+ } while (headerBytesRead < TdsEnums.HEADER_LEN);
+
+ // read the packet data size from the header and store it in case it is needed for a subsequent call
+ _packetBytes = ((headerBytes[TdsEnums.HEADER_LEN_FIELD_OFFSET] << 8) | headerBytes[TdsEnums.HEADER_LEN_FIELD_OFFSET + 1]) - TdsEnums.HEADER_LEN;
+
+ // read as much from the packet as the caller can accept
+ int packetBytesRead = _stream.Read(buffer.Slice(0, Math.Min(buffer.Length, _packetBytes)));
+ _packetBytes -= packetBytesRead;
+ return packetBytesRead;
+ }
+ }
+ }
+
+ public override async ValueTask ReadAsync(Memory buffer, CancellationToken cancellationToken = default)
+ {
+ if (!_encapsulate)
+ {
+ int read;
+ {
+ ValueTask readValueTask = _stream.ReadAsync(buffer, cancellationToken);
+ if (readValueTask.IsCompletedSuccessfully)
+ {
+ read = readValueTask.Result;
+ }
+ else
+ {
+ read = await readValueTask.ConfigureAwait(false);
+ }
+ }
+ return read;
+ }
+ using (SNIEventScope.Create(" reading encapsulated bytes"))
+ {
+ if (_packetBytes > 0)
+ {
+ // there are queued bytes from a previous packet available
+ // work out how many of the remaining bytes we can consume
+ int wantedCount = Math.Min(buffer.Length, _packetBytes);
+
+ int readCount;
+ {
+ ValueTask remainderReadValueTask = _stream.ReadAsync(buffer.Slice(0, wantedCount), cancellationToken);
+ if (remainderReadValueTask.IsCompletedSuccessfully)
+ {
+ readCount = remainderReadValueTask.Result;
+ }
+ else
+ {
+ readCount = await remainderReadValueTask.ConfigureAwait(false);
+ }
+ }
+ if (readCount == 0)
+ {
+ // 0 means the connection was closed, tell the caller
+ return 0;
+ }
+ _packetBytes -= readCount;
+ return readCount;
+ }
+ else
+ {
+ byte[] headerBytes = ArrayPool.Shared.Rent(TdsEnums.HEADER_LEN);
+
+ // fetch the packet header to determine how long the packet is
+ int headerBytesRead = 0;
+ do
+ {
+ int headerBytesReadIteration;
+ {
+ ValueTask headerReadValueTask = _stream.ReadAsync(headerBytes.AsMemory(headerBytesRead, (TdsEnums.HEADER_LEN - headerBytesRead)), cancellationToken);
+ if (headerReadValueTask.IsCompletedSuccessfully)
+ {
+ headerBytesReadIteration = headerReadValueTask.Result;
+ }
+ else
+ {
+ headerBytesReadIteration = await headerReadValueTask.ConfigureAwait(false);
+ }
+ }
+ if (headerBytesReadIteration == 0)
+ {
+ // 0 means the connection was closed, cleanup the rented array and then tell the caller
+ ArrayPool.Shared.Return(headerBytes, clearArray: true);
+ return 0;
+ }
+ headerBytesRead += headerBytesReadIteration;
+ } while (headerBytesRead < TdsEnums.HEADER_LEN);
+
+ // read the packet data size from the header and store it in case it is needed for a subsequent call
+ _packetBytes = ((headerBytes[TdsEnums.HEADER_LEN_FIELD_OFFSET] << 8) | headerBytes[TdsEnums.HEADER_LEN_FIELD_OFFSET + 1]) - TdsEnums.HEADER_LEN;
+
+ ArrayPool.Shared.Return(headerBytes, clearArray: true);
+
+ // read as much from the packet as the caller can accept
+ int packetBytesRead;
+ {
+ ValueTask packetReadValueTask = _stream.ReadAsync(buffer.Slice(0, Math.Min(buffer.Length, _packetBytes)), cancellationToken);
+ if (packetReadValueTask.IsCompletedSuccessfully)
+ {
+ packetBytesRead = packetReadValueTask.Result;
+ }
+ else
+ {
+ packetBytesRead = await packetReadValueTask.ConfigureAwait(false);
+ }
+ }
+ _packetBytes -= packetBytesRead;
+ return packetBytesRead;
+ }
+ }
+ }
+
+ public override void Write(ReadOnlySpan buffer)
+ {
+ // During the SSL negotiation phase, SSL is tunnelled over TDS packet type 0x12. After
+ // negotiation, the underlying socket only sees SSL frames.
+ if (!_encapsulate)
+ {
+ _stream.Write(buffer);
+ _stream.Flush();
+ return;
+ }
+
+ using (SNIEventScope.Create(" writing encapsulated bytes"))
+ {
+ ReadOnlySpan remaining = buffer;
+ byte[] packetBuffer = null;
+ try
+ {
+ while (remaining.Length > 0)
+ {
+ int dataLength = Math.Min(PACKET_SIZE_WITHOUT_HEADER, remaining.Length);
+ int packetLength = TdsEnums.HEADER_LEN + dataLength;
+
+ if (packetBuffer == null)
+ {
+ packetBuffer = ArrayPool.Shared.Rent(packetLength);
+ }
+ else if (packetBuffer.Length < packetLength)
+ {
+ ArrayPool.Shared.Return(packetBuffer, clearArray: true);
+ packetBuffer = ArrayPool.Shared.Rent(packetLength);
+ }
+
+ SetupPreLoginPacketHeader(packetBuffer, dataLength, remaining.Length - dataLength);
+
+ Span data = packetBuffer.AsSpan(TdsEnums.HEADER_LEN, dataLength);
+ remaining.Slice(0, dataLength).CopyTo(data);
+
+ _stream.Write(packetBuffer.AsSpan(0, packetLength));
+ _stream.Flush();
+
+ remaining = remaining.Slice(dataLength);
+ }
+ }
+ finally
+ {
+ if (packetBuffer != null)
+ {
+ ArrayPool.Shared.Return(packetBuffer, clearArray: true);
+ }
+ }
+ }
+ }
+
+ public override async ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default)
+ {
+ if (!_encapsulate)
+ {
+ {
+ ValueTask valueTask = _stream.WriteAsync(buffer, cancellationToken);
+ if (!valueTask.IsCompletedSuccessfully)
+ {
+ await valueTask.ConfigureAwait(false);
+ }
+ }
+ Task flushTask = _stream.FlushAsync();
+ if (flushTask.IsCompletedSuccessfully)
+ {
+ await flushTask.ConfigureAwait(false);
+ }
+ return;
+ }
+
+ using (SNIEventScope.Create(" writing encapsulated bytes"))
+ {
+ ReadOnlyMemory remaining = buffer;
+ byte[] packetBuffer = null;
+ try
+ {
+ while (remaining.Length > 0)
+ {
+ int dataLength = Math.Min(PACKET_SIZE_WITHOUT_HEADER, remaining.Length);
+ int packetLength = TdsEnums.HEADER_LEN + dataLength;
+
+ if (packetBuffer == null)
+ {
+ packetBuffer = ArrayPool.Shared.Rent(packetLength);
+ }
+ else if (packetBuffer.Length < packetLength)
+ {
+ ArrayPool.Shared.Return(packetBuffer, clearArray: true);
+ packetBuffer = ArrayPool.Shared.Rent(packetLength);
+ }
+
+ SetupPreLoginPacketHeader(packetBuffer, dataLength, remaining.Length - dataLength);
+
+ remaining.Span.Slice(0, dataLength).CopyTo(packetBuffer.AsSpan(TdsEnums.HEADER_LEN, dataLength));
+
+ {
+ ValueTask packetWriteValueTask = _stream.WriteAsync(new ReadOnlyMemory(packetBuffer, 0, packetLength), cancellationToken);
+ if (!packetWriteValueTask.IsCompletedSuccessfully)
+ {
+ await packetWriteValueTask.ConfigureAwait(false);
+ }
+ }
+
+ await _stream.FlushAsync().ConfigureAwait(false);
+
+
+ remaining = remaining.Slice(dataLength);
+ }
+ }
+ finally
+ {
+ if (packetBuffer != null)
+ {
+ ArrayPool.Shared.Return(packetBuffer, clearArray: true);
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SslOverTdsStream.NetStandard.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SslOverTdsStream.NetStandard.cs
new file mode 100644
index 0000000000..7798b25d06
--- /dev/null
+++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SslOverTdsStream.NetStandard.cs
@@ -0,0 +1,224 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Buffers;
+using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Microsoft.Data.SqlClient.SNI
+{
+ internal sealed partial class SslOverTdsStream : Stream
+ {
+ public override int Read(byte[] buffer, int offset, int count)
+ {
+ if (!_encapsulate)
+ {
+ return _stream.Read(buffer, offset, count);
+ }
+
+ using (SNIEventScope.Create(" reading encapsulated bytes"))
+ {
+ if (_packetBytes > 0)
+ {
+ // there are queued bytes from a previous packet available
+ // work out how many of the remaining bytes we can consume
+ int wantedCount = Math.Min(count, _packetBytes);
+ int readCount = _stream.Read(buffer, offset, wantedCount);
+ if (readCount == 0)
+ {
+ // 0 means the connection was closed, tell the caller
+ return 0;
+ }
+ _packetBytes -= readCount;
+ return readCount;
+ }
+ else
+ {
+ byte[] headerBytes = ArrayPool.Shared.Rent(TdsEnums.HEADER_LEN);
+ Array.Clear(headerBytes, 0, headerBytes.Length);
+
+ // fetch the packet header to determine how long the packet is
+ int headerBytesRead = 0;
+ do
+ {
+ int headerBytesReadIteration = _stream.Read(headerBytes, headerBytesRead, (TdsEnums.HEADER_LEN - headerBytesRead));
+ if (headerBytesReadIteration == 0)
+ {
+ // 0 means the connection was closed, cleanup the rented array and then tell the caller
+ ArrayPool.Shared.Return(headerBytes, clearArray: true);
+ return 0;
+ }
+ headerBytesRead += headerBytesReadIteration;
+ } while (headerBytesRead < TdsEnums.HEADER_LEN);
+
+ // read the packet data size from the header and store it in case it is needed for a subsequent call
+ _packetBytes = ((headerBytes[TdsEnums.HEADER_LEN_FIELD_OFFSET] << 8) | headerBytes[TdsEnums.HEADER_LEN_FIELD_OFFSET + 1]) - TdsEnums.HEADER_LEN;
+
+ ArrayPool.Shared.Return(headerBytes, clearArray: true);
+
+ // read as much from the packet as the caller can accept
+ int packetBytesRead = _stream.Read(buffer, offset, Math.Min(count, _packetBytes));
+ _packetBytes -= packetBytesRead;
+ return packetBytesRead;
+ }
+ }
+
+ }
+
+ public override async Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
+ {
+ if (!_encapsulate)
+ {
+ return await _stream.ReadAsync(buffer, offset, count, cancellationToken);
+ }
+
+ using (SNIEventScope.Create(" reading encapsulated bytes"))
+ {
+ if (_packetBytes > 0)
+ {
+ // there are queued bytes from a previous packet available
+ // work out how many of the remaining bytes we can consume
+ int wantedCount = Math.Min(count, _packetBytes);
+ int readCount = await _stream.ReadAsync(buffer, offset, wantedCount, cancellationToken);
+ if (readCount == 0)
+ {
+ // 0 means the connection was closed, tell the caller
+ return 0;
+ }
+ _packetBytes -= readCount;
+ return readCount;
+ }
+ else
+ {
+ byte[] headerBytes = ArrayPool.Shared.Rent(TdsEnums.HEADER_LEN);
+ Array.Clear(headerBytes, 0, headerBytes.Length);
+
+ // fetch the packet header to determine how long the packet is
+ int headerBytesRead = 0;
+ do
+ {
+ int headerBytesReadIteration = await _stream.ReadAsync(headerBytes, headerBytesRead, (TdsEnums.HEADER_LEN - headerBytesRead), cancellationToken);
+ if (headerBytesReadIteration == 0)
+ {
+ // 0 means the connection was closed, cleanup the rented array and then tell the caller
+ ArrayPool.Shared.Return(headerBytes, clearArray: true);
+ return 0;
+ }
+ headerBytesRead += headerBytesReadIteration;
+ } while (headerBytesRead < TdsEnums.HEADER_LEN);
+
+ // read the packet data size from the header and store it in case it is needed for a subsequent call
+ _packetBytes = ((headerBytes[TdsEnums.HEADER_LEN_FIELD_OFFSET] << 8) | headerBytes[TdsEnums.HEADER_LEN_FIELD_OFFSET + 1]) - TdsEnums.HEADER_LEN;
+
+ ArrayPool.Shared.Return(headerBytes, clearArray: true);
+
+ // read as much from the packet as the caller can accept
+ int packetBytesRead = await _stream.ReadAsync(buffer, offset, Math.Min(count, _packetBytes), cancellationToken);
+ _packetBytes -= packetBytesRead;
+ return packetBytesRead;
+ }
+ }
+ }
+
+ public override void Write(byte[] buffer, int offset, int count)
+ {
+ // During the SSL negotiation phase, SSL is tunnelled over TDS packet type 0x12. After
+ // negotiation, the underlying socket only sees SSL frames.
+ if (!_encapsulate)
+ {
+ _stream.Write(buffer, offset, count);
+ _stream.Flush();
+ return;
+ }
+
+ using (SNIEventScope.Create(" writing encapsulated bytes"))
+ {
+ int remainingBytes = count;
+ int dataOffset = offset;
+ byte[] packetBuffer = null;
+ while (remainingBytes > 0)
+ {
+ int dataLength = Math.Min(PACKET_SIZE_WITHOUT_HEADER, remainingBytes);
+ int packetLength = TdsEnums.HEADER_LEN + dataLength;
+ remainingBytes -= dataLength;
+
+ if (packetBuffer == null)
+ {
+ packetBuffer = ArrayPool.Shared.Rent(packetLength);
+ }
+ else if (packetBuffer.Length < packetLength)
+ {
+ ArrayPool.Shared.Return(packetBuffer, clearArray: true);
+ packetBuffer = ArrayPool.Shared.Rent(packetLength);
+ }
+
+ SetupPreLoginPacketHeader(packetBuffer, dataLength, remainingBytes);
+
+ Array.Copy(buffer, dataOffset, packetBuffer, TdsEnums.HEADER_LEN, dataLength);
+
+ _stream.Write(packetBuffer, 0, packetLength);
+ _stream.Flush();
+
+ dataOffset += dataLength;
+ }
+ if (packetBuffer != null)
+ {
+ ArrayPool.Shared.Return(packetBuffer, clearArray: true);
+ }
+ }
+ }
+
+ public override async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
+ {
+ if (!_encapsulate)
+ {
+ await _stream.WriteAsync(buffer, offset, count).ConfigureAwait(false);
+ Task flushTask = _stream.FlushAsync();
+ if (flushTask.Status == TaskStatus.RanToCompletion)
+ {
+ await flushTask.ConfigureAwait(false);
+ }
+ return;
+ }
+
+ using (SNIEventScope.Create(" writing encapsulated bytes"))
+ {
+ int remainingBytes = count;
+ int dataOffset = offset;
+ byte[] packetBuffer = null;
+ while (remainingBytes > 0)
+ {
+ int dataLength = Math.Min(PACKET_SIZE_WITHOUT_HEADER, remainingBytes);
+ int packetLength = TdsEnums.HEADER_LEN + dataLength;
+ remainingBytes -= dataLength;
+
+ if (packetBuffer == null)
+ {
+ packetBuffer = ArrayPool.Shared.Rent(packetLength);
+ }
+ else if (packetBuffer.Length < packetLength)
+ {
+ ArrayPool.Shared.Return(packetBuffer, clearArray: true);
+ packetBuffer = ArrayPool.Shared.Rent(packetLength);
+ }
+
+ SetupPreLoginPacketHeader(packetBuffer, dataLength, remainingBytes);
+
+ Array.Copy(buffer, dataOffset, packetBuffer, TdsEnums.HEADER_LEN, dataLength);
+
+ await _stream.WriteAsync(packetBuffer, 0, packetLength, cancellationToken).ConfigureAwait(false);
+ await _stream.FlushAsync().ConfigureAwait(false);
+
+ dataOffset += dataLength;
+ }
+ if (packetBuffer != null)
+ {
+ ArrayPool.Shared.Return(packetBuffer, clearArray: true);
+ }
+ }
+ }
+ }
+}
diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SslOverTdsStream.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SslOverTdsStream.cs
index 74a2e6226d..58384dfd58 100644
--- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SslOverTdsStream.cs
+++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SslOverTdsStream.cs
@@ -3,11 +3,8 @@
// See the LICENSE file in the project root for more information.
using System;
-using System.Buffers;
using System.IO;
using System.IO.Pipes;
-using System.Threading;
-using System.Threading.Tasks;
namespace Microsoft.Data.SqlClient.SNI
{
@@ -16,7 +13,7 @@ namespace Microsoft.Data.SqlClient.SNI
/// transported in TDS packet type 0x12. Once SSL handshake has completed, SSL
/// packets are sent transparently.
///
- internal sealed class SslOverTdsStream : Stream
+ internal sealed partial class SslOverTdsStream : Stream
{
private readonly Stream _stream;
@@ -42,197 +39,14 @@ public SslOverTdsStream(Stream stream)
public void FinishHandshake()
{
_encapsulate = false;
- }
-
- ///
- /// Read buffer
- ///
- /// Buffer
- /// Offset
- /// Byte count
- /// Bytes read
- public override int Read(byte[] buffer, int offset, int count) =>
- ReadInternal(buffer, offset, count, CancellationToken.None, async: false).GetAwaiter().GetResult();
-
- ///
- /// Write Buffer
- ///
- ///
- ///
- ///
- public override void Write(byte[] buffer, int offset, int count)
- => WriteInternal(buffer, offset, count, CancellationToken.None, async: false).Wait();
-
- ///
- /// Write Buffer Asynchronously
- ///
- ///
- ///
- ///
- ///
- ///
- public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken token)
- => WriteInternal(buffer, offset, count, token, async: true);
-
- ///
- /// Read Buffer Asynchronously
- ///
- ///
- ///
- ///
- ///
- ///
- public override Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken token)
- => ReadInternal(buffer, offset, count, token, async: true);
-
- ///
- /// Read Internal is called synchronously when async is false
- ///
- private async Task ReadInternal(byte[] buffer, int offset, int count, CancellationToken token, bool async)
- {
- int readBytes = 0;
- byte[] packetData = null;
- byte[] readTarget = buffer;
- int readOffset = offset;
- if (_encapsulate)
- {
- packetData = ArrayPool.Shared.Rent(count < TdsEnums.HEADER_LEN ? TdsEnums.HEADER_LEN : count);
- readTarget = packetData;
- readOffset = 0;
- if (_packetBytes == 0)
- {
- // Account for split packets
- while (readBytes < TdsEnums.HEADER_LEN)
- {
- var readBytesForHeader = async ?
- await _stream.ReadAsync(packetData, readBytes, TdsEnums.HEADER_LEN - readBytes, token).ConfigureAwait(false) :
- _stream.Read(packetData, readBytes, TdsEnums.HEADER_LEN - readBytes);
-
- if (readBytesForHeader == 0)
- {
- throw new EndOfStreamException("End of stream reached");
- }
-
- readBytes += readBytesForHeader;
- }
-
- _packetBytes = (packetData[TdsEnums.HEADER_LEN_FIELD_OFFSET] << 8) | packetData[TdsEnums.HEADER_LEN_FIELD_OFFSET + 1];
- _packetBytes -= TdsEnums.HEADER_LEN;
- }
-
- if (count > _packetBytes)
- {
- count = _packetBytes;
- }
- }
-
- readBytes = async ?
- await _stream.ReadAsync(readTarget, readOffset, count, token).ConfigureAwait(false) :
- _stream.Read(readTarget, readOffset, count);
-
- if (_encapsulate)
- {
- _packetBytes -= readBytes;
- }
- if (packetData != null)
- {
- Buffer.BlockCopy(packetData, 0, buffer, offset, readBytes);
- ArrayPool.Shared.Return(packetData, clearArray: true);
- }
- return readBytes;
- }
-
- ///
- /// The internal write method calls Sync APIs when Async flag is false
- ///
- private async Task WriteInternal(byte[] buffer, int offset, int count, CancellationToken token, bool async)
- {
- int currentCount = 0;
- int currentOffset = offset;
-
- while (count > 0)
- {
- // During the SSL negotiation phase, SSL is tunneled over TDS packet type 0x12. After
- // negotiation, the underlying socket only sees SSL frames.
- //
- if (_encapsulate)
- {
- if (count > PACKET_SIZE_WITHOUT_HEADER)
- {
- currentCount = PACKET_SIZE_WITHOUT_HEADER;
- }
- else
- {
- currentCount = count;
- }
-
- count -= currentCount;
-
- // Prepend buffer data with TDS prelogin header
- int combinedLength = TdsEnums.HEADER_LEN + currentCount;
- byte[] combinedBuffer = ArrayPool.Shared.Rent(combinedLength);
-
- // We can only send 4088 bytes in one packet. Header[1] is set to 1 if this is a
- // partial packet (whether or not count != 0).
- combinedBuffer[7] = 0; // touch this first for the jit bounds check
- combinedBuffer[0] = PRELOGIN_PACKET_TYPE;
- combinedBuffer[1] = (byte)(count > 0 ? 0 : 1);
- combinedBuffer[2] = (byte)((currentCount + TdsEnums.HEADER_LEN) / 0x100);
- combinedBuffer[3] = (byte)((currentCount + TdsEnums.HEADER_LEN) % 0x100);
- combinedBuffer[4] = 0;
- combinedBuffer[5] = 0;
- combinedBuffer[6] = 0;
-
- Array.Copy(buffer, currentOffset, combinedBuffer, TdsEnums.HEADER_LEN, (combinedLength - TdsEnums.HEADER_LEN));
-
- if (async)
- {
- await _stream.WriteAsync(combinedBuffer, 0, combinedLength, token).ConfigureAwait(false);
- }
- else
- {
- _stream.Write(combinedBuffer, 0, combinedLength);
- }
-
- Array.Clear(combinedBuffer, 0, combinedLength);
- ArrayPool.Shared.Return(combinedBuffer);
- }
- else
- {
- currentCount = count;
- count = 0;
-
- if (async)
- {
- await _stream.WriteAsync(buffer, currentOffset, currentCount, token).ConfigureAwait(false);
- }
- else
- {
- _stream.Write(buffer, currentOffset, currentCount);
- }
- }
-
- if (async)
- {
- await _stream.FlushAsync().ConfigureAwait(false);
- }
- else
- {
- _stream.Flush();
- }
-
- currentOffset += currentCount;
- }
+ SqlClientEventSource.Log.TrySNITraceEvent(" switched from encapsulation to passthrough mode");
}
///
/// Set stream length.
///
/// Length
- public override void SetLength(long value)
- {
- throw new NotSupportedException();
- }
+ public override void SetLength(long value) => throw new NotSupportedException();
///
/// Flush stream
@@ -252,14 +66,8 @@ public override void Flush()
///
public override long Position
{
- get
- {
- throw new NotSupportedException();
- }
- set
- {
- throw new NotSupportedException();
- }
+ get => throw new NotSupportedException();
+ set => throw new NotSupportedException();
}
///
@@ -268,44 +76,40 @@ public override long Position
/// Offset
/// Origin
/// Position
- public override long Seek(long offset, SeekOrigin origin)
- {
- throw new NotSupportedException();
- }
+ public override long Seek(long offset, SeekOrigin origin) => throw new NotSupportedException();
///
/// Check if stream can be read from
///
- public override bool CanRead
- {
- get { return _stream.CanRead; }
- }
+ public override bool CanRead => _stream.CanRead;
///
/// Check if stream can be written to
///
- public override bool CanWrite
- {
- get { return _stream.CanWrite; }
- }
+ public override bool CanWrite => _stream.CanWrite;
///
/// Check if stream can be seeked
///
- public override bool CanSeek
- {
- get { return false; } // Seek not supported
- }
+ public override bool CanSeek => false;
///
/// Get stream length
///
- public override long Length
+ public override long Length => throw new NotSupportedException();
+
+ private static void SetupPreLoginPacketHeader(byte[] buffer, int dataLength, int remainingLength)
{
- get
- {
- throw new NotSupportedException();
- }
+ // We can only send 4088 bytes in one packet. Header[1] is set to 1 if this is a
+ // partial packet (whether or not count != 0).
+ buffer[7] = 0; // touch this first for the jit bounds check
+ buffer[0] = PRELOGIN_PACKET_TYPE;
+ buffer[1] = (byte)(remainingLength > 0 ? 0 : 1);
+ buffer[2] = (byte)((dataLength + TdsEnums.HEADER_LEN) / 0x100);
+ buffer[3] = (byte)((dataLength + TdsEnums.HEADER_LEN) % 0x100);
+ buffer[4] = 0;
+ buffer[5] = 0;
+ buffer[6] = 0;
}
}
}
diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlClientEventSource.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlClientEventSource.cs
index a2d49e96c8..d7f0decbf5 100644
--- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlClientEventSource.cs
+++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlClientEventSource.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
+using System;
using System.Diagnostics.Tracing;
using System.Threading;
@@ -1011,4 +1012,24 @@ internal void SNIScopeLeave(long scopeId)
}
#endregion
}
+
+ internal readonly struct SNIEventScope : IDisposable
+ {
+ private readonly long _scopeID;
+
+ public SNIEventScope(long scopeID)
+ {
+ _scopeID = scopeID;
+ }
+
+ public void Dispose()
+ {
+ SqlClientEventSource.Log.SNIScopeLeave(_scopeID);
+ }
+
+ public static SNIEventScope Create(string message)
+ {
+ return new SNIEventScope(SqlClientEventSource.Log.SNIScopeEnter(message));
+ }
+ }
}