internal void OnAsyncCompletion(int bytesCompleted, int hresult, IntPtr pAsyncCompletionContext) { // clear _asyncResultBase because when we call Complete the user's callback // may begin another async operation AsyncResultBase ar = _asyncResultBase; _asyncResultBase = null; ar.Complete(bytesCompleted, hresult, pAsyncCompletionContext, synchronous: false); }
// Begin an asynchronous read of the request entity body. To read the entire entity, invoke // repeatedly until total bytes read is equal to Request.ContentLength or EndRead indicates // that zero bytes were read. If Request.ContentLength is zero and the request is chunked, // then invoke repeatedly until EndRead indicates that zero bytes were read. // // If an error occurs and the client is no longer connected, an HttpException will be thrown. // // This implements Stream.BeginRead, and as such, should throw // exceptions as described on MSDN when errors occur. public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, Object state) { // Do as Stream does. if (buffer == null) throw new ArgumentNullException("buffer"); if (offset < 0) throw new ArgumentOutOfRangeException("offset"); if (count < 0) throw new ArgumentOutOfRangeException("count"); if (buffer.Length - offset < count) throw new ArgumentException(SR.GetString(SR.InvalidOffsetOrCount, "offset", "count")); ReadAsyncResult ar = new ReadAsyncResult(callback, state, buffer, offset, count, updatePerfCounter: true); if (count == 0) { ar.Complete(0, HResults.S_OK, IntPtr.Zero, synchronous: true); return ar; } // we only allow one async operation at a time if (Interlocked.CompareExchange(ref _asyncResultBase, ar, null) != null) throw new InvalidOperationException(SR.GetString(SR.Async_operation_pending)); // initiate async operation here bool fAsync = true; int bytesRead = 0; IntPtr ppAsyncReceiveBuffer; int hresult = IIS.MgdReadEntityBody(_context, null, // for async the data is copied from a native buffer to avoid pinning 0, // for async, offset is ignored count, // number of bytes to read fAsync, out bytesRead, out ppAsyncReceiveBuffer); if (hresult < 0) { _asyncResultBase = null; // treat every error as if the client disconnected IncrementRequestsDisconnected(); throw new HttpException(SR.GetString(SR.ClientDisconnected), hresult); } // if we completed synchronously, then ppAsyncReceiveBuffer will be non-null else if (ppAsyncReceiveBuffer != IntPtr.Zero) { _asyncResultBase = null; ar.Complete(bytesRead, HResults.S_OK, ppAsyncReceiveBuffer, synchronous: true); return ar; } else { return ar; } }
// Begin an asynchronous flush of the response. To support this, the worker request buffers // the status, headers, and resonse body until an asynchronous flush operation is initiated. public override IAsyncResult BeginFlush(AsyncCallback callback, Object state) { if (_context == IntPtr.Zero) { throw new InvalidOperationException(); } FlushAsyncResult ar = new FlushAsyncResult(callback, state); // we only allow one async operation at a time if (Interlocked.CompareExchange(ref _asyncResultBase, ar, null) != null) throw new InvalidOperationException(SR.GetString(SR.Async_operation_pending)); // initiate async operation here bool async = true; bool completedSynchronously; int hresult = IIS.MgdExplicitFlush(_context, async, out completedSynchronously); if (hresult < 0) { _asyncResultBase = null; // treat every error as if the client disconnected IncrementRequestsDisconnected(); throw new HttpException(SR.GetString(SR.ClientDisconnected), hresult); } else if (completedSynchronously) { _asyncResultBase = null; ar.Complete(0, HResults.S_OK, IntPtr.Zero, synchronous: true); return ar; } else { return ar; } }