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