start_response = out
Yesterday I asked if we should support optional start_response like Python's WSGI, and Yuval came in on this blog comment as well as #http-engine (irc.perl.org) to say No. Rather than just discussing what to do instead, we challenged him to implement the echo streaming server (that prints the current time every one second as a server push) without start_response stuff, blockingly in most apps but non-blockingly in some backends like AnyEvent and Danga::Socket.
He proved that you can do it with creating a mock IO object (temporarily called IO::Writer in addition to some nifty IO related utilities like IO::Coerce) that can be called non-blocking read from some specific implementations like AnyEvent, but still can be coerced into a normal filehandle by other implementations by using his magic of pipe, fork and then re-bless.
At first I was afraid of depending on such a black magic technique, but in the 4 hours of chat session we agreed that it's not a hard dependency, and it doesn't add any complexity to the PSGI spec itself while leaving the room for non-blocking optimization for backends like AnyEvent.
So, start_response is out. We do pass one arg $env and return 3 arg response, while $body can be a IO object that can possibly do non-blocking push if the server supports it (and otherwise still works as a blocking read).
CGI::PSGI
Mark Stosberg is back from his vacation and wrote about possible PSGI support in CGI.pm. My patch was actually merged to his main repo under psgi_support branch, and he also asked for feedbacks from other CGI.pm maintainers.
He specifically asked three things: 1) Do we need CGI::PSGI just to set the flag, 2) is the API sane and 3) Can this be implemented externally.
My initial reaction to these questions, a few weeks ago, was that 1) Yes (for the app developers to inspect if CGI.pm supports PSGI) 2) Sort of and 3) No.
Today I've been chatting and toying with my experimental code and the answer has changed to all of them: 1) No, we need CGI::PSGI as a subclass to implement PSGI support, not as a flag setter 2) the proposed CGI.pm patch and API is not cool, CGI::PSGI can do it better and 3) Yes, CGI::PSGI can be implemented without touching CGI.pm internal at all.
This is a subclass that stores PSGI's $env internally and sets that to local *ENV appropriately whenever calling methods that touches %ENV. Yes it still depends on CGI.pm internal a little bit, but it's much less risky and most importantly, doesn't modify CGI.pm internal code at all.
I cloned a few test case from CGI.pm and adjusted to make sure it works sane. All tests passed, and this new CGI::PSGI has already ported and tested with CGI::Application::PSGI, Squatting::On::PSGI and Dancer!
With CGI::PSGI all you need to modify in your code is to change CGI->new into CGI::PSGI->new($env), and then call $q->psgi_header($content_type) if you want to get $status and $headers_ref, but you can completely ignore it and construct status code and headers by yourself, like most web application frameworks do.
This CGI::PSGI can individually be shipped to CPAN and web application framework can switch from directly using CGI.pm to CGI::PSGI. Separately CGI.pm can add a native support of PSGI if they want to: (but not with CGI::PSGI as a flag setter -- Mark. let's remove it!) As said, my CGI.pm patch was as minimal as possible and its API to support PSGI is a little awkward -- it's user's responsibility to localize %ENV hash to refer to PSGI $env, and $q->header() behaves differently, even though the app users need to change that part anyway.
The "complete" PSGI support on CGI.pm is to tie STDOUT and then capture the output to create $status, $headers, [ $body ] thing but then that's something we already do with CGI::Emulate::PSGI to generally work with any (even if it doesn't use CGI.pm) CGI scripts in Perl.
So, hm. We could just hold on a little for a native PSGI support in CGI.pm. We can discuss further, but not right now since we have an alternative that can be shipped today.
I like this new approach.
I'll delete the "psgi_support" branch in favor of the new CGI::PSGI distribution, and we can consider later what, if any, related changes we'd like to make to CGI.pm
Posted by: Mark Stosberg | 2009.09.25 at 06:41
The proposed solution requires s/CGI/CGI::PSGI/ – that’s deployment details leaking into the application logic, which sucks.
But I agree that it’s better to ship now than to wait. By the sounds of how complex the external solution is, it shouldn’t take very long at all to implement native support either, but it’s still more work, and since PSGI is not completely nailed down yet it makes more sense to ship something that can evolve more rapidly than CGI.pm.
Another advantage is of the external implementation in CGI::PSGI is that it will work with older versions of CGI.pm without requiring an update. For some people that might be useful.
And once PSGI is nailed down and CGI.pm has native support for it, CGI::PSGI could test whether it’s running under a CGI.pm with PSGI support and mutate into a no-op wrapper in that case.
Posted by: Aristotle Pagaltzis | 2009.09.25 at 06:46
Cool, glad we agree on this!
Posted by: miyagawa | 2009.09.25 at 06:49
> The proposed solution requires s/CGI/CGI::PSGI/ – that’s deployment details leaking into the application logic, which sucks.
No, it doesn't. Current PSGI's target is web application frameworks that uses CGI.pm to get parameters, for instance: Jifty, Dancer, CGI::Application and Squatting. Their adapters need to be updated, which I actually did myself or volunteered to patch the code for three of them (except Jifty, which I've been working with clkao and obra to add support), but that's the only part which needs modification. The *users* of those frameworks don't need to update ANY of their application code.
Your observation about future CGI and CGI::PSGI are still relevant and correct, though. Native support of PSGI in CGI.pm means the script that uses CGI.pm don't need to be updated at all, but that actually can be already done if you use CGI::Emulate::PSGI.
Posted by: miyagawa | 2009.09.25 at 06:53
BTW I updated the POD document to clarify who should use CGI::PSGI. Thanks!
http://github.com/miyagawa/CGI-PSGI/commit/906a2e5ccac87fe65492753cedf2275e21445149
Posted by: miyagawa | 2009.09.25 at 07:17
> Current PSGI’s target is web application frameworks that uses CGI.pm to get parameters
Ah, I was thinking of apps using CGI.pm directly.
> but that actually can be already done if you use CGI::Emulate::PSGI.
Ah! Now the whole plan makes more sense. Of course that’s still not as nice as native support in CGI.pm, but that’s coming anyway, so, yeah.
Excellent. Carry on.
Posted by: Aristotle Pagaltzis | 2009.09.25 at 07:20
Of course, I don't know about the details that made you decide to remove start_response. Anyway, consider also the potential benefit of having a consistent xSGI implementation between different languages. Black magic should be avoided when possible. Tends to produce unmaintainable monsters. Keep it simple!
Posted by: Cosimo | 2009.09.26 at 07:27
This decision was solely to make it as simple as possible, and to make it more compatible to other languages:Ruby's Rack and JavaScript's JSGI. Python's WSGI dev is specing WSGI 2.0 and they'll drop start_response too.
Posted by: miyagawa | 2009.09.26 at 11:58