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) { 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) { Response response = new Response(env); Stream orriginalStream = response.OutputStream; TriggerStream triggerStream = new TriggerStream(orriginalStream); response.OutputStream = triggerStream; FilterStream filterStream = null; triggerStream.OnFirstWrite = () => { if (IsStatusWithNoNoEntityBody(response.StatusCode) || response.Headers.ContainsKey("Content-Length") || response.Headers.ContainsKey("Transfer-Encoding")) { return; } // Buffer response.Headers.SetHeader("Transfer-Encoding", "chunked"); filterStream = new FilterStream(orriginalStream, OnWriteFilter); triggerStream.InnerStream = filterStream; }; env[OwinConstants.ResponseBody] = triggerStream; return(nextApp(env).Then(() => { if (filterStream != null) { // Write the chunked terminator return orriginalStream.WriteAsync(FinalChunk.Array, FinalChunk.Offset, FinalChunk.Count); } else if (!IsStatusWithNoNoEntityBody(response.StatusCode) && !response.Headers.ContainsKey("Content-Length") && !response.Headers.ContainsKey("Transfer-Encoding")) { // There were no Writes. response.Headers.SetHeader("Transfer-Encoding", "chunked"); return orriginalStream.WriteAsync(FinalChunk.Array, FinalChunk.Offset, FinalChunk.Count); } return TaskHelpers.Completed(); })); }
public Task Invoke(IDictionary<string, object> env) { Response response = new Response(env); Stream orriginalStream = response.OutputStream; TriggerStream triggerStream = new TriggerStream(orriginalStream); response.OutputStream = triggerStream; FilterStream filterStream = null; triggerStream.OnFirstWrite = () => { if (IsStatusWithNoNoEntityBody(response.StatusCode) || response.Headers.ContainsKey("Content-Length") || response.Headers.ContainsKey("Transfer-Encoding")) { return; } // Buffer response.Headers.SetHeader("Transfer-Encoding", "chunked"); filterStream = new FilterStream(orriginalStream, OnWriteFilter); triggerStream.InnerStream = filterStream; }; env[OwinConstants.ResponseBody] = triggerStream; return nextApp(env).Then(() => { if (filterStream != null) { // Write the chunked terminator return orriginalStream.WriteAsync(FinalChunk.Array, FinalChunk.Offset, FinalChunk.Count); } else if (!IsStatusWithNoNoEntityBody(response.StatusCode) && !response.Headers.ContainsKey("Content-Length") && !response.Headers.ContainsKey("Transfer-Encoding")) { // There were no Writes. response.Headers.SetHeader("Transfer-Encoding", "chunked"); return orriginalStream.WriteAsync(FinalChunk.Array, FinalChunk.Offset, FinalChunk.Count); } return TaskHelpers.Completed(); }); }
public Task Invoke(IDictionary<string, object> env) { Stream orriginalStream = env.Get<Stream>(OwinConstants.ResponseBody); TriggerStream triggerStream = new TriggerStream(orriginalStream); env[OwinConstants.ResponseBody] = triggerStream; triggerStream.OnFirstWrite = () => { var responseHeaders = env.Get<IDictionary<string, string[]>>(OwinConstants.ResponseHeaders); 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) { Stream orriginalStream = env.Get <Stream>(OwinConstants.ResponseBody); TriggerStream triggerStream = new TriggerStream(orriginalStream); env[OwinConstants.ResponseBody] = triggerStream; triggerStream.OnFirstWrite = () => { var responseHeaders = env.Get <IDictionary <string, string[]> >(OwinConstants.ResponseHeaders); 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) { 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) { 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) { // 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>(); Stream outputStream = env.Get<Stream>(OwinConstants.ResponseBody); 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 (env.Get<int>(OwinConstants.ResponseStatusCode) == 404) { triggerStream.InnerStream = Stream.Null; } }; env[OwinConstants.ResponseBody] = triggerStream; tryAgainOnSameThread = false; iter.Current.Invoke(env) .Then(() => { if (env.Get<int>(OwinConstants.ResponseStatusCode) != 404) { tcs.TrySetResult(null); return; } // Cleanup and try the next one. env.Get<IDictionary<string, string[]>>(OwinConstants.ResponseHeaders).Clear(); env[OwinConstants.ResponseBody] = 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; }
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); }); var response = new Response(env); Func<Exception, Task> showErrorPage = ex => { response.Status = "500 Internal Server Error"; response.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(response.Body); response.Body = 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) { // 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); }
/// <inheritdoc/> public void Execute() { TriggerStream.EmitValue(true); }
/// <inheritdoc/> public void Start() { TriggerStream.Start(); EnabledStream.Start(); }
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 response.EndAsync(); }; // 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) { 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; env[OwinConstants.ResponseBody] = triggerStream; // Hook SendFileFunc SendFileFunc sendFile = env.Get <SendFileFunc>("sendfile.Func"); if (sendFile != null) { SendFileFunc sendFileChunked = (name, offset, count) => { if (!finalizeHeadersExecuted) { finalizeHeaders(); } if (filterStream == null) { // Due to headers we are not doing chunked, just pass through. return(sendFile(name, offset, count)); } 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)) .Then(() => orriginalStream.WriteAsync(EndOfChunk.Array, EndOfChunk.Offset, EndOfChunk.Count))); }; env["sendfile.Func"] = sendFileChunked; } 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) { 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; env[OwinConstants.ResponseBody] = triggerStream; // Hook SendFileFunc SendFileFunc sendFile = env.Get<SendFileFunc>("sendfile.Func"); if (sendFile != null) { SendFileFunc sendFileChunked = (name, offset, count) => { if (!finalizeHeadersExecuted) { finalizeHeaders(); } if (filterStream == null) { // Due to headers we are not doing chunked, just pass through. return sendFile(name, offset, count); } 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)) .Then(() => orriginalStream.WriteAsync(EndOfChunk.Array, EndOfChunk.Offset, EndOfChunk.Count)); }; env["sendfile.Func"] = sendFileChunked; } 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(); }); }