We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
新的一年,新的气象,好久没有更新过博客了。
在这里祝大家新年快乐,大吉大利。
代理,大家都不陌生,大体上分为正向代理和反向代理。常见的有
本文就讲解,如何制作一个反向代理工具,并且代理任意站点。
市面上已经有那么多代理工具,为什么还要折腾一个?
在开发时,我们可能会有这样的一些需求:
像 Chrome 浏览器的开发者工具里面就有 override 功能,可以修改服务器返回的资源(HTML/CSS/JS),并映射到本地目录。
但它有一个缺陷:如果修改的站点带有端口,就无法映射到本地,因为 : 是一个非法字符,无法在文件系统中创建,或者你可以试试创建一个目录 localhost:8080
:
localhost:8080
ref: https://stackoverflow.com/questions/70337046/chrome-local-overrides-with-port-number
对于一些平台的上线,有要求使用 HTTPS,但你的站点又没有,在开发阶段,可以暂时通过代理的方式解决。
不止微信,还有支付宝等内嵌的 H5 页面如何调试?简单的修改和打印信息,根本就不需要发布,通过代理即可解决。
比如我要给同事展示某网站,需要科学上网,但是他/她却没有,这时候就可以在本机通过代理的方式呈现出来。
就出于以上几点,我觉得,撸一个反向代理工具。
采用 Golang 进行开发,除了标准库里面支持反向代理之外,还因为它非常简单的交叉编译。
这是网上随便找的代码
import ( "log" "net/http" "net/http/httputil" "net/url" ) // NewProxy takes target host and creates a reverse proxy // NewProxy 拿到 targetHost 后,创建一个反向代理 func NewProxy(targetHost string) (*httputil.ReverseProxy, error) { url, err := url.Parse(targetHost) if err != nil { return nil, err } return httputil.NewSingleHostReverseProxy(url), nil } // ProxyRequestHandler handles the http request using proxy // ProxyRequestHandler 使用 proxy 处理请求 func ProxyRequestHandler(proxy *httputil.ReverseProxy) func(http.ResponseWriter, *http.Request) { return func(w http.ResponseWriter, r *http.Request) { proxy.ServeHTTP(w, r) } } func main() { // initialize a reverse proxy and pass the actual backend server url here // 初始化反向代理并传入真正后端服务的地址 proxy, err := NewProxy("http://my-api-server.com") if err != nil { panic(err) } // handle all requests to your server using the proxy // 使用 proxy 处理所有请求到你的服务 http.HandleFunc("/", ProxyRequestHandler(proxy)) log.Fatal(http.ListenAndServe(":8080", nil)) }
不,事情没有那么简单,如果仅代理接口,那么一般都不会有什么问题,但要代理站点,就有一大堆的问题要处理。
HTTP 状态码 301
301 是永久重定向,如果浏览器收到 301,那么下次再请求这个地址,就不会再次发送请求,这对于一个代理来说,这并不好。
所以我们需要将 301 改成 302 临时重定向。
HTTP header Location
Location 表示重定向的地址,可以是绝对路径,可以是相对路径。如果我们代理了某个站点,就要重写 Location
例如代理了 https://github.com
如果某个请求返回了 Location: https://github.com
那么我们就需要对其进行修改
- Location: https://github.com + Location: <我的代理地址>
文件内容替换
有些站点的资源/链接等直接使用一个完整的 URL 而不是相对路径,例如
<a href="https://github.com">Click</a>
那么我们要对其替换
- <a href="https://github.com">Click</a> + <a href="<我的代理地址>">Click</a>
这不仅仅是要替换 HTML,还有 CSS/Javascript/XML/JSON 等文件
处理 Content-Encoding
在替换文件之前,首先是解压,我们接收到的响应,都是经过压缩之后的,那么就无法替换。
大多数的站点,都会经过压缩后返回。根据规范,服务器可能会采用以下几种压缩格式:
这几类压缩算法都是公开的,并且社区已有现成的库。
只需要 解压 -> 替换 -> 压缩 -> 返回响应
甚至可以省略压缩这一步以提高性能。
处理 Cookies
有些 Cookie 指定了域名,被代理之后域名就不正确,所以我们需要对其进行重写。
- Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT; Domain=github.com + Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT; Domain=<我的代理地址>
有些 Cookie 指定了 Secure,必须要在 HTTPS 下才可用,但代理服务器如果是 HTTP 的话,也要替换
- Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT; Secure=true + Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT;
处理 HTML 的 Content-Security-Policy
CSP 是一个安全策略,简单来说,只允许站点信任域名的资源。
代理之后域名发送变化,如果不替换就无法使用
- <meta http-equiv="Content-Security-Policy" content="default-src 'self' https://github.com"> + <meta http-equiv="Content-Security-Policy" content="default-src 'self' <我的代理地址>">
以及 HTTP 返回的头部信息
- Content-Security-Policy: default-src 'self' *.mailsite.com; img-src * + Content-Security-Policy: default-src 'self' <我的代理地址>; img-src *
处理 HTML 的 integrity
integrity 属性用于校验资源的完整性,浏览器在下载资源后再进行 HASH 校验,如果校验不通过,则说明内容已被篡改,浏览器就不会加载。
<link crossorigin="anonymous" media="all" integrity="sha512-MCJFYfbQoT4EXC6aWx5Wghs8FC/jslHEeN2iWXphliccmede2dQlhIBTAUCBq9Yu5poltu4askungzvyCsycGg==" rel="stylesheet" href="https://github.githubassets.com/assets/tab-size-fix-30224561f6d0a13e045c2e9a5b1e5682.css" />
这里偷个懒,直接把 integrity 属性移除
- <link crossorigin="anonymous" media="all" integrity="sha512-MCJFYfbQoT4EXC6aWx5Wghs8FC/jslHEeN2iWXphliccmede2dQlhIBTAUCBq9Yu5poltu4askungzvyCsycGg==" rel="stylesheet" href="https://github.githubassets.com/assets/tab-size-fix-30224561f6d0a13e045c2e9a5b1e5682.css" /> + <link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/tab-size-fix-30224561f6d0a13e045c2e9a5b1e5682.css" />
处理页面中的其他链接
在很多网站中,他们引入自家的 CDN,比如百度搜索出来的图片 https://t8.baidu.com/it/u=2652343384,1723246354&fm=218&app=126&f=JPEG?w=121&h=75&s=182A5D32DCBB7D8A06F8DCC6030070A2
https://t8.baidu.com/it/u=2652343384,1723246354&fm=218&app=126&f=JPEG?w=121&h=75&s=182A5D32DCBB7D8A06F8DCC6030070A2
显然替换成 http://t8.<我的代理地址>/xxxxx 就不对,并且这类请求依赖与 Cookie,否则请求失败。
http://t8.<我的代理地址>/xxxxx
要处理这类,我们也要对其进行代理
代理之后的地址变成 http://<我的代理地址>/?forward_url=<原地址>
http://<我的代理地址>/?forward_url=<原地址>
处理前:
处理后:
以上几个处理,大多都是替换 URL,无非就是把目标服务器地址,替换成代理服务器地址,例如代理 Github
github.com -> localhost
这里我想到几种解决方案:
能否直接替换字符串,简单粗暴?
答: 不行
否则就会出现这样的情况 api.github.com -> api.localhost
api.github.com
api.localhost
能否使用正则表达式替换?
例如 /http?s:/\/\/google\.com/ 照样可以匹配 https://google.com.hk
/http?s:/\/\/google\.com/
https://google.com.hk
最后变成 http://localhost.hk
http://localhost.hk
把内容解析成 AST 再替换节点内容
这属于高级一点的玩法,替换是最准确的,但同时也是最费时费力,消耗性能的。
而且一点有语法错误,无法解析的情况,就很难处理(比如总有些站点,写的 HTML 都不符合规范,甚至闭合标签都没有)
最终方案
最终方案就是提取文本中的 URL,然后比对域名,域名匹配的才替换。
所以这又回到大难题: 如果从一堆文本中提取 URL?
我写了一大串的正则表达式去匹配,但你永远想不到,有些网站的 URL 是长什么样子
比如有这样的 https://avatars.githubusercontent.com/u/9758711?s=40&v=4,有一个特殊字符 ;
https://avatars.githubusercontent.com/u/9758711?s=40&v=4
;
你很难判定,这是不是一个完整的 URL
最终也只是做大匹配绝大多数的地址
最后到这里已经讲完大部分的细节。
经过我的测试,反向代理 Google/Facebook/Github/百度 等几个主流网站都没有问题。更不用说自己开发的站点。
希望能帮助到大家,有 BUG 欢迎反馈,顺便给个小 ✨✨。
https://github.com/axetroy/forward-cli
The text was updated successfully, but these errors were encountered:
No branches or pull requests
新的一年,新的气象,好久没有更新过博客了。
在这里祝大家新年快乐,大吉大利。
如何反向代理一个站点?
代理,大家都不陌生,大体上分为正向代理和反向代理。常见的有
本文就讲解,如何制作一个反向代理工具,并且代理任意站点。
市面上已经有那么多代理工具,为什么还要折腾一个?
起因
在开发时,我们可能会有这样的一些需求:
像 Chrome 浏览器的开发者工具里面就有 override 功能,可以修改服务器返回的资源(HTML/CSS/JS),并映射到本地目录。
但它有一个缺陷:如果修改的站点带有端口,就无法映射到本地,因为
:
是一个非法字符,无法在文件系统中创建,或者你可以试试创建一个目录localhost:8080
ref: https://stackoverflow.com/questions/70337046/chrome-local-overrides-with-port-number
对于一些平台的上线,有要求使用 HTTPS,但你的站点又没有,在开发阶段,可以暂时通过代理的方式解决。
不止微信,还有支付宝等内嵌的 H5 页面如何调试?简单的修改和打印信息,根本就不需要发布,通过代理即可解决。
比如我要给同事展示某网站,需要科学上网,但是他/她却没有,这时候就可以在本机通过代理的方式呈现出来。
就出于以上几点,我觉得,撸一个反向代理工具。
技术选型
采用 Golang 进行开发,除了标准库里面支持反向代理之外,还因为它非常简单的交叉编译。
这是网上随便找的代码
这么简单,这不就完成了吗?
不,事情没有那么简单,如果仅代理接口,那么一般都不会有什么问题,但要代理站点,就有一大堆的问题要处理。
HTTP 状态码 301
301 是永久重定向,如果浏览器收到 301,那么下次再请求这个地址,就不会再次发送请求,这对于一个代理来说,这并不好。
所以我们需要将 301 改成 302 临时重定向。
HTTP header Location
Location 表示重定向的地址,可以是绝对路径,可以是相对路径。如果我们代理了某个站点,就要重写 Location
例如代理了 https://github.com
如果某个请求返回了 Location: https://github.com
那么我们就需要对其进行修改
文件内容替换
有些站点的资源/链接等直接使用一个完整的 URL 而不是相对路径,例如
那么我们要对其替换
这不仅仅是要替换 HTML,还有 CSS/Javascript/XML/JSON 等文件
处理 Content-Encoding
在替换文件之前,首先是解压,我们接收到的响应,都是经过压缩之后的,那么就无法替换。
大多数的站点,都会经过压缩后返回。根据规范,服务器可能会采用以下几种压缩格式:
这几类压缩算法都是公开的,并且社区已有现成的库。
只需要 解压 -> 替换 -> 压缩 -> 返回响应
甚至可以省略压缩这一步以提高性能。
处理 Cookies
有些 Cookie 指定了域名,被代理之后域名就不正确,所以我们需要对其进行重写。
有些 Cookie 指定了 Secure,必须要在 HTTPS 下才可用,但代理服务器如果是 HTTP 的话,也要替换
处理 HTML 的 Content-Security-Policy
CSP 是一个安全策略,简单来说,只允许站点信任域名的资源。
代理之后域名发送变化,如果不替换就无法使用
以及 HTTP 返回的头部信息
处理 HTML 的 integrity
integrity 属性用于校验资源的完整性,浏览器在下载资源后再进行 HASH 校验,如果校验不通过,则说明内容已被篡改,浏览器就不会加载。
这里偷个懒,直接把 integrity 属性移除
处理页面中的其他链接
在很多网站中,他们引入自家的 CDN,比如百度搜索出来的图片
https://t8.baidu.com/it/u=2652343384,1723246354&fm=218&app=126&f=JPEG?w=121&h=75&s=182A5D32DCBB7D8A06F8DCC6030070A2
显然替换成
http://t8.<我的代理地址>/xxxxx
就不对,并且这类请求依赖与 Cookie,否则请求失败。要处理这类,我们也要对其进行代理
代理之后的地址变成
http://<我的代理地址>/?forward_url=<原地址>
处理前:
处理后:
最困难的部分
以上几个处理,大多都是替换 URL,无非就是把目标服务器地址,替换成代理服务器地址,例如代理 Github
github.com -> localhost
这里我想到几种解决方案:
能否直接替换字符串,简单粗暴?
答: 不行
否则就会出现这样的情况
api.github.com
->api.localhost
能否使用正则表达式替换?
答: 不行
例如
/http?s:/\/\/google\.com/
照样可以匹配https://google.com.hk
最后变成
http://localhost.hk
把内容解析成 AST 再替换节点内容
这属于高级一点的玩法,替换是最准确的,但同时也是最费时费力,消耗性能的。
而且一点有语法错误,无法解析的情况,就很难处理(比如总有些站点,写的 HTML 都不符合规范,甚至闭合标签都没有)
最终方案
最终方案就是提取文本中的 URL,然后比对域名,域名匹配的才替换。
所以这又回到大难题: 如果从一堆文本中提取 URL?
我写了一大串的正则表达式去匹配,但你永远想不到,有些网站的 URL 是长什么样子
比如有这样的
https://avatars.githubusercontent.com/u/9758711?s=40&v=4
,有一个特殊字符;
你很难判定,这是不是一个完整的 URL
最终也只是做大匹配绝大多数的地址
项目地址
最后到这里已经讲完大部分的细节。
经过我的测试,反向代理 Google/Facebook/Github/百度 等几个主流网站都没有问题。更不用说自己开发的站点。
希望能帮助到大家,有 BUG 欢迎反馈,顺便给个小 ✨✨。
https://github.com/axetroy/forward-cli
The text was updated successfully, but these errors were encountered: