1. 程式人生 > >NodeJS實現HTTP/HTTPS代理

NodeJS實現HTTP/HTTPS代理

身在天朝,難免會用到代理的時候。 比如在學校內網用代理免費上外網,在牆內用代理上404網站等。

    現在使用的代理大部分為HTTP和Socket代理。 Socket代理更底層,需要本地解析域名,而HTTP代理則是基於HTTP協議之上的,不需要本地解析域名。下面我講講HTTP(S)代理的設計思路以及NodeJS程式碼實現。

HTTP協議

    HTTP協議簡單說來就是瀏覽器把一串字串傳送到目標伺服器,然後把目標伺服器返回回來的一串字串顯示給使用者。

    瀏覽器傳送的這串字元主要分為兩個部分,一部分是頭,裡面包含目標伺服器域名,當前請求的檔案路徑等資訊。另一部分是正文,一般的GET請求沒有正文。

    伺服器返回來的字串也分為頭和正文。

HTTP代理原理

    HTTP代理需要做的事情就是接收瀏覽器發來的請求字串,再從請求字串的頭部分找出瀏覽器請求的目標主機,然後直接把這串請求字串發給目標主機,再把目標主機返回的資料發給瀏覽器。 “什麼?就這麼簡單?” “呃。。是啊,但這還沒完。。”

    現代瀏覽器一般都是預設採用HTTP/1.1版本,並且預設會發送Connection: keep-alive請求。 這些資訊是寫在請求的頭部的,意思是通知目標伺服器採用keep-alive技術繼續處理後續的請求。 但是我們做的代理程式要想支援keep-alive是比較麻煩的。所以乾脆就把這個篡改成Connection: close。 這樣就可以保證瀏覽器請求的每個檔案都會單獨傳送一個HTTP請求。

下面是NodeJS程式碼實現

var net = require('net');
var local_port = 8893;

//在本地建立一個server監聽本地local_port埠
net

.createServer(function (client)
{
   
    //首先監聽瀏覽器的資料傳送事件,直到收到的資料包含完整的http請求頭
    var buffer = new Buffer(0);
    client.on('data',function(data)
    {
        buffer = buffer_add(buffer,data);
        if (buffer_find_body(buffer) == -1) return;
        var req = parse_request(buffer);
        if (req
=== false) return;
        client.removeAllListeners('data');
        relay_connection(req);
    });

    //從http請求頭部取得請求資訊後,繼續監聽瀏覽器傳送資料,同時連線目標伺服器,並把目標伺服器的資料傳給瀏覽器
    function relay_connection(req)
    {
        console.log(req.method+' '+req.host+':'+req.port);
       
        //如果請求不是CONNECT方法(GET, POST),那麼替換掉頭部的一些東西
        if (req.method != 'CONNECT')
        {
            //先從buffer中取出頭部
            var _body_pos = buffer_find_body(buffer);
            if (_body_pos < 0) _body_pos = buffer.length;
            var header = buffer.slice(0,_body_pos).toString('utf8');
            //替換connection頭
            header = header.replace(/(proxy\-)?connection\:.+\r\n/ig,'')
                    .replace(/Keep\-Alive\:.+\r\n/i,'')
                    .replace("\r\n",'\r\nConnection: close\r\n');
            //替換網址格式(去掉域名部分)
            if (req.httpVersion == '1.1')
            {
                var url = req.path.replace(/http\:\/\/[^\/]+/,'');
                if (url.path != url) header = header.replace(req.path,url);
            }
            buffer = buffer_add(new Buffer(header,'utf8'),buffer.slice(_body_pos));
        }
       
        //建立到目標伺服器的連線
        var server = net.createConnection(req.port,req.host);
        //交換伺服器與瀏覽器的資料
        client.on("data", function(data){ server.write(data); });
        server.on("data", function(data){ client.write(data); });

        if (req.method == 'CONNECT')
            client.write(new Buffer("HTTP/1.1 200 Connection established\r\nConnection: close\r\n\r\n"));
        else
            server.write(buffer);
    }
}).listen(local_port);

console.log('Proxy server running at localhost:'+local_port);


