public static AppFunc Middleware(AppFunc app) { return env => { Action<Exception, Action<byte[], int, int>> showErrorMessage = (ex, write) => ErrorPage(env, ex, text => { var data = Encoding.ASCII.GetBytes(text); write(data, 0, data.Length); }); Func<Exception, Task> showErrorPage = ex => { var response = new Response(env) { Status = "500 Internal Server Error", ContentType = "text/html" }; showErrorMessage(ex, response.Write); return TaskHelpers.Completed(); }; // Don't try to modify the headers after the first write has occurred. TriggerStream triggerStream = new TriggerStream(env.Get<Stream>(OwinConstants.ResponseBody)); env[OwinConstants.ResponseBody] = triggerStream; bool bodyHasStarted = false; triggerStream.OnFirstWrite = () => { bodyHasStarted = true; }; try { return app(env) .Catch(errorInfo => { if (!bodyHasStarted) { showErrorPage(errorInfo.Exception).Wait(); } else { showErrorMessage(errorInfo.Exception, triggerStream.Write); } return errorInfo.Handled(); }); } catch (Exception exception) { if (!bodyHasStarted) { return showErrorPage(exception); } else { showErrorMessage(exception, triggerStream.Write); return TaskHelpers.Completed(); } } }; }
public Task Invoke(IDictionary<string, object> env) { IList<string> warnings = new List<string>(); if (!TryValidateCall(env, warnings)) { return TaskHelpers.Completed(); } var req = new Request(env); Stream originalStream = req.Body; TriggerStream triggerStream = new TriggerStream(originalStream); req.Body = triggerStream; // Validate response headers and values on first write. bool responseValidated = false; Action responseValidation = () => { if (responseValidated) { return; } responseValidated = true; if (!TryValidateResult(env, warnings)) { return; } if (warnings.Count > 0) { req.Headers["X-OwinValidatorWarning"] = warnings.ToArray(); } }; triggerStream.OnFirstWrite = responseValidation; try { return nextApp(env) // Run response validation explicitly in case there was no response data written. .Then(responseValidation) .Catch(errorInfo => { SetFatalResult(env, "6.1", "An asynchronous exception was thrown from the AppFunc: <br>" + errorInfo.Exception.ToString().Replace(Environment.NewLine, "<br>")); return errorInfo.Handled(); }); } catch (Exception ex) { SetFatalResult(env, "6.1", "A synchronous exception was thrown from the AppFunc: <br>" + ex.ToString().Replace(Environment.NewLine, "<br>")); return TaskHelpers.Completed(); } }
public Task Invoke(IDictionary<string, object> env) { Request request = new Request(env); Response response = new Response(env); Stream orriginalStream = response.OutputStream; TriggerStream triggerStream = new TriggerStream(orriginalStream); response.OutputStream = triggerStream; MemoryStream buffer = null; triggerStream.OnFirstWrite = () => { if (IsStatusWithNoNoEntityBody(response.StatusCode) || response.Headers.ContainsKey("Content-Length") || response.Headers.ContainsKey("Transfer-Encoding")) { return; } // Buffer buffer = new MemoryStream(); triggerStream.InnerStream = buffer; }; env[OwinConstants.ResponseBody] = triggerStream; return nextApp(env).Then(() => { if (buffer != null) { if (buffer.Length == 0) { response.Headers.SetHeader("Content-Length", "0"); } else { response.Headers.SetHeader("Content-Length", buffer.Length.ToString(CultureInfo.InvariantCulture)); // Suppress the body for HEAD requests. if (!"HEAD".Equals(request.Method, StringComparison.OrdinalIgnoreCase)) { buffer.Seek(0, SeekOrigin.Begin); return buffer.CopyToAsync(orriginalStream); } } } else if (!IsStatusWithNoNoEntityBody(response.StatusCode) && !response.Headers.ContainsKey("Content-Length") && !response.Headers.ContainsKey("Transfer-Encoding")) { // There were no Writes. response.Headers.SetHeader("Content-Length", "0"); } return TaskHelpers.Completed(); }); }
public Task Invoke(IDictionary<string, object> env) { var resp = new Response(env); Stream orriginalStream = resp.Body; TriggerStream triggerStream = new TriggerStream(orriginalStream); resp.Body = triggerStream; triggerStream.OnFirstWrite = () => { var responseHeaders = resp.Headers; if (!responseHeaders.HasHeader("Content-Type")) { responseHeaders.SetHeader("Content-Type", contentType); } }; return nextApp(env).Then(() => { // Make sure this gets run even if there were no writes. triggerStream.OnFirstWrite(); }); }
public Task Invoke(IDictionary<string, object> env) { Request request = new Request(env); Response response = new Response(env); Stream orriginalStream = response.OutputStream; TriggerStream triggerStream = new TriggerStream(orriginalStream); response.OutputStream = triggerStream; FilterStream filterStream = null; bool finalizeHeadersExecuted = false; Action finalizeHeaders = () => { finalizeHeadersExecuted = true; if (IsStatusWithNoNoEntityBody(response.StatusCode) || response.Headers.ContainsKey("Content-Length") || response.Headers.ContainsKey("Transfer-Encoding")) { return; } // Buffer response.Headers.SetHeader("Transfer-Encoding", "chunked"); if ("HEAD".Equals(request.Method, StringComparison.OrdinalIgnoreCase)) { // Someone tried to write a body for a HEAD request. Suppress it. triggerStream.InnerStream = Stream.Null; } else { filterStream = new FilterStream(orriginalStream, OnWriteFilter); triggerStream.InnerStream = filterStream; } }; // Hook first write triggerStream.OnFirstWrite = finalizeHeaders; response.Body = triggerStream; // Hook SendFileFunc var sendFile = response.SendFileAsync; if (sendFile != null) { response.SendFileAsync = (name, offset, count, cancel) => { if (!finalizeHeadersExecuted) { finalizeHeaders(); } if (filterStream == null) { // Due to headers we are not doing chunked, just pass through. return sendFile(name, offset, count, cancel); } count = count ?? new FileInfo(name).Length - offset; // Insert chunking around the file body ArraySegment<byte> prefix = ChunkPrefix((uint)count); return orriginalStream.WriteAsync(prefix.Array, prefix.Offset, prefix.Count) .Then(() => orriginalStream.FlushAsync()) // Flush to ensure the data hits the wire before sendFile. .Then(() => sendFile(name, offset, count, cancel)) .Then(() => orriginalStream.WriteAsync(EndOfChunk.Array, EndOfChunk.Offset, EndOfChunk.Count)); }; } return nextApp(env).Then(() => { if (!finalizeHeadersExecuted) { finalizeHeaders(); } if (filterStream != null) { // Write the chunked terminator return orriginalStream.WriteAsync(FinalChunk.Array, FinalChunk.Offset, FinalChunk.Count); } return TaskHelpers.Completed(); }); }
public Task Invoke(IDictionary<string, object> env) { // the first non-404 result will the the one to take effect // any subsequent apps are not called var iter = apps.GetEnumerator(); TaskCompletionSource<object> tcs = new TaskCompletionSource<object>(); var resp = new Response(env); Stream outputStream = resp.Body; Action fallback = () => { }; fallback = () => { fallbackApp(env) .Then(() => tcs.TrySetResult(null)) .Catch(errorInfo => { tcs.TrySetException(errorInfo.Exception); return errorInfo.Handled(); }); }; // Empty list if (!iter.MoveNext()) { fallback(); return tcs.Task; } Action loop = () => { }; loop = () => { var threadId = Thread.CurrentThread.ManagedThreadId; for (var tryAgainOnSameThread = true; tryAgainOnSameThread; ) { TriggerStream triggerStream = new TriggerStream(outputStream); triggerStream.OnFirstWrite = () => { if (resp.StatusCode == 404) { triggerStream.InnerStream = Stream.Null; } }; env[OwinConstants.ResponseBody] = triggerStream; tryAgainOnSameThread = false; iter.Current.Invoke(env) .Then(() => { if (resp.StatusCode != 404) { tcs.TrySetResult(null); return; } // Cleanup and try the next one. resp.Headers.Clear(); resp.Body = outputStream; if (iter.MoveNext()) { // ReSharper disable AccessToModifiedClosure if (threadId == Thread.CurrentThread.ManagedThreadId) { tryAgainOnSameThread = true; } else { loop(); } // ReSharper restore AccessToModifiedClosure } else { fallback(); } }) .Catch(errorInfo => { tcs.TrySetException(errorInfo.Exception); return errorInfo.Handled(); }); } threadId = 0; }; loop(); return tcs.Task; }