private IAsyncResult InternalWrite(bool async, byte[] buffer, int offset, int size, AsyncCallback callback, object state ) {
//
// if we have a stream error, or we've already shut down this socket
// then we must prevent new BeginRead/BeginWrite's from getting
// submited to the socket, since we've already closed the stream.
//
if (ErrorInStream) {
GlobalLog.Print("ConnectStream#" + ValidationHelper.HashString(this) + "::InternalWrite() throwing:" + m_ErrorException.ToString());
throw m_ErrorException;
}
if (IsClosed && !IgnoreSocketErrors) {
GlobalLog.Print("ConnectStream#" + ValidationHelper.HashString(this) + "::InternalWrite() throwing");
throw new WebException(
NetRes.GetWebStatusString("net_requestaborted", WebExceptionStatus.ConnectionClosed),
WebExceptionStatus.ConnectionClosed);
}
if (m_Request.Aborted && !IgnoreSocketErrors) {
GlobalLog.Print("ConnectStream#" + ValidationHelper.HashString(this) + "::InternalWrite() throwing");
throw new WebException(
NetRes.GetWebStatusString("net_requestaborted", WebExceptionStatus.RequestCanceled),
WebExceptionStatus.RequestCanceled);
}
int nesting = Interlocked.CompareExchange(ref m_CallNesting, Nesting.IoInProgress, Nesting.Idle);
GlobalLog.Print((async?"Async ":"") + "InternalWrite() In: callNesting : " + nesting.ToString());
if (nesting != Nesting.Idle && nesting != Nesting.Closed)
{
throw new NotSupportedException(SR.GetString(SR.net_no_concurrent_io_allowed));
}
//
// buffer data to the ScatterGatherBuffers
// regardles of chunking, we buffer the data as if we were not chunking
// and on resubmit, we don't bother chunking.
//
if (BufferedData!=null && size != 0 && (m_Request.ContentLength != 0 || !IsPostStream || !m_Request.NtlmKeepAlive)) {
//
// if we don't need to, we shouldn't send data on the wire as well
// but in this case we gave a stream to the user so we have transport
//
BufferedData.Write(buffer, offset, size);
}
LazyAsyncResult asyncResult = null;
bool completeSync = false;
try
{
if (size == 0 || BufferOnly || m_SuppressWrite || IgnoreSocketErrors)
{
//
// We're not putting this data on the wire, then we're done
//
if(m_SuppressWrite && m_BytesLeftToWrite > 0 && size > 0)
{
m_BytesLeftToWrite -= size;
}
GlobalLog.Print("ConnectStream#" + ValidationHelper.HashString(this) + "::InternalWrite() swallowing: size==0 || BufferOnly || IgnoreSocketErrors= " + (size==0) + BufferOnly + IgnoreSocketErrors);
if (async) {
asyncResult = new LazyAsyncResult(this, state, callback);
completeSync = true;
}
return asyncResult;
}
else if (WriteChunked) {
//
// We're chunking. Write the chunk header out first,
// then the data, then a CRLF.
// for this we'll use BeginMultipleSend();
//
int chunkHeaderOffset = 0;
byte[] chunkHeaderBuffer = GetChunkHeader(size, out chunkHeaderOffset);
BufferOffsetSize[] buffers;
GlobalLog.Print("ConnectStream#" + ValidationHelper.HashString(this) + "::InternalWrite() m_ErrorResponseStatus:" + m_ErrorResponseStatus);
if (m_ErrorResponseStatus) {
//if we already got a (>200) response, then just terminate chunking and
//switch to simple buffering (if any)
GlobalLog.Print("ConnectStream#" + ValidationHelper.HashString(this) + "::InternalWrite() setting m_IgnoreSocketErrors to True (was:" + m_IgnoreSocketErrors + ") sending chunk terminator");
m_IgnoreSocketErrors = true;
buffers = new BufferOffsetSize[1];
buffers[0] = new BufferOffsetSize(NclConstants.ChunkTerminator, 0, NclConstants.ChunkTerminator.Length, false);
}
else {
buffers = new BufferOffsetSize[3];
buffers[0] = new BufferOffsetSize(chunkHeaderBuffer, chunkHeaderOffset, chunkHeaderBuffer.Length - chunkHeaderOffset, false);
buffers[1] = new BufferOffsetSize(buffer, offset, size, false);
buffers[2] = new BufferOffsetSize(NclConstants.CRLF, 0, NclConstants.CRLF.Length, false);
}
asyncResult = (async) ? new NestedMultipleAsyncResult(this, state, callback, buffers) : null;
//
// after setting up the buffers and error checking do the async Write Call
//
try {
if (async) {
m_Connection.BeginMultipleWrite(buffers, m_WriteCallbackDelegate, asyncResult);
}
else {
SafeSetSocketTimeout(SocketShutdown.Send);
m_Connection.MultipleWrite(buffers);
}
}
catch (Exception exception) {
// IgnoreSocketErrors can be set at any time - need to check it again.
if (IgnoreSocketErrors && !NclUtilities.IsFatal(exception))
{
GlobalLog.Print("ConnectStream#" + ValidationHelper.HashString(this) + "::InternalWrite() swallowing: IgnoreSocketErrors set after throw.");
if (async)
{
completeSync = true;
}
return asyncResult;
}
if (m_Request.Aborted && (exception is IOException || exception is ObjectDisposedException)) {
GlobalLog.Print("ConnectStream#" + ValidationHelper.HashString(this) + "::InternalWrite() throwing");
throw new WebException(
NetRes.GetWebStatusString("net_requestaborted", WebExceptionStatus.RequestCanceled),
WebExceptionStatus.RequestCanceled);
}
nesting = Nesting.InError;
if (NclUtilities.IsFatal(exception))
{
m_ErrorResponseStatus = false;
IOError(exception);
throw;
}
if (m_ErrorResponseStatus) {
// We already got a error response, hence server could drop the connection,
// Here we are recovering for future (optional) resubmit ...
m_IgnoreSocketErrors = true;
GlobalLog.Print("ConnectStream#" + ValidationHelper.HashString(this) + "::InternalWrite() IGNORE write fault");
if (async)
{
completeSync = true;
}
}
else {
// Note we could swallow this since receive callback is already posted and
// should give us similar failure
IOError(exception);
GlobalLog.Print("ConnectStream#" + ValidationHelper.HashString(this) + "::InternalWrite() throwing:" + exception.ToString());
throw;
}
}
GlobalLog.Print("ConnectStream#" + ValidationHelper.HashString(this) + "::InternalWrite chunked");
return asyncResult;
}
else {
//
// We're not chunking. See if we're sending too much; if not,
// go ahead and write it.
//
asyncResult = (async) ? new NestedSingleAsyncResult(this, state, callback, buffer, offset, size) : null;
if (BytesLeftToWrite != -1) {
//
// but only check if we aren't writing to an unknown content-length size,
// as we can be buffering.
//
if (BytesLeftToWrite < (long)size) {
//
// writing too much data.
//
GlobalLog.Print("ConnectStream#" + ValidationHelper.HashString(this) + "::InternalWrite()");
throw new ProtocolViolationException(SR.GetString(SR.net_entitytoobig));
}
if (!async) {
//
// Otherwise update our bytes left to send and send it.
//
m_BytesLeftToWrite -= (long)size;
}
}
//
// After doing, the m_WriteByte size calculations, and error checking
// here doing the async Write Call
//
try {
if (async) {
if(m_Request.ContentLength == 0 && IsPostStream) {
m_BytesLeftToWrite -=size;
completeSync = true;
}
else{
m_BytesAlreadyTransferred = size;
m_Connection.BeginWrite(buffer, offset, size, m_WriteCallbackDelegate, asyncResult);
}
}
else {
SafeSetSocketTimeout(SocketShutdown.Send);
//If we are doing the ntlm handshake, contentlength
//could be 0 for the first part, even if there is data
//to write.
if (m_Request.ContentLength != 0 || !IsPostStream || !m_Request.NtlmKeepAlive) {
m_Connection.Write(buffer, offset, size);
}
}
}
catch (Exception exception) {
// IgnoreSocketErrors can be set at any time - need to check it again.
if (IgnoreSocketErrors && !NclUtilities.IsFatal(exception))
{
GlobalLog.Print("ConnectStream#" + ValidationHelper.HashString(this) + "::InternalWrite() swallowing: IgnoreSocketErrors set after throw.");
if (async)
{
completeSync = true;
}
return asyncResult;
}
if (m_Request.Aborted && (exception is IOException || exception is ObjectDisposedException)) {
GlobalLog.Print("ConnectStream#" + ValidationHelper.HashString(this) + "::InternalWrite() throwing");
throw new WebException(
NetRes.GetWebStatusString("net_requestaborted", WebExceptionStatus.RequestCanceled),
WebExceptionStatus.RequestCanceled);
}
nesting = Nesting.InError;
if (NclUtilities.IsFatal(exception))
{
m_ErrorResponseStatus = false;
IOError(exception);
throw;
}
if (m_ErrorResponseStatus) {
// We already got a error response, hence server could drop the connection,
// Here we are recovering for future (optional) resubmit ...
m_IgnoreSocketErrors = true;
GlobalLog.Print("ConnectStream#" + ValidationHelper.HashString(this) + "::InternWrite() IGNORE write fault");
if (async)
{
completeSync = true;
}
}
else {
// Note we could swallow this since receive callback is already posted and
// should give us similar failure
IOError(exception);
GlobalLog.Print("ConnectStream#" + ValidationHelper.HashString(this) + "::InternalWrite() throwing:" + exception.ToString());
throw;
}
}
GlobalLog.Print("ConnectStream#" + ValidationHelper.HashString(this) + "::InternalWrite");
return asyncResult;
}
}
finally {
if (!async || nesting == Nesting.InError || completeSync)
{
nesting = Interlocked.CompareExchange(ref m_CallNesting, (nesting == Nesting.InError? Nesting.InError: Nesting.Idle), Nesting.IoInProgress);
GlobalLog.Print("InternalWrite() Out callNesting: " + nesting.ToString());
if (nesting == Nesting.Closed)
{
//send closing bytes
ResumeInternalClose(asyncResult);
}
else if (completeSync && asyncResult != null)
{
asyncResult.InvokeCallback();
}
}
}
}