//處理各種錯誤
process.on('uncaughtException', function(err)
{
    console.log("\nError!!!!");
    console.log(err);
});



/**
* 從請求頭部取得請求詳細資訊
* 如果是 CONNECT 方法,那麼會返回 { method,host,port,httpVersion}
* 如果是 GET/POST 方法,那麼返回 { metod,host,port,path,httpVersion}
*/
function parse_request(buffer)
{
    var s = buffer.toString('utf8');
    var method = s.split('\n')[0].match(/^([A-Z]+)\s/)[1];
    if (method == 'CONNECT')
    {
        var arr = s.match(/^([A-Z]+)\s([^\:\s]+)\:(\d+)\sHTTP\/(\d\.\d)/);
        if (arr && arr[1] && arr[2] && arr[3] && arr[4])
            return { method: arr[1], host:arr[2], port:arr[3],httpVersion:arr[4] };
    }
    else
    {
        var arr = s.match(/^([A-Z]+)\s([^\s]+)\sHTTP\/(\d\.\d)/);
        if (arr && arr[1] && arr[2] && arr[3])
        {
            var host = s.match(/Host\:\s+([^\n\s\r]+)/)[1];
            if (host)
            {
                var _p = host.split(':',2);
                return { method: arr[1], host:_p[0], port:_p[1]?_p[1]:80, path: arr[2],httpVersion:arr[3] };
            }
        }
    }
    return false;
}




/**
* 兩個buffer物件加起來
*/
function buffer_add(buf1,buf2)
{
    var re = new Buffer(buf1.length + buf2.length);
    buf1.copy(re);
    buf2.copy(re,buf1.length);
    return re;
}

/**
* 從快取中找到頭部結束標記("\r\n\r\n")的位置
*/
function buffer_find_body(b)
{
    for(var i=0,len=b.length-3;i<len;i++)
    {
        if (b[i] == 0x0d && b[i+1] == 0x0a && b[i+2] == 0x0d && b[i+3] == 0x0a)
        {
            return i+4;
        }
    }
    return -1;
}

另外,可以用 "nohup node some.js > /dev/null &" 命令讓nodejs程式在後臺執行。

相關推薦

NodeJS實現HTTP/HTTPS代理

身在天朝,難免會用到代理的時候。 比如在學校內網用代理免費上外網,在牆內用代理上404網站等。     現在使用的代理大部分為HTTP和Socket代理。 Socket代理更底層,需要本地解析域名,而HTTP代理則是基於HTTP協議之上的,不需要本地解析域名。下面我講講HT

實現http/https代理及證書匯入

import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.net.URI; import

java實現 HTTP/HTTPS請求繞過證書檢測程式碼實現

1、開發需求 需要實現在服務端發起HTTP/HTTPS請求,訪問其他程式資源。 2、URLConnection和HTTPClient的比較 HttpClient是個很不錯的開源框架,封裝了訪問http的請求頭,引數,內容體,響應等等, De

Go 實現HTTP中間人代理

goproxy Go HTTP(S)代理庫, 支援中間人代理解密HTTPS 專案地址 安裝 go get github.com/ouqiang/goproxy 使用 package main import ( "net/http

IIS 7如何實現http重定向https

技術 文件 down gif tail ros 描述 web asp 在不少的企業當中,網站設計出於安全的考慮使用了https協議,但同時公司也開放了80協議,不少用戶因為輸入網址的習慣不喜歡帶上https協議,導致訪問異常。 第一步:從微軟的官方網站下載HTTP重寫模塊

使用Tornado實現http代理

有時 fin support article edi cor 替換 ons async 0x00 http代理 http代理的用處非常多,市面上也有公開的代理,可是有時候為了工作須要,比方分析應用層流量、做數據訪問控制、甚至做監控等等。Tornado提

Linux裏HTTP實現HTTPS

它的 inux sts 修改配置 客戶端瀏覽器 驗證 protoc dir 保護     HTTP即超文本傳輸協議(Hypertext Transfer Protocol)。     這是一個文件的傳輸協議,我們上網的時候,所有的文件都是通過HTTP這個協議,從服務器上傳輸

ASP.NET Core 使用 URL Rewrite 中間件實現 HTTP 重定向到 HTTPS

添加引用 傳統 add arch rewrite direct get true configure 在傳統 ASP.NET 程序中,我們可以通過配置 IIS 的“URL 重寫”功能實現將 HTTP 請求重定向為 HTTPS 。但是該方法在 ASP.

Apache mod_rewrite實現HTTPHTTPS重定向跳轉

告訴 ace mod iter bing space tac lai contain 當你的站點使用了HTTPS之後,你可能會想把所有的HTTP請求(即端口80的請求),全部都重定向至HTTPS(即端口443)。這時候你可以用以下的方式來做到:(Apache mod_rew

Centos 7.4 中http-2.4 的基本實現https實現

http-2.4 https 1.建立httpd服務,要求: 1) 提供兩個基於名稱的虛擬主機www1, www2;要求每個虛擬主機都有單獨的錯誤日誌和訪問日誌; 2) 通過www1的/server-status提供狀態信

