public override void Write(byte[] buffer, int offset, int size)
{
if (NetEventSource.IsEnabled)
{
NetEventSource.Enter(this);
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);
return;
}
if (_leftToWrite >= 0 && size > _leftToWrite)
{
throw new ProtocolViolationException(SR.net_entitytoobig);
}
uint statusCode;
uint dataToWrite = (uint)size;
SafeLocalAllocHandle bufferAsIntPtr = null;
IntPtr pBufferAsIntPtr = IntPtr.Zero;
bool sentHeaders = _httpContext.Response.SentHeaders;
try
{
if (size == 0)
{
statusCode = _httpContext.Response.SendHeaders(null, null, flags, false);
}
else
{
fixed (byte* pDataBuffer = buffer)
{
byte* pBuffer = pDataBuffer;
if (_httpContext.Response.BoundaryType == BoundaryType.Chunked)
{
string chunkHeader = size.ToString("x", CultureInfo.InvariantCulture);
dataToWrite = dataToWrite + (uint)(chunkHeader.Length + 4);
bufferAsIntPtr = SafeLocalAllocHandle.LocalAlloc((int)dataToWrite);
pBufferAsIntPtr = bufferAsIntPtr.DangerousGetHandle();
for (int i = 0; i < chunkHeader.Length; i++)
{
Marshal.WriteByte(pBufferAsIntPtr, i, (byte)chunkHeader[i]);
}
Marshal.WriteInt16(pBufferAsIntPtr, chunkHeader.Length, 0x0A0D);
Marshal.Copy(buffer, offset, pBufferAsIntPtr + chunkHeader.Length + 2, size);
Marshal.WriteInt16(pBufferAsIntPtr, (int)(dataToWrite - 2), 0x0A0D);
pBuffer = (byte*)pBufferAsIntPtr;
offset = 0;
}
Interop.HttpApi.HTTP_DATA_CHUNK dataChunk = new Interop.HttpApi.HTTP_DATA_CHUNK();
dataChunk.DataChunkType = Interop.HttpApi.HTTP_DATA_CHUNK_TYPE.HttpDataChunkFromMemory;
dataChunk.pBuffer = (byte*)(pBuffer + offset);
dataChunk.BufferLength = dataToWrite;
flags |= _leftToWrite == size ? Interop.HttpApi.HTTP_FLAGS.NONE : Interop.HttpApi.HTTP_FLAGS.HTTP_SEND_RESPONSE_FLAG_MORE_DATA;
if (!sentHeaders)
{
statusCode = _httpContext.Response.SendHeaders(&dataChunk, null, flags, false);
}
else
{
if (NetEventSource.IsEnabled) NetEventSource.Info(this, "Calling Interop.HttpApi.HttpSendResponseEntityBody");
statusCode =
Interop.HttpApi.HttpSendResponseEntityBody(
_httpContext.RequestQueueHandle,
_httpContext.RequestId,
(uint)flags,
1,
&dataChunk,
null,
SafeLocalAllocHandle.Zero,
0,
null,
null);
if (NetEventSource.IsEnabled) NetEventSource.Info(this, "Call to Interop.HttpApi.HttpSendResponseEntityBody returned:" + statusCode);
if (_httpContext.Listener.IgnoreWriteExceptions)
{
if (NetEventSource.IsEnabled) NetEventSource.Info(this, "Write() suppressing error");
statusCode = Interop.HttpApi.ERROR_SUCCESS;
}
}
}
}
}
finally
{
// free unmanaged buffer
bufferAsIntPtr?.Close();
}
if (statusCode != Interop.HttpApi.ERROR_SUCCESS && statusCode != Interop.HttpApi.ERROR_HANDLE_EOF)
{
Exception exception = new HttpListenerException((int)statusCode);
if (NetEventSource.IsEnabled) NetEventSource.Error(this, exception.ToString());
_closed = true;
_httpContext.Abort();
throw exception;
}
UpdateAfterWrite(dataToWrite);
if (NetEventSource.IsEnabled) NetEventSource.DumpBuffer(this, buffer, offset, (int)dataToWrite);
if (NetEventSource.IsEnabled) NetEventSource.Exit(this);
}