diff --git a/pep-0333.txt b/pep-0333.txt index 7fced7620..35b28a7f2 100644 --- a/pep-0333.txt +++ b/pep-0333.txt @@ -125,6 +125,17 @@ Other servers and gateways may use configuration files or other mechanisms to specify where an application object should be imported from, or otherwise obtained. +In addition to "pure" servers/gateways and applications/frameworks, +it is also possible to create "middleware" components that implement +both sides of this specification. Such components act as an +application to their containing server, and as a server to a +contained application, and can be used to provide extended APIs, +content transformation, navigation, and other useful functions. + + +The Application/Framework Side +------------------------------ + The application object is simply a callable object that accepts two arguments. The term "object" should not be misconstrued as requiring an actual object instance: a function, method, class, @@ -184,6 +195,10 @@ invoking a callable must *not* have any dependency on what kind of callable was provided to it. Callables are only to be called, not introspected upon. + +The Server/Gateway Side +----------------------- + The server or gateway invokes the application callable once for each request it receives from an HTTP client, that is directed at the application. To illustrate, here is a simple CGI gateway, implemented @@ -254,8 +269,89 @@ server. if hasattr(result,'close'): result.close() -In the next section, we will specify the precise semantics that -these illustrations are examples of. + +Middleware: Components that Play Both Sides +------------------------------------------- + +Note that a single object may play the role of a server with respect +to some application(s), while also acting as an application with +respect to some server(s). Such "middleware" components can perform +such functions as: + +* Routing a request to different application objects based on the + target URL, after rewriting the ``environ`` accordingly. + +* Allowing multiple applications or frameworks to run side-by-side + in the same process + +* Load balancing and remote processing, by forwarding requests and + responses over a network + +* Perform content postprocessing, such as applying XSL stylesheets + +The presence of middleware in general is transparent to both the +"server/gateway" and the "application/framework" sides of the +interface, and should require no special support. A user who +desires to incorporate middleware into an application simply +provides the middleware component to the server, as if it were +an application, and configures the middleware component to +invoke the application, as if the middleware component were a +server. Of course, the "application" that the middleware wraps +may in fact be another middleware component wrapping another +application, and so on, creating what is referred to as a +"middleware stack". + +For the most part, middleware must conform to the restrictions +and requirements of both the server and application sides of +WSGI. In some cases, however, requirements for middleware +are more stringent than for a "pure" server or application, +and these points will be noted in the specification. + +Here is a (tongue-in-cheek) example of a middleware component that +converts ``text/plain`` responses to pig latin, using Joe Strout's +``piglatin.py``. (Note: a "real" middleware component would +probably use a more robust way of checking the content type, and +should also check for a content encoding.) + +:: + + from piglatin import piglatin + + class Latinator: + + # by default, don't transform output + transform = str + + def __init__(self, application): + self.application = application + + def __call__(environ, start_response): + + def write_latin(data): + self.write(self.transform(data)) + + def start_latin(status,headers,exc_info=None): + + for name,value in headers: + if name.lower()=='content-type' and value=='text/plain': + self.transform = piglatin + # Strip content-length if present, else it'll be wrong + headers = [(name,value) + for name,value in headers + if name.lower()<>'content-length' + ] + break + + self.write = start_response(status,headers,exc_info) + return write_latin + + for data in self.application(environ,start_latin): + yield self.transform(data) + + # Run foo_app under a Latinator's control, using the example CGI gateway + from foo_app import foo_app + run_with_cgi(Latinator(foo_app)) + Specification Details @@ -851,6 +947,8 @@ of its use:: start_response(status, headers) return ["normal body goes here"] except: + # XXX should trap runtime issues like MemoryError, KeyboardInterrupt + # in a separate handler before this bare 'except:'... status = "500 Oops" headers = [("content-type","text/plain")] start_response(status, headers, sys.exc_info()) @@ -976,30 +1074,6 @@ course, applications should cache such configuration, to avoid having to re-read it upon each invocation.) -Middleware ----------- - -Note that a single object may play the role of a server with respect -to some application(s), while also acting as an application with -respect to some server(s). Such "middleware" components can perform -such functions as: - -* Routing a request to different application objects based on the - target URL, after rewriting the ``environ`` accordingly. - -* Allowing multiple applications or frameworks to run side-by-side - in the same process - -* Load balancing and remote processing, by forwarding requests and - responses over a network - -* Perform content postprocessing, such as applying XSL stylesheets - -Given the existence of applications and servers conforming to this -specification, the appearance of such reusable middleware becomes -a possibility. - - Supporting Older (<2.2) Versions of Python ------------------------------------------