使用 Perl HTTP::Response 和 LWP 代理 HTTP 请求的更好方法?
Better way to proxy an HTTP request using Perl HTTP::Response and LWP?
我需要一个 Perl CGI 脚本来获取 URL 然后 returns 获取的结果 - 状态,headers 和内容 - 未更改到 CGI 环境所以"proxied" URL 由 Web 服务器 return 发送到用户的浏览器,就像他们直接访问 URL 一样。
我在 Ubuntu 14.04 主机上的 Apache Web 服务器中 运行 从 cgi-bin 中安装我的脚本,但这个问题应该独立于服务器平台 - 任何可以运行 Perl CGI 脚本应该可以做到。
我试过使用 LWP::UserAgent::request(),我已经非常接近了。它 return 是一个 HTTP::Response object,其中包含状态代码、headers 和内容,甚至还有一个 "as_string" 方法将其转换为 human-readable 形式。从 CGI 的角度来看,问题在于 "as string" 将状态代码转换为 "HTTP/1.1 200 OK" 而不是 "Status: 200 OK",因此 Apache 服务器无法将输出识别为有效的 CGI 响应。
我可以通过使用 HTTP::Response 中的其他方法来拆分各个部分来解决这个问题,但似乎没有 public 获得封装的方法 HTTP::Headers object 以便调用它的 as_string 方法;相反,我必须侵入 Perl blessed object 哈希并直接抽出私有“_headers”成员。在我看来这有点邪恶,那么有更好的方法吗?
下面是一些代码来说明上面的内容。如果你把它放在你的 cgi-bin 目录中,那么你可以将它命名为
http://localhost/cgi-bin/lwp-test?url=http://localhost/&http-response=1&show=1
如果需要,您可以使用不同的 URL 进行测试。如果您设置 http-response=0
(或完全删除该参数),那么您将获得有效的 piece-by-piece 解决方案。如果您设置 show=0
(或删除它),则代理请求将由脚本 return 编辑。如果 http-response=0,Apache 将 return 代理页面,如果是 1.
,Apache 将出现 500 内部服务器错误。
#!/usr/bin/perl
use strict;
use warnings;
use CGI::Simple;
use HTTP::Request;
use HTTP::Response;
use LWP::UserAgent;
my $q = CGI::Simple->new();
my $ua = LWP::UserAgent->new();
my $req = HTTP::Request->new(GET => $q->param('url'));
my $res = $ua->request($req);
# print a text/plain header if called with "show=1" in the query string
# so proxied URL response is shown in browser, otherwise just output
# the proxied response as if it was ours.
if ($q->param('show')) {
print $q->header("text/plain");
print "\n";
}
if ($q->param('http-response')) {
# This prints the status as "HTTP/1.1 200 OK", not "Status: 200 OK".
print $res->as_string;
} else {
# This works correctly as a proxy, but using {_headers} to get at
# the private encapsulated HTTP:Response object seems a bit evil.
# There must be a better way!
print "Status: ", $res->status_line, "\n";
print $res->{_headers}->as_string;
print "\n";
print $res->content;
}
请记住,编写此脚本纯粹是为了演示如何将 HTTP::Response
object 转发到 CGI 环境,与我的实际应用程序没有任何相似之处。
您可以使用 $res->headers
方法绕过 $res->{_headers}
处的响应对象的内部结构,returns 实际使用的 HTTP::Headers 实例。 HTTP::Response 继承自 HTTP::Message.
它看起来像这样:
print "Status: ", $res->status_line, "\n";
print $res->headers->as_string;
看起来不那么邪恶了,虽然它仍然不漂亮。
正如 simbabque 指出的那样,HTTP::Response 通过继承自 HTTP::Message 有一个 headers
方法。我们可以通过使用 HTTP::Response->header
将其推送到嵌入式 HTTP::Headers object 来整理状态代码的处理,然后使用 headers_as_string
打印出 headers更干净。这是最终脚本:-
#!/usr/bin/perl
use strict;
use warnings;
use CGI::Simple;
use HTTP::Request;
use HTTP::Response;
use LWP::UserAgent;
my $q = CGI::Simple->new();
my $ua = LWP::UserAgent->new();
my $req = HTTP::Request->new(GET => $q->param('url'));
my $res = $ua->request($req);
# print a text/plain header if called with "show=1" in the query string
# so proxied URL response is shown in browser, otherwise just output
# the proxied response as if it was ours.
if ($q->param('show')) {
print $q->header("text/plain");
}
# $res->as_string returns the status in a "HTTP/1.1 200 OK" line rather than
# a "Status: 200 OK" header field so it can't be used for a CGI response.
# We therefore have a little more work to do...
# convert status from line to header field
$res->header("Status", $res->status_line);
# now print headers and content - don't forget a blank line between the two
print $res->headers_as_string, "\n", $res->content;
我需要一个 Perl CGI 脚本来获取 URL 然后 returns 获取的结果 - 状态,headers 和内容 - 未更改到 CGI 环境所以"proxied" URL 由 Web 服务器 return 发送到用户的浏览器,就像他们直接访问 URL 一样。
我在 Ubuntu 14.04 主机上的 Apache Web 服务器中 运行 从 cgi-bin 中安装我的脚本,但这个问题应该独立于服务器平台 - 任何可以运行 Perl CGI 脚本应该可以做到。
我试过使用 LWP::UserAgent::request(),我已经非常接近了。它 return 是一个 HTTP::Response object,其中包含状态代码、headers 和内容,甚至还有一个 "as_string" 方法将其转换为 human-readable 形式。从 CGI 的角度来看,问题在于 "as string" 将状态代码转换为 "HTTP/1.1 200 OK" 而不是 "Status: 200 OK",因此 Apache 服务器无法将输出识别为有效的 CGI 响应。
我可以通过使用 HTTP::Response 中的其他方法来拆分各个部分来解决这个问题,但似乎没有 public 获得封装的方法 HTTP::Headers object 以便调用它的 as_string 方法;相反,我必须侵入 Perl blessed object 哈希并直接抽出私有“_headers”成员。在我看来这有点邪恶,那么有更好的方法吗?
下面是一些代码来说明上面的内容。如果你把它放在你的 cgi-bin 目录中,那么你可以将它命名为
http://localhost/cgi-bin/lwp-test?url=http://localhost/&http-response=1&show=1
如果需要,您可以使用不同的 URL 进行测试。如果您设置 http-response=0
(或完全删除该参数),那么您将获得有效的 piece-by-piece 解决方案。如果您设置 show=0
(或删除它),则代理请求将由脚本 return 编辑。如果 http-response=0,Apache 将 return 代理页面,如果是 1.
#!/usr/bin/perl
use strict;
use warnings;
use CGI::Simple;
use HTTP::Request;
use HTTP::Response;
use LWP::UserAgent;
my $q = CGI::Simple->new();
my $ua = LWP::UserAgent->new();
my $req = HTTP::Request->new(GET => $q->param('url'));
my $res = $ua->request($req);
# print a text/plain header if called with "show=1" in the query string
# so proxied URL response is shown in browser, otherwise just output
# the proxied response as if it was ours.
if ($q->param('show')) {
print $q->header("text/plain");
print "\n";
}
if ($q->param('http-response')) {
# This prints the status as "HTTP/1.1 200 OK", not "Status: 200 OK".
print $res->as_string;
} else {
# This works correctly as a proxy, but using {_headers} to get at
# the private encapsulated HTTP:Response object seems a bit evil.
# There must be a better way!
print "Status: ", $res->status_line, "\n";
print $res->{_headers}->as_string;
print "\n";
print $res->content;
}
请记住,编写此脚本纯粹是为了演示如何将 HTTP::Response
object 转发到 CGI 环境,与我的实际应用程序没有任何相似之处。
您可以使用 $res->headers
方法绕过 $res->{_headers}
处的响应对象的内部结构,returns 实际使用的 HTTP::Headers 实例。 HTTP::Response 继承自 HTTP::Message.
它看起来像这样:
print "Status: ", $res->status_line, "\n";
print $res->headers->as_string;
看起来不那么邪恶了,虽然它仍然不漂亮。
正如 simbabque 指出的那样,HTTP::Response 通过继承自 HTTP::Message 有一个 headers
方法。我们可以通过使用 HTTP::Response->header
将其推送到嵌入式 HTTP::Headers object 来整理状态代码的处理,然后使用 headers_as_string
打印出 headers更干净。这是最终脚本:-
#!/usr/bin/perl
use strict;
use warnings;
use CGI::Simple;
use HTTP::Request;
use HTTP::Response;
use LWP::UserAgent;
my $q = CGI::Simple->new();
my $ua = LWP::UserAgent->new();
my $req = HTTP::Request->new(GET => $q->param('url'));
my $res = $ua->request($req);
# print a text/plain header if called with "show=1" in the query string
# so proxied URL response is shown in browser, otherwise just output
# the proxied response as if it was ours.
if ($q->param('show')) {
print $q->header("text/plain");
}
# $res->as_string returns the status in a "HTTP/1.1 200 OK" line rather than
# a "Status: 200 OK" header field so it can't be used for a CGI response.
# We therefore have a little more work to do...
# convert status from line to header field
$res->header("Status", $res->status_line);
# now print headers and content - don't forget a blank line between the two
print $res->headers_as_string, "\n", $res->content;