public override IAsyncResult BeginWrite(byte[] buffer, int offset, int size, AsyncCallback callback, object state)
{
if (NetEventSource.IsEnabled) NetEventSource.Info(this, "buffer.Length:" + buffer.Length + " size:" + size + " offset:" + offset);
if (buffer == null)
{
throw new ArgumentNullException(nameof(buffer));
}
if (offset < 0 || offset > buffer.Length)
{
throw new ArgumentOutOfRangeException(nameof(offset));
}
if (size < 0 || size > buffer.Length - offset)
{
throw new ArgumentOutOfRangeException(nameof(size));
}
Interop.HttpApi.HTTP_FLAGS flags = ComputeLeftToWrite();
if (_closed || (size == 0 && _leftToWrite != 0))
{
if (NetEventSource.IsEnabled) NetEventSource.Exit(this);
HttpResponseStreamAsyncResult result = new HttpResponseStreamAsyncResult(this, state, callback);
result.InvokeCallback((uint)0);
return result;
}
if (_leftToWrite >= 0 && size > _leftToWrite)
{
throw new ProtocolViolationException(SR.net_entitytoobig);
}
uint statusCode;
uint bytesSent = 0;
flags |= _leftToWrite == size ? Interop.HttpApi.HTTP_FLAGS.NONE : Interop.HttpApi.HTTP_FLAGS.HTTP_SEND_RESPONSE_FLAG_MORE_DATA;
bool sentHeaders = _httpContext.Response.SentHeaders;
HttpResponseStreamAsyncResult asyncResult = new HttpResponseStreamAsyncResult(this, state, callback, buffer, offset, size, _httpContext.Response.BoundaryType == BoundaryType.Chunked, sentHeaders, _httpContext.RequestQueueBoundHandle);
// Update m_LeftToWrite now so we can queue up additional BeginWrite's without waiting for EndWrite.
UpdateAfterWrite((uint)((_httpContext.Response.BoundaryType == BoundaryType.Chunked) ? 0 : size));
try
{
if (!sentHeaders)
{
statusCode = _httpContext.Response.SendHeaders(null, asyncResult, flags, false);
}
else
{
if (NetEventSource.IsEnabled) NetEventSource.Info(this, "Calling Interop.HttpApi.HttpSendResponseEntityBody");
statusCode =
Interop.HttpApi.HttpSendResponseEntityBody(
_httpContext.RequestQueueHandle,
_httpContext.RequestId,
(uint)flags,
asyncResult.dataChunkCount,
asyncResult.pDataChunks,
&bytesSent,
SafeLocalAllocHandle.Zero,
0,
asyncResult._pOverlapped,
null);
if (NetEventSource.IsEnabled) NetEventSource.Info(this, "Call to Interop.HttpApi.HttpSendResponseEntityBody returned:" + statusCode);
}
}
catch (Exception e)
{
if (NetEventSource.IsEnabled) NetEventSource.Error(this, e.ToString());
asyncResult.InternalCleanup();
_closed = true;
_httpContext.Abort();
throw;
}
if (statusCode != Interop.HttpApi.ERROR_SUCCESS && statusCode != Interop.HttpApi.ERROR_IO_PENDING)
{
asyncResult.InternalCleanup();
if (_httpContext.Listener.IgnoreWriteExceptions && sentHeaders)
{
if (NetEventSource.IsEnabled) NetEventSource.Info(this, "BeginWrite() Suppressing error");
}
else
{
Exception exception = new HttpListenerException((int)statusCode);
if (NetEventSource.IsEnabled) NetEventSource.Error(this, exception.ToString());
_closed = true;
_httpContext.Abort();
throw exception;
}
}
if (statusCode == Interop.HttpApi.ERROR_SUCCESS && HttpListener.SkipIOCPCallbackOnSuccess)
{
// IO operation completed synchronously - callback won't be called to signal completion.
asyncResult.IOCompleted(statusCode, bytesSent);
}
// Last write, cache it for special cancelation handling.
if ((flags & Interop.HttpApi.HTTP_FLAGS.HTTP_SEND_RESPONSE_FLAG_MORE_DATA) == 0)
{
_lastWrite = asyncResult;
}
if (NetEventSource.IsEnabled) NetEventSource.Exit(this);
return asyncResult;
}