/// <summary>
        /// Finishes sending a message through the connection.
        /// </summary>
        /// <param name="httpClientConnection">The connection.</param>
        /// <param name="httpWebResponse">The received response.</param>
        public void Listener_OnEndReceiving(HttpClientConnection httpClientConnection, HttpWebResponse httpWebResponse)
        {
            BinaryLogWriter binaryLogWriter = this.ITransportContext.BinaryLogWriter;
            Stream inputStream = null;

            try
            {
            #if DEBUG
                Debug.Assert(httpClientConnection.GenuineConnectionType == GenuineConnectionType.Persistent);
            #endif

                httpClientConnection.Remote.Renew(this._hostRenewingSpan, false);
                httpClientConnection.LastMessageWasReceviedAt = GenuineUtility.TickCount;
                httpClientConnection.SignalState(GenuineEventType.GeneralConnectionEstablished, null, null);

                // process the content
                inputStream = httpWebResponse.GetResponseStream();

                // LOG:
                if ( binaryLogWriter != null && binaryLogWriter[LogCategory.LowLevelTransport] > 0 )
                {
                    bool writeContent = binaryLogWriter[LogCategory.LowLevelTransport] > 1;
                    GenuineChunkedStream content = null;
                    if (writeContent)
                    {
                        content = new GenuineChunkedStream(false);
                        GenuineUtility.CopyStreamToStream(inputStream, content, (int) httpWebResponse.ContentLength);
                    }

                    binaryLogWriter.WriteTransportContentEvent(LogCategory.LowLevelTransport, "HttpClientConnectionManager.Listener_OnEndReceiving",
                        LogMessageType.LowLevelTransport_AsyncReceivingCompleted, null, null, httpClientConnection.Remote,
                        writeContent ? content : null,
                        GenuineUtility.CurrentThreadId, Thread.CurrentThread.Name,
                        httpClientConnection.DbgConnectionId, (int) httpWebResponse.ContentLength,
                        null, null, null,
                        "The content has been received.");

                    if (writeContent)
                        inputStream = content;
                }

                if (httpClientConnection.Listener_ConnectionLevelSecurity != null)
                    inputStream = httpClientConnection.Listener_ConnectionLevelSecurity.Decrypt(inputStream);

                BinaryReader binaryReader = new BinaryReader(inputStream);
                string serverUri;
                int sequenceNo;
                HttpPacketType httpPacketType;
                int remoteHostUniqueIdentifier;
                HttpMessageCoder.ReadResponseHeader(binaryReader, out serverUri, out sequenceNo, out httpPacketType, out remoteHostUniqueIdentifier);

                // LOG:
                if ( binaryLogWriter != null && binaryLogWriter[LogCategory.Connection] > 0 )
                {
                    binaryLogWriter.WriteEvent(LogCategory.Connection, "HttpClientConnectionManager.Listener_OnEndReceiving",
                        LogMessageType.ReceivingFinished, null, null, httpClientConnection.Remote, null,
                        GenuineUtility.CurrentThreadId, Thread.CurrentThread.Name, httpClientConnection.Listener_ConnectionLevelSecurity, null,
                        httpClientConnection.DbgConnectionId, 0, 0, 0, null, null, null, null,
                        "A response to the listener request has been received. Server uri: {0}. Sequence no: {1}. Packet type: {2}. Content-encoding: {3}. Content-length: {4}. Protocol version: {5}. Response uri: \"{6}\". Server: \"{7}\". Status code: {8}. Status description: \"{9}\".",
                        serverUri, sequenceNo, Enum.Format(typeof(HttpPacketType), httpPacketType, "g"),
                        httpWebResponse.ContentEncoding, httpWebResponse.ContentLength,
                        httpWebResponse.ProtocolVersion, httpWebResponse.ResponseUri,
                        httpWebResponse.Server, httpWebResponse.StatusCode, httpWebResponse.StatusDescription);
                }

                if (httpPacketType == HttpPacketType.Desynchronization)
                    throw GenuineExceptions.Get_Channel_Desynchronization();

                if (sequenceNo != httpClientConnection.ListenerSequenceNo)
                    throw GenuineExceptions.Get_Processing_LogicError(
                        string.Format("Impossible situation. The request stream number: {0}. The received stream number: {1}.", httpClientConnection.ListenerSequenceNo, sequenceNo)
                        );

                if (httpClientConnection.GenuineConnectionType == GenuineConnectionType.Persistent)
                    httpClientConnection.Remote.UpdateUri(serverUri, remoteHostUniqueIdentifier);

                // if the remote host has asked to terminate a connection
                if (httpPacketType == HttpPacketType.ClosedManually || httpPacketType == HttpPacketType.Desynchronization)
                    throw GenuineExceptions.Get_Receive_ConnectionClosed();

                // fetch and process messages
                if (httpPacketType != HttpPacketType.ListenerTimedOut)
                    this.LowLevel_ParseLabelledStream(inputStream, httpClientConnection.Listener_ReceiveBuffer, httpClientConnection, httpClientConnection.Listener_ConnectionLevelSecurity);

                GenuineUtility.CopyStreamToStream(inputStream, Stream.Null);
            }
            catch(Exception ex)
            {
                this.ConnectionFailed(httpClientConnection, false, ex, true);
                return ;
            }
            finally
            {
                if (inputStream != null)
                    inputStream.Close();
                httpWebResponse.Close();
            }

            GenuineThreadPool.QueueUserWorkItem(new WaitCallback(this.Listener_OnEndReceiving_ContinueExchange), httpClientConnection, false);
        }
        /// <summary>
        /// Finishes sending a message through the connection.
        /// </summary>
        /// <param name="httpClientConnection">The connection.</param>
        /// <param name="httpWebResponse">The received response.</param>
        public void Pool_Sender_OnEndSending(HttpClientConnection httpClientConnection, HttpWebResponse httpWebResponse)
        {
            BinaryLogWriter binaryLogWriter = this.ITransportContext.BinaryLogWriter;
            Stream inputStream = null;

            try
            {
                httpClientConnection.Remote.Renew(this._hostRenewingSpan, false);
                httpClientConnection.LastMessageWasReceviedAt = GenuineUtility.TickCount;
                httpClientConnection.SignalState(GenuineEventType.GeneralConnectionEstablished, null, null);

                httpClientConnection.SentContent.Close();
                httpClientConnection.SentContent = null;
                httpClientConnection.MessagesBeingSent.Clear();

                // process the content
                inputStream = httpWebResponse.GetResponseStream();

                // LOG:
                if ( binaryLogWriter != null && binaryLogWriter[LogCategory.LowLevelTransport] > 0 )
                {
                    GenuineChunkedStream content = null;
                    if (binaryLogWriter[LogCategory.Transport] > 1)
                    {
                        content = new GenuineChunkedStream(false);
                        GenuineUtility.CopyStreamToStream(inputStream, content, (int) httpWebResponse.ContentLength);
                    }

                    binaryLogWriter.WriteTransportContentEvent(LogCategory.LowLevelTransport, "HttpClientConnectionManager.Pool_Sender_OnEndSending",
                        LogMessageType.AsynchronousSendingFinished, null, null, httpClientConnection.Remote,
                        binaryLogWriter[LogCategory.LowLevelTransport] > 1 ? content : null,
                        GenuineUtility.CurrentThreadId, Thread.CurrentThread.Name, httpClientConnection.DbgConnectionId,
                        (int) httpWebResponse.ContentLength, null, null, null,
                        "The content of the response received by the sender connection.");

                    if (binaryLogWriter[LogCategory.Transport] > 1)
                        inputStream = content;
                }

                BinaryReader binaryReader = new BinaryReader(inputStream);
                string serverUri;
                int sequenceNo;
                HttpPacketType httpPacketType;
                int remoteHostUniqueIdentifier;
                HttpMessageCoder.ReadResponseHeader(binaryReader, out serverUri, out sequenceNo, out httpPacketType, out remoteHostUniqueIdentifier);

                if (sequenceNo != httpClientConnection.SendSequenceNo)
                    throw GenuineExceptions.Get_Receive_IncorrectData();
                if (httpClientConnection.GenuineConnectionType == GenuineConnectionType.Persistent)
                    httpClientConnection.Remote.UpdateUri(serverUri, remoteHostUniqueIdentifier);

                if ( binaryLogWriter != null && binaryLogWriter[LogCategory.LowLevelTransport] > 0 )
                {
                    binaryLogWriter.WriteEvent(LogCategory.LowLevelTransport, "HttpClientConnectionManager.Pool_Sender_OnEndSending",
                        LogMessageType.LowLevelTransport_AsyncSendingCompleted, null, null, httpClientConnection.Remote, null,
                        GenuineUtility.CurrentThreadId, Thread.CurrentThread.Name, httpClientConnection.Sender_ConnectionLevelSecurity,
                        httpClientConnection.Sender_ConnectionLevelSecurity == null ? null : httpClientConnection.Sender_ConnectionLevelSecurity.Name,
                        httpClientConnection.DbgConnectionId, (int) httpPacketType, sequenceNo, 0, null, null, null, null,
                        "SENDER invocation completed. Type of the packet: {0}. Server Uri: {1}. Seq No: {2}",
                        Enum.Format(typeof(HttpPacketType), httpPacketType, "g"), serverUri, sequenceNo);
                }

                if (httpPacketType == HttpPacketType.Desynchronization)
                {
                    this.ConnectionFailed(httpClientConnection, true, GenuineExceptions.Get_Channel_Desynchronization(), false);
                    return ;
                }

                if (httpPacketType == HttpPacketType.SenderError)
                {
                    Exception deserializedException = null;

                    try
                    {
                        BinaryFormatter binaryFormatter = new BinaryFormatter(new RemotingSurrogateSelector(), new StreamingContext(StreamingContextStates.Other));
                        deserializedException = binaryFormatter.Deserialize(inputStream) as Exception;
                    }
                    catch
                    {
                    }

                    if (deserializedException != null)
                        this.ConnectionFailed(httpClientConnection, true, GenuineExceptions.Get_Receive_ConnectionClosed(deserializedException.Message), false);
                    else
                        this.ConnectionFailed(httpClientConnection, true, GenuineExceptions.Get_Receive_ConnectionClosed("Remote host was not able to parse the request. Probably due to the security reasons."), false);
                    return ;
                }

                // fetch and process messages
                this.LowLevel_ParseLabelledStream(inputStream, httpClientConnection.Sender_ReceiveBuffer, httpClientConnection, httpClientConnection.Sender_ConnectionLevelSecurity);
            }
            finally
            {
                if (inputStream != null)
                    inputStream.Close();
                httpWebResponse.Close();

                // TODO: remove this
                if ( binaryLogWriter != null && binaryLogWriter[LogCategory.Debugging] > 0 )
                {
                    binaryLogWriter.WriteEvent(LogCategory.Debugging, "HttpClientConnectionManager.Pool_Sender_OnEndSending",
                        LogMessageType.DebuggingSuccess, null, null, httpClientConnection.Remote, null,
                        GenuineUtility.CurrentThreadId, Thread.CurrentThread.Name, httpClientConnection.Sender_ConnectionLevelSecurity,
                        null,
                        httpClientConnection.DbgConnectionId, -1, -1, -1, null, null, null, null,
                        "HttpClientConnectionManager.Pool_Sender_OnEndSending is completed.");
                }

            }

            GenuineThreadPool.QueueUserWorkItem(this._pool_Sender_OnEndSending_ContinueExchange, httpClientConnection, false);
        }
        /// <summary>
        /// Processes failed or closed connections.
        /// </summary>
        /// <param name="httpClientConnection">The connection.</param>
        /// <param name="sender">The type of the failed connection (P/Async).</param>
        /// <param name="exception">The exception.</param>
        /// <param name="tryToReestablish">True if it's a good idea to try to reestablish the connection.</param>
        public void ConnectionFailed(HttpClientConnection httpClientConnection, bool sender, Exception exception, bool tryToReestablish)
        {
            BinaryLogWriter binaryLogWriter = this.ITransportContext.BinaryLogWriter;

            try
            {
                if (tryToReestablish)
                    tryToReestablish = ! ConnectionManager.IsExceptionCritical(exception as OperationException);

                // LOG:
                if ( binaryLogWriter != null && binaryLogWriter[LogCategory.Connection] > 0 )
                {
                    binaryLogWriter.WriteEvent(LogCategory.Connection, "HttpClientConnectionManager.ConnectionFailed",
                        LogMessageType.ConnectionFailed, exception, null, httpClientConnection.Remote, null,
                        GenuineUtility.CurrentThreadId, Thread.CurrentThread.Name, null, null,
                        httpClientConnection.DbgConnectionId,
                        0, 0, 0, null, null, null, null,
                        "Connection failure. Sender: {0}. Connection type: {1}. Try to reestablish: {2}.",
                        sender.ToString(), Enum.GetName(typeof(GenuineConnectionType), httpClientConnection.GenuineConnectionType),
                        tryToReestablish.ToString());
                }

                using (new ReaderAutoLocker(this._disposeLock))
                {
                    if (this._disposed)
                        tryToReestablish = false;
                }

                lock (httpClientConnection.Remote.PersistentConnectionEstablishingLock)
                {
                    if (! tryToReestablish || httpClientConnection._disposed)
                    {
                        using (new ReaderAutoLocker(this._disposeLock))
                        {
                            this._persistent.Remove(httpClientConnection.Remote.Url);
                        }

                        // close the content
                        if (httpClientConnection.SentContent != null)
                        {
                            httpClientConnection.SentContent.Close();
                            httpClientConnection.SentContent = null;
                        }

                        // release all resources
                        this.ITransportContext.KnownHosts.ReleaseHostResources(httpClientConnection.Remote, exception);
                        foreach (Message message in httpClientConnection.MessagesBeingSent)
                            this.ITransportContext.IIncomingStreamHandler.DispatchException(message, exception);
                        httpClientConnection.MessagesBeingSent.Clear();
                        httpClientConnection.Dispose(exception);

                        httpClientConnection.SignalState(GenuineEventType.GeneralConnectionClosed, exception, null);
                        if (exception is OperationException && ((OperationException) exception).OperationErrorMessage.ErrorIdentifier == "GenuineChannels.Exception.Receive.ServerHasBeenRestarted")
                            httpClientConnection.SignalState(GenuineEventType.GeneralServerRestartDetected, exception, null);

                        // LOG:
                        if ( binaryLogWriter != null && binaryLogWriter[LogCategory.Connection] > 0 )
                        {
                            binaryLogWriter.WriteEvent(LogCategory.Connection, "HttpClientConnectionManager.ConnectionFailed",
                                LogMessageType.ConnectionFailed, exception, null, httpClientConnection.Remote, null,
                                GenuineUtility.CurrentThreadId, Thread.CurrentThread.Name, null, null,
                                httpClientConnection.DbgConnectionId,
                                0, 0, 0, null, null, null, null,
                                "The connection has been completely terminated and removed from all collection. Sender: {0}. Connection type: {1}. Try to reestablish: {2}.",
                                sender.ToString(), Enum.GetName(typeof(GenuineConnectionType), httpClientConnection.GenuineConnectionType),
                                tryToReestablish.ToString());
                        }

                        return ;
                    }

                    httpClientConnection.SignalState(GenuineEventType.GeneralConnectionReestablishing, exception, null);

                    HttpClientConnection.ReconnectionContainer reconnectionContainer = sender ? httpClientConnection.SenderReconnectionLock : httpClientConnection.ListenerReconnectionLock;
                    lock (reconnectionContainer.SyncLock)
                    {
                        if (GenuineUtility.IsTimeoutExpired(reconnectionContainer.ReconnectionStartedAt + GenuineUtility.ConvertToMilliseconds(this.ITransportContext.IParameterProvider[GenuineParameter.MaxTimeSpanToReconnect])))
                        {
                            // LOG:
                            if ( binaryLogWriter != null && binaryLogWriter[LogCategory.Connection] > 0 )
                            {
                                binaryLogWriter.WriteEvent(LogCategory.Connection, "HttpClientConnectionManager.ConnectionFailed",
                                    LogMessageType.ConnectionReestablishing, GenuineExceptions.Get_Debugging_GeneralWarning("Connection was not reestablished within the specified time boundaries."),
                                    null, httpClientConnection.Remote, null,
                                    GenuineUtility.CurrentThreadId, Thread.CurrentThread.Name, null, null,
                                    httpClientConnection.DbgConnectionId,
                                    0, 0, 0, null, null, null, null,
                                    "The connection has not been reestablished within the specified time boundaries.");
                            }

                            // just close the connection
                            using (new ReaderAutoLocker(this._disposeLock))
                            {
                                this._persistent.Remove(httpClientConnection.Remote.Url);
                            }

                            this.ITransportContext.KnownHosts.ReleaseHostResources(httpClientConnection.Remote, exception);
                            httpClientConnection.Dispose(exception);
                            httpClientConnection.MessageContainer.Dispose(exception);

                            // and fire a warning
                            httpClientConnection.SignalState(GenuineEventType.GeneralConnectionClosed, exception, null);
                            return ;
                        }

                        // resend the content or rerequest the previous content
                        if (sender)
                        {
                            if (httpClientConnection.SentContent != null)
                                this.LowLevel_Sender_Send(null, httpClientConnection, httpClientConnection.OnEndSending, true);
                        }
                        else
                            this.LowLevel_InitiateListening(httpClientConnection, false);
                    }
                }
            }
            catch(Exception ex)
            {
                if ( binaryLogWriter != null )
                    binaryLogWriter.WriteImplementationWarningEvent("HttpClientConnectionManager.ConnectionFailed",
                        LogMessageType.Error, ex, GenuineUtility.CurrentThreadId, Thread.CurrentThread.Name,
                        "Unexpected exception occurred in the HttpClientConnectionManager.ConnectionFailed method. Most likely, something must be fixed.");
            }
        }