private void ComunicateWithTomcat()
{
int numOfBytesReceived = 0;
byte[] receivedPacketBuffer = new byte[BonCodeAJP13Settings.MAX_BONCODEAJP13_PACKET_LENGTH];
byte[] notProcessedBytes = null;
int sendPacketCount = 0;
p_IsLastPacket = false;
int listenAfterPacket = 2; //this is standard behavior, we sent first two packets for posts then listen
if (BonCodeAJP13Settings.BONCODEAJP13_ADOBE_SUPPORT || p_IsChunked)
{
listenAfterPacket = 1;
}
//send packages. If multiple forward requests (i.e. form data or files) there is a different behavior expected
if (p_PacketsToSend.Count > 1)
{
bool delayWrite = false;
//TODO: move this loop to a function by itself so we can reuse it.
foreach (Object oIterate in p_PacketsToSend)
{
//we will continue sending all packets in queue unless tomcat sends us End Comm package
if (!p_IsLastPacket)
{
sendPacketCount++;
BonCodeAJP13Packet sendPacket = oIterate as BonCodeAJP13Packet; //only objects derived from this class should be in the collection
//send first packet immediatly (most likely a post), unless there is a delay write indicator (will be set during analysis)
if (!delayWrite)
{
p_NetworkStream.Write(sendPacket.GetDataBytes(), 0, sendPacket.PacketLength);
}
else
{
delayWrite = false;
}
//log packet
if (p_Logger != null) p_Logger.LogPacket(sendPacket, false, BonCodeAJP13LogLevels.BONCODEAJP13_LOG_HEADERS);
//after the second packet (first packet for Adobe) in a packet collection we have to listen and receive a TomcatGetBodyChunk
if (sendPacketCount >= listenAfterPacket)
{
bool subRoutineReadInProgress = true;
int sanityCheck = 0;
while (subRoutineReadInProgress)
{
sanityCheck++;
try {
numOfBytesReceived = p_NetworkStream.Read(receivedPacketBuffer, 0, receivedPacketBuffer.Length);
notProcessedBytes = AnalyzePackage(ref delayWrite, receivedPacketBuffer, true); //no flush processing during sending of data
//we expect a 7 byte response except for the last package record, if not record a warning
if (sendPacketCount != p_PacketsToSend.Count && numOfBytesReceived > 7)
{
if (p_Logger != null) p_Logger.LogMessageAndType("Incorrect response received from Tomcat", "warning", BonCodeAJP13LogLevels.BONCODEAJP13_LOG_BASIC);
}
//check whether we are finished with subroutine
if (delayWrite)
{
delayWrite = false; // assume that we will exit with next loop unless Analyze package resets this to true;
}
else
{
subRoutineReadInProgress = false;
}
} catch (Exception e)
{
if (p_Logger != null)
{
p_Logger.LogMessageAndType("Stream reading problem (1). Null packet received in stream.", "warning", BonCodeAJP13LogLevels.BONCODEAJP13_LOG_BASIC);
p_Logger.LogException(e);
}
}
//in case we go in cycle without receiving data
if (sanityCheck > 500)
{
if (p_Logger != null) p_Logger.LogMessageAndType("SubRoutine Communication Process suspicious iterations (>500). This indicates problems with communication to tomcat", "warning", BonCodeAJP13LogLevels.BONCODEAJP13_LOG_BASIC);
subRoutineReadInProgress = false;
}
}
}
}
else
{
break;
}
}
// if the last received message from tomcat is "GET_BODY_CHUNK" we need to send terminator package
// We have to do a complex type multi-comparison rather than using just one 'is' operator since c# seems to have an issue determining class type in collection
if (p_PacketsReceived[p_PacketsReceived.Count - 1] is TomcatSendBodyChunk || p_PacketsReceived[p_PacketsReceived.Count - 1].GetType() == typeof(TomcatGetBodyChunk) || p_PacketsReceived[p_PacketsReceived.Count - 1] is BonCodeAJP13.TomcatPackets.TomcatSendBodyChunk) {
BonCodeAJP13Packet sendPacket = new BonCodeAJP13ForwardRequest(new byte[] { }); //create terminator (empty) package
p_NetworkStream.Write(sendPacket.GetDataBytes(), 0, sendPacket.PacketLength);
//log packet as it is sent
if (p_Logger != null) p_Logger.LogPacket(sendPacket, false, BonCodeAJP13LogLevels.BONCODEAJP13_LOG_HEADERS);
}
}
else if (p_PacketsToSend.Count == 1)
{
//send package
BonCodeAJP13Packet sendPacket = p_PacketsToSend[0] as BonCodeAJP13Packet; //only objects derived from this class should be in the collection
p_NetworkStream.Write(sendPacket.GetDataBytes(), 0, sendPacket.PacketLength);
//log each packet as it is sent
if (p_Logger != null) p_Logger.LogPacket(sendPacket, false, BonCodeAJP13LogLevels.BONCODEAJP13_LOG_HEADERS);
}
else
{
//nothing to do
CloseConnectionNoError("Nothing to send. Closing Connection.");
return;
}
//switch into Receiving Mode. Receive the TcpServer.response.
if (!p_IsLastPacket)
{
try
{
p_NetworkStream.Read(receivedPacketBuffer, 0, 0); //call empty read so we block this thread until we receive a response or we time out
} catch (Exception e)
{
p_Logger.LogException(e);
}
}
numOfBytesReceived = 0;
try
{
int readCount = 0;
while (p_NetworkStream.CanRead && !p_AbortConnection && !p_IsLastPacket)
{
//check to see whether we need to send extra termination package
if (p_SendTermPacket)
{
p_SendTermPacket = false;
BonCodeAJP13ForwardRequest terminatorFR = new BonCodeAJP13ForwardRequest(new byte[] { });
p_NetworkStream.Write(terminatorFR.GetDataBytes(), 0, terminatorFR.PacketLength);
}
//clear reading array
Array.Clear(receivedPacketBuffer,0,receivedPacketBuffer.Length);
//flush detection by ticks check if we we have no data on channel
//check whether we need monitor for tomcat flush signs
if (!p_NetworkStream.DataAvailable && BonCodeAJP13Settings.BONCODEAJP13_AUTOFLUSHDETECTION_TICKS > 0)
{
long elapsedTicks = p_StopWatch.ElapsedTicks;
p_TickDelta = elapsedTicks - p_LastTick;
p_LastTick = elapsedTicks;
if (p_TickDelta > BonCodeAJP13Settings.BONCODEAJP13_AUTOFLUSHDETECTION_TICKS)
{
//flush has been detected set the flag. We should flush after this receiveBuffer has been processed.
//no flush is needed if we see end marker during receiveBuffer processing since all data would have been transferred
p_IsFlush = true;
p_TimeFlushOccurred = true;
}
}
//read incoming packets until timeout or last package has been received.
readCount++;
try
{
//read or wait on next package
numOfBytesReceived = p_NetworkStream.Read(receivedPacketBuffer, 0, receivedPacketBuffer.Length);
//flush detection by bytes -- in case where time flush is also defined (ticks>0) we will wait until a time flush occurs (p_TimeFlushOccurred)
//before we trigger a byte flushes
if (BonCodeAJP13Settings.BONCODEAJP13_AUTOFLUSHDETECTION_BYTES > 0 &&
(BonCodeAJP13Settings.BONCODEAJP13_AUTOFLUSHDETECTION_TICKS == 0 ||
(BonCodeAJP13Settings.BONCODEAJP13_AUTOFLUSHDETECTION_TICKS > 0 && p_TimeFlushOccurred))
)
{
p_BytesInBuffer = p_BytesInBuffer + numOfBytesReceived;
if (p_BytesInBuffer > BonCodeAJP13Settings.BONCODEAJP13_AUTOFLUSHDETECTION_BYTES)
{
p_IsFlush = true;
p_BytesInBuffer = 0;
}
}
//analyze packet so far (adjust bytes from Receiving buffer):combine notProcessed with new Read bytes into new Received buffer if needed
if (notProcessedBytes != null)
{
//create tempArray that contains new set of bytes to be send a combination of newly received bytes as well as bytes that we were not able to process yet
byte[] tempArray = new byte[numOfBytesReceived + notProcessedBytes.Length];
Array.Copy(notProcessedBytes, 0, tempArray, 0, notProcessedBytes.Length);
Array.Copy(receivedPacketBuffer, 0, tempArray, notProcessedBytes.Length, numOfBytesReceived);
notProcessedBytes = AnalyzePackage(tempArray);
}
else
{
//send bytes we received for analysis
byte[] tempArray = new byte[numOfBytesReceived];
Array.Copy(receivedPacketBuffer, 0, tempArray, 0, numOfBytesReceived);
notProcessedBytes = AnalyzePackage(tempArray);
}
} catch (Exception e)
{
p_AbortConnection = true;
p_Logger.LogMessageAndType("Stream reading problem (2)(" + readCount.ToString() + "), you may have shutdown Tomcat unexpectedly", "warning", BonCodeAJP13LogLevels.BONCODEAJP13_LOG_BASIC);
//p_Logger.LogException(e);
}
}
//check error condition that tomcat produces sometimes where additional data is sent after end_transmission has been indicated
int sanityCheck = 0;
while (p_NetworkStream.DataAvailable && sanityCheck < 100)
{
//we need to clear the tcp pipe so the next request does not pick up data we will do this up to 100 times and write warning
try
{
numOfBytesReceived = p_NetworkStream.Read(receivedPacketBuffer, 0, receivedPacketBuffer.Length);
} catch
{
//do nothing here
}
if (sanityCheck == 0)
{
if (p_Logger != null) p_Logger.LogMessageAndType("extra data after transmission-end from tomcat" ,"warning", BonCodeAJP13LogLevels.BONCODEAJP13_LOG_HEADERS);
}
sanityCheck++;
}
}
catch (System.IO.IOException ex)
{
ConnectionError("Server Connection is closing, Read timeout reached and no tomcat activity was detected.", "TimeOut");
if (p_Logger != null) p_Logger.LogException(ex);
return;
}
if (p_AbortConnection)
{
ConnectionError("Server Connection was aborted" , "failed");
return;
}
if (numOfBytesReceived == 0)
{
// Nothing received from tomcat, log warning
p_Logger.LogMessageAndType("Empty packet received from tomcat", "warning", BonCodeAJP13LogLevels.BONCODEAJP13_LOG_BASIC);
//return;
}
if (p_IsLastPacket == true)
{
// keep alive timer needs reset (we are maintaining connection but resetting the timer
if (p_KeepAliveTimer != null)
{
p_KeepAliveTimer.Stop();
p_KeepAliveTimer.Start();
}
CloseConnectionNoError();
}
else
{
//do nothing for now
}
}