diff --git a/src/Microsoft.Net.Http.Server/RequestProcessing/RequestStream.cs b/src/Microsoft.Net.Http.Server/RequestProcessing/RequestStream.cs index 8ed8cbb2f2..cd14f84ca4 100644 --- a/src/Microsoft.Net.Http.Server/RequestProcessing/RequestStream.cs +++ b/src/Microsoft.Net.Http.Server/RequestProcessing/RequestStream.cs @@ -22,7 +22,6 @@ // ------------------------------------------------------------------------------ using System; -using System.Diagnostics.CodeAnalysis; using System.IO; using System.Runtime.InteropServices; using System.Threading; @@ -184,7 +183,7 @@ namespace Microsoft.Net.Http.Server return (int)dataRead; } - private void UpdateAfterRead(uint statusCode, uint dataRead) + internal void UpdateAfterRead(uint statusCode, uint dataRead) { if (statusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_HANDLE_EOF || dataRead == 0) { @@ -446,183 +445,5 @@ namespace Microsoft.Net.Http.Server base.Dispose(disposing); } } - - private unsafe class RequestStreamAsyncResult : IAsyncResult, IDisposable - { - private static readonly IOCompletionCallback IOCallback = new IOCompletionCallback(Callback); - - private SafeNativeOverlapped _overlapped; - private IntPtr _pinnedBuffer; - private uint _dataAlreadyRead = 0; - private TaskCompletionSource _tcs; - private RequestStream _requestStream; - private AsyncCallback _callback; - private CancellationTokenRegistration _cancellationRegistration; - - internal RequestStreamAsyncResult(RequestStream requestStream, object userState, AsyncCallback callback) - { - _requestStream = requestStream; - _tcs = new TaskCompletionSource(userState); - _callback = callback; - } - - internal RequestStreamAsyncResult(RequestStream requestStream, object userState, AsyncCallback callback, uint dataAlreadyRead) - : this(requestStream, userState, callback) - { - _dataAlreadyRead = dataAlreadyRead; - } - - internal RequestStreamAsyncResult(RequestStream requestStream, object userState, AsyncCallback callback, byte[] buffer, int offset, uint dataAlreadyRead) - : this(requestStream, userState, callback, buffer, offset, dataAlreadyRead, new CancellationTokenRegistration()) - { - } - - internal RequestStreamAsyncResult(RequestStream requestStream, object userState, AsyncCallback callback, byte[] buffer, int offset, uint dataAlreadyRead, CancellationTokenRegistration cancellationRegistration) - : this(requestStream, userState, callback) - { - _dataAlreadyRead = dataAlreadyRead; - Overlapped overlapped = new Overlapped(); - overlapped.AsyncResult = this; - _overlapped = new SafeNativeOverlapped(overlapped.Pack(IOCallback, buffer)); - _pinnedBuffer = (Marshal.UnsafeAddrOfPinnedArrayElement(buffer, offset)); - _cancellationRegistration = cancellationRegistration; - } - - internal RequestStream RequestStream - { - get { return _requestStream; } - } - - internal SafeNativeOverlapped NativeOverlapped - { - get { return _overlapped; } - } - - internal IntPtr PinnedBuffer - { - get { return _pinnedBuffer; } - } - - internal uint DataAlreadyRead - { - get { return _dataAlreadyRead; } - } - - internal Task Task - { - get { return _tcs.Task; } - } - - internal bool EndCalled { get; set; } - - internal void IOCompleted(uint errorCode, uint numBytes) - { - IOCompleted(this, errorCode, numBytes); - } - - [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Redirecting to callback")] - private static void IOCompleted(RequestStreamAsyncResult asyncResult, uint errorCode, uint numBytes) - { - try - { - if (errorCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS && errorCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_HANDLE_EOF) - { - asyncResult.Fail(new WebListenerException((int)errorCode)); - } - else - { - // TODO: Verbose log dump data read - asyncResult.Complete((int)numBytes, errorCode); - } - } - catch (Exception e) - { - asyncResult.Fail(e); - } - } - - private static unsafe void Callback(uint errorCode, uint numBytes, NativeOverlapped* nativeOverlapped) - { - Overlapped callbackOverlapped = Overlapped.Unpack(nativeOverlapped); - RequestStreamAsyncResult asyncResult = callbackOverlapped.AsyncResult as RequestStreamAsyncResult; - - IOCompleted(asyncResult, errorCode, numBytes); - } - - internal void Complete(int read, uint errorCode = UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS) - { - if (_tcs.TrySetResult(read + (int)DataAlreadyRead)) - { - RequestStream.UpdateAfterRead((uint)errorCode, (uint)(read + DataAlreadyRead)); - if (_callback != null) - { - try - { - _callback(this); - } - catch (Exception) - { - // TODO: Exception handling? This may be an IO callback thread and throwing here could crash the app. - } - } - } - Dispose(); - } - - internal void Fail(Exception ex) - { - if (_tcs.TrySetException(ex) && _callback != null) - { - try - { - _callback(this); - } - catch (Exception) - { - // TODO: Exception handling? This may be an IO callback thread and throwing here could crash the app. - // TODO: Log - } - } - Dispose(); - } - - [SuppressMessage("Microsoft.Usage", "CA2216:DisposableTypesShouldDeclareFinalizer", Justification = "The disposable resource referenced does have a finalizer.")] - public void Dispose() - { - Dispose(true); - } - - protected virtual void Dispose(bool disposing) - { - if (disposing) - { - if (_overlapped != null) - { - _overlapped.Dispose(); - } - _cancellationRegistration.Dispose(); - } - } - - public object AsyncState - { - get { return _tcs.Task.AsyncState; } - } - - public WaitHandle AsyncWaitHandle - { - get { return ((IAsyncResult)_tcs.Task).AsyncWaitHandle; } - } - - public bool CompletedSynchronously - { - get { return ((IAsyncResult)_tcs.Task).CompletedSynchronously; } - } - - public bool IsCompleted - { - get { return _tcs.Task.IsCompleted; } - } - } } } diff --git a/src/Microsoft.Net.Http.Server/RequestProcessing/RequestStreamAsyncResult.cs b/src/Microsoft.Net.Http.Server/RequestProcessing/RequestStreamAsyncResult.cs new file mode 100644 index 0000000000..effc75c6f7 --- /dev/null +++ b/src/Microsoft.Net.Http.Server/RequestProcessing/RequestStreamAsyncResult.cs @@ -0,0 +1,209 @@ +// Copyright (c) Microsoft Open Technologies, Inc. +// All Rights Reserved +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING +// WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF +// TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR +// NON-INFRINGEMENT. +// See the Apache 2 License for the specific language governing +// permissions and limitations under the License. + +// ------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ------------------------------------------------------------------------------ + +using System; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices; +using System.Threading; +using System.Threading.Tasks; + +namespace Microsoft.Net.Http.Server +{ + internal unsafe class RequestStreamAsyncResult : IAsyncResult, IDisposable + { + private static readonly IOCompletionCallback IOCallback = new IOCompletionCallback(Callback); + + private SafeNativeOverlapped _overlapped; + private IntPtr _pinnedBuffer; + private uint _dataAlreadyRead; + private TaskCompletionSource _tcs; + private RequestStream _requestStream; + private AsyncCallback _callback; + private CancellationTokenRegistration _cancellationRegistration; + + internal RequestStreamAsyncResult(RequestStream requestStream, object userState, AsyncCallback callback) + { + _requestStream = requestStream; + _tcs = new TaskCompletionSource(userState); + _callback = callback; + } + + internal RequestStreamAsyncResult(RequestStream requestStream, object userState, AsyncCallback callback, uint dataAlreadyRead) + : this(requestStream, userState, callback) + { + _dataAlreadyRead = dataAlreadyRead; + } + + internal RequestStreamAsyncResult(RequestStream requestStream, object userState, AsyncCallback callback, byte[] buffer, int offset, uint dataAlreadyRead) + : this(requestStream, userState, callback, buffer, offset, dataAlreadyRead, new CancellationTokenRegistration()) + { + } + + internal RequestStreamAsyncResult(RequestStream requestStream, object userState, AsyncCallback callback, byte[] buffer, int offset, uint dataAlreadyRead, CancellationTokenRegistration cancellationRegistration) + : this(requestStream, userState, callback) + { + _dataAlreadyRead = dataAlreadyRead; + Overlapped overlapped = new Overlapped(); + overlapped.AsyncResult = this; + _overlapped = new SafeNativeOverlapped(overlapped.Pack(IOCallback, buffer)); + _pinnedBuffer = (Marshal.UnsafeAddrOfPinnedArrayElement(buffer, offset)); + _cancellationRegistration = cancellationRegistration; + } + + internal RequestStream RequestStream + { + get { return _requestStream; } + } + + internal SafeNativeOverlapped NativeOverlapped + { + get { return _overlapped; } + } + + internal IntPtr PinnedBuffer + { + get { return _pinnedBuffer; } + } + + internal uint DataAlreadyRead + { + get { return _dataAlreadyRead; } + } + + internal Task Task + { + get { return _tcs.Task; } + } + + internal bool EndCalled { get; set; } + + internal void IOCompleted(uint errorCode, uint numBytes) + { + IOCompleted(this, errorCode, numBytes); + } + + [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Redirecting to callback")] + private static void IOCompleted(RequestStreamAsyncResult asyncResult, uint errorCode, uint numBytes) + { + try + { + if (errorCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS && errorCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_HANDLE_EOF) + { + asyncResult.Fail(new WebListenerException((int)errorCode)); + } + else + { + // TODO: Verbose log dump data read + asyncResult.Complete((int)numBytes, errorCode); + } + } + catch (Exception e) + { + asyncResult.Fail(e); + } + } + + private static unsafe void Callback(uint errorCode, uint numBytes, NativeOverlapped* nativeOverlapped) + { + Overlapped callbackOverlapped = Overlapped.Unpack(nativeOverlapped); + RequestStreamAsyncResult asyncResult = callbackOverlapped.AsyncResult as RequestStreamAsyncResult; + + IOCompleted(asyncResult, errorCode, numBytes); + } + + internal void Complete(int read, uint errorCode = UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS) + { + if (_tcs.TrySetResult(read + (int)DataAlreadyRead)) + { + RequestStream.UpdateAfterRead((uint)errorCode, (uint)(read + DataAlreadyRead)); + if (_callback != null) + { + try + { + _callback(this); + } + catch (Exception) + { + // TODO: Exception handling? This may be an IO callback thread and throwing here could crash the app. + } + } + } + Dispose(); + } + + internal void Fail(Exception ex) + { + if (_tcs.TrySetException(ex) && _callback != null) + { + try + { + _callback(this); + } + catch (Exception) + { + // TODO: Exception handling? This may be an IO callback thread and throwing here could crash the app. + // TODO: Log + } + } + Dispose(); + } + + [SuppressMessage("Microsoft.Usage", "CA2216:DisposableTypesShouldDeclareFinalizer", Justification = "The disposable resource referenced does have a finalizer.")] + public void Dispose() + { + Dispose(true); + } + + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + if (_overlapped != null) + { + _overlapped.Dispose(); + } + _cancellationRegistration.Dispose(); + } + } + + public object AsyncState + { + get { return _tcs.Task.AsyncState; } + } + + public WaitHandle AsyncWaitHandle + { + get { return ((IAsyncResult)_tcs.Task).AsyncWaitHandle; } + } + + public bool CompletedSynchronously + { + get { return ((IAsyncResult)_tcs.Task).CompletedSynchronously; } + } + + public bool IsCompleted + { + get { return _tcs.Task.IsCompleted; } + } + } +} \ No newline at end of file