Nodejs http-proxy代理實戰應用

port proxy delete eat tar rip you app this var https = require(‘https‘); var express = require(‘express‘); var app = express() var h

httpshttps的本地測試環境搭建,asp.net結合https的代碼實現,http網站轉換成https網站之後遇到的問題

基本 解密 req with 網址 orm forms 訪問 art 一:什麽是https SSL(Security Socket Layer)全稱是加密套接字協議層,它位於HTTP協議層和TCP協議層之間,用於建立用戶與服務器之間的加密通信,確保所傳遞信息的安全性

編譯安裝Apache HTTP Server 2.4.23 以及配置HTTP/HTTPS反向代理

chan .so har 替換 quest pre and for 大小 編譯安裝Apache HTTP Server 2.4.23以及配置HTTP/HTTPS反向代理一,依賴軟件: 1.1 GCC和C++編譯器 GCC C++ Compiler 1.1.1 如果沒有安

nginx學習筆記(一) 用nginx實現本地https請求轉http請求

error erro 需要 ror har file key media nginx代理 接到項目需求需要將一些https請求利用nginx代理到http接口上,因此要在本地上搭環境進行測試,現在將該過程記錄一下。 生成證書 1. 使用openssl生成密鑰privkey.

python使用httphttps代理

在國內利用Python從Internet上爬取資料時,有些網站或API介面被限速或遮蔽,這時使用代理可以加速爬取過程,減少請求失敗,Python程式使用代理的方法主要有以下幾種: (1)如果是在程式碼中使用一些網路庫或爬蟲框架進行資料爬取,一般這種框架都會支援設定代理,例如: import urllib.

Java Socket 實現HTTPHTTPS協議傳送POST/GET請求

JAVA Socket 實現HTTP與HTTPS客戶端傳送POST與GET方式請求         哇,一看標題怎麼這麼長啊,其實意思很簡單,哥討厭用HTTP Client做POST與GET提交覺得那個畢竟是別人寫得AP

curl實現httphttps請求的方法

每次要使用curl的時候,總要查一堆資料。 現在將常用的幾句儲存下來,省的每次都去谷歌。 常規curl請求: $url = '//www.jb51.net'; $curl = curl_init(); curl_setopt($curl, CURLOPT_URL, $url); curl_s

libevent和libcurl實現httphttps伺服器 cJSON使用

  前言   libevent和libcurl都是功能強大的開源庫;libevent主要實現伺服器,包含了select、epoll等高併發的實現;libcurl實現了curl命令的API封裝,主要作為客戶端。這兩個開源庫的安裝可以參考我的這篇部落格:https://www.cnblogs.com/liudw

VC++實現HTTP代理

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!        

基於libcurl實現REST風格http/https的get和post

c/c++開發中經常要用到http/https協議,直接使用socket工作量很大,要是使用socket實現https,那更不可思議,開源的c/c++的http客戶端框架,libcurl是首選,而且也相當成熟穩定,最近C++專案中用到https請求,就做下研究。 libcurl簡介(來源官網