public static WebSocket CreateClientWebSocket(Stream innerStream, string subProtocol, int receiveBufferSize, int sendBufferSize, TimeSpan keepAliveInterval, bool useZeroMaskingKey, ArraySegment <byte> internalBuffer) { if (!WebSocketProtocolComponent.IsSupported) { WebSocketHelpers.ThrowPlatformNotSupportedException_WSPC(); } WebSocketHelpers.ValidateInnerStream(innerStream); WebSocketHelpers.ValidateOptions(subProtocol, receiveBufferSize, sendBufferSize, keepAliveInterval); WebSocketHelpers.ValidateArraySegment <byte>(internalBuffer, "internalBuffer"); WebSocketBuffer.Validate(internalBuffer.Count, receiveBufferSize, sendBufferSize, false); return(new InternalClientWebSocket(innerStream, subProtocol, receiveBufferSize, sendBufferSize, keepAliveInterval, useZeroMaskingKey, internalBuffer)); }
public void SetBuffer(int receiveBufferSize, int sendBufferSize, ArraySegment <byte> buffer) { ThrowIfReadOnly(); WebSocketHelpers.ValidateBufferSizes(receiveBufferSize, sendBufferSize); WebSocketHelpers.ValidateArraySegment(buffer, "buffer"); WebSocketBuffer.Validate(buffer.Count, receiveBufferSize, sendBufferSize, false); this.receiveBufferSize = receiveBufferSize; this.sendBufferSize = sendBufferSize; // Only full-trust applications can specify their own buffer to be used as the // internal buffer for the WebSocket object. This is because the contents of the // buffer are used internally by the WebSocket as it marshals data with embedded // pointers to native code. A malicious application could use this to corrupt // native memory. if (AppDomain.CurrentDomain.IsFullyTrusted) { this.buffer = buffer; } else { // We silently ignore the passed in buffer and will create an internal // buffer later. this.buffer = null; } }
internal static string GetSupportedVersion() { if (s_WebSocketDllHandle.IsInvalid) { WebSocketHelpers.ThrowPlatformNotSupportedException_WSPC(); } SafeWebSocketHandle webSocketHandle = null; try { int errorCode = WebSocketCreateClientHandle_Raw(null, 0, out webSocketHandle); ThrowOnError(errorCode); if (webSocketHandle == null || webSocketHandle.IsInvalid) { WebSocketHelpers.ThrowPlatformNotSupportedException_WSPC(); } IntPtr additionalHeadersPtr; uint additionalHeaderCount; errorCode = WebSocketBeginClientHandshake_Raw(webSocketHandle, IntPtr.Zero, 0, IntPtr.Zero, 0, s_InitialClientRequestHeaders, (uint)s_InitialClientRequestHeaders.Length, out additionalHeadersPtr, out additionalHeaderCount); ThrowOnError(errorCode); HttpHeader[] additionalHeaders = MarshalHttpHeaders(additionalHeadersPtr, (int)additionalHeaderCount); string version = null; foreach (HttpHeader header in additionalHeaders) { if (string.Compare(header.Name, HttpKnownHeaderNames.SecWebSocketVersion, StringComparison.OrdinalIgnoreCase) == 0) { version = header.Value; break; } } Contract.Assert(version != null, "'version' MUST NOT be NULL."); return(version); } finally { if (webSocketHandle != null) { webSocketHandle.Dispose(); } } }
private async Task WriteAsyncCore(byte[] buffer, int offset, int count, CancellationToken cancellationToken) { if (WebSocketBase.LoggingEnabled) { Logging.Enter(Logging.WebSockets, this, Methods.WriteAsyncCore, WebSocketHelpers.GetTraceMsgForParameters(offset, count, cancellationToken)); } CancellationTokenRegistration cancellationTokenRegistration = new CancellationTokenRegistration(); try { if (cancellationToken.CanBeCanceled) { cancellationTokenRegistration = cancellationToken.Register(s_OnCancel, this, false); } if (!m_InOpaqueMode) { await m_OutputStream.WriteAsync(buffer, offset, count, cancellationToken).SuppressContextFlow(); } 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. Contract.Assert(Interlocked.Increment(ref m_OutstandingOperations.m_Writes) == 1, "Only one outstanding write allowed at any given time."); #endif m_WriteTaskCompletionSource = new TaskCompletionSource <object>(); m_WriteEventArgs.BufferList = null; m_WriteEventArgs.SetBuffer(buffer, offset, count); if (WriteAsyncFast(m_WriteEventArgs)) { await m_WriteTaskCompletionSource.Task.SuppressContextFlow(); } } } catch (Exception error) { if (s_CanHandleException(error)) { cancellationToken.ThrowIfCancellationRequested(); } throw; } finally { cancellationTokenRegistration.Dispose(); if (WebSocketBase.LoggingEnabled) { Logging.Exit(Logging.WebSockets, this, Methods.WriteAsyncCore, string.Empty); } } }
public void SetBuffer(int receiveBufferSize, int sendBufferSize) { ThrowIfReadOnly(); WebSocketHelpers.ValidateBufferSizes(receiveBufferSize, sendBufferSize); this.buffer = null; this.receiveBufferSize = receiveBufferSize; this.sendBufferSize = sendBufferSize; }
public Task MultipleWriteAsync(IList <ArraySegment <byte> > sendBuffers, CancellationToken cancellationToken) { Contract.Assert(this.SupportsMultipleWrite, "This method MUST NOT be used for custom NetworkStream implementations."); if (!m_InOpaqueMode) { // We can't use fast path over SSL return(Task.Factory.FromAsync <IList <ArraySegment <byte> > >(s_BeginMultipleWrite, s_EndMultipleWrite, sendBuffers, this)); } if (WebSocketBase.LoggingEnabled) { Logging.Enter(Logging.WebSockets, this, Methods.MultipleWriteAsync, string.Empty); } bool completedAsynchronously = false; try { cancellationToken.ThrowIfCancellationRequested(); #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. Contract.Assert(Interlocked.Increment(ref m_OutstandingOperations.m_Writes) == 1, "Only one outstanding write allowed at any given time."); #endif WebSocketHelpers.ThrowIfConnectionAborted(m_InnerStream, false); m_WriteTaskCompletionSource = new TaskCompletionSource <object>(); m_WriteEventArgs.SetBuffer(null, 0, 0); m_WriteEventArgs.BufferList = sendBuffers; completedAsynchronously = InnerSocket.SendAsync(m_WriteEventArgs); if (!completedAsynchronously) { if (m_WriteEventArgs.SocketError != SocketError.Success) { throw new SocketException(m_WriteEventArgs.SocketError); } return(Task.CompletedTask); } return(m_WriteTaskCompletionSource.Task); } finally { if (WebSocketBase.LoggingEnabled) { Logging.Exit(Logging.WebSockets, this, Methods.MultipleWriteAsync, completedAsynchronously); } } }
public override Task <int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) { WebSocketHelpers.ValidateBuffer(buffer, offset, count); if (!m_InOpaqueMode) { return(base.ReadAsync(buffer, offset, count, cancellationToken)); } return(ReadAsyncCore(buffer, offset, count, cancellationToken, false)); }
public void SetBuffer(int receiveBufferSize, int sendBufferSize, ArraySegment <byte> buffer) { ThrowIfReadOnly(); WebSocketHelpers.ValidateBufferSizes(receiveBufferSize, sendBufferSize); WebSocketHelpers.ValidateArraySegment(buffer, "buffer"); WebSocketBuffer.Validate(buffer.Count, receiveBufferSize, sendBufferSize, false); this.receiveBufferSize = receiveBufferSize; this.sendBufferSize = sendBufferSize; this.buffer = buffer; }
internal static void WebSocketCreateServerHandle(Property[] properties, int propertyCount, out SafeWebSocketHandle webSocketHandle) { Contract.Assert(propertyCount >= 0, "'propertyCount' MUST NOT be negative."); Contract.Assert((properties == null && propertyCount == 0) || (properties != null && propertyCount == properties.Length), "'propertyCount' MUST MATCH 'properties.Length'."); if (s_WebSocketDllHandle.IsInvalid) { WebSocketHelpers.ThrowPlatformNotSupportedException_WSPC(); } int errorCode = WebSocketCreateServerHandle_Raw(properties, (uint)propertyCount, out webSocketHandle); ThrowOnError(errorCode); if (webSocketHandle == null || webSocketHandle.IsInvalid) { WebSocketHelpers.ThrowPlatformNotSupportedException_WSPC(); } IntPtr responseHeadersPtr; uint responseHeaderCount; // Currently the WSPC doesn't allow to initiate a data session // without also being involved in the http handshake // There is no information whatsoever, which is needed by the // WSPC for parsing WebSocket frames from the HTTP handshake // In the managed implementation the HTTP header handling // will be done using the managed HTTP stack and we will // just fake an HTTP handshake for the WSPC calling // WebSocketBeginServerHandshake and WebSocketEndServerHandshake // with statically defined dummy headers. errorCode = WebSocketBeginServerHandshake_Raw(webSocketHandle, IntPtr.Zero, IntPtr.Zero, 0, s_ServerFakeRequestHeaders, (uint)s_ServerFakeRequestHeaders.Length, out responseHeadersPtr, out responseHeaderCount); ThrowOnError(errorCode); HttpHeader[] responseHeaders = MarshalHttpHeaders(responseHeadersPtr, (int)responseHeaderCount); errorCode = WebSocketEndServerHandshake_Raw(webSocketHandle); ThrowOnError(errorCode); Contract.Assert(webSocketHandle != null, "'webSocketHandle' MUST NOT be NULL at this point."); }
private static void EndMultipleWrite(IAsyncResult asyncResult) { Contract.Assert(asyncResult != null, "'asyncResult' MUST NOT be NULL."); Contract.Assert(asyncResult.AsyncState != null, "'asyncResult.AsyncState' MUST NOT be NULL."); WebSocketConnection connection = asyncResult.AsyncState as WebSocketConnection; Contract.Assert(connection != null, "'connection' MUST NOT be NULL."); WebSocketHelpers.ThrowIfConnectionAborted(connection.m_InnerStream, false); connection.m_InnerStream.NetworkStream.EndMultipleWrite(asyncResult); }
internal static Task <HttpListenerWebSocketContext> AcceptWebSocketAsync(HttpListenerContext context, string subProtocol, int receiveBufferSize, TimeSpan keepAliveInterval, ArraySegment <byte> internalBuffer) { WebSocketHelpers.ValidateOptions(subProtocol, receiveBufferSize, WebSocketBuffer.MinSendBufferSize, keepAliveInterval); WebSocketHelpers.ValidateArraySegment <byte>(internalBuffer, "internalBuffer"); WebSocketBuffer.Validate(internalBuffer.Count, receiveBufferSize, WebSocketBuffer.MinSendBufferSize, true); return(AcceptWebSocketAsyncCore(context, subProtocol, receiveBufferSize, keepAliveInterval, internalBuffer)); }
public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) { WebSocketHelpers.ValidateBuffer(buffer, offset, count); if (!m_InOpaqueMode) { return(base.WriteAsync(buffer, offset, count, cancellationToken)); } if (WebSocketBase.LoggingEnabled) { Logging.Enter(Logging.WebSockets, this, Methods.WriteAsync, WebSocketHelpers.GetTraceMsgForParameters(offset, count, cancellationToken)); } bool completedAsynchronously = false; try { cancellationToken.ThrowIfCancellationRequested(); #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. Contract.Assert(Interlocked.Increment(ref m_OutstandingOperations.m_Writes) == 1, "Only one outstanding write allowed at any given time."); #endif WebSocketHelpers.ThrowIfConnectionAborted(m_InnerStream, false); m_WriteTaskCompletionSource = new TaskCompletionSource <object>(); m_WriteEventArgs.BufferList = null; m_WriteEventArgs.SetBuffer(buffer, offset, count); completedAsynchronously = InnerSocket.SendAsync(m_WriteEventArgs); if (!completedAsynchronously) { if (m_WriteEventArgs.SocketError != SocketError.Success) { throw new SocketException(m_WriteEventArgs.SocketError); } return(Task.CompletedTask); } return(m_WriteTaskCompletionSource.Task); } finally { if (WebSocketBase.LoggingEnabled) { Logging.Exit(Logging.WebSockets, this, Methods.WriteAsync, completedAsynchronously); } } }
public void AddSubProtocol(string subProtocol) { ThrowIfReadOnly(); WebSocketHelpers.ValidateSubprotocol(subProtocol); // Duplicates not allowed. foreach (string item in requestedSubProtocols) { if (string.Equals(item, subProtocol, StringComparison.OrdinalIgnoreCase)) { throw new ArgumentException(SR.GetString(SR.net_WebSockets_NoDuplicateProtocol, subProtocol), "subProtocol"); } } requestedSubProtocols.Add(subProtocol); }
public InternalClientWebSocket(Stream innerStream, string subProtocol, int receiveBufferSize, int sendBufferSize, TimeSpan keepAliveInterval, bool useZeroMaskingKey, ArraySegment <byte> internalBuffer) : base(innerStream, subProtocol, keepAliveInterval, WebSocketBuffer.CreateClientBuffer(internalBuffer, receiveBufferSize, sendBufferSize)) { m_Properties = this.InternalBuffer.CreateProperties(useZeroMaskingKey); m_SessionHandle = this.CreateWebSocketHandle(); if (m_SessionHandle == null || m_SessionHandle.IsInvalid) { WebSocketHelpers.ThrowPlatformNotSupportedException_WSPC(); } StartKeepAliveTimer(); }
public async override Task <int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) { if (WebSocketBase.LoggingEnabled) { Logging.Enter(Logging.WebSockets, this, Methods.ReadAsync, WebSocketHelpers.GetTraceMsgForParameters(offset, count, cancellationToken)); } CancellationTokenRegistration cancellationTokenRegistration = new CancellationTokenRegistration(); int bytesRead = 0; try { if (cancellationToken.CanBeCanceled) { cancellationTokenRegistration = cancellationToken.Register(s_OnCancel, this, false); } WebSocketHelpers.ThrowIfConnectionAborted(m_ConnectStream.Connection, true); bytesRead = await base.ReadAsync(buffer, offset, count, cancellationToken).SuppressContextFlow <int>(); if (WebSocketBase.LoggingEnabled) { Logging.Dump(Logging.WebSockets, this, Methods.ReadAsync, buffer, offset, bytesRead); } } catch (Exception error) { if (s_CanHandleException(error)) { cancellationToken.ThrowIfCancellationRequested(); } throw; } finally { cancellationTokenRegistration.Dispose(); if (WebSocketBase.LoggingEnabled) { Logging.Exit(Logging.WebSockets, this, Methods.ReadAsync, bytesRead); } } return(bytesRead); }
public async Task MultipleWriteAsync(IList <ArraySegment <byte> > sendBuffers, CancellationToken cancellationToken) { Contract.Assert(this.SupportsMultipleWrite, "This method MUST NOT be used for custom NetworkStream implementations."); if (WebSocketBase.LoggingEnabled) { Logging.Enter(Logging.WebSockets, this, Methods.MultipleWriteAsync, string.Empty); } CancellationTokenRegistration cancellationTokenRegistration = new CancellationTokenRegistration(); try { if (cancellationToken.CanBeCanceled) { cancellationTokenRegistration = cancellationToken.Register(s_OnCancel, this, false); } WebSocketHelpers.ThrowIfConnectionAborted(m_ConnectStream.Connection, false); await((WebSocketBase.IWebSocketStream)base.BaseStream).MultipleWriteAsync(sendBuffers, cancellationToken).SuppressContextFlow(); if (WebSocketBase.LoggingEnabled) { foreach (ArraySegment <byte> buffer in sendBuffers) { Logging.Dump(Logging.WebSockets, this, Methods.MultipleWriteAsync, buffer.Array, buffer.Offset, buffer.Count); } } } catch (Exception error) { if (s_CanHandleException(error)) { cancellationToken.ThrowIfCancellationRequested(); } throw; } finally { cancellationTokenRegistration.Dispose(); if (WebSocketBase.LoggingEnabled) { Logging.Exit(Logging.WebSockets, this, Methods.MultipleWriteAsync, string.Empty); } } }
private static IAsyncResult BeginMultipleWrite(IList <ArraySegment <byte> > sendBuffers, AsyncCallback callback, object asyncState) { Contract.Assert(sendBuffers != null, "'sendBuffers' MUST NOT be NULL."); Contract.Assert(asyncState != null, "'asyncState' MUST NOT be NULL."); WebSocketConnection connection = asyncState as WebSocketConnection; Contract.Assert(connection != null, "'connection' MUST NOT be NULL."); BufferOffsetSize[] buffers = new BufferOffsetSize[sendBuffers.Count]; for (int index = 0; index < sendBuffers.Count; index++) { ArraySegment <byte> sendBuffer = sendBuffers[index]; buffers[index] = new BufferOffsetSize(sendBuffer.Array, sendBuffer.Offset, sendBuffer.Count, false); } WebSocketHelpers.ThrowIfConnectionAborted(connection.m_InnerStream, false); return(connection.m_InnerStream.NetworkStream.BeginMultipleWrite(buffers, callback, asyncState)); }
// 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 m_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(); } m_PinnedSendBuffer = payload; m_PinnedSendBufferHandle = GCHandle.Alloc(m_PinnedSendBuffer.Array, GCHandleType.Pinned); bufferHasBeenPinned = true; m_PinnedSendBufferStartAddress = Marshal.UnsafeAddrOfPinnedArrayElement(m_PinnedSendBuffer.Array, m_PinnedSendBuffer.Offset).ToInt64(); m_PinnedSendBufferEndAddress = m_PinnedSendBufferStartAddress + m_PinnedSendBuffer.Count; }
public ClientWebSocket() { if (Logging.On) { Logging.Enter(Logging.WebSockets, this, ".ctor", null); } if (!WebSocketProtocolComponent.IsSupported) { WebSocketHelpers.ThrowPlatformNotSupportedException_WSPC(); } state = created; options = new ClientWebSocketOptions(); cts = new CancellationTokenSource(); if (Logging.On) { Logging.Exit(Logging.WebSockets, this, ".ctor", null); } }
internal static WebSocket CreateServerWebSocket(Stream innerStream, string subProtocol, int receiveBufferSize, TimeSpan keepAliveInterval, ArraySegment <byte> internalBuffer) { if (!WebSocketProtocolComponent.IsSupported) { WebSocketHelpers.ThrowPlatformNotSupportedException_WSPC(); } WebSocketHelpers.ValidateInnerStream(innerStream); WebSocketHelpers.ValidateOptions(subProtocol, receiveBufferSize, WebSocketBuffer.MinSendBufferSize, keepAliveInterval); WebSocketHelpers.ValidateArraySegment <byte>(internalBuffer, "internalBuffer"); WebSocketBuffer.Validate(internalBuffer.Count, receiveBufferSize, WebSocketBuffer.MinSendBufferSize, true); return(new ServerWebSocket(innerStream, subProtocol, receiveBufferSize, keepAliveInterval, internalBuffer)); }
internal static void WebSocketCreateClientHandle(Property[] properties, out SafeWebSocketHandle webSocketHandle) { uint propertyCount = properties == null ? 0 : (uint)properties.Length; if (s_WebSocketDllHandle.IsInvalid) { WebSocketHelpers.ThrowPlatformNotSupportedException_WSPC(); } int errorCode = WebSocketCreateClientHandle_Raw(properties, propertyCount, out webSocketHandle); ThrowOnError(errorCode); if (webSocketHandle == null || webSocketHandle.IsInvalid) { WebSocketHelpers.ThrowPlatformNotSupportedException_WSPC(); } IntPtr additionalHeadersPtr; uint additionalHeaderCount; // Currently the WSPC doesn't allow to initiate a data session // without also being involved in the http handshake // There is no information whatsoever, which is needed by the // WSPC for parsing WebSocket frames from the HTTP handshake // In the managed implementation the HTTP header handling // will be done using the managed HTTP stack and we will // just fake an HTTP handshake for the WSPC calling // WebSocketBeginClientHandshake and WebSocketEndClientHandshake // with statically defined dummy headers. errorCode = WebSocketBeginClientHandshake_Raw(webSocketHandle, IntPtr.Zero, 0, IntPtr.Zero, 0, s_InitialClientRequestHeaders, (uint)s_InitialClientRequestHeaders.Length, out additionalHeadersPtr, out additionalHeaderCount); ThrowOnError(errorCode); HttpHeader[] additionalHeaders = MarshalHttpHeaders(additionalHeadersPtr, (int)additionalHeaderCount); string key = null; foreach (HttpHeader header in additionalHeaders) { if (string.Compare(header.Name, HttpKnownHeaderNames.SecWebSocketKey, StringComparison.OrdinalIgnoreCase) == 0) { key = header.Value; break; } } Contract.Assert(key != null, "'key' MUST NOT be NULL."); string acceptValue = WebSocketHelpers.GetSecWebSocketAcceptString(key); HttpHeader[] responseHeaders = new HttpHeader[] { new HttpHeader() { Name = HttpKnownHeaderNames.Connection, NameLength = (uint)HttpKnownHeaderNames.Connection.Length, Value = HttpKnownHeaderNames.Upgrade, ValueLength = (uint)HttpKnownHeaderNames.Upgrade.Length }, new HttpHeader() { Name = HttpKnownHeaderNames.Upgrade, NameLength = (uint)HttpKnownHeaderNames.Upgrade.Length, Value = WebSocketHelpers.WebSocketUpgradeToken, ValueLength = (uint)WebSocketHelpers.WebSocketUpgradeToken.Length }, new HttpHeader() { Name = HttpKnownHeaderNames.SecWebSocketAccept, NameLength = (uint)HttpKnownHeaderNames.SecWebSocketAccept.Length, Value = acceptValue, ValueLength = (uint)acceptValue.Length } }; errorCode = WebSocketEndClientHandshake_Raw(webSocketHandle, responseHeaders, (uint)responseHeaders.Length, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero); ThrowOnError(errorCode); Contract.Assert(webSocketHandle != null, "'webSocketHandle' MUST NOT be NULL at this point."); }
public static ArraySegment <byte> CreateClientBuffer(int receiveBufferSize, int sendBufferSize) { WebSocketHelpers.ValidateBufferSizes(receiveBufferSize, sendBufferSize); return(WebSocketBuffer.CreateInternalBufferArraySegment(receiveBufferSize, sendBufferSize, false)); }
// Validate the response headers and return the sub-protocol. private string ValidateResponse(HttpWebRequest request, HttpWebResponse response) { // 101 if (response.StatusCode != HttpStatusCode.SwitchingProtocols) { throw new WebSocketException(SR.GetString(SR.net_WebSockets_Connect101Expected, (int)response.StatusCode)); } // Upgrade: websocket string upgradeHeader = response.Headers[HttpKnownHeaderNames.Upgrade]; if (!string.Equals(upgradeHeader, WebSocketHelpers.WebSocketUpgradeToken, StringComparison.OrdinalIgnoreCase)) { throw new WebSocketException(SR.GetString(SR.net_WebSockets_InvalidResponseHeader, HttpKnownHeaderNames.Upgrade, upgradeHeader)); } // Connection: Upgrade string connectionHeader = response.Headers[HttpKnownHeaderNames.Connection]; if (!string.Equals(connectionHeader, HttpKnownHeaderNames.Upgrade, StringComparison.OrdinalIgnoreCase)) { throw new WebSocketException(SR.GetString(SR.net_WebSockets_InvalidResponseHeader, HttpKnownHeaderNames.Connection, connectionHeader)); } // Sec-WebSocket-Accept derived from request Sec-WebSocket-Key string websocketAcceptHeader = response.Headers[HttpKnownHeaderNames.SecWebSocketAccept]; string expectedAcceptHeader = WebSocketHelpers.GetSecWebSocketAcceptString( request.Headers[HttpKnownHeaderNames.SecWebSocketKey]); if (!string.Equals(websocketAcceptHeader, expectedAcceptHeader, StringComparison.OrdinalIgnoreCase)) { throw new WebSocketException(SR.GetString(SR.net_WebSockets_InvalidResponseHeader, HttpKnownHeaderNames.SecWebSocketAccept, websocketAcceptHeader)); } // Sec-WebSocket-Protocol matches one from request // A missing header is ok. It's also ok if the client didn't specify any. string subProtocol = response.Headers[HttpKnownHeaderNames.SecWebSocketProtocol]; if (!string.IsNullOrWhiteSpace(subProtocol) && options.RequestedSubProtocols.Count > 0) { bool foundMatch = false; foreach (string requestedSubProtocol in options.RequestedSubProtocols) { if (string.Equals(requestedSubProtocol, subProtocol, StringComparison.OrdinalIgnoreCase)) { foundMatch = true; break; } } if (!foundMatch) { throw new WebSocketException(SR.GetString(SR.net_WebSockets_AcceptUnsupportedProtocol, string.Join(", ", options.RequestedSubProtocols), subProtocol)); } } return(string.IsNullOrWhiteSpace(subProtocol) ? null : subProtocol); // May be null or valid. }
public static ArraySegment <byte> CreateServerBuffer(int receiveBufferSize) { WebSocketHelpers.ValidateBufferSizes(receiveBufferSize, WebSocketBuffer.MinSendBufferSize); return(WebSocketBuffer.CreateInternalBufferArraySegment(receiveBufferSize, WebSocketBuffer.MinSendBufferSize, true)); }
public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) { WebSocketHelpers.ValidateBuffer(buffer, offset, count); return(WriteAsyncCore(buffer, offset, count, cancellationToken)); }
public async Task CloseNetworkConnectionAsync(CancellationToken cancellationToken) { // need to yield here to make sure that we don't get any exception synchronously await Task.Yield(); if (WebSocketBase.LoggingEnabled) { Logging.Enter(Logging.WebSockets, this, Methods.CloseNetworkConnectionAsync, string.Empty); } CancellationTokenSource reasonableTimeoutCancellationTokenSource = null; CancellationTokenSource linkedCancellationTokenSource = null; CancellationToken linkedCancellationToken = CancellationToken.None; CancellationTokenRegistration cancellationTokenRegistration = new CancellationTokenRegistration(); int bytesRead = 0; try { reasonableTimeoutCancellationTokenSource = new CancellationTokenSource(WebSocketHelpers.ClientTcpCloseTimeout); linkedCancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource( reasonableTimeoutCancellationTokenSource.Token, cancellationToken); linkedCancellationToken = linkedCancellationTokenSource.Token; cancellationTokenRegistration = linkedCancellationToken.Register(s_OnCancel, this, false); WebSocketHelpers.ThrowIfConnectionAborted(m_ConnectStream.Connection, true); byte[] buffer = new byte[1]; if (m_WebSocketConnection != null && m_InOpaqueMode) { bytesRead = await m_WebSocketConnection.ReadAsyncCore(buffer, 0, 1, linkedCancellationToken, true).SuppressContextFlow <int>(); } else { bytesRead = await base.ReadAsync(buffer, 0, 1, linkedCancellationToken).SuppressContextFlow <int>(); } if (bytesRead != 0) { Contract.Assert(false, "'bytesRead' MUST be '0' at this point. Instead more payload was received ('" + buffer[0].ToString() + "')"); if (WebSocketBase.LoggingEnabled) { Logging.Dump(Logging.WebSockets, this, Methods.CloseNetworkConnectionAsync, buffer, 0, bytesRead); } throw new WebSocketException(WebSocketError.NotAWebSocket); } } catch (Exception error) { if (!s_CanHandleException(error)) { throw; } // throw OperationCancelledException when canceled by the caller // ignore cancellation due to the timeout cancellationToken.ThrowIfCancellationRequested(); } finally { cancellationTokenRegistration.Dispose(); if (linkedCancellationTokenSource != null) { linkedCancellationTokenSource.Dispose(); } if (reasonableTimeoutCancellationTokenSource != null) { reasonableTimeoutCancellationTokenSource.Dispose(); } if (WebSocketBase.LoggingEnabled) { Logging.Exit(Logging.WebSockets, this, Methods.CloseNetworkConnectionAsync, bytesRead); } } }
internal Task <int> ReadAsyncCore(byte[] buffer, int offset, int count, CancellationToken cancellationToken, bool ignoreReadError) { if (WebSocketBase.LoggingEnabled) { Logging.Enter(Logging.WebSockets, this, Methods.ReadAsyncCore, WebSocketHelpers.GetTraceMsgForParameters(offset, count, cancellationToken)); } bool completedAsynchronously = false; m_IgnoreReadError = ignoreReadError; try { cancellationToken.ThrowIfCancellationRequested(); #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. Contract.Assert(Interlocked.Increment(ref m_OutstandingOperations.m_Reads) == 1, "Only one outstanding read allowed at any given time."); #endif WebSocketHelpers.ThrowIfConnectionAborted(m_InnerStream, true); m_ReadTaskCompletionSource = new TaskCompletionSource <int>(); Contract.Assert(m_ReadEventArgs != null, "'m_ReadEventArgs' MUST NOT be NULL."); m_ReadEventArgs.SetBuffer(buffer, offset, count); Socket innerSocket; if (ignoreReadError) { // The State of the WebSocket instance is already Closed at this point // Skipping call to WebSocketBase.ThrowIfClosedOrAborted innerSocket = GetInnerSocket(true); } else { innerSocket = InnerSocket; } completedAsynchronously = innerSocket.ReceiveAsync(m_ReadEventArgs); if (!completedAsynchronously) { if (m_ReadEventArgs.SocketError != SocketError.Success) { if (!m_IgnoreReadError) { throw new SocketException(m_ReadEventArgs.SocketError); } else { return(Task.FromResult <int>(0)); } } return(Task.FromResult <int>(m_ReadEventArgs.BytesTransferred)); } return(m_ReadTaskCompletionSource.Task); } finally { if (WebSocketBase.LoggingEnabled) { Logging.Exit(Logging.WebSockets, this, Methods.ReadAsyncCore, completedAsynchronously); } } }
private static async Task <HttpListenerWebSocketContext> AcceptWebSocketAsyncCore(HttpListenerContext context, string subProtocol, int receiveBufferSize, TimeSpan keepAliveInterval, ArraySegment <byte> internalBuffer) { HttpListenerWebSocketContext webSocketContext = null; if (Logging.On) { Logging.Enter(Logging.WebSockets, context, "AcceptWebSocketAsync", ""); } 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 = WebSocketHelpers.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 = WebSocketHelpers.GetSecWebSocketAcceptString(secWebSocketKey); response.Headers.Add(HttpKnownHeaderNames.Connection, HttpKnownHeaderNames.Upgrade); response.Headers.Add(HttpKnownHeaderNames.Upgrade, WebSocketHelpers.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.GetString(SR.net_WebSockets_NativeSendResponseHeaders, WebSocketHelpers.MethodNames.AcceptWebSocketAsync, hresult)); } if (Logging.On) { Logging.PrintInfo(Logging.WebSockets, string.Format("{0} = {1}", HttpKnownHeaderNames.Origin, origin)); Logging.PrintInfo(Logging.WebSockets, string.Format("{0} = {1}", HttpKnownHeaderNames.SecWebSocketVersion, secWebSocketVersion)); Logging.PrintInfo(Logging.WebSockets, string.Format("{0} = {1}", HttpKnownHeaderNames.SecWebSocketKey, secWebSocketKey)); Logging.PrintInfo(Logging.WebSockets, string.Format("{0} = {1}", HttpKnownHeaderNames.SecWebSocketAccept, secWebSocketAccept)); Logging.PrintInfo(Logging.WebSockets, string.Format("Request {0} = {1}", HttpKnownHeaderNames.SecWebSocketProtocol, request.Headers[HttpKnownHeaderNames.SecWebSocketProtocol])); Logging.PrintInfo(Logging.WebSockets, string.Format("Response {0} = {1}", HttpKnownHeaderNames.SecWebSocketProtocol, outgoingSecWebSocketProtocolString)); } await response.OutputStream.FlushAsync().SuppressContextFlow(); HttpResponseStream responseStream = response.OutputStream as HttpResponseStream; Contract.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 = WebSocket.CreateServerWebSocket(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 (Logging.On) { Logging.Associate(Logging.WebSockets, context, webSocketContext); Logging.Associate(Logging.WebSockets, webSocketContext, webSocket); } } catch (Exception ex) { if (Logging.On) { Logging.Exception(Logging.WebSockets, context, "AcceptWebSocketAsync", ex); } throw; } finally { if (Logging.On) { Logging.Exit(Logging.WebSockets, context, "AcceptWebSocketAsync", ""); } } return(webSocketContext); }