使用 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 类型的对象作为数组

使用 API 路由时,在未授权时返回 Http Response 401 而不是重定向到登录页面

HTTP–Response详解