private void Completion(Exception exception) { // NOTE (steveb): result.Return()/Throw() must be called by the coroutine; // if the coroutine omits to call it, we will throw an exception // since 'result' will not be marked as finished if (exception != null) { try { // try to forward exception to recipient exception.SetCoroutineInfo(this); _result.Throw(exception); } catch { // result object was already set _log.ErrorExceptionFormat(exception, "Completion(): unhandled exception in {0} coroutine [1]", FullName); } } else if (!_result.HasFinished) { try { exception = new CoroutineMissingResultException(); exception.SetCoroutineInfo(this); // operation completed, but result hasn't been set _result.Throw(exception); // always log the fact that a coroutine failed to set a result; this is a pretty bad situation! _log.Error("Completion() failed", exception); } catch { // result object must have been set in the meantime if (exception != null) { _log.ErrorExceptionFormat(exception, "Completion(): unhandled exception in {0} coroutine [2]", FullName); } else { _log.ErrorFormat("Coroutine.Completion(): missing result caused unhandled exception in {0} coroutine [3]", FullName); } } } }
//--- Methods --- internal void Handler(Result <DreamMessage> result) { DreamMessage request; // check if previous feature handler threw an exception try { if (result.HasException) { DreamAbortException abort = result.Exception as DreamAbortException; if (abort != null) { // extract contained message request = abort.Response; } else { // check if this is a cached response we need to forward DreamCachedResponseException cached = result.Exception as DreamCachedResponseException; if (cached != null) { _response.Throw(cached); return; } // convert exception into message DreamMessage exceptionMessage = null; ExceptionTranslator[] translators = _context.Feature.ExceptionTranslators; if (translators != null) { bool locallyAttachedContext = false; try { if (DreamContext.CurrentOrNull == null) { _context.AttachToCurrentTaskEnv(); locallyAttachedContext = true; } foreach (var translate in translators) { exceptionMessage = translate(_context, result.Exception); if (exceptionMessage != null) { break; } } } finally { if (locallyAttachedContext) { _context.DetachFromTaskEnv(); } } } if (exceptionMessage == null) { _log.ErrorExceptionFormat(result.Exception, "handler for {0}:{1} failed ({2})", _context.Verb, _context.Uri.ToString(false), _previousName); exceptionMessage = DreamMessage.InternalError(result.Exception); } request = exceptionMessage; } } else { request = result.Value; } } catch (Exception e) { if (result.HasException) { _log.ErrorExceptionFormat(result.Exception, "handler for {0}:{1} failed ({2}), cascading via processing exception '{3}'", _context.Verb, _context.Uri.ToString(false), _previousName, e); request = DreamMessage.InternalError(result.Exception); } else { _log.ErrorExceptionFormat(e, "handler for {0}:{1} failed completing stage '{2}'", _context.Verb, _context.Uri.ToString(false), _previousName); request = DreamMessage.InternalError(e); } } // check if feature handler can handle this message if (!MainStage || request.IsSuccessful) { TaskEnv.ExecuteNew(() => Handler_DreamMessage(request)); } else { // non-success messages skip the main stage _response.Return(request); } }
private Yield ResponseHandler(string verb, XUri requestUri, DreamMessage request, Result <DreamMessage> response, HttpListenerContext httpContext, Action <string> activity, Result result) { DreamMessage item = null; request.Close(); try { activity("begin ResponseHandler"); if (response.HasException) { _log.ErrorExceptionFormat(response.Exception, "Request Failed [{0}:{1}]: {2}", verb, requestUri.Path, response.Exception.Message); } item = response.HasException ? DreamMessage.InternalError(response.Exception) : response.Value; // set status _log.TraceMethodCall("ResponseHandler: Status", item.Status, httpContext.Request.HttpMethod, httpContext.Request.Url); httpContext.Response.StatusCode = (int)item.Status; // remove internal headers item.Headers.DreamTransport = null; item.Headers.DreamPublicUri = null; // add out-going headers if (item.Headers.Server == null) { item.Headers.Server = ServerSignature; } // create stream for response (this will force the creation of the 'Content-Length' header as well) Stream stream = item.ToStream(); // copy headers httpContext.Response.Headers.Clear(); foreach (KeyValuePair <string, string> pair in item.Headers) { _log.TraceMethodCall("SendHttpResponse: Header", pair.Key, pair.Value); HttpUtil.AddHeader(httpContext.Response, pair.Key, pair.Value); } // add set-cookie headers to response if (item.HasCookies) { foreach (DreamCookie cookie in item.Cookies) { httpContext.Response.Headers.Add(DreamHeaders.SET_COOKIE, cookie.ToSetCookieHeader()); } } // disable keep alive behavior httpContext.Response.KeepAlive = false; // send message stream long size = item.ContentLength; if (((size == -1) || (size > 0)) && (stream != Stream.Null)) { activity(string.Format("pre CopyStream ({0} bytes)", size)); yield return(CopyStream(activity, stream, httpContext.Response.OutputStream, size, new Result <long>(DreamHostService.MAX_REQUEST_TIME)).CatchAndLog(_log)); activity("post CopyStream"); } activity("pre Flush"); httpContext.Response.OutputStream.Flush(); activity("post Flush"); result.Return(); activity("end ResponseHandler"); } finally { activity(null); if (item != null) { item.Close(); } httpContext.Response.Close(); } }