aspnetcore/src/Microsoft.Net.WebSockets/WebSocketBuffer.cs

717 lines
34 KiB
C#

// Copyright (c) Microsoft Open Technologies, Inc.
// All Rights Reserved
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
// WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF
// TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR
// NON-INFRINGEMENT.
// See the Apache 2 License for the specific language governing
// permissions and limitations under the License.
//------------------------------------------------------------------------------
// <copyright file="WebSocketBuffer.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
using System;
using System.Diagnostics;
using System.Diagnostics.Contracts;
using System.Globalization;
using System.Net.WebSockets;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
namespace Microsoft.Net.WebSockets
{
// This class helps to abstract the internal WebSocket buffer, which is used to interact with the native WebSocket
// protocol component (WSPC). It helps to shield the details of the layout and the involved pointer arithmetic.
// The internal WebSocket buffer also contains a segment, which is used by the WebSocketBase class to buffer
// payload (parsed by WSPC already) for the application, if the application requested fewer bytes than the
// WSPC returned. The internal buffer is pinned for the whole lifetime if this class.
// LAYOUT:
// | Native buffer | PayloadReceiveBuffer | PropertyBuffer |
// | RBS + SBS + 144 | RBS | PBS |
// | Only WSPC may modify | Only WebSocketBase may modify |
//
// *RBS = ReceiveBufferSize, *SBS = SendBufferSize
// *PBS = PropertyBufferSize (32-bit: 16, 64 bit: 20 bytes)
public class WebSocketBuffer : IDisposable
{
private const int NativeOverheadBufferSize = 144;
public const int MinSendBufferSize = 16;
internal const int MinReceiveBufferSize = 256;
internal const int MaxBufferSize = 64 * 1024;
#if ASPNET50
private static readonly int SizeOfUInt = Marshal.SizeOf(typeof(uint));
private static readonly int SizeOfBool = Marshal.SizeOf(typeof(bool));
#else
private static readonly int SizeOfUInt = Marshal.SizeOf<uint>();
private static readonly int SizeOfBool = Marshal.SizeOf<bool>();
#endif
private static readonly int PropertyBufferSize = (2 * SizeOfUInt) + SizeOfBool + IntPtr.Size;
private readonly int _ReceiveBufferSize;
// Indicates the range of the pinned byte[] that can be used by the WSPC (nativeBuffer + pinnedSendBuffer)
private readonly long _StartAddress;
private readonly long _EndAddress;
private readonly GCHandle _GCHandle;
private readonly ArraySegment<byte> _InternalBuffer;
private readonly ArraySegment<byte> _NativeBuffer;
private readonly ArraySegment<byte> _PayloadBuffer;
private readonly ArraySegment<byte> _PropertyBuffer;
private readonly int _SendBufferSize;
private volatile int _PayloadOffset;
private volatile WebSocketReceiveResult _BufferedPayloadReceiveResult;
private long _PinnedSendBufferStartAddress;
private long _PinnedSendBufferEndAddress;
private ArraySegment<byte> _PinnedSendBuffer;
private GCHandle _PinnedSendBufferHandle;
private int _StateWhenDisposing = int.MinValue;
private int _SendBufferState;
private WebSocketBuffer(ArraySegment<byte> internalBuffer, int receiveBufferSize, int sendBufferSize)
{
Contract.Assert(internalBuffer.Array != null, "'internalBuffer' MUST NOT be NULL.");
Contract.Assert(receiveBufferSize >= MinReceiveBufferSize,
"'receiveBufferSize' MUST be at least " + MinReceiveBufferSize.ToString(NumberFormatInfo.InvariantInfo) + ".");
Contract.Assert(sendBufferSize >= MinSendBufferSize,
"'sendBufferSize' MUST be at least " + MinSendBufferSize.ToString(NumberFormatInfo.InvariantInfo) + ".");
Contract.Assert(receiveBufferSize <= MaxBufferSize,
"'receiveBufferSize' MUST NOT exceed " + MaxBufferSize.ToString(NumberFormatInfo.InvariantInfo) + ".");
Contract.Assert(sendBufferSize <= MaxBufferSize,
"'sendBufferSize' MUST NOT exceed " + MaxBufferSize.ToString(NumberFormatInfo.InvariantInfo) + ".");
_ReceiveBufferSize = receiveBufferSize;
_SendBufferSize = sendBufferSize;
_InternalBuffer = internalBuffer;
_GCHandle = GCHandle.Alloc(internalBuffer.Array, GCHandleType.Pinned);
// Size of the internal buffer owned exclusively by the WSPC.
int nativeBufferSize = _ReceiveBufferSize + _SendBufferSize + NativeOverheadBufferSize;
_StartAddress = Marshal.UnsafeAddrOfPinnedArrayElement(internalBuffer.Array, internalBuffer.Offset).ToInt64();
_EndAddress = _StartAddress + nativeBufferSize;
_NativeBuffer = new ArraySegment<byte>(internalBuffer.Array, internalBuffer.Offset, nativeBufferSize);
_PayloadBuffer = new ArraySegment<byte>(internalBuffer.Array,
_NativeBuffer.Offset + _NativeBuffer.Count,
_ReceiveBufferSize);
_PropertyBuffer = new ArraySegment<byte>(internalBuffer.Array,
_PayloadBuffer.Offset + _PayloadBuffer.Count,
PropertyBufferSize);
_SendBufferState = SendBufferState.None;
}
public int ReceiveBufferSize
{
get { return _ReceiveBufferSize; }
}
public int SendBufferSize
{
get { return _SendBufferSize; }
}
internal static WebSocketBuffer CreateClientBuffer(ArraySegment<byte> internalBuffer, int receiveBufferSize, int sendBufferSize)
{
Contract.Assert(internalBuffer.Count >= GetInternalBufferSize(receiveBufferSize, sendBufferSize, false),
"Array 'internalBuffer' is TOO SMALL. Call Validate before instantiating WebSocketBuffer.");
return new WebSocketBuffer(internalBuffer, receiveBufferSize, GetNativeSendBufferSize(sendBufferSize, false));
}
internal static WebSocketBuffer CreateServerBuffer(ArraySegment<byte> internalBuffer, int receiveBufferSize)
{
int sendBufferSize = GetNativeSendBufferSize(MinSendBufferSize, true);
Contract.Assert(internalBuffer.Count >= GetInternalBufferSize(receiveBufferSize, sendBufferSize, true),
"Array 'internalBuffer' is TOO SMALL. Call Validate before instantiating WebSocketBuffer.");
return new WebSocketBuffer(internalBuffer, receiveBufferSize, sendBufferSize);
}
public void Dispose(WebSocketState webSocketState)
{
if (Interlocked.CompareExchange(ref _StateWhenDisposing, (int)webSocketState, int.MinValue) != int.MinValue)
{
return;
}
this.CleanUp();
}
public void Dispose()
{
this.Dispose(WebSocketState.None);
}
internal UnsafeNativeMethods.WebSocketProtocolComponent.Property[] CreateProperties(bool useZeroMaskingKey)
{
ThrowIfDisposed();
// serialize marshaled property values in the property segment of the internal buffer
IntPtr internalBufferPtr = _GCHandle.AddrOfPinnedObject();
int offset = _PropertyBuffer.Offset;
Marshal.WriteInt32(internalBufferPtr, offset, _ReceiveBufferSize);
offset += SizeOfUInt;
Marshal.WriteInt32(internalBufferPtr, offset, _SendBufferSize);
offset += SizeOfUInt;
Marshal.WriteIntPtr(internalBufferPtr, offset, internalBufferPtr);
offset += IntPtr.Size;
Marshal.WriteInt32(internalBufferPtr, offset, useZeroMaskingKey ? (int)1 : (int)0);
int propertyCount = useZeroMaskingKey ? 4 : 3;
UnsafeNativeMethods.WebSocketProtocolComponent.Property[] properties =
new UnsafeNativeMethods.WebSocketProtocolComponent.Property[propertyCount];
// Calculate the pointers to the positions of the properties within the internal buffer
offset = _PropertyBuffer.Offset;
properties[0] = new UnsafeNativeMethods.WebSocketProtocolComponent.Property()
{
Type = UnsafeNativeMethods.WebSocketProtocolComponent.PropertyType.ReceiveBufferSize,
PropertySize = (uint)SizeOfUInt,
PropertyData = IntPtr.Add(internalBufferPtr, offset)
};
offset += SizeOfUInt;
properties[1] = new UnsafeNativeMethods.WebSocketProtocolComponent.Property()
{
Type = UnsafeNativeMethods.WebSocketProtocolComponent.PropertyType.SendBufferSize,
PropertySize = (uint)SizeOfUInt,
PropertyData = IntPtr.Add(internalBufferPtr, offset)
};
offset += SizeOfUInt;
properties[2] = new UnsafeNativeMethods.WebSocketProtocolComponent.Property()
{
Type = UnsafeNativeMethods.WebSocketProtocolComponent.PropertyType.AllocatedBuffer,
PropertySize = (uint)_NativeBuffer.Count,
PropertyData = IntPtr.Add(internalBufferPtr, offset)
};
offset += IntPtr.Size;
if (useZeroMaskingKey)
{
properties[3] = new UnsafeNativeMethods.WebSocketProtocolComponent.Property()
{
Type = UnsafeNativeMethods.WebSocketProtocolComponent.PropertyType.DisableMasking,
PropertySize = (uint)SizeOfBool,
PropertyData = IntPtr.Add(internalBufferPtr, offset)
};
}
return properties;
}
// This method is not thread safe. It must only be called after enforcing at most 1 outstanding send operation
internal void PinSendBuffer(ArraySegment<byte> payload, out bool bufferHasBeenPinned)
{
bufferHasBeenPinned = false;
WebSocketHelpers.ValidateBuffer(payload.Array, payload.Offset, payload.Count);
int previousState = Interlocked.Exchange(ref _SendBufferState, SendBufferState.SendPayloadSpecified);
if (previousState != SendBufferState.None)
{
Contract.Assert(false, "'m_SendBufferState' MUST BE 'None' at this point.");
// Indicates a violation in the API contract that could indicate
// memory corruption because the pinned sendbuffer is shared between managed and native code
throw new AccessViolationException();
}
_PinnedSendBuffer = payload;
_PinnedSendBufferHandle = GCHandle.Alloc(_PinnedSendBuffer.Array, GCHandleType.Pinned);
bufferHasBeenPinned = true;
_PinnedSendBufferStartAddress =
Marshal.UnsafeAddrOfPinnedArrayElement(_PinnedSendBuffer.Array, _PinnedSendBuffer.Offset).ToInt64();
_PinnedSendBufferEndAddress = _PinnedSendBufferStartAddress + _PinnedSendBuffer.Count;
}
// This method is not thread safe. It must only be called after enforcing at most 1 outstanding send operation
internal IntPtr ConvertPinnedSendPayloadToNative(ArraySegment<byte> payload)
{
return ConvertPinnedSendPayloadToNative(payload.Array, payload.Offset, payload.Count);
}
// This method is not thread safe. It must only be called after enforcing at most 1 outstanding send operation
internal IntPtr ConvertPinnedSendPayloadToNative(byte[] buffer, int offset, int count)
{
if (!IsPinnedSendPayloadBuffer(buffer, offset, count))
{
// Indicates a violation in the API contract that could indicate
// memory corruption because the pinned sendbuffer is shared between managed and native code
throw new AccessViolationException();
}
Contract.Assert(Marshal.UnsafeAddrOfPinnedArrayElement(_PinnedSendBuffer.Array,
_PinnedSendBuffer.Offset).ToInt64() == _PinnedSendBufferStartAddress,
"'m_PinnedSendBuffer.Array' MUST be pinned during the entire send operation.");
return new IntPtr(_PinnedSendBufferStartAddress + offset - _PinnedSendBuffer.Offset);
}
// This method is not thread safe. It must only be called after enforcing at most 1 outstanding send operation
internal ArraySegment<byte> ConvertPinnedSendPayloadFromNative(UnsafeNativeMethods.WebSocketProtocolComponent.Buffer buffer,
UnsafeNativeMethods.WebSocketProtocolComponent.BufferType bufferType)
{
if (!IsPinnedSendPayloadBuffer(buffer, bufferType))
{
// Indicates a violation in the API contract that could indicate
// memory corruption because the pinned sendbuffer is shared between managed and native code
throw new AccessViolationException();
}
Contract.Assert(Marshal.UnsafeAddrOfPinnedArrayElement(_PinnedSendBuffer.Array,
_PinnedSendBuffer.Offset).ToInt64() == _PinnedSendBufferStartAddress,
"'m_PinnedSendBuffer.Array' MUST be pinned during the entire send operation.");
IntPtr bufferData;
uint bufferSize;
UnwrapWebSocketBuffer(buffer, bufferType, out bufferData, out bufferSize);
int internalOffset = (int)(bufferData.ToInt64() - _PinnedSendBufferStartAddress);
return new ArraySegment<byte>(_PinnedSendBuffer.Array, _PinnedSendBuffer.Offset + internalOffset, (int)bufferSize);
}
// This method is not thread safe. It must only be called after enforcing at most 1 outstanding send operation
private bool IsPinnedSendPayloadBuffer(byte[] buffer, int offset, int count)
{
if (_SendBufferState != SendBufferState.SendPayloadSpecified)
{
return false;
}
return object.ReferenceEquals(buffer, _PinnedSendBuffer.Array) &&
offset >= _PinnedSendBuffer.Offset &&
offset + count <= _PinnedSendBuffer.Offset + _PinnedSendBuffer.Count;
}
// This method is not thread safe. It must only be called after enforcing at most 1 outstanding send operation
private bool IsPinnedSendPayloadBuffer(UnsafeNativeMethods.WebSocketProtocolComponent.Buffer buffer,
UnsafeNativeMethods.WebSocketProtocolComponent.BufferType bufferType)
{
if (_SendBufferState != SendBufferState.SendPayloadSpecified)
{
return false;
}
IntPtr bufferData;
uint bufferSize;
UnwrapWebSocketBuffer(buffer, bufferType, out bufferData, out bufferSize);
long nativeBufferStartAddress = bufferData.ToInt64();
long nativeBufferEndAddress = nativeBufferStartAddress + bufferSize;
return nativeBufferStartAddress >= _PinnedSendBufferStartAddress &&
nativeBufferEndAddress >= _PinnedSendBufferStartAddress &&
nativeBufferStartAddress <= _PinnedSendBufferEndAddress &&
nativeBufferEndAddress <= _PinnedSendBufferEndAddress;
}
// This method is only thread safe for races between Abort and at most 1 uncompleted send operation
internal void ReleasePinnedSendBuffer()
{
int previousState = Interlocked.Exchange(ref _SendBufferState, SendBufferState.None);
if (previousState != SendBufferState.SendPayloadSpecified)
{
return;
}
if (_PinnedSendBufferHandle.IsAllocated)
{
_PinnedSendBufferHandle.Free();
}
_PinnedSendBuffer = WebSocketHelpers.EmptyPayload;
}
internal void BufferPayload(ArraySegment<byte> payload,
int unconsumedDataOffset,
WebSocketMessageType messageType,
bool endOfMessage)
{
ThrowIfDisposed();
int bytesBuffered = payload.Count - unconsumedDataOffset;
Contract.Assert(_PayloadOffset == 0,
"'m_PayloadOffset' MUST be '0' at this point.");
Contract.Assert(_BufferedPayloadReceiveResult == null || _BufferedPayloadReceiveResult.Count == 0,
"'m_BufferedPayloadReceiveResult.Count' MUST be '0' at this point.");
Buffer.BlockCopy(payload.Array,
payload.Offset + unconsumedDataOffset,
_PayloadBuffer.Array,
_PayloadBuffer.Offset,
bytesBuffered);
_BufferedPayloadReceiveResult =
new WebSocketReceiveResult(bytesBuffered, messageType, endOfMessage);
this.ValidateBufferedPayload();
}
internal bool ReceiveFromBufferedPayload(ArraySegment<byte> buffer, out WebSocketReceiveResult receiveResult)
{
ThrowIfDisposed();
ValidateBufferedPayload();
int bytesTransferred = Math.Min(buffer.Count, _BufferedPayloadReceiveResult.Count);
receiveResult = WebSocketReceiveResultExtensions.DecrementAndClone(ref _BufferedPayloadReceiveResult, bytesTransferred);
Buffer.BlockCopy(_PayloadBuffer.Array,
_PayloadBuffer.Offset + _PayloadOffset,
buffer.Array,
buffer.Offset,
bytesTransferred);
bool morePayloadBuffered;
if (_BufferedPayloadReceiveResult.Count == 0)
{
_PayloadOffset = 0;
_BufferedPayloadReceiveResult = null;
morePayloadBuffered = false;
}
else
{
_PayloadOffset += bytesTransferred;
morePayloadBuffered = true;
this.ValidateBufferedPayload();
}
return morePayloadBuffered;
}
internal ArraySegment<byte> ConvertNativeBuffer(UnsafeNativeMethods.WebSocketProtocolComponent.Action action,
UnsafeNativeMethods.WebSocketProtocolComponent.Buffer buffer,
UnsafeNativeMethods.WebSocketProtocolComponent.BufferType bufferType)
{
ThrowIfDisposed();
IntPtr bufferData;
uint bufferLength;
UnwrapWebSocketBuffer(buffer, bufferType, out bufferData, out bufferLength);
if (bufferData == IntPtr.Zero)
{
return WebSocketHelpers.EmptyPayload;
}
if (this.IsNativeBuffer(bufferData, bufferLength))
{
return new ArraySegment<byte>(_InternalBuffer.Array,
this.GetOffset(bufferData),
(int)bufferLength);
}
Contract.Assert(false, "'buffer' MUST reference a memory segment within the pinned InternalBuffer.");
// Indicates a violation in the contract with native Websocket.dll and could indicate
// memory corruption because the internal buffer is shared between managed and native code
throw new AccessViolationException();
}
internal void ConvertCloseBuffer(UnsafeNativeMethods.WebSocketProtocolComponent.Action action,
UnsafeNativeMethods.WebSocketProtocolComponent.Buffer buffer,
out WebSocketCloseStatus closeStatus,
out string reason)
{
ThrowIfDisposed();
IntPtr bufferData;
uint bufferLength;
closeStatus = (WebSocketCloseStatus)buffer.CloseStatus.CloseStatus;
UnwrapWebSocketBuffer(buffer, UnsafeNativeMethods.WebSocketProtocolComponent.BufferType.Close, out bufferData, out bufferLength);
if (bufferData == IntPtr.Zero)
{
reason = null;
}
else
{
ArraySegment<byte> reasonBlob;
if (this.IsNativeBuffer(bufferData, bufferLength))
{
reasonBlob = new ArraySegment<byte>(_InternalBuffer.Array,
this.GetOffset(bufferData),
(int)bufferLength);
}
else
{
Contract.Assert(false, "'buffer' MUST reference a memory segment within the pinned InternalBuffer.");
// Indicates a violation in the contract with native Websocket.dll and could indicate
// memory corruption because the internal buffer is shared between managed and native code
throw new AccessViolationException();
}
// No need to wrap DecoderFallbackException for invalid UTF8 chacters, because
// Encoding.UTF8 will not throw but replace invalid characters instead.
reason = Encoding.UTF8.GetString(reasonBlob.Array, reasonBlob.Offset, reasonBlob.Count);
}
}
internal void ValidateNativeBuffers(UnsafeNativeMethods.WebSocketProtocolComponent.Action action,
UnsafeNativeMethods.WebSocketProtocolComponent.BufferType bufferType,
UnsafeNativeMethods.WebSocketProtocolComponent.Buffer[] dataBuffers,
uint dataBufferCount)
{
Contract.Assert(dataBufferCount <= (uint)int.MaxValue,
"'dataBufferCount' MUST NOT be bigger than Int32.MaxValue.");
Contract.Assert(dataBuffers != null, "'dataBuffers' MUST NOT be NULL.");
ThrowIfDisposed();
if (dataBufferCount > dataBuffers.Length)
{
Contract.Assert(false, "'dataBufferCount' MUST NOT be bigger than 'dataBuffers.Length'.");
// Indicates a violation in the contract with native Websocket.dll and could indicate
// memory corruption because the internal buffer is shared between managed and native code
throw new AccessViolationException();
}
int count = dataBuffers.Length;
bool isSendActivity = action == UnsafeNativeMethods.WebSocketProtocolComponent.Action.IndicateSendComplete ||
action == UnsafeNativeMethods.WebSocketProtocolComponent.Action.SendToNetwork;
if (isSendActivity)
{
count = (int)dataBufferCount;
}
bool nonZeroBufferFound = false;
for (int i = 0; i < count; i++)
{
UnsafeNativeMethods.WebSocketProtocolComponent.Buffer dataBuffer = dataBuffers[i];
IntPtr bufferData;
uint bufferLength;
UnwrapWebSocketBuffer(dataBuffer, bufferType, out bufferData, out bufferLength);
if (bufferData == IntPtr.Zero)
{
continue;
}
nonZeroBufferFound = true;
bool isPinnedSendPayloadBuffer = IsPinnedSendPayloadBuffer(dataBuffer, bufferType);
if (bufferLength > GetMaxBufferSize())
{
if (!isSendActivity || !isPinnedSendPayloadBuffer)
{
Contract.Assert(false,
"'dataBuffer.BufferLength' MUST NOT be bigger than 'm_ReceiveBufferSize' and 'm_SendBufferSize'.");
// Indicates a violation in the contract with native Websocket.dll and could indicate
// memory corruption because the internal buffer is shared between managed and native code
throw new AccessViolationException();
}
}
if (!isPinnedSendPayloadBuffer && !IsNativeBuffer(bufferData, bufferLength))
{
Contract.Assert(false,
"WebSocketGetAction MUST return a pointer within the pinned internal buffer.");
// Indicates a violation in the contract with native Websocket.dll and could indicate
// memory corruption because the internal buffer is shared between managed and native code
throw new AccessViolationException();
}
}
if (!nonZeroBufferFound &&
action != UnsafeNativeMethods.WebSocketProtocolComponent.Action.NoAction &&
action != UnsafeNativeMethods.WebSocketProtocolComponent.Action.IndicateReceiveComplete &&
action != UnsafeNativeMethods.WebSocketProtocolComponent.Action.IndicateSendComplete)
{
Contract.Assert(false, "At least one 'dataBuffer.Buffer' MUST NOT be NULL.");
}
}
private static int GetNativeSendBufferSize(int sendBufferSize, bool isServerBuffer)
{
return isServerBuffer ? MinSendBufferSize : sendBufferSize;
}
internal static void UnwrapWebSocketBuffer(UnsafeNativeMethods.WebSocketProtocolComponent.Buffer buffer,
UnsafeNativeMethods.WebSocketProtocolComponent.BufferType bufferType,
out IntPtr bufferData,
out uint bufferLength)
{
bufferData = IntPtr.Zero;
bufferLength = 0;
switch (bufferType)
{
case UnsafeNativeMethods.WebSocketProtocolComponent.BufferType.Close:
bufferData = buffer.CloseStatus.ReasonData;
bufferLength = buffer.CloseStatus.ReasonLength;
break;
case UnsafeNativeMethods.WebSocketProtocolComponent.BufferType.None:
case UnsafeNativeMethods.WebSocketProtocolComponent.BufferType.BinaryFragment:
case UnsafeNativeMethods.WebSocketProtocolComponent.BufferType.BinaryMessage:
case UnsafeNativeMethods.WebSocketProtocolComponent.BufferType.UTF8Fragment:
case UnsafeNativeMethods.WebSocketProtocolComponent.BufferType.UTF8Message:
case UnsafeNativeMethods.WebSocketProtocolComponent.BufferType.PingPong:
case UnsafeNativeMethods.WebSocketProtocolComponent.BufferType.UnsolicitedPong:
bufferData = buffer.Data.BufferData;
bufferLength = buffer.Data.BufferLength;
break;
default:
Contract.Assert(false,
string.Format(CultureInfo.InvariantCulture,
"BufferType '{0}' is invalid/unknown.",
bufferType));
break;
}
}
private void ThrowIfDisposed()
{
switch (_StateWhenDisposing)
{
case int.MinValue:
return;
case (int)WebSocketState.Closed:
case (int)WebSocketState.Aborted:
throw new WebSocketException(WebSocketError.InvalidState,
SR.GetString(SR.net_WebSockets_InvalidState_ClosedOrAborted, typeof(WebSocketBase), _StateWhenDisposing));
default:
throw new ObjectDisposedException(GetType().FullName);
}
}
[Conditional("DEBUG"), Conditional("CONTRACTS_FULL")]
private void ValidateBufferedPayload()
{
Contract.Assert(_BufferedPayloadReceiveResult != null,
"'m_BufferedPayloadReceiveResult' MUST NOT be NULL.");
Contract.Assert(_BufferedPayloadReceiveResult.Count >= 0,
"'m_BufferedPayloadReceiveResult.Count' MUST NOT be negative.");
Contract.Assert(_PayloadOffset >= 0, "'m_PayloadOffset' MUST NOT be smaller than 0.");
Contract.Assert(_PayloadOffset <= _PayloadBuffer.Count,
"'m_PayloadOffset' MUST NOT be bigger than 'm_PayloadBuffer.Count'.");
Contract.Assert(_PayloadOffset + _BufferedPayloadReceiveResult.Count <= _PayloadBuffer.Count,
"'m_PayloadOffset + m_PayloadBytesBuffered' MUST NOT be bigger than 'm_PayloadBuffer.Count'.");
}
private int GetOffset(IntPtr pBuffer)
{
Contract.Assert(pBuffer != IntPtr.Zero, "'pBuffer' MUST NOT be IntPtr.Zero.");
int offset = (int)(pBuffer.ToInt64() - _StartAddress + _InternalBuffer.Offset);
Contract.Assert(offset >= 0, "'offset' MUST NOT be negative.");
return offset;
}
[Pure]
private int GetMaxBufferSize()
{
return Math.Max(_ReceiveBufferSize, _SendBufferSize);
}
internal bool IsInternalBuffer(byte[] buffer, int offset, int count)
{
Contract.Assert(buffer != null, "'buffer' MUST NOT be NULL.");
Contract.Assert(_InternalBuffer.Array != null, "'m_InternalBuffer.Array' MUST NOT be NULL.");
Contract.Assert(offset >= 0, "'offset' MUST NOT be negative.");
Contract.Assert(count >= 0, "'count' MUST NOT be negative.");
Contract.Assert(offset + count <= buffer.Length, "'offset + count' MUST NOT exceed 'buffer.Length'.");
return object.ReferenceEquals(buffer, _InternalBuffer.Array);
}
internal IntPtr ToIntPtr(int offset)
{
Contract.Assert(offset >= 0, "'offset' MUST NOT be negative.");
Contract.Assert(_StartAddress + offset <= _EndAddress, "'offset' is TOO BIG.");
return new IntPtr(_StartAddress + offset);
}
private bool IsNativeBuffer(IntPtr pBuffer, uint bufferSize)
{
Contract.Assert(pBuffer != IntPtr.Zero, "'pBuffer' MUST NOT be NULL.");
Contract.Assert(bufferSize <= GetMaxBufferSize(),
"'bufferSize' MUST NOT be bigger than 'm_ReceiveBufferSize' and 'm_SendBufferSize'.");
long nativeBufferStartAddress = pBuffer.ToInt64();
long nativeBufferEndAddress = bufferSize + nativeBufferStartAddress;
Contract.Assert(Marshal.UnsafeAddrOfPinnedArrayElement(_InternalBuffer.Array, _InternalBuffer.Offset).ToInt64() == _StartAddress,
"'m_InternalBuffer.Array' MUST be pinned for the whole lifetime of a WebSocket.");
if (nativeBufferStartAddress >= _StartAddress &&
nativeBufferStartAddress <= _EndAddress &&
nativeBufferEndAddress >= _StartAddress &&
nativeBufferEndAddress <= _EndAddress)
{
return true;
}
return false;
}
private void CleanUp()
{
if (_GCHandle.IsAllocated)
{
_GCHandle.Free();
}
ReleasePinnedSendBuffer();
}
public static ArraySegment<byte> CreateInternalBufferArraySegment(int receiveBufferSize, int sendBufferSize, bool isServerBuffer)
{
Contract.Assert(receiveBufferSize >= MinReceiveBufferSize,
"'receiveBufferSize' MUST be at least " + MinReceiveBufferSize.ToString(NumberFormatInfo.InvariantInfo) + ".");
Contract.Assert(sendBufferSize >= MinSendBufferSize,
"'sendBufferSize' MUST be at least " + MinSendBufferSize.ToString(NumberFormatInfo.InvariantInfo) + ".");
int internalBufferSize = GetInternalBufferSize(receiveBufferSize, sendBufferSize, isServerBuffer);
return new ArraySegment<byte>(new byte[internalBufferSize]);
}
public static void Validate(int count, int receiveBufferSize, int sendBufferSize, bool isServerBuffer)
{
Contract.Assert(receiveBufferSize >= MinReceiveBufferSize,
"'receiveBufferSize' MUST be at least " + MinReceiveBufferSize.ToString(NumberFormatInfo.InvariantInfo) + ".");
Contract.Assert(sendBufferSize >= MinSendBufferSize,
"'sendBufferSize' MUST be at least " + MinSendBufferSize.ToString(NumberFormatInfo.InvariantInfo) + ".");
int minBufferSize = GetInternalBufferSize(receiveBufferSize, sendBufferSize, isServerBuffer);
if (count < minBufferSize)
{
throw new ArgumentOutOfRangeException("internalBuffer",
SR.GetString(SR.net_WebSockets_ArgumentOutOfRange_InternalBuffer, minBufferSize));
}
}
private static int GetInternalBufferSize(int receiveBufferSize, int sendBufferSize, bool isServerBuffer)
{
Contract.Assert(receiveBufferSize >= MinReceiveBufferSize,
"'receiveBufferSize' MUST be at least " + MinReceiveBufferSize.ToString(NumberFormatInfo.InvariantInfo) + ".");
Contract.Assert(sendBufferSize >= MinSendBufferSize,
"'sendBufferSize' MUST be at least " + MinSendBufferSize.ToString(NumberFormatInfo.InvariantInfo) + ".");
Contract.Assert(receiveBufferSize <= MaxBufferSize,
"'receiveBufferSize' MUST be less than or equal to " + MaxBufferSize.ToString(NumberFormatInfo.InvariantInfo) + ".");
Contract.Assert(sendBufferSize <= MaxBufferSize,
"'sendBufferSize' MUST be at less than or equal to " + MaxBufferSize.ToString(NumberFormatInfo.InvariantInfo) + ".");
int nativeSendBufferSize = GetNativeSendBufferSize(sendBufferSize, isServerBuffer);
return (2 * receiveBufferSize) + nativeSendBufferSize + NativeOverheadBufferSize + PropertyBufferSize;
}
private static class SendBufferState
{
public const int None = 0;
public const int SendPayloadSpecified = 1;
}
}
}