2016-01-07 65 views
2

我需要一個Perl CGI腳本來獲取一個URL,然後將獲取的結果 - 狀態,標題和內容 - 未改變地返回給CGI環境,以便Web服務器將「代理」URL返回給用戶的瀏覽器就好像他們直接訪問了URL一樣。使用Perl HTTP :: Response和LWP代理HTTP請求的更好方法?

我正在Ubuntu 14.04主機上的Apache Web服務器上運行cgi-bin的腳本,但這個問題應該獨立於服務器平臺 - 任何可以運行Perl CGI腳本的應該都可以。

我試過使用LWP :: UserAgent :: request(),我已經非常接近。它返回一個HTTP :: Response對象,其中包含狀態碼,標題和內容,甚至還有一個「as_string」方法,可將其轉換爲可讀的形式。從CGI角度來看,問題是「as string」將狀態代碼轉換爲「HTTP/1.1 200 OK」而不是「Status: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響應= 0,將與500內部服務器錯誤嗆Apache會返回代理的網頁,如果是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反對CGI環境,並且與我的實際應用程序沒有相似之處。

+0

你能編輯並使實際questin更明顯嗎?我覺得它太隱藏在文本中。什麼是問題? – simbabque

+1

我編輯了這個問題。這是否使它更清晰?基本上它分爲四部分:(1)這是我正在嘗試做的事(代理一個URL獲取); (2)以下是我如何做到的(使用LWP); (3)這裏是我的解決方案的問題(依賴於HTTP :: Response內部結構的知識); (4)有沒有更好的方法? – kbro

+1

在另一種想法上,我發現了[這個答案](http://stackoverflow.com/a/273756/1331451)關於如何創建一個代理。它使我[檢查HTTP :: Daemon](https://metacpan.org/source/GAAS/HTTP-Daemon-6.01/lib/HTTP/Daemon.pm#L440),它有一個send_response方法,它需要一個HTTP :: Response對象。在那裏可能有一些轉換可以滿足你的需求。值得閱讀我認爲的代碼。 – simbabque

回答

3

通過使用$res->headers方法,您可以繞過$res->{_headers}響應對象的內部,該方法返回使用的實際HTTP :: Headers實例。 HTTP :: Response繼承了HTTP::Message

然後,它將是這樣的:

print "Status: ", $res->status_line, "\n"; 
print $res->headers->as_string; 

這看起來不太邪惡,但它仍然是不漂亮。

+0

一點都不邪惡 - 公共接口很好。在我的辯護中,在[POD](http://search.cpan.org/~ether/HTTP-Message-6.11/lib/HTTP/Response.pm)中沒有提到'headers' - 它只是談論'頭部「,這就是爲什麼我沿着傾倒結構的路線首先進入私人數據的原因! – kbro

+2

真的應該一直有RTFMed。 HTTP :: Response來源於HTTP :: Message(我知道!)和[POD](http://search.cpan.org/~ether/HTTP-Message-6.11/lib/HTTP/Message.pm)爲此提到'$ mess-> headers','$ mess-> headers_as_string'和'$ mess-> headers_as_string($ eol)'。所以'headers_as_string'正是我想要的(在沒有按照我想要的方式工作的'as_string'方法的情況下) – kbro

1

正如simbabque指出的那樣,HTTP :: Response通過繼承HTTP :: Message有一個headers方法。我們可以通過使用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;