// ------------------------------------------------------------------------------ // // Copyright (c) Microsoft Corporation. All rights reserved. // // ------------------------------------------------------------------------------ namespace System.Net { using System.IO; using System.Runtime.InteropServices; using System.Threading; using System.Globalization; using System.Security.Permissions; using System.Threading.Tasks; using System.Diagnostics.CodeAnalysis; unsafe class HttpResponseStream : Stream { private HttpListenerContext m_HttpContext; private long m_LeftToWrite = long.MinValue; private bool m_Closed; private bool m_InOpaqueMode; // The last write needs special handling to cancel. private HttpResponseStreamAsyncResult m_LastWrite; internal HttpResponseStream(HttpListenerContext httpContext) { GlobalLog.Print("HttpResponseStream#" + ValidationHelper.HashString(this) + "::.ctor() HttpListenerContext##" + ValidationHelper.HashString(httpContext)); m_HttpContext = httpContext; } internal UnsafeNclNativeMethods.HttpApi.HTTP_FLAGS ComputeLeftToWrite() { GlobalLog.Print("HttpResponseStream#" + ValidationHelper.HashString(this) + "::ComputeLeftToWrite() on entry m_LeftToWrite:" + m_LeftToWrite); UnsafeNclNativeMethods.HttpApi.HTTP_FLAGS flags = UnsafeNclNativeMethods.HttpApi.HTTP_FLAGS.NONE; if (!m_HttpContext.Response.ComputedHeaders) { flags = m_HttpContext.Response.ComputeHeaders(); } if (m_LeftToWrite==long.MinValue) { UnsafeNclNativeMethods.HttpApi.HTTP_VERB method = m_HttpContext.GetKnownMethod(); m_LeftToWrite = method != UnsafeNclNativeMethods.HttpApi.HTTP_VERB.HttpVerbHEAD ? m_HttpContext.Response.ContentLength64 : 0; GlobalLog.Print("HttpResponseStream#" + ValidationHelper.HashString(this) + "::ComputeLeftToWrite() computed m_LeftToWrite:" + m_LeftToWrite); } return flags; } public override bool CanSeek { get { return false; } } public override bool CanWrite { get { return true; } } public override bool CanRead { get { return false; } } internal bool Closed { get { return m_Closed; } } internal HttpListenerContext InternalHttpContext { get { return m_HttpContext; } } internal void SetClosedFlag() { m_Closed = true; } public override void Flush() { } public override Task FlushAsync(CancellationToken cancellationToken) { return Task.CompletedTask; } public override long Length { get { throw new NotSupportedException(SR.GetString(SR.net_noseek)); } } public override long Position { get { throw new NotSupportedException(SR.GetString(SR.net_noseek)); } set { throw new NotSupportedException(SR.GetString(SR.net_noseek)); } } public override long Seek(long offset, SeekOrigin origin) { throw new NotSupportedException(SR.GetString(SR.net_noseek)); } public override void SetLength(long value) { throw new NotSupportedException(SR.GetString(SR.net_noseek)); } public override int Read([In, Out] byte[] buffer, int offset, int size) { throw new InvalidOperationException(SR.GetString(SR.net_writeonlystream)); } [HostProtection(ExternalThreading=true)] public override IAsyncResult BeginRead(byte[] buffer, int offset, int size, AsyncCallback callback, object state) { throw new InvalidOperationException(SR.GetString(SR.net_writeonlystream)); } public override int EndRead(IAsyncResult asyncResult) { throw new InvalidOperationException(SR.GetString(SR.net_writeonlystream)); } public override void Write(byte[] buffer, int offset, int size) { if(Logging.On)Logging.Enter(Logging.HttpListener, this, "Write", ""); GlobalLog.Print("HttpResponseStream#" + ValidationHelper.HashString(this) + "::Write() buffer.Length:" + buffer.Length + " size:" + size + " offset:" + offset); if (buffer==null) { throw new ArgumentNullException("buffer"); } if (offset<0 || offset>buffer.Length) { throw new ArgumentOutOfRangeException("offset"); } if (size<0 || size>buffer.Length-offset) { throw new ArgumentOutOfRangeException("size"); } UnsafeNclNativeMethods.HttpApi.HTTP_FLAGS flags = ComputeLeftToWrite(); if (m_Closed || (size == 0 && m_LeftToWrite != 0)) { if(Logging.On)Logging.Exit(Logging.HttpListener, this, "Write", ""); return; } if (m_LeftToWrite>=0 && size>m_LeftToWrite) { throw new ProtocolViolationException(SR.GetString(SR.net_entitytoobig)); } uint statusCode; uint dataToWrite = (uint)size; SafeLocalFree bufferAsIntPtr = null; IntPtr pBufferAsIntPtr = IntPtr.Zero; bool sentHeaders = m_HttpContext.Response.SentHeaders; try { if (size == 0) { statusCode = m_HttpContext.Response.SendHeaders(null, null, flags, false); } else { fixed (byte* pDataBuffer = buffer) { byte* pBuffer = pDataBuffer; if (m_HttpContext.Response.BoundaryType == BoundaryType.Chunked) { // string chunkHeader = size.ToString("x", CultureInfo.InvariantCulture); dataToWrite = dataToWrite + (uint)(chunkHeader.Length + 4); bufferAsIntPtr = SafeLocalFree.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, IntPtrHelper.Add(pBufferAsIntPtr, chunkHeader.Length + 2), size); Marshal.WriteInt16(pBufferAsIntPtr, (int)(dataToWrite - 2), 0x0A0D); pBuffer = (byte*)pBufferAsIntPtr; offset = 0; } UnsafeNclNativeMethods.HttpApi.HTTP_DATA_CHUNK dataChunk = new UnsafeNclNativeMethods.HttpApi.HTTP_DATA_CHUNK(); dataChunk.DataChunkType = UnsafeNclNativeMethods.HttpApi.HTTP_DATA_CHUNK_TYPE.HttpDataChunkFromMemory; dataChunk.pBuffer = (byte*)(pBuffer + offset); dataChunk.BufferLength = dataToWrite; flags |= m_LeftToWrite == size ? UnsafeNclNativeMethods.HttpApi.HTTP_FLAGS.NONE : UnsafeNclNativeMethods.HttpApi.HTTP_FLAGS.HTTP_SEND_RESPONSE_FLAG_MORE_DATA; if (!sentHeaders) { statusCode = m_HttpContext.Response.SendHeaders(&dataChunk, null, flags, false); } else { GlobalLog.Print("HttpResponseStream#" + ValidationHelper.HashString(this) + "::Write() calling UnsafeNclNativeMethods.HttpApi.HttpSendResponseEntityBody"); statusCode = UnsafeNclNativeMethods.HttpApi.HttpSendResponseEntityBody( m_HttpContext.RequestQueueHandle, m_HttpContext.RequestId, (uint)flags, 1, &dataChunk, null, SafeLocalFree.Zero, 0, null, null); GlobalLog.Print("HttpResponseStream#" + ValidationHelper.HashString(this) + "::Write() call to UnsafeNclNativeMethods.HttpApi.HttpSendResponseEntityBody returned:" + statusCode); if (m_HttpContext.Listener.IgnoreWriteExceptions) { GlobalLog.Print("HttpResponseStream#" + ValidationHelper.HashString(this) + "::Write() suppressing error"); statusCode = UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS; } } } } } finally { if (bufferAsIntPtr != null) { // free unmanaged buffer bufferAsIntPtr.Close(); } } if (statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS && statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_HANDLE_EOF) { Exception exception = new HttpListenerException((int)statusCode); if(Logging.On)Logging.Exception(Logging.HttpListener, this, "Write", exception); m_Closed = true; m_HttpContext.Abort(); throw exception; } UpdateAfterWrite(dataToWrite); if(Logging.On)Logging.Dump(Logging.HttpListener, this, "Write", buffer, offset, (int)dataToWrite); if(Logging.On)Logging.Exit(Logging.HttpListener, this, "Write", ""); } [HostProtection(ExternalThreading=true)] public override IAsyncResult BeginWrite(byte[] buffer, int offset, int size, AsyncCallback callback, object state) { GlobalLog.Print("HttpResponseStream#" + ValidationHelper.HashString(this) + "::BeginWrite() buffer.Length:" + buffer.Length + " size:" + size + " offset:" + offset); if (buffer==null) { throw new ArgumentNullException("buffer"); } if (offset<0 || offset>buffer.Length) { throw new ArgumentOutOfRangeException("offset"); } if (size<0 || size>buffer.Length-offset) { throw new ArgumentOutOfRangeException("size"); } UnsafeNclNativeMethods.HttpApi.HTTP_FLAGS flags = ComputeLeftToWrite(); if (m_Closed || (size == 0 && m_LeftToWrite != 0)) { if(Logging.On)Logging.Exit(Logging.HttpListener, this, "BeginWrite", ""); HttpResponseStreamAsyncResult result = new HttpResponseStreamAsyncResult(this, state, callback); result.InvokeCallback((uint) 0); return result; } if (m_LeftToWrite>=0 && size>m_LeftToWrite) { throw new ProtocolViolationException(SR.GetString(SR.net_entitytoobig)); } uint statusCode; uint bytesSent = 0; flags |= m_LeftToWrite==size ? UnsafeNclNativeMethods.HttpApi.HTTP_FLAGS.NONE : UnsafeNclNativeMethods.HttpApi.HTTP_FLAGS.HTTP_SEND_RESPONSE_FLAG_MORE_DATA; bool sentHeaders = m_HttpContext.Response.SentHeaders; HttpResponseStreamAsyncResult asyncResult = new HttpResponseStreamAsyncResult(this, state, callback, buffer, offset, size, m_HttpContext.Response.BoundaryType==BoundaryType.Chunked, sentHeaders); // Update m_LeftToWrite now so we can queue up additional BeginWrite's without waiting for EndWrite. UpdateAfterWrite((uint)((m_HttpContext.Response.BoundaryType == BoundaryType.Chunked) ? 0 : size)); try { if (!sentHeaders) { statusCode = m_HttpContext.Response.SendHeaders(null, asyncResult, flags, false); } else { GlobalLog.Print("HttpResponseStream#" + ValidationHelper.HashString(this) + "::BeginWrite() calling UnsafeNclNativeMethods.HttpApi.HttpSendResponseEntityBody"); m_HttpContext.EnsureBoundHandle(); statusCode = UnsafeNclNativeMethods.HttpApi.HttpSendResponseEntityBody( m_HttpContext.RequestQueueHandle, m_HttpContext.RequestId, (uint)flags, asyncResult.dataChunkCount, asyncResult.pDataChunks, &bytesSent, SafeLocalFree.Zero, 0, asyncResult.m_pOverlapped, null); GlobalLog.Print("HttpResponseStream#" + ValidationHelper.HashString(this) + "::BeginWrite() call to UnsafeNclNativeMethods.HttpApi.HttpSendResponseEntityBody returned:" + statusCode); } } catch (Exception e) { if (Logging.On) Logging.Exception(Logging.HttpListener, this, "BeginWrite", e); asyncResult.InternalCleanup(); m_Closed = true; m_HttpContext.Abort(); throw; } if (statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS && statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_IO_PENDING) { asyncResult.InternalCleanup(); if (m_HttpContext.Listener.IgnoreWriteExceptions && sentHeaders) { GlobalLog.Print("HttpResponseStream#" + ValidationHelper.HashString(this) + "::BeginWrite() suppressing error"); } else { Exception exception = new HttpListenerException((int)statusCode); if (Logging.On) Logging.Exception(Logging.HttpListener, this, "BeginWrite", exception); m_Closed = true; m_HttpContext.Abort(); throw exception; } } if (statusCode == UnsafeNclNativeMethods.ErrorCodes.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 & UnsafeNclNativeMethods.HttpApi.HTTP_FLAGS.HTTP_SEND_RESPONSE_FLAG_MORE_DATA) == 0) { m_LastWrite = asyncResult; } if(Logging.On)Logging.Exit(Logging.HttpListener, this, "BeginWrite", ""); return asyncResult; } public override void EndWrite(IAsyncResult asyncResult) { if(Logging.On)Logging.Enter(Logging.HttpListener, this, "EndWrite", ""); GlobalLog.Print("HttpResponseStream#" + ValidationHelper.HashString(this) + "::EndWrite() asyncResult#" + ValidationHelper.HashString(asyncResult)); if (asyncResult==null) { throw new ArgumentNullException("asyncResult"); } HttpResponseStreamAsyncResult castedAsyncResult = asyncResult as HttpResponseStreamAsyncResult; if (castedAsyncResult==null || castedAsyncResult.AsyncObject!=this) { throw new ArgumentException(SR.GetString(SR.net_io_invalidasyncresult), "asyncResult"); } if (castedAsyncResult.EndCalled) { throw new InvalidOperationException(SR.GetString(SR.net_io_invalidendcall, "EndWrite")); } castedAsyncResult.EndCalled = true; // wait & then check for errors object returnValue = castedAsyncResult.InternalWaitForCompletion(); Exception exception = returnValue as Exception; if (exception!=null) { GlobalLog.Print("HttpResponseStream#" + ValidationHelper.HashString(this) + "::EndWrite() rethrowing exception:" + exception); if(Logging.On)Logging.Exception(Logging.HttpListener, this, "EndWrite", exception); m_Closed = true; m_HttpContext.Abort(); throw exception; } // GlobalLog.Print("HttpResponseStream#" + ValidationHelper.HashString(this) + "::EndWrite()"); if(Logging.On)Logging.Exit(Logging.HttpListener, this, "EndWrite", ""); } void UpdateAfterWrite(uint dataWritten) { GlobalLog.Print("HttpResponseStream#" + ValidationHelper.HashString(this) + "::UpdateAfterWrite() dataWritten:" + dataWritten + " m_LeftToWrite:" + m_LeftToWrite + " m_Closed:" + m_Closed); if (!m_InOpaqueMode) { if (m_LeftToWrite>0) { // keep track of the data transferred m_LeftToWrite -= dataWritten; } if (m_LeftToWrite==0) { // in this case we already passed 0 as the flag, so we don't need to call HttpSendResponseEntityBody() when we Close() m_Closed = true; } } GlobalLog.Print("HttpResponseStream#" + ValidationHelper.HashString(this) + "::UpdateAfterWrite() dataWritten:" + dataWritten + " m_LeftToWrite:" + m_LeftToWrite + " m_Closed:" + m_Closed); } protected override void Dispose(bool disposing) { if(Logging.On)Logging.Enter(Logging.HttpListener, this, "Close", ""); try { if(disposing){ GlobalLog.Print("HttpResponseStream#" + ValidationHelper.HashString(this) + "::Close() m_Closed:" + m_Closed); if (m_Closed) { if(Logging.On)Logging.Exit(Logging.HttpListener, this, "Close", ""); return; } m_Closed = true; UnsafeNclNativeMethods.HttpApi.HTTP_FLAGS flags = ComputeLeftToWrite(); if (m_LeftToWrite>0 && !m_InOpaqueMode) { throw new InvalidOperationException(SR.GetString(SR.net_io_notenoughbyteswritten)); } bool sentHeaders = m_HttpContext.Response.SentHeaders; if (sentHeaders && m_LeftToWrite==0) { if(Logging.On)Logging.Exit(Logging.HttpListener, this, "Close", ""); return; } uint statusCode = 0; if ((m_HttpContext.Response.BoundaryType==BoundaryType.Chunked || m_HttpContext.Response.BoundaryType==BoundaryType.None) && (String.Compare(m_HttpContext.Request.HttpMethod, "HEAD", StringComparison.OrdinalIgnoreCase)!=0)) { if (m_HttpContext.Response.BoundaryType==BoundaryType.None) { flags |= UnsafeNclNativeMethods.HttpApi.HTTP_FLAGS.HTTP_SEND_RESPONSE_FLAG_DISCONNECT; } fixed (void* pBuffer = NclConstants.ChunkTerminator) { UnsafeNclNativeMethods.HttpApi.HTTP_DATA_CHUNK* pDataChunk = null; if (m_HttpContext.Response.BoundaryType==BoundaryType.Chunked) { UnsafeNclNativeMethods.HttpApi.HTTP_DATA_CHUNK dataChunk = new UnsafeNclNativeMethods.HttpApi.HTTP_DATA_CHUNK(); dataChunk.DataChunkType = UnsafeNclNativeMethods.HttpApi.HTTP_DATA_CHUNK_TYPE.HttpDataChunkFromMemory; dataChunk.pBuffer = (byte *)pBuffer; dataChunk.BufferLength = (uint) NclConstants.ChunkTerminator.Length; pDataChunk = &dataChunk; } if (!sentHeaders) { statusCode = m_HttpContext.Response.SendHeaders(pDataChunk, null, flags, false); } else { GlobalLog.Print("HttpResponseStream#" + ValidationHelper.HashString(this) + "::Close() calling UnsafeNclNativeMethods.HttpApi.HttpSendResponseEntityBody"); statusCode = UnsafeNclNativeMethods.HttpApi.HttpSendResponseEntityBody( m_HttpContext.RequestQueueHandle, m_HttpContext.RequestId, (uint)flags, pDataChunk!=null ? (ushort)1 : (ushort)0, pDataChunk, null, SafeLocalFree.Zero, 0, null, null); GlobalLog.Print("HttpResponseStream#" + ValidationHelper.HashString(this) + "::Close() call to UnsafeNclNativeMethods.HttpApi.HttpSendResponseEntityBody returned:" + statusCode); if (m_HttpContext.Listener.IgnoreWriteExceptions) { GlobalLog.Print("HttpResponseStream#" + ValidationHelper.HashString(this) + "::Close() suppressing error"); statusCode = UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS; } } } } else { if (!sentHeaders) { statusCode = m_HttpContext.Response.SendHeaders(null, null, flags, false); } } if (statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS && statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_HANDLE_EOF) { Exception exception = new HttpListenerException((int)statusCode); if(Logging.On)Logging.Exception(Logging.HttpListener, this, "Close", exception); m_HttpContext.Abort(); throw exception; } m_LeftToWrite = 0; } } finally { base.Dispose(disposing); } if(Logging.On)Logging.Exit(Logging.HttpListener, this, "Dispose", ""); } internal void SwitchToOpaqueMode() { GlobalLog.Print("HttpResponseStream#" + ValidationHelper.HashString(this) + "::SwitchToOpaqueMode()"); m_InOpaqueMode = true; m_LeftToWrite = long.MaxValue; } // The final Content-Length async write can only be cancelled by CancelIoEx. // [....] can only be cancelled by CancelSynchronousIo, but we don't attempt this right now. [SuppressMessage("Microsoft.Usage", "CA1806:DoNotIgnoreMethodResults", Justification = "It is safe to ignore the return value on a cancel operation because the connection is being closed")] internal void CancelLastWrite(CriticalHandle requestQueueHandle) { HttpResponseStreamAsyncResult asyncState = m_LastWrite; if (asyncState != null && !asyncState.IsCompleted) { UnsafeNclNativeMethods.CancelIoEx(requestQueueHandle, asyncState.m_pOverlapped); } } } unsafe class HttpResponseStreamAsyncResult : LazyAsyncResult { internal NativeOverlapped* m_pOverlapped; private UnsafeNclNativeMethods.HttpApi.HTTP_DATA_CHUNK[] m_DataChunks; internal bool m_SentHeaders; private static readonly IOCompletionCallback s_IOCallback = new IOCompletionCallback(Callback); internal ushort dataChunkCount { get { if (m_DataChunks == null) { return 0; } else { return (ushort)m_DataChunks.Length; } } } internal UnsafeNclNativeMethods.HttpApi.HTTP_DATA_CHUNK* pDataChunks { get { if (m_DataChunks == null) { return null; } else { return (UnsafeNclNativeMethods.HttpApi.HTTP_DATA_CHUNK*)(Marshal.UnsafeAddrOfPinnedArrayElement(m_DataChunks, 0)); } } } internal HttpResponseStreamAsyncResult(object asyncObject, object userState, AsyncCallback callback) : base(asyncObject, userState, callback) { } internal HttpResponseStreamAsyncResult(object asyncObject, object userState, AsyncCallback callback, byte[] buffer, int offset, int size, bool chunked, bool sentHeaders): base(asyncObject, userState, callback){ m_SentHeaders = sentHeaders; Overlapped overlapped = new Overlapped(); overlapped.AsyncResult = this; if (size == 0) { m_DataChunks = null; m_pOverlapped = overlapped.Pack(s_IOCallback, null); } else { m_DataChunks = new UnsafeNclNativeMethods.HttpApi.HTTP_DATA_CHUNK[chunked ? 3 : 1]; GlobalLog.Print("HttpResponseStreamAsyncResult#" + ValidationHelper.HashString(this) + "::.ctor() m_pOverlapped:0x" + ((IntPtr)m_pOverlapped).ToString("x8")); object[] objectsToPin = new object[1 + m_DataChunks.Length]; objectsToPin[m_DataChunks.Length] = m_DataChunks; int chunkHeaderOffset = 0; byte[] chunkHeaderBuffer = null; if (chunked) { chunkHeaderBuffer = ConnectStream.GetChunkHeader(size, out chunkHeaderOffset); m_DataChunks[0] = new UnsafeNclNativeMethods.HttpApi.HTTP_DATA_CHUNK(); m_DataChunks[0].DataChunkType = UnsafeNclNativeMethods.HttpApi.HTTP_DATA_CHUNK_TYPE.HttpDataChunkFromMemory; m_DataChunks[0].BufferLength = (uint)(chunkHeaderBuffer.Length - chunkHeaderOffset); objectsToPin[0] = chunkHeaderBuffer; m_DataChunks[1] = new UnsafeNclNativeMethods.HttpApi.HTTP_DATA_CHUNK(); m_DataChunks[1].DataChunkType = UnsafeNclNativeMethods.HttpApi.HTTP_DATA_CHUNK_TYPE.HttpDataChunkFromMemory; m_DataChunks[1].BufferLength = (uint)size; objectsToPin[1] = buffer; m_DataChunks[2] = new UnsafeNclNativeMethods.HttpApi.HTTP_DATA_CHUNK(); m_DataChunks[2].DataChunkType = UnsafeNclNativeMethods.HttpApi.HTTP_DATA_CHUNK_TYPE.HttpDataChunkFromMemory; m_DataChunks[2].BufferLength = (uint)NclConstants.CRLF.Length; objectsToPin[2] = NclConstants.CRLF; } else { m_DataChunks[0] = new UnsafeNclNativeMethods.HttpApi.HTTP_DATA_CHUNK(); m_DataChunks[0].DataChunkType = UnsafeNclNativeMethods.HttpApi.HTTP_DATA_CHUNK_TYPE.HttpDataChunkFromMemory; m_DataChunks[0].BufferLength = (uint)size; objectsToPin[0] = buffer; } // This call will pin needed memory m_pOverlapped = overlapped.Pack(s_IOCallback, objectsToPin); if (chunked) { m_DataChunks[0].pBuffer = (byte*)(Marshal.UnsafeAddrOfPinnedArrayElement(chunkHeaderBuffer, chunkHeaderOffset)); m_DataChunks[1].pBuffer = (byte*)(Marshal.UnsafeAddrOfPinnedArrayElement(buffer, offset)); m_DataChunks[2].pBuffer = (byte*)(Marshal.UnsafeAddrOfPinnedArrayElement(NclConstants.CRLF, 0)); } else { m_DataChunks[0].pBuffer = (byte*)(Marshal.UnsafeAddrOfPinnedArrayElement(buffer, offset)); } } } internal void IOCompleted(uint errorCode, uint numBytes) { IOCompleted(this, errorCode, numBytes); } private static void IOCompleted(HttpResponseStreamAsyncResult asyncResult, uint errorCode, uint numBytes) { GlobalLog.Print("HttpResponseStreamAsyncResult#" + ValidationHelper.HashString(asyncResult) + "::Callback() errorCode:0x" + errorCode.ToString("x8") + " numBytes:" + numBytes); object result = null; try { if (errorCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS && errorCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_HANDLE_EOF) { asyncResult.ErrorCode = (int)errorCode; result = new HttpListenerException((int)errorCode); } else { // if we sent headers and body together, numBytes will be the total, but we need to only account for the data // result = numBytes; if (asyncResult.m_DataChunks == null) { result = (uint) 0; if (Logging.On) { Logging.Dump(Logging.HttpListener, asyncResult, "Callback", IntPtr.Zero, 0); } } else { result = asyncResult.m_DataChunks.Length == 1 ? asyncResult.m_DataChunks[0].BufferLength : 0; if (Logging.On) { for (int i = 0; i < asyncResult.m_DataChunks.Length; i++) { Logging.Dump(Logging.HttpListener, asyncResult, "Callback", (IntPtr)asyncResult.m_DataChunks[0].pBuffer, (int)asyncResult.m_DataChunks[0].BufferLength); } } } } GlobalLog.Print("HttpResponseStreamAsyncResult#" + ValidationHelper.HashString(asyncResult) + "::Callback() calling Complete()"); } catch (Exception e) { result = e; } asyncResult.InvokeCallback(result); } private static unsafe void Callback(uint errorCode, uint numBytes, NativeOverlapped* nativeOverlapped) { Overlapped callbackOverlapped = Overlapped.Unpack(nativeOverlapped); HttpResponseStreamAsyncResult asyncResult = callbackOverlapped.AsyncResult as HttpResponseStreamAsyncResult; GlobalLog.Print("HttpResponseStreamAsyncResult#" + ValidationHelper.HashString(asyncResult) + "::Callback() errorCode:0x" + errorCode.ToString("x8") + " numBytes:" + numBytes + " nativeOverlapped:0x" + ((IntPtr)nativeOverlapped).ToString("x8")); IOCompleted(asyncResult, errorCode, numBytes); } // Will be called from the base class upon InvokeCallback() protected override void Cleanup() { base.Cleanup(); if (m_pOverlapped != null) { Overlapped.Free(m_pOverlapped); } } } }