Hi all, What would be the best way to implement something like a generic caching layer that can be placed between mongrel2 and a backend? I can think of 2 possible ways for now and I propose a third one. First possibility would be to just plug the caching app into mongrel2 and have its backends connect to that (probably using 0mq for that). I think this complicates the infrastructure, because the caching app needs to replicate a lot of the functionality in mongrel2 to interact with its backends. Secondly: Plug both the caching app and the backend into mongrel2, caching app on "normal" url, backend on some "internal" url. The caching app can then just use http to do new requests on mongrel2 on this "internal" url. A nicer solution (which I think isn't there at the moment) that I think would be nicer: Allow a backend to issue requests at mongrel2 using a 0mq socket. This way, requests don't have to be parsed twice, probably increasing performance. A lot of special "middleware" apps can be created that use this functionality. Are there plans to support anything like this? Thanks, Mathijs
On Mon, May 09, 2011 at 09:05:14AM +0200, Mathijs Kwik wrote: > Hi all, > A nicer solution (which I think isn't there at the moment) that I > think would be nicer: > Allow a backend to issue requests at mongrel2 using a 0mq socket. > This way, requests don't have to be parsed twice, probably increasing > performance. So, something like special responses that say things like: * just send data X again. * cache data X for 20 minutes * invalidate data X right now And so on? -- Zed A. Shaw http://zedshaw.com/
Hi Zed, Well I wasn't saying mongrel2 should do the caching or something like that. I was suggesting that there can be a wide range of apps that are not "endpoints" that generate the full response themselves, but rather intercept requests (or responses) to change/enhance/check them some more. Think about additional/separate authentication, special logging/tracing, optimizing for certain devices (compressing images), single-signon between apps, and caching as well. These applications need to mangle the incoming request, forward that to the "real" app (or some other layer in the middleware stack), receive the response back, mangle that some more, and send the final response back. If they need to communicate/signal each other, this usually piggy-backs on some special headers. In other words, apps need to be able to nest and forward (changed) requests and responses to each other. Like a before- and after filter. I'm sure you're familiar with the middleware concept that ruby's rack stack uses, this is exactly what I'm looking for. Having to connect those "middleware" apps yourself largely defeats the purpose of mongrel2, even while 0mq is a big help. So it would be great if mongrel2's config could be used to put these stacks together. One way to emulate this now is to have the "real" app listen on "myapp.internal" on mongrel2 and have a middleware app listen on myapp.com. After doing its thing, the middleware app can just use an http client to send the modified request to myapp.internal (on mongrel2), wait for the response(preferably without blocking), modify that, and finally hand it back to mongrel2 to be replied to the client. This however, is not very efficient. For one, the modified request has to be re-parsed when it arrives for myapp.internal. Also, the middleware app itself is responsible for an efficient event-loop (so it can accept new requests while waiting for the response of the first one). It would be great if mongrel2's internals can be exposed a little more, so these middleware apps can just let mongrel2 handle these dirty details. So what I would like is a special response like: "this is not the real response, but rather a new request for app X" (headers/body netstrings, so no need to re-parse) And a special kind of request like: "this is the response from app X" Then you should be able to configure mongrel2 to "insert" apps between the request/response streams of other apps. I think there are 3 cases to consider: - middleware apps that just modify a request They receive the incoming request, decide to answer themselves (for example a "hit" on a cache middleware), or to forward the (modified) request to app X. For that they use the special response. The listener id is forwarded to app X as well, so when app X responds, it responds directly to the original client - middleware apps that just modify a response Mongrel2 receives a request for app X. Mongrel2 modifies the listener id to something special. When app X responds, mongrel2 recognizes the special listener id and knows to forward the response to the middleware using the special request type. The middleware app does its thing, and responds to the original listener id. - middleware apps that do both the above I hope this makes my wishes a bit clearer. As stated, the behavior of ruby's rack stack is just the thing I need. However, it would be very cool if mongrel2 could give me this same behavior, but with the usual advantages (especially language agnostic) so I can write middlewares in languages that I see fit. Any chance something like that will make it into mongrel2? Thanks, Mathijs On Tue, May 10, 2011 at 7:26 PM, Zed A. Shaw <zedshaw@zedshaw.com> wrote: > On Mon, May 09, 2011 at 09:05:14AM +0200, Mathijs Kwik wrote: >> Hi all, >> A nicer solution (which I think isn't there at the moment) that I >> think would be nicer: >> Allow a backend to issue requests at mongrel2 using a 0mq socket. >> This way, requests don't have to be parsed twice, probably increasing >> performance. > > So, something like special responses that say things like: > > * just send data X again. > * cache data X for 20 minutes > * invalidate data X right now > > And so on? > > -- > Zed A. Shaw > http://zedshaw.com/ >
I guess I must be confused. This seems more inefficient then just modifying the request and sending it on to the handler. That is just have the middleware be *actually* in the middle. If you don't need to rewrite responses, then you only really have to be in the middle for the push/pull, which is even better. I don't see how having to connect the middleware apps yourself defeats the purpose of mongrel2 at all. If it's something that is actually valuable, then there will eventually be library(ies) that make it simple -Jason On 21:44 Tue 10 May , Mathijs Kwik wrote: > Hi Zed, > > Well I wasn't saying mongrel2 should do the caching or something like that. > I was suggesting that there can be a wide range of apps that are not > "endpoints" that generate the full response themselves, but rather > intercept requests (or responses) to change/enhance/check them some > more. > Think about additional/separate authentication, special > logging/tracing, optimizing for certain devices (compressing images), > single-signon between apps, and caching as well. > These applications need to mangle the incoming request, forward that > to the "real" app (or some other layer in the middleware stack), > receive the response back, mangle that some more, and send the final > response back. > If they need to communicate/signal each other, this usually > piggy-backs on some special headers. > In other words, apps need to be able to nest and forward (changed) > requests and responses to each other. > Like a before- and after filter. > I'm sure you're familiar with the middleware concept that ruby's rack > stack uses, this is exactly what I'm looking for. > > Having to connect those "middleware" apps yourself largely defeats the > purpose of mongrel2, even while 0mq is a big help. > So it would be great if mongrel2's config could be used to put these > stacks together. > > One way to emulate this now is to have the "real" app listen on > "myapp.internal" on mongrel2 and have a middleware app listen on > myapp.com. After doing its thing, the middleware app can just use an > http client to send the modified request to myapp.internal (on > mongrel2), wait for the response(preferably without blocking), modify > that, and finally hand it back to mongrel2 to be replied to the > client. > This however, is not very efficient. For one, the modified request has > to be re-parsed when it arrives for myapp.internal. Also, the > middleware app itself is responsible for an efficient event-loop (so > it can accept new requests while waiting for the response of the first > one). It would be great if mongrel2's internals can be exposed a > little more, so these middleware apps can just let mongrel2 handle > these dirty details. > > So what I would like is a special response like: > "this is not the real response, but rather a new request for app X" > (headers/body netstrings, so no need to re-parse) > > And a special kind of request like: > "this is the response from app X" > > Then you should be able to configure mongrel2 to "insert" apps between > the request/response streams of other apps. > I think there are 3 cases to consider: > > - middleware apps that just modify a request > They receive the incoming request, decide to answer themselves (for > example a "hit" on a cache middleware), or to forward the (modified) > request to app X. > For that they use the special response. > The listener id is forwarded to app X as well, so when app X responds, > it responds directly to the original client > > - middleware apps that just modify a response > Mongrel2 receives a request for app X. > Mongrel2 modifies the listener id to something special. > When app X responds, mongrel2 recognizes the special listener id and > knows to forward the response to the middleware using the special > request type. > The middleware app does its thing, and responds to the original listener id. > > - middleware apps that do both the above > > > I hope this makes my wishes a bit clearer. > As stated, the behavior of ruby's rack stack is just the thing I need. > However, it would be very cool if mongrel2 could give me this same > behavior, but with the usual advantages (especially language agnostic) > so I can write middlewares in languages that I see fit. > > Any chance something like that will make it into mongrel2? > > Thanks, > Mathijs > > > > On Tue, May 10, 2011 at 7:26 PM, Zed A. Shaw <zedshaw@zedshaw.com> wrote: > > On Mon, May 09, 2011 at 09:05:14AM +0200, Mathijs Kwik wrote: > >> Hi all, > >> A nicer solution (which I think isn't there at the moment) that I > >> think would be nicer: > >> Allow a backend to issue requests at mongrel2 using a 0mq socket. > >> This way, requests don't have to be parsed twice, probably increasing > >> performance. > > > > So, something like special responses that say things like: > > > > * just send data X again. > > * cache data X for 20 minutes > > * invalidate data X right now > > > > And so on? > > > > -- > > Zed A. Shaw > > http://zedshaw.com/ > > >
On Tue, May 10, 2011 at 03:17:07PM -0700, Jason Miller wrote: > I guess I must be confused. This seems more inefficient then just > modifying the request and sending it on to the handler. That is just > have the middleware be *actually* in the middle. If you don't need to > rewrite responses, then you only really have to be in the middle for the > push/pull, which is even better. Nobody said more "efficient", I said easier to do, and mostly because it involves a lot less gear and configuration. If it's efficiency you need then that's what the filters will be for. > I don't see how having to connect the middleware apps yourself defeats > the purpose of mongrel2 at all. If it's something that is actually > valuable, then there will eventually be library(ies) that make it simple That's what I'm imagining too. It'll be the same pattern I go for in most of my software: There's a core simple API for filters and then people go crazy making their own things others can inject. That's what happened with the handler protocol, so no reason it won't happen with the filters too. The thing I'm sort of worried about is that people will take the filters, and then just cram their old shitty way of doing things back into mongrel2, work that way for a while, and *then* realize they don't need it. That's always the progression, but before they had to follow this progression without making hard to debug and support filters. -- Zed A. Shaw http://zedshaw.com/
On Tue, May 10, 2011 at 09:44:27PM +0200, Mathijs Kwik wrote: > Hi Zed, > > Well I wasn't saying mongrel2 should do the caching or something like that. > I was suggesting that there can be a wide range of apps that are not > "endpoints" that generate the full response themselves, but rather > intercept requests (or responses) to change/enhance/check them some > more. I do that by just having a request go to a handler, who then sends it to other handlers that need to it in the chain. With mongrel2, the receiver of a request doesn't have to be the responder, so you can do this: m2 -> [auth -> unzip -> confabulate -> query -> reply_html] -> m2 And each of those is just a handler. In fact, in Tir this is directly supported as Tasks, which are just 0mq messages sent async to a backend from any handler, who then can respond how they want. > In other words, apps need to be able to nest and forward (changed) > requests and responses to each other. So try the above idea where they do this nest/forward/signal to each other through 0mq messaging. It works surprisingly well and fast, and ends up being much easier to work with than constantly reconfiguring the front-end server, at the expense of small initial installs are more complicated. Look at the Tir code for insights though into how to make this easier. > Like a before- and after filter. > I'm sure you're familiar with the middleware concept that ruby's rack > stack uses, this is exactly what I'm looking for. Yes, very familiar since they were basically stolen from the Mongrel Ruby API. :-) Now that I've said the above, filters are coming in the next rounds of development for things like this, but the goal with those is that they be only for extending mongrel2 for things that should be done on the edge or for weird uses like "inflight architecture upgrades". Examples are like, changing PHP's weird insistence on using GET parameters into nice URLs instead of just rewriting your code, or working around Rails inability to handle more than one request at a time reliably. So in theory you could do that, but try the chained 0mq instead first. You'll be surprised how easy it is in practice. > Having to connect those "middleware" apps yourself largely defeats the > purpose of mongrel2, even while 0mq is a big help. > > So it would be great if mongrel2's config could be used to put these > stacks together. So with this, it already does. It's a database, so you can just add a table for your own setup and then use it. I do this in Tir a bit where I just read the config.sqlite to configure it. I'm imagining you'd just make a table for these chains you want, then handlers wire themselves up based on how the database is. > One way to emulate this now is to have the "real" app listen on > "myapp.internal" on mongrel2 and have a middleware app listen on > myapp.com. After doing its thing, the middleware app can just use an > http client to send the modified request to myapp.internal (on Huh? Why would you use an http client? Just use 0mq between the two. > This however, is not very efficient. For one, the modified request has > to be re-parsed when it arrives for myapp.internal. Also, the > middleware app itself is responsible for an efficient event-loop (so > it can accept new requests while waiting for the response of the first > one). It would be great if mongrel2's internals can be exposed a > little more, so these middleware apps can just let mongrel2 handle > these dirty details. The filters would allow this, but I really think you're under estimating the speed of 0mq. It looks like you're thinking about this as if you had to use HTTP, but once the request is parsed out by Mongrel2 you can just shove it around using 0mq as-is all you want. Add to that the new tnetstrings format and it's pretty damn fast. Additionally, putting your whole app inside mongrel2 as a set of .so linked in filters is a horrible deployment story. Having to shutdown whole front-end servers just to change a few headers or some HTML is teh suck. > I hope this makes my wishes a bit clearer. > As stated, the behavior of ruby's rack stack is just the thing I need. So, I think this is the entire disconnect in your thinking that I'd like to correct. Rack is *entirely* "request response", it's not async. Because of this you have to do all this chaining and middleware config ninjitsu just to process a request. It's also brittle and doesn't handle large scale deployments very well because, should you have to alter the chain even a tiny bit, you're having to run around changing server configs on both sides. In Mongrel2, it doesn't care how receives and who sends its 0mq messages, *and* it doesn't even care if the responses it receives are complete. You can stream out 1 byte sequences of 0mq messages from 100 handler backends to a browser and mongrel2 won't care. I think until you actually try to grasp this you won't be able to articulate a good feature request for this. When I read what you're asking for, it's effectively: "I want to go backwards to a strict request/response chaining model." Here's some examples from your feature list ideas: > "this is not the real response, but rather a new request for app X" > (headers/body netstrings, so no need to re-parse) Create a ipc:// handler that takes all requests it receives, pulls the raw 0mq message (no parsing needed), slaps this tag on it, then sends it on to wherever it needs to go. In fact, it doesn't have to parse anything but the first few chars since we include the path and everything right at the beginning. No json or tnetstring parsing required. > And a special kind of request like: > "this is the response from app X" So, let's say the request from above bounces around between 10 different 0mq services, all dealing with it. *Any* of the 10 services can abort the request right there and send the response is if they are "the app". In fact the concept of "an app" is sort of pointless because you can carve your "app" up into as many pieces are you need or have the sanity to maintain. > Then you should be able to configure mongrel2 to "insert" apps between > the request/response streams of other apps. Create an ipc:/// that looks at the path, doesn't parse and then from that shuttles the request to the first part of the chain. No "insert" needed, it just sends it where it needs to go, and then the response goes directly to mongrel2 from whoever handled it. If you need chains, then you can use any number of simple patterns for work coordination to pull this off. This also has the added advantage that if the response can take a while, this handler can send an initial "ok got it" response and then the browser can hit a totally *different* url to receive the reply just like long polling. > - middleware apps that just modify a request > They receive the incoming request, decide to answer themselves (for > example a "hit" on a cache middleware), or to forward the (modified) > request to app X. I call these "inflight architecture upgrades". Any time chains of things are modifying the requests it's almost *always* because you're trying to avoid changing broken architecture of application choices. Things like URL rewriting, adding headers so Perl don't freak out, converting GET requests to pretty URLs, etc. This is the one place I'd see filters coming into play rather than the above chained handlers. I can see two ways people would go for this: 1) handlers just reparse and modify the requests before sending it on, and probably would use tnetstrings to avoid performance hits. 2) they write a filter and then beat their programmers in the face until the filter isn't needed anymore. I really like option #2. :-) > For that they use the special response. > The listener id is forwarded to app X as well, so when app X responds, > it responds directly to the original client Yep, and all of that works with the existing 0mq handler setup. So, sorry this is so long, but my response is these three points: 1. You can already do most of this with 0mq and it's better, please go try it out and then come back when you have a better understanding of how the async nature of mongrel2 just really makes this stuff so much easier. 2. Filters are coming so you'll be able to get this anyway, and they'll probably be pretty easy to write if you know C. 3. I think you should really throw out what you know about web servers based on Rack and middleware. The reason middleware exists is because CGI sucked so bad but was so ubiquitous that people had to work around it. To do that they made "middleware" that sat between apps and web servers with CGI vars as the glue. Mongrel2 just ditches all of that. Once we started really using it we all realized that the middleware is just pointless. Mongrel2 already sends everything you need and 0mq is already your "middleware", so why add another layer. Existing middleware is also *not* async so it can't handle a wide range of modern HTTP needs. Once you realize this there's this whole world that opens up. Hopefully that long email explains it, but feel free to do some more research and/or wait for filters (probably 1.7) and then try to implement what you were thinking about. -- Zed A. Shaw http://zedshaw.com/
Zed and Jason, Thanks for your replies. I must confess I've never actually used mongrel2 yet, so my question and assumptions are probably based on ignorance. I do see the enormous potential that mongrel2 will eventually give me and I'm quite familiar with 0mq and its power for internal communication between different pieces of an application. However, using this power for web purposes is new to me, and my main investigation as of yet was about how to "port" an old-style request-reply application to mongrel2. Specially, how to start splitting that monolithic beast into lots of smaller pieces of functionality. The first parts that came to mind were the different middleware layers I'm using. It indeed seems my thinking was a bit too conservative in trying to keep the request-reply flow the way it is. Indeed I need to just wire the pieces up myself and maybe start thinking about unusual flows that now become possible, because there's probably a lot of benefit to gain by thinking async there as well. I didn't know about tir, but I've done some lua (ex wow addict :) so I'll look into it as well to get some ideas and maybe even use it for certain new parts. So thanks for now, I need to investigate (and just try stuff) before requesting changes :) On Wed, May 11, 2011 at 12:13 AM, Zed A. Shaw <zedshaw@zedshaw.com> wrote: > On Tue, May 10, 2011 at 09:44:27PM +0200, Mathijs Kwik wrote: >> Hi Zed, >> >> Well I wasn't saying mongrel2 should do the caching or something like that. >> I was suggesting that there can be a wide range of apps that are not >> "endpoints" that generate the full response themselves, but rather >> intercept requests (or responses) to change/enhance/check them some >> more. > > I do that by just having a request go to a handler, who then sends it to > other handlers that need to it in the chain. With mongrel2, the > receiver of a request doesn't have to be the responder, so you can do > this: > > m2 -> [auth -> unzip -> confabulate -> query -> reply_html] -> m2 > > And each of those is just a handler. In fact, in Tir this is directly > supported as Tasks, which are just 0mq messages sent async to a backend > from any handler, who then can respond how they want. > >> In other words, apps need to be able to nest and forward (changed) >> requests and responses to each other. > > So try the above idea where they do this nest/forward/signal to each > other through 0mq messaging. It works surprisingly well and fast, and > ends up being much easier to work with than constantly reconfiguring the > front-end server, at the expense of small initial installs are more > complicated. > > Look at the Tir code for insights though into how to make this easier. > >> Like a before- and after filter. >> I'm sure you're familiar with the middleware concept that ruby's rack >> stack uses, this is exactly what I'm looking for. > > Yes, very familiar since they were basically stolen from the Mongrel > Ruby API. :-) > > Now that I've said the above, filters are coming in the next rounds of > development for things like this, but the goal with those is that they > be only for extending mongrel2 for things that should be done on the > edge or for weird uses like "inflight architecture upgrades". Examples > are like, changing PHP's weird insistence on using GET parameters into > nice URLs instead of just rewriting your code, or working around Rails > inability to handle more than one request at a time reliably. > > So in theory you could do that, but try the chained 0mq instead first. > You'll be surprised how easy it is in practice. > >> Having to connect those "middleware" apps yourself largely defeats the >> purpose of mongrel2, even while 0mq is a big help. >> >> So it would be great if mongrel2's config could be used to put these >> stacks together. > > So with this, it already does. It's a database, so you can just add a > table for your own setup and then use it. I do this in Tir a bit where > I just read the config.sqlite to configure it. I'm imagining you'd just > make a table for these chains you want, then handlers wire themselves up > based on how the database is. > >> One way to emulate this now is to have the "real" app listen on >> "myapp.internal" on mongrel2 and have a middleware app listen on >> myapp.com. After doing its thing, the middleware app can just use an >> http client to send the modified request to myapp.internal (on > > Huh? Why would you use an http client? Just use 0mq between the two. > >> This however, is not very efficient. For one, the modified request has >> to be re-parsed when it arrives for myapp.internal. Also, the >> middleware app itself is responsible for an efficient event-loop (so >> it can accept new requests while waiting for the response of the first >> one). It would be great if mongrel2's internals can be exposed a >> little more, so these middleware apps can just let mongrel2 handle >> these dirty details. > > The filters would allow this, but I really think you're under estimating > the speed of 0mq. It looks like you're thinking about this as if you > had to use HTTP, but once the request is parsed out by Mongrel2 you can > just shove it around using 0mq as-is all you want. Add to that the new > tnetstrings format and it's pretty damn fast. > > Additionally, putting your whole app inside mongrel2 as a set of .so > linked in filters is a horrible deployment story. Having to shutdown > whole front-end servers just to change a few headers or some HTML is > teh suck. > >> I hope this makes my wishes a bit clearer. >> As stated, the behavior of ruby's rack stack is just the thing I need. > > So, I think this is the entire disconnect in your thinking that I'd like > to correct. Rack is *entirely* "request response", it's not async. > Because of this you have to do all this chaining and middleware config > ninjitsu just to process a request. It's also brittle and doesn't > handle large scale deployments very well because, should you have to > alter the chain even a tiny bit, you're having to run around changing > server configs on both sides. > > In Mongrel2, it doesn't care how receives and who sends its 0mq > messages, *and* it doesn't even care if the responses it receives are > complete. You can stream out 1 byte sequences of 0mq messages from 100 > handler backends to a browser and mongrel2 won't care. > > I think until you actually try to grasp this you won't be able to > articulate a good feature request for this. When I read what you're > asking for, it's effectively: > > "I want to go backwards to a strict request/response chaining model." > > Here's some examples from your feature list ideas: > >> "this is not the real response, but rather a new request for app X" >> (headers/body netstrings, so no need to re-parse) > > Create a ipc:// handler that takes all requests it receives, pulls the > raw 0mq message (no parsing needed), slaps this tag on it, then sends it > on to wherever it needs to go. In fact, it doesn't have to parse > anything but the first few chars since we include the path and > everything right at the beginning. No json or tnetstring parsing > required. > >> And a special kind of request like: >> "this is the response from app X" > > So, let's say the request from above bounces around between 10 different > 0mq services, all dealing with it. *Any* of the 10 services can abort > the request right there and send the response is if they are "the app". > In fact the concept of "an app" is sort of pointless because you can > carve your "app" up into as many pieces are you need or have the sanity > to maintain. > >> Then you should be able to configure mongrel2 to "insert" apps between >> the request/response streams of other apps. > > Create an ipc:/// that looks at the path, doesn't parse and then from > that shuttles the request to the first part of the chain. No "insert" > needed, it just sends it where it needs to go, and then the response > goes directly to mongrel2 from whoever handled it. If you need chains, > then you can use any number of simple patterns for work coordination to > pull this off. This also has the added advantage that if the response > can take a while, this handler can send an initial "ok got it" response > and then the browser can hit a totally *different* url to receive the > reply just like long polling. > >> - middleware apps that just modify a request >> They receive the incoming request, decide to answer themselves (for >> example a "hit" on a cache middleware), or to forward the (modified) >> request to app X. > > I call these "inflight architecture upgrades". Any time chains of > things are modifying the requests it's almost *always* because you're > trying to avoid changing broken architecture of application choices. > Things like URL rewriting, adding headers so Perl don't freak out, > converting GET requests to pretty URLs, etc. This is the one place I'd > see filters coming into play rather than the above chained handlers. > > I can see two ways people would go for this: 1) handlers just reparse > and modify the requests before sending it on, and probably would use > tnetstrings to avoid performance hits. 2) they write a filter and then > beat their programmers in the face until the filter isn't needed > anymore. I really like option #2. :-) > >> For that they use the special response. >> The listener id is forwarded to app X as well, so when app X responds, >> it responds directly to the original client > > Yep, and all of that works with the existing 0mq handler setup. > > So, sorry this is so long, but my response is these three points: > > 1. You can already do most of this with 0mq and it's better, please go > try it out and then come back when you have a better understanding of > how the async nature of mongrel2 just really makes this stuff so much > easier. > > 2. Filters are coming so you'll be able to get this anyway, and they'll > probably be pretty easy to write if you know C. > > 3. I think you should really throw out what you know about web servers > based on Rack and middleware. The reason middleware exists is because > CGI sucked so bad but was so ubiquitous that people had to work around > it. To do that they made "middleware" that sat between apps and web > servers with CGI vars as the glue. Mongrel2 just ditches all of that. > Once we started really using it we all realized that the middleware is > just pointless. Mongrel2 already sends everything you need and 0mq is > already your "middleware", so why add another layer. Existing > middleware is also *not* async so it can't handle a wide range of modern > HTTP needs. Once you realize this there's this whole world that opens > up. > > Hopefully that long email explains it, but feel free to do some more > research and/or wait for filters (probably 1.7) and then try to > implement what you were thinking about. > > -- > Zed A. Shaw > http://zedshaw.com/ >