public ServerWebSocket(Stream innerStream, string subProtocol, int receiveBufferSize, TimeSpan keepAliveInterval, ArraySegment <byte> internalBuffer) : base(innerStream, subProtocol, keepAliveInterval, WebSocketBuffer.CreateServerBuffer(internalBuffer, receiveBufferSize)) { _properties = InternalBuffer.CreateProperties(false); _sessionHandle = CreateWebSocketHandle(); if (_sessionHandle == null || _sessionHandle.IsInvalid) { WebSocketValidate.ThrowPlatformNotSupportedException_WSPC(); } StartKeepAliveTimer(); }
public override Task CloseAsync(WebSocketCloseStatus closeStatus, string?statusDescription, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); ThrowIfDisposed(); WebSocketValidate.ValidateCloseStatus(closeStatus, statusDescription); var state = State; if (state == WebSocketState.None || state == WebSocketState.Closed) { throw new WebSocketException(WebSocketError.InvalidState, SR.Format(SR.net_WebSockets_InvalidState, state, "Connecting, Open, CloseSent, Aborted")); } return(state == WebSocketState.Open || state == WebSocketState.Connecting || state == WebSocketState.Aborted || state == WebSocketState.CloseSent ? CloseAsyncCore(closeStatus, statusDescription, state != WebSocketState.Aborted, cancellationToken) : Task.CompletedTask); }
internal static void ValidateOptions(string?subProtocol, int receiveBufferSize, int sendBufferSize, TimeSpan keepAliveInterval) { if (subProtocol != null) { WebSocketValidate.ValidateSubprotocol(subProtocol); } if (receiveBufferSize < MinReceiveBufferSize) { throw new ArgumentOutOfRangeException(nameof(receiveBufferSize), receiveBufferSize, SR.Format(SR.net_WebSockets_ArgumentOutOfRange_TooSmall, MinReceiveBufferSize)); } if (sendBufferSize < MinSendBufferSize) { throw new ArgumentOutOfRangeException(nameof(sendBufferSize), sendBufferSize, SR.Format(SR.net_WebSockets_ArgumentOutOfRange_TooSmall, MinSendBufferSize)); } if (receiveBufferSize > MaxBufferSize) { throw new ArgumentOutOfRangeException(nameof(receiveBufferSize), receiveBufferSize, SR.Format(SR.net_WebSockets_ArgumentOutOfRange_TooBig, nameof(receiveBufferSize), receiveBufferSize, MaxBufferSize)); } if (sendBufferSize > MaxBufferSize) { throw new ArgumentOutOfRangeException(nameof(sendBufferSize), sendBufferSize, SR.Format(SR.net_WebSockets_ArgumentOutOfRange_TooBig, nameof(sendBufferSize), sendBufferSize, MaxBufferSize)); } if (keepAliveInterval < Timeout.InfiniteTimeSpan) // -1 millisecond { throw new ArgumentOutOfRangeException(nameof(keepAliveInterval), keepAliveInterval, SR.Format(SR.net_WebSockets_ArgumentOutOfRange_TooSmall, Timeout.InfiniteTimeSpan.ToString())); } }
internal static void ValidateOptions(string subProtocol, int receiveBufferSize, int sendBufferSize, TimeSpan keepAliveInterval) { // We allow the subProtocol to be null. Validate if it is not null. if (subProtocol != null) { WebSocketValidate.ValidateSubprotocol(subProtocol); } ValidateBufferSizes(receiveBufferSize, sendBufferSize); if (keepAliveInterval < Timeout.InfiniteTimeSpan) // -1 { throw new ArgumentOutOfRangeException(nameof(keepAliveInterval), keepAliveInterval, SR.Format(SR.net_WebSockets_ArgumentOutOfRange_TooSmall, Timeout.InfiniteTimeSpan.ToString())); } }
// 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; WebSocketValidate.ValidateBuffer(payload.Array, payload.Offset, payload.Count); int previousState = Interlocked.Exchange(ref _sendBufferState, SendBufferState.SendPayloadSpecified); if (previousState != SendBufferState.None) { Debug.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; }
public static WebSocket CreateClientWebSocket(Stream innerStream, string subProtocol, int receiveBufferSize, int sendBufferSize, TimeSpan keepAliveInterval, bool useZeroMaskingKey, ArraySegment <byte> internalBuffer) { if (innerStream == null) { throw new ArgumentNullException(nameof(innerStream)); } if (!innerStream.CanRead || !innerStream.CanWrite) { throw new ArgumentException(!innerStream.CanRead ? SR.NotReadableStream : SR.NotWriteableStream, nameof(innerStream)); } if (subProtocol != null) { WebSocketValidate.ValidateSubprotocol(subProtocol); } if (keepAliveInterval != Timeout.InfiniteTimeSpan && keepAliveInterval < TimeSpan.Zero) { throw new ArgumentOutOfRangeException(nameof(keepAliveInterval), keepAliveInterval, SR.Format(SR.net_WebSockets_ArgumentOutOfRange_TooSmall, 0)); } if (receiveBufferSize <= 0 || sendBufferSize <= 0) { throw new ArgumentOutOfRangeException( receiveBufferSize <= 0 ? nameof(receiveBufferSize) : nameof(sendBufferSize), receiveBufferSize <= 0 ? receiveBufferSize : sendBufferSize, SR.Format(SR.net_WebSockets_ArgumentOutOfRange_TooSmall, 0)); } Memory <byte> internalMemoryBuffer = internalBuffer.Count >= receiveBufferSize ? internalBuffer : receiveBufferSize >= ManagedWebSocket.MaxMessageHeaderLength ? new byte[receiveBufferSize] : Memory <byte> .Empty; // let ManagedWebSocket create it return(ManagedWebSocket.CreateFromConnectedStream(innerStream, false, subProtocol, keepAliveInterval, internalMemoryBuffer)); }
public override Task SendAsync(ArraySegment <byte> buffer, WebSocketMessageType messageType, bool endOfMessage, CancellationToken cancellationToken) { ThrowIfNotConnected(); if (!((messageType == WebSocketMessageType.Text) || (messageType == WebSocketMessageType.Binary))) { string errorMessage = SR.Format( SR.net_WebSockets_Argument_InvalidMessageType, "Close", "SendAsync", "Binary", "Text", "CloseOutputAsync"); throw new ArgumentException(errorMessage, nameof(messageType)); } WebSocketValidate.ValidateArraySegment <byte>(buffer, "buffer"); return(_innerWebSocket.SendAsync(buffer, messageType, endOfMessage, cancellationToken)); }
public Task SendAsync( ArraySegment <byte> buffer, WebSocketMessageType messageType, bool endOfMessage, CancellationToken cancellationToken) { if (messageType != WebSocketMessageType.Text && messageType != WebSocketMessageType.Binary) { string errorMessage = SR.Format( SR.net_WebSockets_Argument_InvalidMessageType, nameof(WebSocketMessageType.Close), nameof(SendAsync), nameof(WebSocketMessageType.Binary), nameof(WebSocketMessageType.Text), nameof(CloseOutputAsync)); throw new ArgumentException(errorMessage, nameof(messageType)); } WebSocketValidate.ValidateArraySegment(buffer, nameof(buffer)); return(_webSocket.SendAsync(buffer, messageType, endOfMessage, cancellationToken)); }
public override ValueTask <ValueWebSocketReceiveResult> ReceiveAsync(Memory <byte> buffer, CancellationToken cancellationToken) { try { WebSocketValidate.ThrowIfInvalidState(_state, _disposed, s_validReceiveStates); Debug.Assert(!Monitor.IsEntered(StateUpdateLock), $"{nameof(StateUpdateLock)} must never be held when acquiring {nameof(ReceiveAsyncLock)}"); lock (ReceiveAsyncLock) // synchronize with receives in CloseAsync { ThrowIfOperationInProgress(_lastReceiveAsync); ValueTask <ValueWebSocketReceiveResult> t = ReceiveAsyncPrivate <ValueWebSocketReceiveResultGetter, ValueWebSocketReceiveResult>(buffer, cancellationToken); _lastReceiveAsync = t.IsCompletedSuccessfully ? (t.Result.MessageType == WebSocketMessageType.Close ? s_cachedCloseTask : Task.CompletedTask) : t.AsTask(); return(t); } } catch (Exception exc) { return(new ValueTask <ValueWebSocketReceiveResult>(Task.FromException <ValueWebSocketReceiveResult>(exc))); } }
internal static WebSocket Create(Stream innerStream, string subProtocol, int receiveBufferSize, TimeSpan keepAliveInterval, ArraySegment <byte> internalBuffer) { if (!WebSocketProtocolComponent.IsSupported) { WebSocketValidate.ThrowPlatformNotSupportedException_WSPC(); } WebSocketValidate.ValidateInnerStream(innerStream); WebSocketValidate.ValidateOptions(subProtocol, receiveBufferSize, WebSocketBuffer.MinSendBufferSize, keepAliveInterval); WebSocketValidate.ValidateArraySegment <byte>(internalBuffer, nameof(internalBuffer)); WebSocketBuffer.Validate(internalBuffer.Count, receiveBufferSize, WebSocketBuffer.MinSendBufferSize, true); return(new ServerWebSocket(innerStream, subProtocol, receiveBufferSize, keepAliveInterval, internalBuffer)); }
public static WebSocket CreateClientWebSocket(Stream innerStream, string subProtocol, int receiveBufferSize, int sendBufferSize, TimeSpan keepAliveInterval, bool useZeroMaskingKey, ArraySegment <byte> internalBuffer) { if (innerStream == null) { throw new ArgumentNullException(nameof(innerStream)); } if (!innerStream.CanRead || !innerStream.CanWrite) { throw new ArgumentException(!innerStream.CanRead ? SR.NotReadableStream : SR.NotWriteableStream, nameof(innerStream)); } if (subProtocol != null) { WebSocketValidate.ValidateSubprotocol(subProtocol); } if (keepAliveInterval != Timeout.InfiniteTimeSpan && keepAliveInterval < TimeSpan.Zero) { throw new ArgumentOutOfRangeException(nameof(keepAliveInterval), keepAliveInterval, SR.Format(SR.net_WebSockets_ArgumentOutOfRange_TooSmall, 0)); } if (receiveBufferSize <= 0 || sendBufferSize <= 0) { throw new ArgumentOutOfRangeException( receiveBufferSize <= 0 ? nameof(receiveBufferSize) : nameof(sendBufferSize), receiveBufferSize <= 0 ? receiveBufferSize : sendBufferSize, SR.Format(SR.net_WebSockets_ArgumentOutOfRange_TooSmall, 0)); } // Ignore useZeroMaskingKey. ManagedWebSocket doesn't currently support that debugging option. // Ignore internalBuffer. ManagedWebSocket uses its own small buffer for headers/control messages. return(ManagedWebSocket.CreateFromConnectedStream(innerStream, false, subProtocol, keepAliveInterval)); }
public void SetBuffer(int receiveBufferSize, int sendBufferSize, ArraySegment <byte> buffer) { ThrowIfReadOnly(); if (receiveBufferSize <= 0) { throw new ArgumentOutOfRangeException(nameof(receiveBufferSize), receiveBufferSize, SR.Format(SR.net_WebSockets_ArgumentOutOfRange_TooSmall, 1)); } if (sendBufferSize <= 0) { throw new ArgumentOutOfRangeException(nameof(sendBufferSize), sendBufferSize, SR.Format(SR.net_WebSockets_ArgumentOutOfRange_TooSmall, 1)); } WebSocketValidate.ValidateArraySegment(buffer, nameof(buffer)); if (buffer.Count == 0) { throw new ArgumentOutOfRangeException(nameof(buffer)); } _receiveBufferSize = receiveBufferSize; _sendBufferSize = sendBufferSize; _buffer = buffer; }
/// <summary>Creates a <see cref="WebSocket"/> that operates on a <see cref="Stream"/> representing a web socket connection.</summary> /// <param name="stream">The <see cref="Stream"/> for the connection.</param> /// <param name="isServer"><code>true</code> if this is the server-side of the connection; <code>false</code> if it's the client side.</param> /// <param name="subProtocol">The agreed upon sub-protocol that was used when creating the connection.</param> /// <param name="keepAliveInterval">The keep-alive interval to use, or <see cref="Timeout.InfiniteTimeSpan"/> to disable keep-alives.</param> /// <returns>The created <see cref="WebSocket"/>.</returns> public static WebSocket CreateFromStream(Stream stream, bool isServer, string?subProtocol, TimeSpan keepAliveInterval) { ArgumentNullException.ThrowIfNull(stream); if (!stream.CanRead || !stream.CanWrite) { throw new ArgumentException(!stream.CanRead ? SR.NotReadableStream : SR.NotWriteableStream, nameof(stream)); } if (subProtocol != null) { WebSocketValidate.ValidateSubprotocol(subProtocol); } if (keepAliveInterval != Timeout.InfiniteTimeSpan && keepAliveInterval < TimeSpan.Zero) { throw new ArgumentOutOfRangeException(nameof(keepAliveInterval), keepAliveInterval, SR.Format(SR.net_WebSockets_ArgumentOutOfRange_TooSmall, 0)); } return(new ManagedWebSocket(stream, isServer, subProtocol, keepAliveInterval)); }
public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) { WebSocketValidate.ValidateBuffer(buffer, offset, count); return(WriteAsyncCore(buffer, offset, count, cancellationToken)); }
private async Task <int> ReadAsyncCore(byte[] buffer, int offset, int count, CancellationToken cancellationToken) { if (NetEventSource.IsEnabled) { NetEventSource.Enter(this, WebSocketValidate.GetTraceMsgForParameters(offset, count, cancellationToken)); } CancellationTokenRegistration cancellationTokenRegistration = new CancellationTokenRegistration(); int bytesRead = 0; try { if (cancellationToken.CanBeCanceled) { cancellationTokenRegistration = cancellationToken.Register(s_OnCancel, this, false); } if (!_inOpaqueMode) { bytesRead = await _inputStream.ReadAsync(buffer, offset, count, cancellationToken).SuppressContextFlow <int>(); } else { #if DEBUG // When using fast path only one outstanding read is permitted. By switching into opaque mode // via IWebSocketStream.SwitchToOpaqueMode (see more detailed comments in interface definition) // caller takes responsibility for enforcing this constraint. Debug.Assert(Interlocked.Increment(ref _outstandingOperations._reads) == 1, "Only one outstanding read allowed at any given time."); #endif _readTaskCompletionSource = new TaskCompletionSource <int>(); _readEventArgs.SetBuffer(buffer, offset, count); if (!ReadAsyncFast(_readEventArgs)) { if (_readEventArgs.Exception != null) { throw _readEventArgs.Exception; } bytesRead = _readEventArgs.BytesTransferred; } else { bytesRead = await _readTaskCompletionSource.Task.SuppressContextFlow <int>(); } } } catch (Exception error) { if (s_CanHandleException(error)) { cancellationToken.ThrowIfCancellationRequested(); } throw; } finally { cancellationTokenRegistration.Dispose(); if (NetEventSource.IsEnabled) { NetEventSource.Exit(this, bytesRead); } } return(bytesRead); }
// Must be called with Lock taken. public void CheckValidState(WebSocketState[] validStates) { WebSocketValidate.ThrowIfInvalidState(_state, _disposed, validStates); }
private static async Task <HttpListenerWebSocketContext> AcceptWebSocketAsyncCore(HttpListenerContext context, string subProtocol, int receiveBufferSize, TimeSpan keepAliveInterval, ArraySegment <byte> internalBuffer) { HttpListenerWebSocketContext webSocketContext = null; if (NetEventSource.IsEnabled) { NetEventSource.Enter(null, context); } try { // get property will create a new response if one doesn't exist. HttpListenerResponse response = context.Response; HttpListenerRequest request = context.Request; ValidateWebSocketHeaders(context); string secWebSocketVersion = request.Headers[HttpKnownHeaderNames.SecWebSocketVersion]; // Optional for non-browser client string origin = request.Headers[HttpKnownHeaderNames.Origin]; List <string> secWebSocketProtocols = new List <string>(); string outgoingSecWebSocketProtocolString; bool shouldSendSecWebSocketProtocolHeader = ProcessWebSocketProtocolHeader( request.Headers[HttpKnownHeaderNames.SecWebSocketProtocol], subProtocol, out outgoingSecWebSocketProtocolString); if (shouldSendSecWebSocketProtocolHeader) { secWebSocketProtocols.Add(outgoingSecWebSocketProtocolString); response.Headers.Add(HttpKnownHeaderNames.SecWebSocketProtocol, outgoingSecWebSocketProtocolString); } // negotiate the websocket key return value string secWebSocketKey = request.Headers[HttpKnownHeaderNames.SecWebSocketKey]; string secWebSocketAccept = WebSocketValidate.GetSecWebSocketAcceptString(secWebSocketKey); response.Headers.Add(HttpKnownHeaderNames.Connection, HttpKnownHeaderNames.Upgrade); response.Headers.Add(HttpKnownHeaderNames.Upgrade, WebSocketUpgradeToken); response.Headers.Add(HttpKnownHeaderNames.SecWebSocketAccept, secWebSocketAccept); response.StatusCode = (int)HttpStatusCode.SwitchingProtocols; // HTTP 101 response.ComputeCoreHeaders(); ulong hresult = SendWebSocketHeaders(response); if (hresult != 0) { throw new WebSocketException((int)hresult, SR.Format(SR.net_WebSockets_NativeSendResponseHeaders, nameof(AcceptWebSocketAsync), hresult)); } if (NetEventSource.IsEnabled) { NetEventSource.Info(null, $"{HttpKnownHeaderNames.Origin} = {origin}"); NetEventSource.Info(null, $"{HttpKnownHeaderNames.SecWebSocketVersion} = {secWebSocketVersion}"); NetEventSource.Info(null, $"{HttpKnownHeaderNames.SecWebSocketKey} = {secWebSocketKey}"); NetEventSource.Info(null, $"{HttpKnownHeaderNames.SecWebSocketAccept} = {secWebSocketAccept}"); NetEventSource.Info(null, $"{HttpKnownHeaderNames.SecWebSocketProtocol} = {request.Headers[HttpKnownHeaderNames.SecWebSocketProtocol]}"); NetEventSource.Info(null, $"{HttpKnownHeaderNames.SecWebSocketProtocol} = {outgoingSecWebSocketProtocolString}"); } await response.OutputStream.FlushAsync().SuppressContextFlow(); HttpResponseStream responseStream = response.OutputStream as HttpResponseStream; Debug.Assert(responseStream != null, "'responseStream' MUST be castable to System.Net.HttpResponseStream."); ((HttpResponseStream)response.OutputStream).SwitchToOpaqueMode(); HttpRequestStream requestStream = new HttpRequestStream(context); requestStream.SwitchToOpaqueMode(); WebSocketHttpListenerDuplexStream webSocketStream = new WebSocketHttpListenerDuplexStream(requestStream, responseStream, context); WebSocket webSocket = ServerWebSocket.Create(webSocketStream, subProtocol, receiveBufferSize, keepAliveInterval, internalBuffer); webSocketContext = new HttpListenerWebSocketContext( request.Url, request.Headers, request.Cookies, context.User, request.IsAuthenticated, request.IsLocal, request.IsSecureConnection, origin, secWebSocketProtocols.AsReadOnly(), secWebSocketVersion, secWebSocketKey, webSocket); if (NetEventSource.IsEnabled) { NetEventSource.Associate(context, webSocketContext); NetEventSource.Associate(webSocketContext, webSocket); } } catch (Exception ex) { if (NetEventSource.IsEnabled) { NetEventSource.Error(context, ex); } throw; } finally { if (NetEventSource.IsEnabled) { NetEventSource.Exit(context); } } return(webSocketContext); }