private void ReceiveCommandResponseCallback(ReceiveState state, int bytesRead)
{
// completeLength will be set to a nonnegative number by CheckValid if the response is complete:
// it will set completeLength to the length of a complete response.
int completeLength = -1;
while (true)
{
int validThrough = state.ValidThrough; // passed to checkvalid
// If we have a Buffered response (ie data was received with the last response that was past the end of that response)
// deal with it as if we had just received it now instead of actually doing another receive
if (_buffer.Length > 0)
{
// Append the string we got from the buffer, and flush it out.
state.Resp.StatusBuffer.Append(_buffer);
_buffer = string.Empty;
// invoke checkvalid.
if (!CheckValid(state.Resp, ref validThrough, ref completeLength))
{
throw GenerateException(SR.net_ftp_protocolerror, WebExceptionStatus.ServerProtocolViolation, null);
}
}
else // we did a Connection.BeginReceive. Note that in this case, all bytes received are in the receive buffer (because bytes from
// the buffer were transferred there if necessary
{
// this indicates the connection was closed.
if (bytesRead <= 0)
{
throw GenerateException(SR.net_ftp_protocolerror, WebExceptionStatus.ServerProtocolViolation, null);
}
// decode the bytes in the receive buffer into a string, append it to the statusbuffer, and invoke checkvalid.
// Decoder automatically takes care of caching partial codepoints at the end of a buffer.
char[] chars = new char[_decoder.GetCharCount(state.Buffer, 0, bytesRead)];
int numChars = _decoder.GetChars(state.Buffer, 0, bytesRead, chars, 0, false);
string szResponse = new string(chars, 0, numChars);
state.Resp.StatusBuffer.Append(szResponse);
if (!CheckValid(state.Resp, ref validThrough, ref completeLength))
{
throw GenerateException(SR.net_ftp_protocolerror, WebExceptionStatus.ServerProtocolViolation, null);
}
// If the response is complete, then determine how many characters are left over...these bytes need to be set into Buffer.
if (completeLength >= 0)
{
int unusedChars = state.Resp.StatusBuffer.Length - completeLength;
if (unusedChars > 0)
{
_buffer = szResponse.Substring(szResponse.Length - unusedChars, unusedChars);
}
}
}
// Now, in general, if the response is not complete, update the "valid through" length for the efficiency of checkValid,
// and perform the next receive.
// Note that there may NOT be bytes in the beginning of the receive buffer (even if there were partial characters left over after the
// last encoding), because they get tracked in the Decoder.
if (completeLength < 0)
{
state.ValidThrough = validThrough;
try
{
if (_isAsync)
{
BeginRead(state.Buffer, 0, state.Buffer.Length, s_readCallbackDelegate, state);
return;
}
else
{
bytesRead = Read(state.Buffer, 0, state.Buffer.Length);
if (bytesRead == 0)
CloseSocket();
continue;
}
}
catch (IOException)
{
MarkAsRecoverableFailure();
throw;
}
catch
{
throw;
}
}
// The response is completed
break;
}
// Otherwise, we have a complete response.
string responseString = state.Resp.StatusBuffer.ToString();
state.Resp.StatusDescription = responseString.Substring(0, completeLength);
// Set the StatusDescription to the complete part of the response. Note that the Buffer has already been taken care of above.
if (NetEventSource.IsEnabled) NetEventSource.Info(this, $"Received response: {responseString.Substring(0, completeLength - 2)}");
if (_isAsync)
{
// Tell who is listening what was received.
if (state.Resp != null)
{
_currentResponseDescription = state.Resp;
}
Stream stream = null;
if (PostReadCommandProcessing(ref stream))
return;
ContinueCommandPipeline();
}
}
} // class CommandStream