internal static string GetSupportedVersion() { if (!IsSupported) { HttpWebSocket.ThrowPlatformNotSupportedException_WSPC(); } SafeWebSocketHandle?webSocketHandle = null; try { int errorCode = Interop.WebSocket.WebSocketCreateClientHandle(null !, 0, out webSocketHandle); ThrowOnError(errorCode); if (webSocketHandle == null || webSocketHandle.IsInvalid) { HttpWebSocket.ThrowPlatformNotSupportedException_WSPC(); } IntPtr additionalHeadersPtr; uint additionalHeaderCount; errorCode = Interop.WebSocket.WebSocketBeginClientHandshake(webSocketHandle !, IntPtr.Zero, 0, IntPtr.Zero, 0, s_initialClientRequestHeaders, (uint)s_initialClientRequestHeaders.Length, out additionalHeadersPtr, out additionalHeaderCount); ThrowOnError(errorCode); Interop.WebSocket.HttpHeader[] additionalHeaders = MarshalHttpHeaders(additionalHeadersPtr, (int)additionalHeaderCount); string?version = null; foreach (Interop.WebSocket.HttpHeader header in additionalHeaders) { if (string.Equals(header.Name, HttpKnownHeaderNames.SecWebSocketVersion, StringComparison.OrdinalIgnoreCase)) { version = header.Value; break; } } Debug.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 (NetEventSource.IsEnabled) { NetEventSource.Enter(this, HttpWebSocket.GetTraceMsgForParameters(offset, count, cancellationToken)); } CancellationTokenRegistration cancellationTokenRegistration = default; try { if (cancellationToken.CanBeCanceled) { cancellationTokenRegistration = cancellationToken.Register(s_OnCancel, this, false); } if (!_inOpaqueMode) { await _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. Debug.Assert(Interlocked.Increment(ref _outstandingOperations._writes) == 1, "Only one outstanding write allowed at any given time."); #endif _writeTaskCompletionSource = new TaskCompletionSource <object>(); _writeEventArgs.BufferList = null; _writeEventArgs.SetBuffer(buffer, offset, count); if (WriteAsyncFast(_writeEventArgs)) { await _writeTaskCompletionSource.Task.SuppressContextFlow(); } } } catch (Exception error) { if (s_CanHandleException(error)) { cancellationToken.ThrowIfCancellationRequested(); } throw; } finally { cancellationTokenRegistration.Dispose(); if (NetEventSource.IsEnabled) { NetEventSource.Exit(this); } } }
internal static void WebSocketCreateServerHandle(Interop.WebSocket.Property[] properties, int propertyCount, out SafeWebSocketHandle webSocketHandle) { Debug.Assert(propertyCount >= 0, "'propertyCount' MUST NOT be negative."); Debug.Assert((properties == null && propertyCount == 0) || (properties != null && propertyCount == properties.Length), "'propertyCount' MUST MATCH 'properties.Length'."); if (!IsSupported) { HttpWebSocket.ThrowPlatformNotSupportedException_WSPC(); } int errorCode = Interop.WebSocket.WebSocketCreateServerHandle(properties, (uint)propertyCount, out webSocketHandle); ThrowOnError(errorCode); if (webSocketHandle == null || webSocketHandle.IsInvalid) { HttpWebSocket.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 = Interop.WebSocket.WebSocketBeginServerHandshake(webSocketHandle, IntPtr.Zero, IntPtr.Zero, 0, s_ServerFakeRequestHeaders, (uint)s_ServerFakeRequestHeaders.Length, out responseHeadersPtr, out responseHeaderCount); ThrowOnError(errorCode); Interop.WebSocket.HttpHeader[] responseHeaders = MarshalHttpHeaders(responseHeadersPtr, (int)responseHeaderCount); errorCode = Interop.WebSocket.WebSocketEndServerHandshake(webSocketHandle); ThrowOnError(errorCode); Debug.Assert(webSocketHandle != null, "'webSocketHandle' MUST NOT be NULL at this point."); }
internal static SafeWebSocketHandle WebSocketCreateServerHandle(Interop.WebSocket.Property[] properties, int propertyCount) { Debug.Assert(propertyCount >= 0, "'propertyCount' MUST NOT be negative."); Debug.Assert((properties == null && propertyCount == 0) || (properties != null && propertyCount == properties.Length), "'propertyCount' MUST MATCH 'properties.Length'."); if (!IsSupported) { HttpWebSocket.ThrowPlatformNotSupportedException_WSPC(); } SafeWebSocketHandle?webSocketHandle = null; try { int errorCode = Interop.WebSocket.WebSocketCreateServerHandle(properties !, (uint)propertyCount, out webSocketHandle); ThrowOnError(errorCode); if (webSocketHandle.IsInvalid) { HttpWebSocket.ThrowPlatformNotSupportedException_WSPC(); } // 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 = Interop.WebSocket.WebSocketBeginServerHandshake(webSocketHandle, IntPtr.Zero, IntPtr.Zero, 0, s_ServerFakeRequestHeaders !, (uint)s_ServerFakeRequestHeaders !.Length, out _, out _); ThrowOnError(errorCode); errorCode = Interop.WebSocket.WebSocketEndServerHandshake(webSocketHandle); ThrowOnError(errorCode); } catch { webSocketHandle?.Dispose(); throw; } return(webSocketHandle); }
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) { HttpWebSocket.ThrowPlatformNotSupportedException_WSPC(); } StartKeepAliveTimer(); }
internal static WebSocket Create(Stream innerStream, string subProtocol, int receiveBufferSize, TimeSpan keepAliveInterval, ArraySegment <byte> internalBuffer) { if (!WebSocketProtocolComponent.IsSupported) { HttpWebSocket.ThrowPlatformNotSupportedException_WSPC(); } HttpWebSocket.ValidateInnerStream(innerStream); HttpWebSocket.ValidateOptions(subProtocol, receiveBufferSize, HttpWebSocket.MinSendBufferSize, keepAliveInterval); WebSocketValidate.ValidateArraySegment(internalBuffer, nameof(internalBuffer)); WebSocketBuffer.Validate(internalBuffer.Count, receiveBufferSize, HttpWebSocket.MinSendBufferSize, true); return(new ServerWebSocket(innerStream, subProtocol, receiveBufferSize, keepAliveInterval, internalBuffer)); }
internal static async Task <HttpListenerWebSocketContext> AcceptWebSocketAsyncCore(HttpListenerContext context, string subProtocol, int receiveBufferSize, TimeSpan keepAliveInterval, ArraySegment <byte>?internalBuffer = null) { ValidateOptions(subProtocol, receiveBufferSize, MinSendBufferSize, keepAliveInterval); // 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]; string[] secWebSocketProtocols = null; string outgoingSecWebSocketProtocolString; bool shouldSendSecWebSocketProtocolHeader = ProcessWebSocketProtocolHeader( request.Headers[HttpKnownHeaderNames.SecWebSocketProtocol], subProtocol, out outgoingSecWebSocketProtocolString); if (shouldSendSecWebSocketProtocolHeader) { secWebSocketProtocols = new string[] { outgoingSecWebSocketProtocolString }; response.Headers.Add(HttpKnownHeaderNames.SecWebSocketProtocol, outgoingSecWebSocketProtocolString); } // negotiate the websocket key return value string secWebSocketKey = request.Headers[HttpKnownHeaderNames.SecWebSocketKey]; string secWebSocketAccept = HttpWebSocket.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.StatusDescription = HttpStatusDescription.Get(HttpStatusCode.SwitchingProtocols); HttpResponseStream responseStream = response.OutputStream as HttpResponseStream; // Send websocket handshake headers await responseStream.WriteWebSocketHandshakeHeadersAsync().ConfigureAwait(false); WebSocket webSocket = WebSocket.CreateFromStream(context.Connection.ConnectedStream, isServer: true, subProtocol, keepAliveInterval); HttpListenerWebSocketContext webSocketContext = new HttpListenerWebSocketContext( request.Url, request.Headers, request.Cookies, context.User, request.IsAuthenticated, request.IsLocal, request.IsSecureConnection, origin, secWebSocketProtocols != null ? secWebSocketProtocols : Array.Empty <string>(), secWebSocketVersion, secWebSocketKey, webSocket); return(webSocketContext); }
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 = HttpWebSocket.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); }