3

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

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

规范继续说:

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

现在,我使用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] ] }'

但我对什么有效,什么无效感兴趣。那么,Plack 在调用句柄时是否应该更加小心,FILENO以免遇到异常?

并添加另一个:

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

更新:

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

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

但很明显,从失败的示例中可以看出某个地方存在问题,一旦我确定它实际上是什么,就会报告它。

4

3 回答 3

9

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

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

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

那说:

a) IO::Scalar 是一个实现 getline() 方法的对象,所以它应该被接受。正如其他人所指出的那样,由于模块的错误,Lint 死在上面是很不幸的。它可以很容易地通过猴子修补来解决,并且也很容易修补 Plack::Util::is_real_fh 以在 ->fileno 调用中捕获错误,但同样,我需要考虑它是否正确做。

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

最后但并非最不重要的一点是,这可能应该在常见问题解答中得到解决。随意在psgi-specs存储库中打开一个案例。

于 2011-05-16T03:23:25.910 回答
8

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

我会把它作为一个 bug 报告给 Plack

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

sub IO::Scalar::FILENO { return }

这将是对 IO::Scalar 的改进,但它已经六年没有更新了,所以我不会屏住呼吸。

为了允许内存中的文件句柄,您可以通过祝福文件句柄来欺骗 Plack。在打开它和把它交给它之间的某个时候,这样做:

bless $fh, "IO::Handle";

这是无害的,因为任何文件句柄都会响应 IO::Handle 方法。但也请务必将其报告为错误。

于 2011-05-16T02:48:17.560 回答
0

看起来像 IO::Scalar 中的错误。报告它,而不是使用 IO::String 或 5.8 中添加的内置内存文件支持。

于 2011-05-16T00:27:16.550 回答