PSGI响应:PSGI和Plack可以使用哪种文件句柄?

时间:2011-05-15 22:54:55

标签: perl plack psgi

PSGI specification将HTTP响应定义为由三部分组成,第三部分可以是数组引用,也可以是文件句柄。文件句柄可能是:

一个IO :: Handle-like对象或内置文件句柄。

规范继续说:

服务器可以使用fileno和Scalar :: Util :: reftype检查主体是否是真正的文件句柄,如果它是具有文件描述符的真实文件句柄,它可以使用sendfile(2)等技术优化文件服务。 / BLOCKQUOTE>

现在我使用plackup(Plack版本0.9978)拼凑了一个命令行示例,看来检查正文是否是真正的文件句柄会导致致命错误:

Can't locate object method "FILENO" via package "IO::Scalar" at /usr/lib/perl5/5.10/i686-cygwin/IO/Handle.pm line 390

这是命令行示例:

plackup -MData::Dumper -MIO::Scalar -e \
'sub { $env=shift; return [200, [], IO::Scalar->new(\Dumper $env) ] }'

当然我可以不使用文件句柄:

plackup --port 9999 -MData::Dumper -e \
'sub { $env=shift; return [200, [], [Dumper $env] ] }'

但我对什么有效,什么不有意义感兴趣。因此,当在句柄上调用FILENO时,Plack不应该更加谨慎,这样它就不会遇到异常吗?

并添加另一个:

plackup --port 9999 -MData::Dumper -e \
'sub{$env=shift; $s=Dumper $env; open $fh,q(<),\$s or die; return [200,[],$fh ]}'

看起来像文件句柄不被识别。错误消息是:

body should be an array ref or filehandle at /usr/lib/perl5/site_perl/5.10/Plack/Middleware/StackTrace.pm line 35

更新

正如他在回答中所述,以下内容将起作用(至少在Cygwin的5.10.1上):

plackup -p 9999 -MData::Dumper -MIO::String -e \
'sub { return [200, [], IO::String->new(\Dumper shift) ] }'

但显然,从失败的例子中可以看出某个地方存在问题,一旦我弄清楚它实际上是什么,就会报告。

3 个答案:

答案 0 :(得分:9)

这些不是错误 - 实际上更容易将其称为Plack中的错误并修复以将其作为有效响应处理。但这会让事情变得更糟,因为现在Plack处理的事情并没有(明确地)定义为PSGI规范中的正确响应。 (PSGI!= Plack,同样是HTTP!= Apache)

PSGI规范的要点是它是Web服务器和应用程序之间的通用接口。如果服务器/应用程序需要添加额外的2-3行代码以符合规范,那么这是一个很好的折衷方案。在每个N个应用程序和M个服务器中拥有2-3行代码要好得多,而不是在应用程序中使用N * 2-3行额外代码来处理服务器中的极端情况,反之亦然。

规范定义响应主体应该是“内置文件句柄”或“实现getline的IO :: Handle-like对象”。在Plack中处理与此类似的工作很容易,但我们不应盲目地这样做 - 请记住,Plack并不是唯一的PSGI实现。 Lint中间件警告您关于该不兼容性是正确的事情。

那说:

a)IO :: Scalar是一个实现getline()方法的对象,因此应该被接受。不幸的是,正如其他人所指出的那样,Lint因为模块的错误而死在它身上。使用猴子修补可以很容易地解决它,并且修复Plack :: Util :: is_real_fh以便在 - &gt; fileno调用中捕获错误也很容易,但是,如果它是正确的事情要做。

b)PerlIO内存文件句柄是一件棘手的事情。规范只说“内置文件句柄”,内存文件句柄也可以作为内置的东西。实际上,如果您禁用Lint中间件(例如,使用-E production选项进行plackup),文件句柄就可以正常工作。但同样,Lint中间件会给你一条消息,因为它不能保证在其他地方工作。

最后但并非最不重要的是,这应该在FAQ中解决。随意在psgi-specs存储库中打开一个案例。

答案 1 :(得分:8)

这似乎是普拉克的一个错误。它试图通过fileno来确定它是否有真正的文件句柄,如果不是,它只接受getline方法的对象。如果没有FILENO定义的绑定文件句柄(有效,如果不礼貌)和内存文件句柄中没有有效的fileno也不是它们是受祝福的对象,则会错过这两个文件句柄。您可以在Plack::Middleware::Lint->validate_resPlack::Util->is_real_fh中的逻辑中看到它。

report it to Plack as a bug

同时,您可以通过定义IO :: Scalar :: FILENO来返回undef来解决IO :: Scalar中的问题。

sub IO::Scalar::FILENO { return }

这将改进IO :: Scalar,但它在六年内没有更新,所以我不会屏住呼吸。

要允许在内存文件句柄中,您可以通过祝福文件句柄来欺骗Plack。打开它并将其交给之间的某个时间,请执行以下操作:

bless $fh, "IO::Handle";

这是无害的,因为任何文件句柄都会响应IO :: Handle方法。但也请把它报告为一个错误。

答案 2 :(得分:0)

看起来像IO :: Scalar中的一个错误。报告它,并且insteaad使用IO :: String或5.8中添加的内置文件支持。