public void CloseConnection()
        {
            using (EneterTrace.Entering())
            {
                using (ThreadLock.Lock(myConnectionManipulatorLock))
                {
                    myStopReceivingRequestedFlag = true;
                    myMessageInSendProgress      = EMessageInSendProgress.None;

                    if (myTcpClient != null)
                    {
                        // Try to send the frame closing the communication.
                        if (myClientStream != null)
                        {
                            try
                            {
                                // If it was not disconnected by the client then try to send the message the connection was closed.
                                if (myIsListeningToResponses)
                                {
                                    // Generate the masking key.
                                    byte[] aCloseFrame = WebSocketFormatter.EncodeCloseFrame(null, 1000);
                                    myClientStream.Write(aCloseFrame, 0, aCloseFrame.Length);
                                }
                            }
                            catch (Exception err)
                            {
                                EneterTrace.Warning(TracedObject + ErrorHandler.FailedToCloseConnection, err);
                            }

                            myClientStream.Close();
                        }

                        try
                        {
                            myTcpClient.Close();
                        }
                        catch (Exception err)
                        {
                            EneterTrace.Warning(TracedObject + "failed to close Tcp connection.", err);
                        }

                        myTcpClient = null;
                    }

                    myReceivedMessages.UnblockProcessingThreads();
                }
            }
        }
