2012-11-20 161 views
12

試圖通過製作一個簡單的http代理服務器來了解關於node.js的更多信息。使用場景很簡單:用戶 - >代理 - >服務器 - >代理 - >用戶簡單的node.js代理通過將http服務器管道到http請求

以下代碼可用,直到最後一步。無法找到管道連接器的輸出回到用戶的方式。

#!/usr/bin/env node 

var 
    url = require('url'), 
    http = require('http'), 
    acceptor = http.createServer().listen(3128); 

acceptor.on('request', function(request, response) { 
    console.log('request ' + request.url); 
    request.pause(); 
    var options = url.parse(request.url); 
    options.headers = request.headers; 
    options.method = request.method; 
    options.agent = false; 

    var connector = http.request(options); 
    request.pipe(connector); 
    request.resume(); 
// connector.pipe(response); // doesn't work 
// connector.pipe(request); // doesn't work either 
}); 

使用tcpflow我看到來自瀏覽器的傳入請求,然後傳出代理請求,然後服務器響應回代理。不知何故,我無法將重新傳輸回瀏覽器。

用管道實現這個邏輯的正確方法是什麼?

回答

19

好的。得到它了。

更新:注意!正如評論中所報道的那樣,這個例子不再適用。最有可能是由於Streams2 API變化(節點0.9+)

管道返回給客戶端具有如下發生內部連接器的回調:

#!/usr/bin/env node 

var 
    url = require('url'), 
    http = require('http'), 
    acceptor = http.createServer().listen(3128); 

acceptor.on('request', function(request, response) { 
    console.log('request ' + request.url); 
    request.pause(); 
    var options = url.parse(request.url); 
    options.headers = request.headers; 
    options.method = request.method; 
    options.agent = false; 

    var connector = http.request(options, function(serverResponse) { 
      serverResponse.pause(); 
      response.writeHeader(serverResponse.statusCode, serverResponse.headers); 
      serverResponse.pipe(response); 
      serverResponse.resume(); 
    }); 
    request.pipe(connector); 
    request.resume(); 
}); 
+0

-1,因爲:stream.js:94 拋兒; //管道中未處理的流錯誤。 ^ 錯誤:連接ECONNREFUSED at errnoException(net.js:901:11) at Object.afterConnect [as oncomplete](net.js:892:19) –

+3

@rob,很可能這個例子不適用於新的Streams2 API。 Streams2在節點0.9中作爲beta引入,並在0.10分支中穩定運行。 0.10於2013年3月發佈 - 本次討論後的幾​​個月。所以,如果你真的想要更有建設性,而不是把-1s,適應新的API的例子。 –

+0

@NikolaiGorchilov上面的例子在節點0.10.13中爲我工作。只有改變我不得不做的是'response.writeHead'而不是'response.writeHeader' – Gireesh

19

你沒有「暫停」,只是「管」是好的

var connector = http.request(options, function(res) { 
    res.pipe(response, {end:true});//tell 'response' end=true 
}); 
request.pipe(connector, {end:true}); 

http請求將不會完成,直到你告訴它'結束';

+4

{end:true}是一個很好的建議。 10X。 關於暫停,我仍然認爲最好使用它。設想一個用戶使用HTTP POST上傳大文件。爲什麼在傳出連接正在構建時,他的文件會緩存在服務器的RAM中?而如果這種傳出連接由於某種原因而不可能呢? –

+1

FWIW,現代節點不再需要'{end:true}' - 默認值爲'true',所以如果禁用,只需要傳遞選項。 – Jesse

1

我用這個帖子的例子來代理http/s請求。面對餅乾在某處丟失的問題。

因此,要解決您需要處理來自代理響應的標頭。

下面的工作示例:

req = service.request(options, function(res) { 
    response.writeHead(res.statusCode, res.headers); 
    return res.pipe(response, {end: true}); 
}); 
request.pipe(req, {end: true});