程序員三大必備網站是:Google、Github、StackOverflow。如果你還在用Baidu搜索技術文章的話,我想說的是,少年你已經被鄙視很多年了,趕緊換成谷歌吧,不要再被鄙視了!Github、StackOverflow在國內能夠正常訪問,但是Google由於眾所周知的原因,國內無法訪問,所以我們需要翻牆訪問Google。個人覺得shadowsocks的是目前最好用的代理,沒有之一!shadowsocks有多牛?前段時間shadowsocks的作者被約談了,還被要求刪除在Github上的源碼,你說有多牛!可以自己在vps上利用shadowsocks搭建自己的專屬代理,如果你懶的話,也可以直接去買個shadowsocks賬號,網上賣家多的是。后面我會寫相關的文章介紹如何在vps上搭建服務器,敬請關注。今天先介紹一下用node-http-proxy搭建谷歌代理,小試牛刀一下。
node-http-proxy是一個用於Node.js的HTTP可編程代理庫,支持 websockets。它是適用於實現例如代理服務器和負載均衡這樣的組件。node-http-proxy使用起來很簡單,下面簡單介紹一下。
核心概念
通過createProxyServer函數創建代理,同時你也可選的傳入options對象
1 var httpProxy = require('http-proxy'); 2 var proxy = httpProxy.createProxyServer(options);
createProxyServer函數返回的proxy對象包含四個方法
- web
req, res, [options]
用來代理http(s)請求 - ws
req, socket, head, [options]
用來代理WS(S)請求 - listen
port
該函數把對象包裝成webserver,方便使用 - close
[callback]
該函數關閉內部的webserver並且停止監聽給定的端口
然后可以如下調用函數代理請求
1 http.createServer(function(req, res) { 2 proxy.web(req, res, { target: 'http://mytarget.com:8080' }); 3 });
錯誤處理可以通過監聽error事件
1 proxy.on('error', function(e) { 2 ... 3 });
或者使用回調API
1 proxy.web(req, res, { target: 'http://mytarget.com:8080' }, function(e) { ... });
Use Cases
下面的例子顯示如何用你自己的http服務器代理請求,你也可以加入自己的業務邏輯處理請求
1 var http = require('http'), 2 httpProxy = require('http-proxy'); 3 4 // 5 // Create a proxy server with custom application logic 6 // 7 var proxy = httpProxy.createProxyServer({}); 8 9 // 10 // Create your custom server and just call `proxy.web()` to proxy 11 // a web request to the target passed in the options 12 // also you can use `proxy.ws()` to proxy a websockets request 13 // 14 var server = http.createServer(function(req, res) { 15 // You can define here your custom logic to handle the request 16 // and then proxy the request. 17 proxy.web(req, res, { target: 'http://127.0.0.1:5060' }); 18 }); 19 20 console.log("listening on port 5050") 21 server.listen(5050);
更多例子請查看官方文檔
Options
httpProxy.createProxyServer
支持下列options:
- target: url字符串
- forward: url字符串
- agent: 傳給http(s).request的對象
- ssl: 密鑰,HTTPS使用
- ws: true/false, 是否代理websockets
- xfwd: true/false, 是否加上x-forward頭字段
- secure: true/false, 是否校驗ssl證書
- toProxy: 傳遞絕對URL作為
path
- prependPath: true/false, 默認值為true,是否在proxy path前面加上target的path
- ignorePath: true/false, 默認值為false,是否忽略傳入的請求的proxy path
- localAddress: 本地地址
- changeOrigin: true/false, 默認值為false, 是否更改原始的host頭字段為target URL
- auth: 基本身份認證,比如:‘用戶名:密碼’來計算Authorization header
- hostRewrite: 重寫重定向(301/302/307/308)的location hostname
- autoRewrite: 是否自動重寫重定向(301/302/307/308)的location host/port,默認值為false
- protocolRewrite: 重寫重定向(301/302/307/308)的location的協議,http或者https,默認值為null
谷歌代理
1 'use strict'; 2 3 var http = require('http'); 4 var https = require('https'); 5 var httpProxy = require('http-proxy'); 6 var url = require('url'); 7 8 var PROXY_PORT = 8000; 9 var proxy, server; 10 11 // Create a proxy server with custom application logic 12 proxy = httpProxy.createProxy({}); 13 14 proxy.on('error', function (err) { 15 console.log('ERROR'); 16 console.log(err); 17 }); 18 19 server = http.createServer(function (req, res) { 20 //var finalUrl = req.url, 21 var finalUrl = 'https://www.google.com'; 22 var finalAgent = null; 23 var parsedUrl = url.parse(finalUrl); 24 25 if (parsedUrl.protocol === 'https:') { 26 finalAgent = https.globalAgent; 27 } else { 28 finalAgent = http.globalAgent; 29 } 30 31 proxy.web(req, res, { 32 target: finalUrl, 33 agent: finalAgent, 34 headers: { host: parsedUrl.hostname }, 35 prependPath: false, 36 xfwd : true, 37 hostRewrite: finalUrl.host, 38 protocolRewrite: parsedUrl.protocol 39 }); 40 }); 41 42 console.log('listening on port ' + PROXY_PORT); 43 server.listen(PROXY_PORT);
你沒看錯,就是這么點代碼就能代理谷歌了,前提是你要有個牆外的vps哈!首先設置瀏覽器的http代理為你的vps,然后再vps上運行上面的代理程序,最后在瀏覽器中訪問www.google.com,然后就是見證奇跡的時候了。有些小伙伴可能迫不及待的拿去試了試,結果發現博主騙人,根本不能代理谷歌。別急,這是因為node-http-proxy有一個小小的bug,不能怪博主,博主也是受害者之一。博主開始也卡在這里很久,最后去閱讀源代碼才發現問題所在!在node-http-proxy的web-outgoing.js里有個setRedirectHostRewrite函數,該函數的功能就是重定向時重寫header中location的host地址,函數代碼如下:
1 function setRedirectHostRewrite(req, res, proxyRes, options) { 2 if ((options.hostRewrite || options.autoRewrite || options.protocolRewrite) 3 && proxyRes.headers['location'] 4 && redirectRegex.test(proxyRes.statusCode)) { 5 var target = url.parse(options.target); 6 var u = url.parse(proxyRes.headers['location']); 7 8 // make sure the redirected host matches the target host before rewriting 9 if (target.host != u.host) { 10 return; 11 } 12 13 if (options.hostRewrite) { 14 u.host = options.hostRewrite; 15 } else if (options.autoRewrite) { 16 u.host = req.headers['host']; 17 } 18 if (options.protocolRewrite) { 19 u.protocol = options.protocolRewrite; 20 } 21 22 proxyRes.headers['location'] = u.format(); 23 } 24 }
問題出在以下代碼:
1 // make sure the redirected host matches the target host before rewriting 2 if (target.host != u.host) { 3 return; 4 }
作者的意圖是確保重定向的host跟target的host的匹配,不匹配就直接返回。代理谷歌時就會發生不匹配的情況直接返回了。
比如,博主來自中國,當我curl www.google.com
時會重定向到www.google.com.hk:
1 [root@iZu1fmzgm3iZ ~]# curl www.google.com 2 <HTML><HEAD><meta http-equiv="content-type" content="text/html;charset=utf-8"> 3 <TITLE>302 Moved</TITLE></HEAD><BODY> 4 <H1>302 Moved</H1> 5 The document has moved 6 <A HREF="http://www.google.com.hk/url?sa=p&hl=zh-CN&pref=hkredirect&pval=yes&q=http://www.google.com.hk/%3Fgws_rd%3Dcr&ust=1448378903186576&usg=AFQjCNHtMfRNndvgHHMAzipRzC9NpycwGw">here</A>. 7 </BODY></HTML>
如果你來自日本,當你curl www.google.com
時會重定向到www.google.co.jp:
1 [ec2-user@ip-172-31-27-165 ~]$ curl www.google.com 2 <HTML><HEAD><meta http-equiv="content-type" content="text/html;charset=utf-8"> 3 <TITLE>302 Moved</TITLE></HEAD><BODY> 4 <H1>302 Moved</H1> 5 The document has moved 6 <A HREF="http://www.google.co.jp/?gfe_rd=cr&ei=toJUVubpIcem8wfGirqQDw">here</A>. 7 </BODY></HTML>
因為重定向的host跟target的host不匹配,程序直接返回,hostRewrite無效,所以我們應該去掉以下代碼:
1 // make sure the redirected host matches the target host before rewriting 2 if (target.host != u.host) { 3 return; 4 }
當我們注釋掉以上代碼,重新運行程序,發現已經可以上谷歌了,是不是很神奇!