Example #2
0
        private void ClearConnection(bool sendCloseMessageFlag)
        {
            using (EneterTrace.Entering())
            {
                using (ThreadLock.Lock(myConnectionManipulatorLock))
                {
                    myStopReceivingRequestedFlag = true;
                    myMessageInSendProgress      = EMessageInSendProgress.None;

                    if (myTcpClient != null)
                    {
                        // Try to send the frame closing the communication.
                        if (myClientStream != null)
                        {
                            try
                            {
                                if (sendCloseMessageFlag)
                                {
                                    // Generate the masking key.
                                    byte[] aMaskingKey = GetMaskingKey();
                                    byte[] aCloseFrame = WebSocketFormatter.EncodeCloseFrame(aMaskingKey, 1000);
                                    myClientStream.Write(aCloseFrame, 0, aCloseFrame.Length);
                                }
                            }
                            catch (Exception err)
                            {
                                EneterTrace.Warning(TracedObject + ErrorHandler.FailedToCloseConnection, err);
                            }

                            myClientStream.Close();
                        }

                        try
                        {
                            myTcpClient.Close();
                        }
                        catch (Exception err)
                        {
                            EneterTrace.Warning(TracedObject + "failed to close Tcp connection.", err);
                        }

                        myTcpClient = null;
                    }


                    if (myResponseReceiverThread != null && myResponseReceiverThread.ThreadState != ThreadState.Unstarted)
                    {
                        if (!myResponseReceiverThread.Join(3000))
                        {
                            EneterTrace.Warning(TracedObject + ErrorHandler.FailedToStopThreadId + myResponseReceiverThread.ManagedThreadId);

                            try
                            {
                                myResponseReceiverThread.Abort();
                            }
                            catch (Exception err)
                            {
                                EneterTrace.Warning(TracedObject + ErrorHandler.FailedToAbortThread, err);
                            }
                        }
                    }
                    myResponseReceiverThread = null;

                    try
                    {
                        myMessageProcessingThread.UnregisterMessageHandler();
                    }
                    catch (Exception err)
                    {
                        EneterTrace.Warning(TracedObject + ErrorHandler.FailedToUnregisterMessageHandler, err);
                    }

                    // Reset the responsibility for starting of threads looping for response messages.
                    myResponsibleForActivatingListening = EResponseListeningResponsible.OpenConnection;
                }
            }
        }
        public void DoRequestListening()
        {
            using (EneterTrace.Entering())
            {
                myIsListeningToResponses = true;
                ushort aCloseCode = 0;

                try
                {
                    DynamicStream aContinuousMessageStream = null;

                    while (!myStopReceivingRequestedFlag)
                    {
                        // Decode the incoming message.
                        WebSocketFrame aFrame = WebSocketFormatter.DecodeFrame(myClientStream);

                        if (!myStopReceivingRequestedFlag && aFrame != null)
                        {
                            // Frames from server must be unmasked.
                            // According the protocol, If the frame was NOT masked, the server must close connection with the client.
                            if (aFrame.MaskFlag == false)
                            {
                                throw new InvalidOperationException(TracedObject + "received unmasked frame from the client. Frames from client shall be masked.");
                            }

                            // Process the frame.
                            if (aFrame.FrameType == EFrameType.Ping)
                            {
                                // Response 'pong'. The response responses same data as received in the 'ping'.
                                SendFrame(maskingKey => WebSocketFormatter.EncodePongFrame(maskingKey, aFrame.Message));
                            }
                            else if (aFrame.FrameType == EFrameType.Close)
                            {
                                EneterTrace.Debug(TracedObject + "received the close frame.");
                                break;
                            }
                            else if (aFrame.FrameType == EFrameType.Pong)
                            {
                                Notify(PongReceived);
                            }
                            // If a new message starts.
                            else if (aFrame.FrameType == EFrameType.Binary || aFrame.FrameType == EFrameType.Text)
                            {
                                // If a previous message is not finished then the new message is not expected -> protocol error.
                                if (aContinuousMessageStream != null)
                                {
                                    EneterTrace.Warning(TracedObject + "detected unexpected new message. (previous message was not finished)");

                                    // Protocol error close code.
                                    aCloseCode = 1002;
                                    break;
                                }

                                WebSocketMessage aReceivedMessage = null;

                                // If the message does not come in multiple frames then optimize the performance
                                // and use MemoryStream instead of DynamicStream.
                                if (aFrame.IsFinal)
                                {
                                    MemoryStream aMessageStream = new MemoryStream(aFrame.Message);
                                    aReceivedMessage = new WebSocketMessage(aFrame.FrameType == EFrameType.Text, aMessageStream);
                                }
                                else
                                // if the message is split to several frames then use DynamicStream so that writing of incoming
                                // frames and reading of already received data can run in parallel.
                                {
                                    // Create stream where the message data will be writen.
                                    aContinuousMessageStream = new DynamicStream();
                                    aContinuousMessageStream.WriteWithoutCopying(aFrame.Message, 0, aFrame.Message.Length);
                                    aReceivedMessage = new WebSocketMessage(aFrame.FrameType == EFrameType.Text, aContinuousMessageStream);
                                }

                                // Put received message to the queue.
                                myReceivedMessages.EnqueueMessage(aReceivedMessage);
                            }
                            // If a message continues. (I.e. message is split into more fragments.)
                            else if (aFrame.FrameType == EFrameType.Continuation)
                            {
                                // If the message does not exist then continuing frame does not have any sense -> protocol error.
                                if (aContinuousMessageStream == null)
                                {
                                    EneterTrace.Warning(TracedObject + "detected unexpected continuing of a message. (none message was started before)");

                                    // Protocol error close code.
                                    aCloseCode = 1002;
                                    break;
                                }

                                aContinuousMessageStream.WriteWithoutCopying(aFrame.Message, 0, aFrame.Message.Length);

                                // If this is the final frame.
                                if (aFrame.IsFinal)
                                {
                                    aContinuousMessageStream.IsBlockingMode = false;
                                    aContinuousMessageStream = null;
                                }
                            }
                        }

                        // If disconnected
                        if (aFrame == null)// || !myTcpClient.Client.Poll(0, SelectMode.SelectWrite))
                        {
                            //EneterTrace.Warning(TracedObject + "detected the TCP connection is not available. The connection will be closed.");
                            break;
                        }
                    }
                }
                catch (IOException)
                {
                    // Ignore this exception. It is often thrown when the connection was closed.
                    // Do not thrace this because the tracing degradates the performance in this case.
                }
                catch (Exception err)
                {
                    EneterTrace.Error(TracedObject + ErrorHandler.FailedInListeningLoop, err);
                }

                // If the connection is being closed due to a protocol error.
                if (aCloseCode > 1000)
                {
                    // Try to send the close message.
                    try
                    {
                        byte[] aCloseMessage = WebSocketFormatter.EncodeCloseFrame(null, aCloseCode);
                        myClientStream.Write(aCloseMessage, 0, aCloseMessage.Length);
                    }
                    catch
                    {
                    }
                }

                myIsListeningToResponses = false;

                myReceivedMessages.UnblockProcessingThreads();

                // Notify the listening to messages stoped.
                Notify(ConnectionClosed);
            }
        }
        // Handles TCP connection.
        // It parses the HTTP request of the websocket to get the requested path.
        // Then it searches the matching PathListeners and calls it to handle the connection.
        protected override void HandleConnection(TcpClient tcpClient)
        {
            using (EneterTrace.Entering())
            {
                try
                {
                    // Get the data stream.
                    // Note: If SSL then perform the authentication and provide the stream encoding/decoding data.
                    Stream aDataStream = SecurityFactory.CreateSecurityStreamAndAuthenticate(tcpClient.GetStream());

                    // Receive open websocket communication request.
                    Match anHttpOpenConnectionRegEx = WebSocketFormatter.DecodeOpenConnectionHttpRequest(aDataStream);
                    if (!anHttpOpenConnectionRegEx.Success)
                    {
                        EneterTrace.Warning(TracedObject + "failed to receive open websocket connection request. (incorrect http request)");
                        byte[] aCloseConnectionResponse = WebSocketFormatter.EncodeCloseFrame(null, 400);
                        aDataStream.Write(aCloseConnectionResponse, 0, aCloseConnectionResponse.Length);

                        return;
                    }


                    // Get http header fields.
                    IDictionary <string, string> aHeaderFields = WebSocketFormatter.GetHttpHeaderFields(anHttpOpenConnectionRegEx);

                    string aSecurityKey;
                    aHeaderFields.TryGetValue("Sec-WebSocket-Key", out aSecurityKey);

                    // If some required header field is missing or has incorrect value.
                    if (!aHeaderFields.ContainsKey("Upgrade") ||
                        !aHeaderFields.ContainsKey("Connection") ||
                        string.IsNullOrEmpty(aSecurityKey))
                    {
                        EneterTrace.Warning(TracedObject + "failed to receive open websocket connection request. (missing or incorrect header field)");
                        byte[] aCloseConnectionResponse = WebSocketFormatter.EncodeCloseFrame(null, 400);
                        aDataStream.Write(aCloseConnectionResponse, 0, aCloseConnectionResponse.Length);

                        return;
                    }

                    // Get the path to identify the end-point.
                    string anIncomingPath = anHttpOpenConnectionRegEx.Groups["path"].Value;
                    if (string.IsNullOrEmpty(anIncomingPath))
                    {
                        EneterTrace.Warning(TracedObject + "failed to process Websocket request because the path is null or empty string.");
                        byte[] aCloseConnectionResponse = WebSocketFormatter.EncodeCloseFrame(null, 400);
                        aDataStream.Write(aCloseConnectionResponse, 0, aCloseConnectionResponse.Length);

                        return;
                    }

                    // if the incoming path is the whole uri then extract the absolute path.
                    Uri anIncomingUri;
                    Uri.TryCreate(anIncomingPath, UriKind.Absolute, out anIncomingUri);
                    string anAbsolutePath = (anIncomingUri != null) ? anIncomingUri.AbsolutePath : anIncomingPath;

                    // Get handler for that path.
                    Uri aHandlerUri;
                    Action <IWebSocketClientContext> aPathHandler;
                    using (ThreadLock.Lock(myHandlers))
                    {
                        KeyValuePair <Uri, object> aPair = myHandlers.FirstOrDefault(x => x.Key.AbsolutePath == anAbsolutePath);
                        aPathHandler = aPair.Value as Action <IWebSocketClientContext>;
                        aHandlerUri  = aPair.Key;
                    }

                    // If the listener does not exist.
                    if (aPathHandler == null)
                    {
                        EneterTrace.Warning(TracedObject + "does not listen to " + anIncomingPath);
                        byte[] aCloseConnectionResponse = WebSocketFormatter.EncodeCloseFrame(null, 404);
                        aDataStream.Write(aCloseConnectionResponse, 0, aCloseConnectionResponse.Length);

                        return;
                    }

                    // Response that the connection is accepted.
                    byte[] anOpenConnectionResponse = WebSocketFormatter.EncodeOpenConnectionHttpResponse(aSecurityKey);
                    aDataStream.Write(anOpenConnectionResponse, 0, anOpenConnectionResponse.Length);


                    // Create the context for conecting client.
                    string aRequestUriStr = aHandlerUri.Scheme + "://" + aHandlerUri.Host + ":" + Address.Port + anAbsolutePath + anHttpOpenConnectionRegEx.Groups["query"].Value;
                    Uri    aRequestUri    = new Uri(aRequestUriStr);
                    WebSocketClientContext aClientContext = new WebSocketClientContext(aRequestUri, aHeaderFields, tcpClient, aDataStream);


                    // Call path handler in a another thread.
                    EneterThreadPool.QueueUserWorkItem(() =>
                    {
                        try
                        {
                            aPathHandler(aClientContext);
                        }
                        catch (Exception err)
                        {
                            EneterTrace.Warning(TracedObject + ErrorHandler.DetectedException, err);
                        }
                    });


                    // Start listening loop in the client context.
                    // The loop will read websocket messages from the underlying tcp connection.
                    // Note: User is responsible to call WebSocketClientContext.CloseConnection() to stop this loop
                    //       or the service must close the conneciton.
                    aClientContext.DoRequestListening();
                }
                catch (Exception err)
                {
                    EneterTrace.Error(TracedObject + "failed to process TCP connection.", err);
                }
            }
        }