private void ReadFrameComplete(IAsyncResult transportResult)
{
do
{
if (!(transportResult.AsyncState is WorkerAsyncResult))
{
NetEventSource.Fail(this, $"The state expected to be WorkerAsyncResult, received {transportResult}.");
}
WorkerAsyncResult workerResult = (WorkerAsyncResult)transportResult.AsyncState;
int bytesRead = _transport.EndRead(transportResult);
workerResult.Offset += bytesRead;
if (!(workerResult.Offset <= workerResult.End))
{
NetEventSource.Fail(this, $"WRONG: offset - end = {workerResult.Offset - workerResult.End}");
}
if (bytesRead <= 0)
{
// (by design) This indicates the stream has receives EOF
// If we are in the middle of a Frame - fail, otherwise - produce EOF
object result = null;
if (!workerResult.HeaderDone && workerResult.Offset == 0)
{
result = (object)-1;
}
else
{
result = new System.IO.IOException(SR.net_frame_read_io);
}
workerResult.InvokeCallback(result);
return;
}
if (workerResult.Offset >= workerResult.End)
{
if (!workerResult.HeaderDone)
{
workerResult.HeaderDone = true;
// This indicates the header has been read successfully
_curReadHeader.CopyFrom(workerResult.Buffer, 0, _readVerifier);
int payloadSize = _curReadHeader.PayloadSize;
if (payloadSize < 0)
{
// Let's call user callback and he call us back and we will throw
workerResult.InvokeCallback(new System.IO.IOException(SR.Format(SR.net_frame_read_size)));
}
if (payloadSize == 0)
{
// report empty frame (NOT eof!) to the caller, he might be interested in
workerResult.InvokeCallback(0);
return;
}
if (payloadSize > _curReadHeader.MaxMessageSize)
{
throw new InvalidOperationException(SR.Format(SR.net_frame_size,
_curReadHeader.MaxMessageSize.ToString(NumberFormatInfo.InvariantInfo),
payloadSize.ToString(NumberFormatInfo.InvariantInfo)));
}
// Start reading the remaining frame data (note header does not count).
byte[] frame = new byte[payloadSize];
// Save the ref of the data block
workerResult.Buffer = frame;
workerResult.End = frame.Length;
workerResult.Offset = 0;
// Transport.BeginRead below will pickup those changes.
}
else
{
workerResult.HeaderDone = false; // Reset for optional object reuse.
workerResult.InvokeCallback(workerResult.End);
return;
}
}
// This means we need more data to complete the data block.
transportResult = _transport.BeginRead(workerResult.Buffer, workerResult.Offset, workerResult.End - workerResult.Offset,
_readFrameCallback, workerResult);
} while (transportResult.CompletedSynchronously);
}