[Web-SIG] Web Container Interface
Phillip J. Eby
pje at telecommunity.com
Sat Jan 24 23:03:10 EST 2004
At 01:05 PM 1/24/04 -0600, Ian Bicking wrote:
>>>Or, the application may want to know something about the URL layout.
>>>Mmm... which makes me think that PATH_INFO and SCRIPT_NAME need to be
>>>well defined for runCGI, or alternate variables need to be considered.
>>>Does SCRIPT_NAME (and several associated CGI variables, PATH_INFO
>>>included) point to the application, or the container, or what?
>>An excellent point. That *should* be added to the spec. I simply
>>assumed they point to the object that is receiving the runCGI() call.
>>It should be made explicit, though. Thanks!
>Hmm... that might not be backward compatible. In both Webware and I
>believe Zope (and probably several others) these point to the container
>root, not the resource root.
Um, that's a container issue, not an app issue. AFAIK, all existing apps
expect SCRIPT_NAME to point to the *app* root. Also AFAIK, no generic WCI
containers exist yet except the ones in PEAK, and they all just pass
SCRIPT_NAME as pointing to the app they're running. This works just fine
with Zope 2 and 3, as well as any properly written CGI.
However, if you were to create a container that routed requests, then it
would need to remove any parts from PATH_INFO that it consumes, and add
them to SCRIPT_NAME, so that the application being run can use
HTTP_HOST+SCRIPT_NAME to form a URL to the application.
I am, however, beginning to see the awkwardness of "container" and
"app". I'm wondering if maybe "gateway" and "service" would be better
terms, and rename the whole thing the Python Web Service Gateway
Interface. That is, the WSGI, perhaps to be pronounced "whisky". :)
Anyway, it would then make sense that the values supplied by a gateway to a
service should be such that SCRIPT_NAME is the path of the service,
relative to HTTP_HOST, with PATH_INFO being the remainder of the
URL. Since a service can also act as a gateway to nested WSGI services, it
should of course deliver different values to them, so that they know their
correct base URLs as well.
>Again, I'm not proposing we set out a standard that covers these, but that
>we leave room for implementations to extend the standard to cover these cases.
Well, for Twisted, Zope, and PEAK at least, each framework has ways of
specifying interface metadata about an object, including adapters. So,
gateways provided by those frameworks would be free to try to introspect a
service for a fancier interface.
For example, PEAK has a 'suggestParentComponent()' API that can be used to
sniff an object for PEAK component-ness (using PyProtocols), and then tell
it what context it's being used in. Zope 3 has getAdapter(), and Twisted
None of these mechanisms require anything special in the WSGI spec to
support them. And even if you don't have those, there's still good old
fashioned 'hasattr'. You'll notice that I keep emphasizing the gateway
inspecting and invoking the service, *not* the other way around. There
should not *be* anything different from one gateway to the next, if at all
possible, since that runs counter to the whole point of the exercise, which
is allowing the same service to run in different gateways, *especially*
ones it wasn't written for. That's why I want the spec to be
mind-numbingly precise about what is and isn't required, and don't want to
throw a vague parameter into it that has no meaning at all, existing solely
to introduce *differences*, which are the mortal enemy of the goal of
>I remain skeptical. I can't really see how you'll run a Zope application
>under plain CGI, or how both a Zope application and a Twisted application
>could successfully run with the same interface.
They can't. But a Twisted-specific application can run in the same process
as a Twisted gateway that runs non-Twisted web services. And Zope 2 and
Zope 3 services should be runnable by any WSGI gateway: certainly I've been
successful with them in my own gateways. (I have an ancient Zope 2
ZPublisher-based app that runs via a runCGI call right now as we speak,
serving millions of dynamic hits per month.)
And, if somebody wrote a WSGI gateway for Twisted, we could finally marry
Zope 3 and Twisted, as people have been flirting with doing for quite some
time now. It would have to use the Twisted threadpool, and a pool of
service instances (Zope 3 "Publication" objects, presumably), but it
certainly could be done.
Again, this presumes that you could configure the Twisted gateway in a way
that would let it manage an app pool properly. But let's say that it
couldn't, and you had to give Twisted a single service instance that had to
be shared for the entire web server. Well, you'd write a WSGI service that
simply pulled a Zope top-level instance from a pool, and invoked it as a
subservice (leaving the 'environ' alone). And now, your "pooling" WSGI
service could be used with a pool of *any* WSGI service objects.
The cool thing about having a spec is, it enables this sort of bridging and
connecting just by its very existence. Somebody has a specific problem to
solve, like reversing the gender of a cable, or creating a splitter or
splicer... instead of trying to invent wire, plugs, and sockets. :)
>I still can see that this is a step in the right direction -- not a big
>step, but it's still the right direction.
I think it's a bigger step, but we won't know until we try, do we? If it
turns out we need more, there's nothing stopping us from doing a WSGI 1.1
after a few months.
>>Actually, I think it's more likely that a WCI 2.0 would grow another
>>method, similar to the Java servlet 'init()' method, to tell the app that
>>it's being "used by" a particular container. However, at that point
>>we'll also want to know something about what people actually want to get
>>from a container, if anything.
>I think WCI 1.0 needs an init(), or something equivalent (like simply
>passing the container in to runCGI()). I think it is useful to provide
>that hook, even if we give implementors no specification beyond that.
>A portable application might even go as far as doing:
>if container.__class__.__name__ == 'AppServer':
> # Probably Webware, we'll do X
>elif container.__class__.__name__ == 'ZPublisher':
> # Zope, we do Y
Hm. ZPublisher isn't a gateway in this spec. It's a service. A gateway
would call runCGI to *get to* ZPublisher. If you want a ZPublisher app,
you just write a ZPublisher app. Then, to run it, you configure a service
wrapper around ZPublisher, and pass it to a gateway. But if you wrote a
ZPublisher app, you're *way* below the level of this interface.
The idea of the WCI/WSGI is to provide an HTTP gateway interface, to let
apps built with any framework run via any sort of HTTP gateway. That's why
I keep saying that access to other services is an app-framework job.
>Portable applications in Python typically require some special cases as
>issues arise -- it's better to enable developers than to enforce agnosticism.
Even so, I think it's better to have that glue controlled by the
application's integrator, rather than buried inside. If the service has
options X and Y, let the integrator choose what behavior is appropriate for
the gateway they're installing it in. Otherwise, you're forcing the app
developer to know about all possible gateways, which again runs counter to
I think I see now why we seem slightly at odds in this discussion. Your
example suggests that you are thinking of "app" as "what I write to do
useful work", and that you will write apps that directly export a service
for use by a gateway. While it's definitely possible to do this, you will
more likely use a service wrapper that's pre-defined by your app framework
such as Zope or Webware. This is a way to take a Zope or Webware
application and run it in a different server environment, not to make the
application itself portable between Zope and Webware.
However, once the interface exists, it becomes possible to make a "router"
service that would let you run both say, Zope and Webware applications
within the same server environment.
But at that point, if you want to actually integrate the apps themselves
(as opposed to simply running them side by side), again I think the
mechanism of choice would be to write a short startup script that passes
references to each app's internal services to the other. At least, that's
my take on "practicality beats purity". Why create an abstract spec for
passing things around, when the integrator "on the spot" can just connect
what they need?
More information about the Web-SIG