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.");
        }
示例#2
0
        // 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);
        }