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."); }
// 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. }
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); }