/// <summary>
        /// Initiates the listening request.
        /// </summary>
        /// <param name="httpClientConnection">The connection.</param>
        /// <param name="requestNextPacket">Whether to request the next packet.</param>
        private void LowLevel_InitiateListening(HttpClientConnection httpClientConnection, bool requestNextPacket)
        {
            BinaryLogWriter binaryLogWriter = this.ITransportContext.BinaryLogWriter;

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

                if (requestNextPacket)
                    httpClientConnection.ListenerSequenceNo ++;

                // write the request header
                using (GenuineChunkedStream listenerStream = new GenuineChunkedStream(false))
                {
                    BinaryWriter binaryWriter = new BinaryWriter(listenerStream);
                    HttpMessageCoder.WriteRequestHeader(binaryWriter, MessageCoder.PROTOCOL_VERSION, httpClientConnection.GenuineConnectionType, httpClientConnection.HostId, HttpPacketType.Listening, httpClientConnection.ListenerSequenceNo, httpClientConnection.ConnectionName, httpClientConnection.Remote.LocalHostUniqueIdentifier);

                    // start the request
                    httpClientConnection.Listener = httpClientConnection.InitializeRequest(false, httpClientConnection._keepalive);
                    httpClientConnection.Listener.ContentLength = listenerStream.Length;

                    this.LowLevel_InitiateHttpWebRequest(httpClientConnection.Listener, listenerStream, httpClientConnection.ListenerClosed, false, httpClientConnection.OnEndReceiving, httpClientConnection);
                }
            }
            catch(Exception ex)
            {
                // It's necessary to schedule connection reestablishing

                // LOG:
                if ( binaryLogWriter != null && binaryLogWriter[LogCategory.Connection] > 0 )
                {
                    binaryLogWriter.WriteEvent(LogCategory.Connection, "HttpClientConnectionManager.LowLevel_InitiateListening",
                        LogMessageType.ConnectionFailed, ex, null, httpClientConnection.Remote, null,
                        GenuineUtility.CurrentThreadId, Thread.CurrentThread.Name, null, null,
                        httpClientConnection.DbgConnectionId, 0, 0, 0, null, null, null, null,
                        "The LISTENER request Seq No={0} has failed. The connection will be reestablished, if possible.", httpClientConnection.ListenerSequenceNo);
                }

                this.StartReestablishingIfNecessary(httpClientConnection, ex, false);
            }
        }
        /// <summary>
        /// Opens connection to the remote host.
        /// </summary>
        /// <param name="remote">The remote host.</param>
        /// <param name="genuineConnectionType">The type of the connection.</param>
        /// <param name="connectionName">The name of the connection.</param>
        /// <returns>The opened connection.</returns>
        private HttpClientConnection LowLevel_OpenConnection(HostInformation remote, GenuineConnectionType genuineConnectionType, string connectionName)
        {
            BinaryLogWriter binaryLogWriter = this.ITransportContext.BinaryLogWriter;

            using (new ReaderAutoLocker(this._disposeLock))
            {
                if (this._disposed)
                    throw OperationException.WrapException(this._disposeReason);
            }

            // the time we should finish connection establishing before
            int timeout = GenuineUtility.GetTimeout((TimeSpan) this.ITransportContext.IParameterProvider[GenuineParameter.ConnectTimeout]);

            // LOG:
            if ( binaryLogWriter != null && binaryLogWriter[LogCategory.Connection] > 0 )
            {
                binaryLogWriter.WriteEvent(LogCategory.Connection, "HttpClientConnectionManager.LowLevel_OpenConnection",
                    LogMessageType.ConnectionEstablishing, null, null, remote, null,
                    GenuineUtility.CurrentThreadId, Thread.CurrentThread.Name, null, null,
                    -1, 0, 0, 0, null, null, null, null,
                    "The connection is being established to \"{0}\".", remote == null ? "?" : remote.Url == null ? "?" : remote.Url);
            }

            // first - send the check request and start CLSSE
            HttpClientConnection httpClientConnection = new HttpClientConnection(this.ITransportContext, connectionName);
            httpClientConnection.Remote = remote;
            httpClientConnection.GenuineConnectionType = genuineConnectionType;

            httpClientConnection.Sender_ConnectionLevelSecurity = this.CreateConnectionLevelSecuritySession(genuineConnectionType);
            httpClientConnection.Listener_ConnectionLevelSecurity = this.CreateConnectionLevelSecuritySession(genuineConnectionType);

            // LOG:
            if ( binaryLogWriter != null && binaryLogWriter[LogCategory.Connection] > 0 )
            {
                binaryLogWriter.WriteConnectionParameterEvent(LogCategory.Connection, "HttpClientConnectionManager.LowLevel_OpenConnection",
                    LogMessageType.ConnectionParameters, null, remote, this.ITransportContext.IParameterProvider,
                    GenuineUtility.CurrentThreadId, Thread.CurrentThread.Name, httpClientConnection.DbgConnectionId,
                    "An HTTP connection is being established.");
            }

            // gather the output stream
            Stream senderInputClsseStream = Stream.Null;
            Stream listenerInputClsseStream = Stream.Null;
            Stream clsseStream = null;
            GenuineChunkedStream outputStream = null;
            bool resend = false;
            bool firstFailure = false;

            // CLSS establishing
            for ( int i = 0; ; i++ )
            {
                bool noClsseToSend = true;

                if (! resend)
                {
                    // build the content
                    outputStream = new GenuineChunkedStream(false);
                    BinaryWriter binaryWriter = new BinaryWriter(outputStream);
                    HttpMessageCoder.WriteRequestHeader(binaryWriter, MessageCoder.PROTOCOL_VERSION, genuineConnectionType, httpClientConnection.HostId,
                        i == 0 ? HttpPacketType.Establishing_ResetConnection : HttpPacketType.Establishing, ++httpClientConnection.SendSequenceNo,
                        httpClientConnection.ConnectionName, remote.LocalHostUniqueIdentifier);

                    // CLSSE info
                    using (new GenuineChunkedStreamSizeLabel(outputStream))
                    {
                        if (httpClientConnection.Sender_ConnectionLevelSecurity != null && ! httpClientConnection.Sender_ConnectionLevelSecurity.IsEstablished)
                        {
                            clsseStream = httpClientConnection.Sender_ConnectionLevelSecurity.EstablishSession(senderInputClsseStream, true);
                            if (clsseStream != null)
                            {
                                noClsseToSend = false;
                                GenuineUtility.CopyStreamToStream(clsseStream, outputStream);
                            }
                        }

                    }

                    // CLSSE info
                    using (new GenuineChunkedStreamSizeLabel(outputStream))
                    {
                        if (httpClientConnection.Listener_ConnectionLevelSecurity != null && ! httpClientConnection.Listener_ConnectionLevelSecurity.IsEstablished)
                        {
                            clsseStream = httpClientConnection.Listener_ConnectionLevelSecurity.EstablishSession(listenerInputClsseStream, true);
                            if (clsseStream != null)
                            {
                                noClsseToSend = false;
                                GenuineUtility.CopyStreamToStream(clsseStream, outputStream);
                            }
                        }
                    }
                }
                else
                    outputStream.Position = 0;

                // parse the response
                HttpWebRequest httpWebRequest = httpClientConnection.InitializeRequest(true, httpClientConnection._keepalive);
                int millisecondsRemain = GenuineUtility.CompareTickCounts(timeout, GenuineUtility.TickCount);
                if ( millisecondsRemain <= 0)
                    throw GenuineExceptions.Get_Channel_ConnectionClosedAfterTimeout();

                httpWebRequest.Timeout = millisecondsRemain;
                httpWebRequest.ContentLength = outputStream.Length;
                Stream requestStream = null;

                try
                {
                    requestStream = httpWebRequest.GetRequestStream();
                    GenuineUtility.CopyStreamToStream(outputStream, requestStream, (int) outputStream.Length);
                    using (HttpWebResponse httpWebResponse = (HttpWebResponse) httpWebRequest.GetResponse())
                    {
                        // LOG:
                        if ( binaryLogWriter != null && binaryLogWriter[LogCategory.Connection] > 0 )
                        {
                            StringBuilder stringBuilderHeaders = new StringBuilder(1024);
                            foreach (string headerName in httpWebResponse.Headers.Keys)
                                stringBuilderHeaders.AppendFormat("{0}=\"{1}\"; ", headerName, httpWebResponse.Headers.Get(headerName));

                            binaryLogWriter.WriteEvent(LogCategory.Connection, "HttpClientConnectionManager.LowLevel_OpenConnection",
                                LogMessageType.ConnectionEstablishing, null, null, remote, null,
                                GenuineUtility.CurrentThreadId, Thread.CurrentThread.Name, null, null,
                                httpClientConnection.DbgConnectionId, 0, 0, 0, null, null, null, null,
                                "The trial connection has been successfully completed. Content-Length: {0}; Content encoding: {1}; Content type: {2}; Protocol version: {3}; Respose uri: {4}; Status code: {5}; Status description: {6}; HTTP headers: {7}.",
                                httpWebResponse.ContentLength, httpWebResponse.ContentEncoding, httpWebResponse.ContentType,
                                httpWebResponse.ProtocolVersion, httpWebResponse.ResponseUri,
                                httpWebResponse.StatusCode, httpWebResponse.StatusDescription,
                                stringBuilderHeaders.ToString());
                        }

                        using (Stream responseStream = httpWebResponse.GetResponseStream())
                        {
                            BinaryReader binaryReader = new BinaryReader(responseStream);
                            string serverUri;
                            int sequenceNo;
                            HttpPacketType httpPacketType;
                            int remoteHostUniqueIdentifier;

                            HttpMessageCoder.ReadResponseHeader(binaryReader, out serverUri, out sequenceNo, out httpPacketType, out remoteHostUniqueIdentifier);

                            if ( httpPacketType != HttpPacketType.Establishing && httpPacketType != HttpPacketType.Establishing_ResetConnection )
                                throw GenuineExceptions.Get_Connect_CanNotConnectToRemoteHost(remote.ToString(), "Wrong response received from the remote host.");

                            // check the restartion if either CLSS or the persistent connection is used
                            if (genuineConnectionType == GenuineConnectionType.Persistent || httpClientConnection.Sender_ConnectionLevelSecurity != null)
                                remote.UpdateUri(serverUri, remoteHostUniqueIdentifier);

                            // the first SS
                            int writtenSize = binaryReader.ReadInt32();
                            senderInputClsseStream = new GenuineChunkedStream(true);
                            GenuineUtility.CopyStreamToStream(responseStream, senderInputClsseStream, writtenSize);

                            writtenSize = binaryReader.ReadInt32();
                            listenerInputClsseStream = new GenuineChunkedStream(true);
                            GenuineUtility.CopyStreamToStream(responseStream, listenerInputClsseStream, writtenSize);
                        }
                    }

                    outputStream.Close();
                }
                catch(Exception ex)
                {
                    if (firstFailure)
                        throw;

                    // LOG:
                    if ( binaryLogWriter != null && binaryLogWriter[LogCategory.Connection] > 0 )
                    {
                        binaryLogWriter.WriteEvent(LogCategory.Connection, "HttpClientConnectionManager.LowLevel_OpenConnection",
                            LogMessageType.ConnectionEstablishing, ex, null, remote, null,
                            GenuineUtility.CurrentThreadId, Thread.CurrentThread.Name, null, null,
                            -1, 0, 0, 0, null, null, null, null,
                            "Exception occurred while establishing the connection to \"{0}\". The connection establishing will be continued.", remote == null ? "?" : remote.Url == null ? "?" : remote.Url);
                    }

                    // try to recover
                    resend = true;
                    continue;
                }
                finally
                {
                    if (requestStream != null)
                        requestStream.Close();
                }

                firstFailure = false;
                resend = false;

                if (httpClientConnection.Sender_ConnectionLevelSecurity != null && ! httpClientConnection.Sender_ConnectionLevelSecurity.IsEstablished)
                    continue;
                if (httpClientConnection.Listener_ConnectionLevelSecurity != null && ! httpClientConnection.Listener_ConnectionLevelSecurity.IsEstablished)
                    continue;
                if (! noClsseToSend)
                    continue;

                break;
            }

            // start listener if it's a persistent connection
            remote.PhysicalAddress = remote.Url;

            // LOG:
            if ( binaryLogWriter != null && binaryLogWriter[LogCategory.HostInformation] > 0 )
            {
                binaryLogWriter.WriteHostInformationEvent("HttpClientConnectionManager.LowLevel_OpenConnection",
                    LogMessageType.HostInformationCreated, null, remote,
                    GenuineUtility.CurrentThreadId, Thread.CurrentThread.Name, httpClientConnection.Sender_ConnectionLevelSecurity,
                    httpClientConnection.Sender_ConnectionLevelSecurity == null ? null : httpClientConnection.Sender_ConnectionLevelSecurity.Name,
                    httpClientConnection.DbgConnectionId,
                    "HostInformation is ready for actions.");
            }

            // LOG:
            if ( binaryLogWriter != null && binaryLogWriter[LogCategory.Connection] > 0 )
            {
                binaryLogWriter.WriteEvent(LogCategory.Connection, "HttpClientConnectionManager.LowLevel_OpenConnection",
                    LogMessageType.ConnectionEstablished, null, null, remote, null,
                    GenuineUtility.CurrentThreadId, Thread.CurrentThread.Name,
                    httpClientConnection.Sender_ConnectionLevelSecurity,
                    httpClientConnection.Sender_ConnectionLevelSecurity == null ? null : httpClientConnection.Sender_ConnectionLevelSecurity.Name,
                    httpClientConnection.DbgConnectionId, (int) genuineConnectionType, 0, 0, this.GetType().Name, remote.Url, remote.Url, null,
                    "The connection to the remote host is established.");
            }

            if (genuineConnectionType == GenuineConnectionType.Persistent)
                this.LowLevel_InitiateListening(httpClientConnection, true);
            return httpClientConnection;
        }
        /// <summary>
        /// Sends the message to the remote host through the sender connection.
        /// </summary>
        /// <param name="message">Message to be sent.</param>
        /// <param name="httpClientConnection">The connection.</param>
        /// <param name="asyncCallback">The callback to be called after the operation will be completed.</param>
        /// <param name="repeatSending">Whether to send the previous content again.</param>
        private void LowLevel_Sender_Send(Message message, HttpClientConnection httpClientConnection, AsyncCallback asyncCallback, bool repeatSending)
        {
            BinaryLogWriter binaryLogWriter = this.ITransportContext.BinaryLogWriter;

            // connection lock obtained, assemble the packet and send it
            try
            {
                if (! repeatSending)
                {
                    httpClientConnection.SendSequenceNo ++;
                    httpClientConnection.LastMessageWasSentAt = GenuineUtility.TickCount;

                    httpClientConnection.SentContent = new GenuineChunkedStream(false);
                    MessageCoder.FillInLabelledStream(message, httpClientConnection.MessageContainer,
                        httpClientConnection.MessagesBeingSent, httpClientConnection.SentContent,
                        httpClientConnection.Sender_SendBuffer,
                        (int) this.ITransportContext.IParameterProvider[GenuineParameter.HttpRecommendedPacketSize]);

                    // LOG:
                    if ( binaryLogWriter != null && binaryLogWriter[LogCategory.Transport] > 0 )
                    {
                        for ( int i = 0; i < httpClientConnection.MessagesBeingSent.Count; i++)
                        {
                            Message nextMessage = (Message) httpClientConnection.MessagesBeingSent[i];

                            binaryLogWriter.WriteEvent(LogCategory.Transport, "HttpClientConnectionManager.LowLevel_Sender_Send",
                                LogMessageType.MessageIsSentAsynchronously, null, nextMessage, httpClientConnection.Remote, null,
                                GenuineUtility.CurrentThreadId, Thread.CurrentThread.Name, httpClientConnection.Sender_ConnectionLevelSecurity,
                                null,
                                httpClientConnection.DbgConnectionId, httpClientConnection.SendSequenceNo, 0, 0, null, null, null, null,
                                "The message will be sent in the SENDER stream N: {0}.", httpClientConnection.SendSequenceNo);
                        }
                    }

                    // apply CLSSE
                    if (httpClientConnection.Sender_ConnectionLevelSecurity != null)
                    {
                        GenuineChunkedStream encryptedContent = new GenuineChunkedStream(false);
                        httpClientConnection.Sender_ConnectionLevelSecurity.Encrypt(httpClientConnection.SentContent, encryptedContent);
                        httpClientConnection.SentContent = encryptedContent;
                    }

                    // prepare the final version of the content
                    GenuineChunkedStream resultStream = new GenuineChunkedStream(false);
                    BinaryWriter binaryWriter = new BinaryWriter(resultStream);
                    HttpMessageCoder.WriteRequestHeader(binaryWriter, MessageCoder.PROTOCOL_VERSION, httpClientConnection.GenuineConnectionType, httpClientConnection.HostId, HttpPacketType.Usual, httpClientConnection.SendSequenceNo, httpClientConnection.ConnectionName, httpClientConnection.Remote.LocalHostUniqueIdentifier);
                    if (httpClientConnection.SentContent.CanSeek)
                        resultStream.WriteStream(httpClientConnection.SentContent);
                    else
                        GenuineUtility.CopyStreamToStream(httpClientConnection.SentContent, resultStream);

                    httpClientConnection.SentContent = resultStream;
                }
                else
                {
                    httpClientConnection.SentContent.Position = 0;
                }

                // try to send it
                httpClientConnection.Sender = httpClientConnection.InitializeRequest(true, httpClientConnection._keepalive);
                httpClientConnection.Sender.ContentLength = httpClientConnection.SentContent.Length;

                this.LowLevel_InitiateHttpWebRequest(httpClientConnection.Sender, httpClientConnection.SentContent, httpClientConnection.SenderClosed, true, asyncCallback, httpClientConnection);
            }
            catch(Exception ex)
            {
                // sending failed, rollback the entire operation

                // LOG:
                if ( binaryLogWriter != null && binaryLogWriter[LogCategory.Connection] > 0 )
                {
                    binaryLogWriter.WriteEvent(LogCategory.Connection, "HttpClientConnectionManager.LowLevel_Sender_Send",
                        LogMessageType.AsynchronousSendingFinished, ex, null, httpClientConnection.Remote, null,
                        GenuineUtility.CurrentThreadId, Thread.CurrentThread.Name, null, null,
                        httpClientConnection.DbgConnectionId, 0, 0, 0, null, null, null, null,
                        "Error occurred while sending a message to the remote host.");
                }

                this.StartReestablishingIfNecessary(httpClientConnection, ex, true);
            }
        }