Hello everyone!
Sorry for the TL;DR email. I’m awful at being simultaneously terse and understandable. Hell, half the time I can’t even manage understandable. :)
I hate to say it, especially because of all the great recent progress. There is a kind of serious problem I’m having with some of the web frameworks we’ve been writing adapters for...
It seems like all the frameworks fit into two categories, double-tap or single-tap. Double-tap: the framework assumes it provides a response with headers and will write to the body in a callback. Single-tap: the framework assumes it can set headers and start writing output from the same point in code without jumping the header/body gap via an async or callback of any kind.
Rack, Web API, OWIN, Nancy are all double-tap… Node, Razor, SignalR, FUBU are all single-tap…
=== what would a single-tap OWIN look like? ===
I think the thing that would change would be instead of returning a task of ResultParameters or Tuple - the server would provide the response fundamentals in the environment as env[“owin.StatusCode”], env[“owin.ResponseHeaders”], and env[“owin.ResponseBody”]. Probably also move the request headers and stream back into the environment to follow suit.
So on the plus side the func becomes the simplest it’s ever been:
using AppFunc = Func<IDictionary<string, object>, Task>;
env goes in, task goes out. The task is done-or-error and lets the middleware and fwks run async. This doesn’t affect anything else in terms of middleware pipeline building, passing the next app argument, calling the next in line with “return app(env)”, etc.
=== why is this a problem? ===
There are ways to run a single-tap web framework on a double-tap owin pipeline, but they all boil down to buffering write data in the web framework side outside of the owin pipeline until the point when initial call into the framework returns all the way back and the server can make the body callback. Increased complexity, more memory to hold the body until the fwk returns, and cpu spent copying that data… Plus for fully synchronous web frameworks it means every response body will be entirely buffered because there’s no way for the server to deliver the output stream until the initial call returns. Unless you always queueuserworkitem onto another thread to call the fwk in order to return the first thread. I mean – I’m usually the last person to complain about complexity – but it’s pretty extreme compared to just passing the server’s output stream and a response header idictionary in the original call.
Running a double-tap framework on a single-tap pipeline by comparison is easy – the adapter just calls framework then calls its callback with the output stream.
My fear is that any of the benefits of having a double-tap pipeline may be small compared to the complexity and overhead of needed to run single-tap frameworks…
Thoughts? Seem reasonable? Good topic for the call tomorrow?
Thanks!
-Lou
Double tap means the application writes all the headers, then yields to the server and waits to be asked for the content. Single tap means the application sets headers and writes content without yielding control to the server at any point, and could possibly continue to set headers after writing content, requiring the server to buffer the output stream.
Mark
What we realized the hard way with SignalR is that it's difficult for a single tap component to bridge that separation when building on top of a double tap component, requiring some strange thread hopping and/or buffering. However, building a double tap component on top of a single tap component only requires delaying the exposure of the output stream.
When brainstorming lists of single and double tap components, there appear to be many more servers and frameworks using the single tap model. In fact, the only servers using double tap I could think of were Kayak and Firefly, and Louis offered to re-write Firefly. Conceptually I prefer the double tap model for a variety of reasons, but for interop purposes I don't think it's practical here.
________________________________
> Date: Sat, 25 Aug 2012 05:05:00 -0700
> From: and...@selfinflicted.org