private async Task<AspNetWebSocket> ProcessRequestImplAsync() { AspNetWebSocket webSocket = null; try { // SendResponse and other asynchronous notifications cannot be process by ASP.NET after this point. _root.WorkerRequest.SuppressSendResponseNotifications(); // A flush is necessary to activate the WebSocket module so that we can get its pointer. // // DevDiv #401948: We can't allow a flush failure to propagate out, otherwise the rest of // this method doesn't run, which could leak resources (by not invoking the user callback) // or cause weird behavior (by not calling CompleteTransitionToWebSocket, which could corrupt // server state). If the flush fails, we'll wait to propagate the exception until a safe // point later in this method. ExceptionDispatchInfo flushExceptionDispatchInfo = DoFlush(); // Create the AspNetWebSocket. There's a chance that the client disconnected before we // hit this code. If this is the case, we'll pass a null WebSocketPipe to the // AspNetWebSocket ctor, which immediately sets the socket into an aborted state. UnmanagedWebSocketContext unmanagedWebSocketContext = _root.WorkerRequest.GetWebSocketContext(); WebSocketPipe pipe = (unmanagedWebSocketContext != null) ? new WebSocketPipe(unmanagedWebSocketContext, PerfCounters.Instance) : null; webSocket = new AspNetWebSocket(pipe, _subProtocol); // slim down the HttpContext as much as possible to allow the GC to reclaim memory _httpContext.CompleteTransitionToWebSocket(); // always install a new SynchronizationContext, even if the user is running in legacy SynchronizationContext mode AspNetSynchronizationContext syncContext = new AspNetSynchronizationContext(this); _httpContext.SyncContext = syncContext; bool webSocketRequestSucceeded = false; try { // need to keep track of this in the manager so that we can abort if it the AppDomain goes down AspNetWebSocketManager.Current.Add(webSocket); // bump up the total count (the currently-executing count is recorded separately) PerfCounters.IncrementCounter(AppPerfCounter.REQUESTS_TOTAL_WEBSOCKETS); // Release the reference to the user delegate (which might just be a simple initialization routine) so that // the GC can claim it. The only thing that needs to remain alive is the Task itself, which we're referencing. Task task = null; syncContext.Send(_ => { task = _userFunc(new AspNetWebSocketContextImpl(new HttpContextWrapper(_httpContext), _root.WorkerRequest, webSocket)); }, null); // Was there an exception from user code? If so, rethrow (which logs). ExceptionDispatchInfo exception = syncContext.ExceptionDispatchInfo; if (exception != null) { exception.Throw(); } _userFunc = null; await task.ConfigureAwait(continueOnCapturedContext: false); // Was there an exception from the earlier call to DoFlush? If so, rethrow (which logs). // This needs to occur after the user's callback finishes, otherwise ASP.NET could try // to complete the request while the callback is still accessing it. if (flushExceptionDispatchInfo != null) { flushExceptionDispatchInfo.Throw(); } // Any final state except Aborted is marked as 'success'. // It's possible execution never reaches this point, e.g. if the user's // callback throws an exception. In that case, 'webSocketRequestSucceeded' // will keep its default value of false, and the performance counter // will mark this request as having failed. if (webSocket.State != WebSocketState.Aborted) { webSocketRequestSucceeded = true; PerfCounters.IncrementCounter(AppPerfCounter.REQUESTS_SUCCEEDED_WEBSOCKETS); } } finally { // we need to make sure the user can't call the WebSocket any more after this point _isProcessingComplete = true; webSocket.DisposeInternal(); AspNetWebSocketManager.Current.Remove(webSocket); if (!webSocketRequestSucceeded) { PerfCounters.IncrementCounter(AppPerfCounter.REQUESTS_FAILED_WEBSOCKETS); } } } catch (Exception ex) { // don't let the exception propagate upward; just log it instead WebBaseEvent.RaiseRuntimeError(ex, null); } return webSocket; }
internal void RestoreSavedAspNetSynchronizationContext(AspNetSynchronizationContext syncContext) { AsyncOperationManager.SynchronizationContext = syncContext; this._syncContext = syncContext; }
private void ExecuteInternal(IHttpHandler handler, TextWriter writer, bool preserveForm, bool setPreviousPage, VirtualPath path, VirtualPath filePath, string physPath, Exception error, string queryStringOverride) { if (handler == null) { throw new ArgumentNullException("handler"); } HttpRequest request = this._context.Request; HttpResponse response = this._context.Response; HttpApplication applicationInstance = this._context.ApplicationInstance; HttpValueCollection form = null; VirtualPath path2 = null; string queryStringText = null; TextWriter writer2 = null; AspNetSynchronizationContext syncContext = null; this.VerifyTransactionFlow(handler); this._context.PushTraceContext(); this._context.SetCurrentHandler(handler); bool enabled = this._context.SyncContext.Enabled; this._context.SyncContext.Disable(); try { try { this._context.ServerExecuteDepth++; path2 = request.SwitchCurrentExecutionFilePath(filePath); if (!preserveForm) { form = request.SwitchForm(new HttpValueCollection()); if (queryStringOverride == null) { queryStringOverride = string.Empty; } } if (queryStringOverride != null) { queryStringText = request.QueryStringText; request.QueryStringText = queryStringOverride; } if (writer != null) { writer2 = response.SwitchWriter(writer); } Page page = handler as Page; if (page != null) { if (setPreviousPage) { page.SetPreviousPage(this._context.PreviousHandler as Page); } Page page2 = this._context.Handler as Page; if ((page2 != null) && page2.SmartNavigation) { page.SmartNavigation = true; } if (page is IHttpAsyncHandler) { syncContext = this._context.InstallNewAspNetSynchronizationContext(); } } if (((handler is StaticFileHandler) || (handler is DefaultHttpHandler)) && !DefaultHttpHandler.IsClassicAspRequest(filePath.VirtualPathString)) { try { response.WriteFile(physPath); } catch { error = new HttpException(0x194, string.Empty); } } else if (!(handler is Page)) { error = new HttpException(0x194, string.Empty); } else { if (handler is IHttpAsyncHandler) { bool isInCancellablePeriod = this._context.IsInCancellablePeriod; if (isInCancellablePeriod) { this._context.EndCancellablePeriod(); } try { IHttpAsyncHandler handler2 = (IHttpAsyncHandler)handler; IAsyncResult result = handler2.BeginProcessRequest(this._context, null, null); if (!result.IsCompleted) { bool flag3 = false; try { try { } finally { Monitor.Exit(applicationInstance); flag3 = true; } WaitHandle asyncWaitHandle = result.AsyncWaitHandle; if (asyncWaitHandle == null) { goto Label_0210; } asyncWaitHandle.WaitOne(); goto Label_0226; Label_020A: Thread.Sleep(1); Label_0210: if (!result.IsCompleted) { goto Label_020A; } } finally { if (flag3) { Monitor.Enter(applicationInstance); } } } Label_0226: try { handler2.EndProcessRequest(result); } catch (Exception exception) { error = exception; } goto Label_0306; } finally { if (isInCancellablePeriod) { this._context.BeginCancellablePeriod(); } } } using (new DisposableHttpContextWrapper(this._context)) { try { handler.ProcessRequest(this._context); } catch (Exception exception2) { error = exception2; } } } } finally { this._context.ServerExecuteDepth--; this._context.RestoreCurrentHandler(); if (writer2 != null) { response.SwitchWriter(writer2); } if ((queryStringOverride != null) && (queryStringText != null)) { request.QueryStringText = queryStringText; } if (form != null) { request.SwitchForm(form); } request.SwitchCurrentExecutionFilePath(path2); if (syncContext != null) { this._context.RestoreSavedAspNetSynchronizationContext(syncContext); } if (enabled) { this._context.SyncContext.Enable(); } this._context.PopTraceContext(); } } catch { throw; } Label_0306: if (error == null) { return; } if ((error is HttpException) && (((HttpException)error).GetHttpCode() != 500)) { error = null; } if (path != null) { throw new HttpException(System.Web.SR.GetString("Error_executing_child_request_for_path", new object[] { path }), error); } throw new HttpException(System.Web.SR.GetString("Error_executing_child_request_for_handler", new object[] { handler.GetType().ToString() }), error); }
internal AspNetSynchronizationContext InstallNewAspNetSynchronizationContext() { AspNetSynchronizationContext context = this._syncContext; if ((context != null) && (context == AsyncOperationManager.SynchronizationContext)) { this._syncContext = new AspNetSynchronizationContext(this.ApplicationInstance); AsyncOperationManager.SynchronizationContext = this._syncContext; return context; } return null; }
internal void ClearReferences() { this._appInstance = null; this._handler = null; this._handlerStack = null; this._currentHandler = null; if (this._isIntegratedPipeline) { this._items = null; this._syncContext = null; } }
internal void RestoreSavedAspNetSynchronizationContext(AspNetSynchronizationContext syncContext);
internal PageAsyncInfo(Page page) { this._page = page; this._app = page.Context.ApplicationInstance; this._syncContext = page.Context.SyncContext; this._completionCallback = new AsyncCallback(this.OnAsyncHandlerCompletion); this._callHandlersThreadpoolCallback = new WaitCallback(this.CallHandlersFromThreadpoolThread); this._callHandlersCancellableCallback = new WaitCallback(this.CallHandlersCancellableCallback); }
private async Task <AspNetWebSocket> ProcessRequestImplAsync() { AspNetWebSocket webSocket = null; try { // SendResponse and other asynchronous notifications cannot be process by ASP.NET after this point. _root.WorkerRequest.SuppressSendResponseNotifications(); // A flush is necessary to activate the WebSocket module so that we can get its pointer. // // DevDiv #401948: We can't allow a flush failure to propagate out, otherwise the rest of // this method doesn't run, which could leak resources (by not invoking the user callback) // or cause weird behavior (by not calling CompleteTransitionToWebSocket, which could corrupt // server state). If the flush fails, we'll wait to propagate the exception until a safe // point later in this method. ExceptionDispatchInfo flushExceptionDispatchInfo = DoFlush(); // Create the AspNetWebSocket. There's a chance that the client disconnected before we // hit this code. If this is the case, we'll pass a null WebSocketPipe to the // AspNetWebSocket ctor, which immediately sets the socket into an aborted state. UnmanagedWebSocketContext unmanagedWebSocketContext = _root.WorkerRequest.GetWebSocketContext(); WebSocketPipe pipe = (unmanagedWebSocketContext != null) ? new WebSocketPipe(unmanagedWebSocketContext, PerfCounters.Instance) : null; webSocket = new AspNetWebSocket(pipe, _subProtocol); // slim down the HttpContext as much as possible to allow the GC to reclaim memory _httpContext.CompleteTransitionToWebSocket(); // always install a new SynchronizationContext, even if the user is running in legacy SynchronizationContext mode AspNetSynchronizationContext syncContext = new AspNetSynchronizationContext(this); _httpContext.SyncContext = syncContext; bool webSocketRequestSucceeded = false; try { // need to keep track of this in the manager so that we can abort if it the AppDomain goes down AspNetWebSocketManager.Current.Add(webSocket); // bump up the total count (the currently-executing count is recorded separately) PerfCounters.IncrementCounter(AppPerfCounter.REQUESTS_TOTAL_WEBSOCKETS); // Release the reference to the user delegate (which might just be a simple initialization routine) so that // the GC can claim it. The only thing that needs to remain alive is the Task itself, which we're referencing. Task task = null; syncContext.Send(_ => { task = _userFunc(new AspNetWebSocketContextImpl(new HttpContextWrapper(_httpContext), _root.WorkerRequest, webSocket)); }, null); // Was there an exception from user code? If so, rethrow (which logs). ExceptionDispatchInfo exception = syncContext.ExceptionDispatchInfo; if (exception != null) { exception.Throw(); } _userFunc = null; await task.ConfigureAwait(continueOnCapturedContext : false); // Was there an exception from the earlier call to DoFlush? If so, rethrow (which logs). // This needs to occur after the user's callback finishes, otherwise ASP.NET could try // to complete the request while the callback is still accessing it. if (flushExceptionDispatchInfo != null) { flushExceptionDispatchInfo.Throw(); } // Any final state except Aborted is marked as 'success'. // It's possible execution never reaches this point, e.g. if the user's // callback throws an exception. In that case, 'webSocketRequestSucceeded' // will keep its default value of false, and the performance counter // will mark this request as having failed. if (webSocket.State != WebSocketState.Aborted) { webSocketRequestSucceeded = true; PerfCounters.IncrementCounter(AppPerfCounter.REQUESTS_SUCCEEDED_WEBSOCKETS); } } finally { // we need to make sure the user can't call the WebSocket any more after this point _isProcessingComplete = true; webSocket.DisposeInternal(); AspNetWebSocketManager.Current.Remove(webSocket); if (!webSocketRequestSucceeded) { PerfCounters.IncrementCounter(AppPerfCounter.REQUESTS_FAILED_WEBSOCKETS); } } } catch (Exception ex) { // don't let the exception propagate upward; just log it instead WebBaseEvent.RaiseRuntimeError(ex, null); } return(webSocket); }