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