使用 Perl HTTP::Response 和 LWP 代理 HTTP 请求的更好方法?
Posted
技术标签:
【中文标题】使用 Perl HTTP::Response 和 LWP 代理 HTTP 请求的更好方法?【英文标题】:Better way to proxy an HTTP request using Perl HTTP::Response and LWP? 【发布时间】:2016-04-11 23:00:33 【问题描述】:我需要一个 Perl CGI 脚本来获取 URL,然后返回获取的结果 - 状态、标题和内容 - 不改变 CGI 环境,以便 Web 服务器将“代理” URL 返回到用户的就像他们直接访问了 URL 一样。
我在 Ubuntu 14.04 主机上的 Apache Web 服务器中从 cgi-bin 运行我的脚本,但这个问题应该独立于服务器平台 - 任何可以运行 Perl CGI 脚本的东西都应该能够做到。
我尝试过使用 LWP::UserAgent::request() 并且非常接近。它返回一个包含状态代码、标头和内容的 HTTP::Response 对象,甚至还有一个“as_string”方法,可以将其转换为人类可读的形式。从 CGI 的角度来看,问题是“作为字符串”将状态代码转换为“HTTP/1.1 200 OK”而不是“状态:200 OK”,因此 Apache 服务器无法将输出识别为有效的 CGI 响应。
我可以通过使用 HTTP::Response 中的其他方法来拆分各个部分来解决此问题,但似乎没有公开的方法可以获取封装的 HTTP::Headers 对象以调用其 as_string 方法;相反,我必须侵入 Perl 祝福对象哈希并直接拉出私有“_headers”成员。对我来说这似乎有点邪恶,那么有更好的方法吗?
这里有一些代码来说明上述内容。如果你把它放在你的 cgi-bin 目录中,那么你可以把它称为
http://localhost/cgi-bin/lwp-test?url=http://localhost/&http-response=1&show=1
如果需要,您可以使用不同的 URL 进行测试。如果您设置http-response=0
(或完全放弃参数),那么您将获得逐个工作的解决方案。如果您设置show=0
(或删除它),那么脚本会返回代理请求。如果您有 http-response=0,Apache 将返回代理页面,如果它是 1,则会出现 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
对象转发到 CGI 环境而编写的,与我的实际应用程序没有任何相似之处。
【问题讨论】:
您可以编辑并使实际的任务更明显吗?我觉得它隐藏在文本中太多了。问题是什么? 我已经编辑了这个问题。这是否使它更清楚?基本上它分为 4 个部分:(1)这是我想要做的(代理 URL 获取); (2) 我是这样做的(使用 LWP); (3)这是我的解决方案的问题(依赖于HTTP::Response内部结构的知识); (4) 有没有更好的方法? 在不同的想法上,我发现this answer 了解如何制作代理。它把我带到check in HTTP::Daemon,它有一个方法send_response,它接受一个HTTP::Response 对象。可能会有一些转换可以满足您的需要。我认为值得一读的代码。 我快速浏览了一下 code 和send_response
并没有做任何与我的解决方案有明显不同的事情:它使用 $res->code
来获取状态代码编号(我使用 status_line
获取代码+消息);它使用$res->header(field,value)
处理标题字段,然后使用$res->headers_as_string
对它们进行字符串化;它使用$res->content
来获取内容。看起来我的公司很好!
【参考方案1】:
您可以使用$res->headers
方法绕过$res->_headers
的响应对象的内部结构,该方法返回实际使用的HTTP::Headers 实例。 HTTP::Response 继承自 HTTP::Message。
它看起来像这样:
print "Status: ", $res->status_line, "\n";
print $res->headers->as_string;
这看起来不那么邪恶,虽然它仍然不漂亮。
【讨论】:
一点也不邪恶——公共接口很好。在我的辩护中,POD 中没有提到headers
- 它只谈论header
,这就是为什么我首先走上了转储结构并侵入私人数据的路线!跨度>
真的应该一路 RTFMed。 HTTP::Response 派生自 HTTP::Message(我知道!)和 POD 提到 $mess->headers
、$mess->headers_as_string
和 $mess->headers_as_string( $eol )
。所以headers_as_string
正是我想要的(在没有按我想要的方式工作的as_string
方法的情况下)【参考方案2】:
正如 simbabque 所指出的,HTTP::Response 有一个 headers
方法,通过继承自 HTTP::Message。我们可以通过使用HTTP::Response->header
将其推送到嵌入的HTTP::Headers 对象中来整理状态码的处理,然后使用headers_as_string
更干净地打印出标头。这是最终的脚本:-
#!/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 HTTP::Response 和 LWP 代理 HTTP 请求的更好方法?的主要内容,如果未能解决你的问题,请参考以下文章
未通过 https 使用 LWP::UserAgent get() 获取预期内容
如何在 Springboot 获取 http request和 http response 的几种方式
node.js中的http.response.end方法使用说明
Slim3 不能使用 Slim\Http\Response 类型的对象作为数组