/// <summary>
        /// Establishes a connection to the remote host.
        /// </summary>
        /// <param name="remote">The remote host.</param>
        /// <param name="connectionName">The name of the connection.</param>
        /// <returns>The established connection.</returns>
        internal XHttpConnection Pool_EstablishPersistentConnection(HostInformation remote, string connectionName)
        {
            BinaryLogWriter binaryLogWriter = this.ITransportContext.BinaryLogWriter;

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

            //			remoteUri = null;
            Stream inputStream = null;

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

            XHttpConnection xHttpConnection = this.Pool_CreateConnection(remote, remote.Url, true, connectionName);
            xHttpConnection.Sender.CheckConnectionStatus();

            // get connection-level SS
            string connectionLevelSSName = this.ITransportContext.IParameterProvider[GenuineParameter.SecuritySessionForPersistentConnections] as string;
            if (connectionLevelSSName != null)
            {
                xHttpConnection.Sender.ConnectionLevelSecurity = this.ITransportContext.IKeyStore.GetKey(connectionLevelSSName).CreateSecuritySession(connectionLevelSSName, null);
                xHttpConnection.Listener.ConnectionLevelSecurity = this.ITransportContext.IKeyStore.GetKey(connectionLevelSSName).CreateSecuritySession(connectionLevelSSName, null);
            }

            // establish it
            // gather the output stream
            Stream senderInputClsseStream = Stream.Null;
            Stream listenerInputClsseStream = Stream.Null;
            GenuineChunkedStream clsseStream = null;
            bool resendContent = false;

            for (;;)
            {
                bool clssShouldBeSent = false;

                if (GenuineUtility.IsTimeoutExpired(timeout))
                    throw GenuineExceptions.Get_Connect_CanNotConnectToRemoteHost(remote.Url, "Timeout expired.");

                if (! resendContent)
                {
                    // the CLSS content
                    clsseStream = new GenuineChunkedStream();
                    BinaryWriter binaryWriter = new BinaryWriter(clsseStream);
                    clssShouldBeSent = xHttpConnection.GatherContentOfConnectionLevelSecuritySessions(senderInputClsseStream,
                        listenerInputClsseStream, clsseStream, xHttpConnection.Sender.ConnectionLevelSecurity, xHttpConnection.Listener.ConnectionLevelSecurity);
                }
                else
                    clsseStream.Position = 0;

                try
                {
                    // send the request
                    this.LowLevel_SendHttpContent(timeout, null, clsseStream, null, null, xHttpConnection.Sender, GenuineConnectionType.Persistent,
                        HttpPacketType.Establishing, resendContent, true, false, false);

                    // parse the response
                    byte protocolVersion;
                    string remoteUri;
                    int sequenceNo;
                    GenuineConnectionType genuineConnectionType;
                    Guid connectionId;
                    HttpPacketType httpPacketType;
                    string receivedConnectionName;
                    int remoteHostUniqueIdentifier;
                    inputStream = this.LowLevel_ParseHttpContent(timeout, true, xHttpConnection.Sender.Socket,
                        xHttpConnection.Sender.ConnectionLevelSecurity, out protocolVersion, out remoteUri, out sequenceNo, out genuineConnectionType, out connectionId, out httpPacketType, out receivedConnectionName, out remoteHostUniqueIdentifier);

                    // check received items
                    if ( httpPacketType == HttpPacketType.SenderError )
                        throw GenuineExceptions.Get_Connect_CanNotConnectToRemoteHost(remote.ToString(), this.LowLevel_ParseException(inputStream).Message);

                    if ( httpPacketType != HttpPacketType.Establishing )
                        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
                    remote.UpdateUri(remoteUri, remoteHostUniqueIdentifier);

                    // receive CLSS data
                    using (inputStream)
                    {
                        BinaryReader binaryReader = new BinaryReader(inputStream);

                        int writtenSize = binaryReader.ReadInt32();
                        senderInputClsseStream = new GenuineChunkedStream(true);
                        GenuineUtility.CopyStreamToStream(inputStream, senderInputClsseStream, writtenSize);

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

                    resendContent = false;
                }
                catch(Exception ex)
                {
                    // LOG:
                    if ( binaryLogWriter != null && binaryLogWriter[LogCategory.Connection] > 0 )
                    {
                        binaryLogWriter.WriteEvent(LogCategory.Connection, "XHttpConnectionManager.Pool_EstablishPersistentConnection",
                            LogMessageType.SynchronousSendingFinished, ex, null, remote, null,
                            GenuineUtility.CurrentThreadId, Thread.CurrentThread.Name,
                            xHttpConnection.Sender.ConnectionLevelSecurity, connectionLevelSSName,
                            xHttpConnection.DbgConnectionId, 0, 0, 0, null, null, null, null,
                            "Cannot send the establishing packet to the remote host.");
                    }

                    if (ConnectionManager.IsExceptionCritical(ex as OperationException))
                        throw;

                    // force content resending
                    resendContent = true;
                    xHttpConnection.Sender.CloseSocket();
                    continue;
                }

                if (xHttpConnection.Sender.ConnectionLevelSecurity != null && ! xHttpConnection.Sender.ConnectionLevelSecurity.IsEstablished)
                    continue;
                if (xHttpConnection.Listener.ConnectionLevelSecurity != null && ! xHttpConnection.Listener.ConnectionLevelSecurity.IsEstablished)
                    continue;
                if (clssShouldBeSent)
                    continue;

                break;
            }

            // LOG:
            if ( binaryLogWriter != null && binaryLogWriter[LogCategory.HostInformation] > 0 )
            {
                binaryLogWriter.WriteHostInformationEvent("XHttpConnectionManager.Pool_EstablishPersistentConnection",
                    LogMessageType.HostInformationCreated, null, remote,
                    GenuineUtility.CurrentThreadId, Thread.CurrentThread.Name,
                    xHttpConnection.Sender.ConnectionLevelSecurity, connectionLevelSSName,
                    xHttpConnection.DbgConnectionId,
                    "HostInformation is ready for actions.");
            }

            xHttpConnection.Remote = remote;
            xHttpConnection.Sender.MarkAsAvailable();

            // LOG:
            if ( binaryLogWriter != null && binaryLogWriter[LogCategory.Connection] > 0 )
            {
                binaryLogWriter.WriteEvent(LogCategory.Connection, "XHttpConnectionManager.Pool_EstablishPersistentConnection",
                    LogMessageType.ConnectionEstablished, null, null, xHttpConnection.Remote, null,
                    GenuineUtility.CurrentThreadId, Thread.CurrentThread.Name,
                    xHttpConnection.Sender.ConnectionLevelSecurity, connectionLevelSSName,
                    xHttpConnection.DbgConnectionId, (int) GenuineConnectionType.Persistent, 0, 0, this.GetType().Name, remote.LocalPhysicalAddress.ToString(), remote.PhysicalAddress.ToString(), null,
                    "The connection to the remote host is established.");
            }

            // try to start the listener
            try
            {
                this.Pool_HandleClientConnection(xHttpConnection.Listener);
            }
            catch(Exception ex)
            {
                this.ConnectionFailed(ex, xHttpConnection, xHttpConnection.Listener);
            }

            return xHttpConnection;
        }
        /// <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;
        }