Tatsuhiko Miyagawa's Blog

new CGI::PSGI; start_response out

September 25, 2009

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.