Go 语言实现 HTTP 层面的反向代理
最近对 Go 语言的反向代理使用得偏多,其实在大概两年前就写过 TCP 层面的代理,而且那时也是用的 Go 语言,不同之处在于之前只是偶尔尝试一下使用,最近是因为工作需要使用的。相比较于 TCP 层面的代理,HTTP 的代理实现起来麻烦事比较多,如果我们仅仅是简单的反向代理,OK,那还好,做个 Host 替换就差不多了。但是,很多时候我们作反向代理,那么需求就比较多样了,例如我们可能希望对代理的响应内容做些改变,也可能希望是 HA 的反向代理。
Go 语言在自身的内置库中就提供了一个很方便的反向代理组件,使用内置的 httputil 的组件我们可以非常快的开发出一个简单的反向代理,稍后我们会看到。但是,一旦与我们前面提到的多样化的需求的时候,就不能简单得写代码了,我们得了解一些组件,然后根据需求对组件进行个性化,从而满足我们的需求。下面,我就从简入繁来看下 Go 语言内置的反向代理库。
内置反向代理
根据 Go 语言官方文档:Package httputil 中描述,我们可以写这么一段简单的代码,这段代码是从 Go 语言的官方文档上抄下来的:
这里我对这段代码解释一下:
- Line 2-5: 创建了一个简单的服务器,响应一些 URL,内容就是里面的字符串
- Line 7-12:这才是真正的创建反向代理的代码,这里创建了一个反向代理的 ReverProxy 实体
- Line 14-24:这些是仿造一个客户端请求前面的反向代理,然后获取输出的响应
执行一下这段代码,应该看到的结果是:
this call was relayed by the reverse proxy
Director
看上去反向代理挺简单的,6 行代码就已经完成了,而且还带错误处理,OK,是时候做点有意思的事情了,不妨我们就做个代理 baidu.com 的反向代理好了,看看效果。为什么是代理 baidu.com,因为它可以进行很浪的操作啊,自行发挥呗:
一个很简单的想法就是这么代理,然后我们尝试运行一下代码就会发现根!本!行!不!通!那怎么办,这个时候就是需要你去思考这中间可能的问题了,或者你该利用一些工具分析一下中间环节哪里不一样。但是,我这里不准备将怎么去发现这个问题,我会告诉你问题就是 HTTP 请求头不对,所以我们该更改一下请求的请求头,所以代码应该修改成这样:
这里有一个关键的地方就是 Line 11,如果你去对比我的这段代码和 Go 语言自带的 defaultDirector 的代码,你也会发现其实就是多了 Line 11 这一行,但是这一行及其关键,很多 Server 可能出于各种考虑,会屏蔽掉 Host 不为自己的请求,而我们作为代理需要将这个补上。
OK,运行这段代码,然后我们就可以通过 localhost:9090 成功访问 baidu.com 了,看上去应该已经成功了。
ModifyResponse
有时候,我这个代理可能会给别人访问,于是乎出于版权等因素,我会做一些小手段,例如这里的响应头:
这个 Server: BWS/1.1 不是很好,我觉得有必要换成我自己的服务器名字:Server:ProjectZoo,于是乎,又有事情可以搞了,要怎么修改这个响应头呢,Go 语言也给我们提供了,那就是 ModifyResponse,来,尝试一下:
在途中这个问题我加了几行代码,然后再访问一遍看看:
一切正如我所期望的,它成了!这其实就是一个简单修改响应的示例,可以做到很强大的功能,但是,这没必要展开说了。需要强调的一点就是,如果需要修改响应体,别忘了同时更新 Header 的 Content-Length,不然,这就是留给自己的一个深坑哦。
Transport
在前面的两个操作里面,我们对 HTTP 请求的反向代理前和后都进行了一番操作,但是,有点不爽的地方在于我们是分别在两个地方进行处理的,这缺乏一些统一性。那么有没有一种方法,可以在一个地方完成这些事情呢,很明显 Go 语言为我们提供了这个一个地方,那就是 Transport,官方文档中是这么描述 Transport 的:
// The transport used to perform proxy requests.
这似乎还有点太简单了,不如看一下 RoundTripper 接口是如何描述的:
RoundTrip executes a single HTTP transaction, returning
a Response for the provided Request.RoundTrip should not attempt to interpret the response. In
particular, RoundTrip must return err == nil if it obtained
a response, regardless of the response's HTTP status code.
A non-nil err should be reserved for failure to obtain a
response. Similarly, RoundTrip should not attempt to
handle higher-level protocol details such as redirects,
authentication, or cookies.RoundTrip should not modify the request, except for
consuming and closing the Request's Body. RoundTrip may
read fields of the request in a separate goroutine. Callers
should not mutate the request until the Response's Body has
been closed.RoundTrip must always close the body, including on errors,
but depending on the implementation may do so in a separate
goroutine even after RoundTrip returns. This means that
callers wanting to reuse the body for subsequent requests
must arrange to wait for the Close call before doing so.
The Request's URL and Header fields must be initialized.
可以看到 Transport 的功能还是很简单的,虽然可以修改 Request 和 Response,但是,就合理性来说是不希望在这里面修改的的,更多的需求还是在于修改 Response 的。既然如此,不妨来看下如何实现 Transport,就以内置的 DefaultTransport
来看吧:
其实 DefaultTransport
自身没什么,无非是设定一些默认参数构建了一下 http.Transport
这个结构体,这里有一个坑就是 Line 2 的 Proxy:ProxyFromEnviroment
会使用系统默认的代理,如果你设置了环境变量 HTTP_PROXY 的话,那么默认是会使用这个代理的,切记切记,这是个坑!
小结
到 DefaultTransport
这里就不再往下深挖了,因为到这里已经可以满足平时的大部分需求了,再往下其实就涉及到 Go 语言中 HTTP 模块的核心功能了,这些会在后面的文章中讲。在这篇文章中,虽然我只是讲了三个组件,但是,这三个小组件已经很够了,而且,在本文中,我也着重描述了可能出现坑的几个地方,这些地方都值得我们去关注,因为在使用 ReverseProxy 的时候,一般遇到的问题都离不开这几个。
Reference
Go 语言实现 HTTP 层面的反向代理的更多相关文章
- GO语言一行代码实现反向代理
本文,介绍了什么是反向代理,如何用go语言实现反向代理. 至于他的标题, "GO语言一行代码实现反向代理 | Writing a Reverse Proxy in just one line ...
- Go语言正/反向代理的姿势
先重温一下什么叫反向代理,正向代理. 鹅厂二面,nginx回忆录 所谓正向,反向代理取决于代理的是出站请求,还是入站请求. 正向代理: 代理的出站请求, 客户端能感知到代理程序,架构上距离客户端更近. ...
- 使用go语言实现简单的反向代理工具激活IntelliJ和PyCharm,持续更新
最近Jetbrians系列IDE更新至2017.3版本,激活检测机制也变成了动态封禁域名,导致大部分域名激活被屏蔽了,所以找了下资料,根据ilanyu的代码,改了下地址,实现了本地反向代理激活服务器. ...
- nginx是一个反向代理的软件
nginx只是一个反向代理的软件,和语言无关,理论上支持任何Web平台,当然http://Asp.net也不例外,http://51aspx.com就是http://Asp.net开发的,前端暴漏的是 ...
- 反向代理代理百度、google
<VirtualHost _default_:443> # ServerAdmin mail@localhost # DocumentRoot "/var/www/html&qu ...
- Ngix 移动端与Pc端 反向代理判断
如神马搜索和百度(http://www.baidu.com),当用桌面浏览器和移动浏览器访问的结果是不一样的.其中的手段可能有两种: 转载Ngix反向代理判断 服务端直接判断UA输出不同的界面,JAV ...
- 基于ssh反向代理实现的远程协助
本文描述了怎么通过ssh反向代理实现远程协助,并提供了相关代码. 可满足web开启远程协助功能后,维护人员能够通过ssh和http登录客户机器(包括在nat环境下) web开启该功能后,ssh才能登录 ...
- aProxy: 带认证授权和权限控制的反向代理
前段时间很多数据库因为没有做好权限控制暴露在外网被删然后遭勒索的事件,而类似的有些内网的web服务也会被开放到公网并且没有做任何权限控制的,这样也会有一定的风险.所以就决定写篇文章简单介绍一个小工具. ...
- Nginx前端设置反向代理,后端Apache如何获取访客的真实IP,结合PHP
nginx反向代理后,在应用中取得的ip都是反向代理服务器的ip,取得的域名也是反向代理配置的url的域名,解决该问题,需要在nginx反向代理配置中添加一些配置信息,目的将客户端的真实ip和域名传递 ...
随机推荐
- SpringMVC 上传文件and过滤器
SpringMVC提供了一个MultipartResolver接口用来实现文件上传,并使用Commons FileUpload技术实现了一个该接口的实现类CommonsMultipartResolve ...
- jzoj4724
DJL为了避免成为一只咸鱼,来找czgj学习Fibonacci数列. 通过czgj的谆谆教导,DJL明白了Fibonacci数列是这样定义的: F(1)=1;F(2)=1;F(n)=F(n-1)+F( ...
- 用navicat远程连接mysql:Can't connect to MySQL server (10060)
出现这种现象的原因有两个,一个是当前用户被mysql服务器拒绝,另外一个原因是3306端口被被防火墙禁掉,无法连接到该端口.解决方法如下: 1.设置远程用户访问权限: // 任何远程主机都可以访问数据 ...
- 算法(Algorithm)
算法就是"把解决问题的步骤无一遗漏地用文字或图表示出来". 在解决问题的步骤中,有了与直觉相关的因素,就不是算法了.既然不是算法,也就不能用程序表示了.
- HTML+纯JS制作音乐播放器
该篇文章会教你通过JavaScript制作一个简单的音乐播放器.包括播放.暂停.上一曲和下一曲. 阅读本文章你需要对HTML.CSS和Javascript有基本的了解. 话不多说,先上图. emmm. ...
- NSCache 的好处
相较于 NSDictionary 线程安全 系统资源将要耗尽时,自动删减缓存 自动删减"最久未使用的"对象 不会自动拷贝键.因为有些键不支持拷贝操作 可以和 NSPurgeable ...
- 【BZOJ3992】【SDOI2015】序列统计 EGF+多项式快速幂+循环卷积
如果是求$n$个数之和在模$m$意义下为$x$,那么做法是显然的. 但是这道题问的是$n$个数之积在模m意义下为$x$,那么做法就和上面的问题不同. 考虑如何把乘法转换成加法(求log): 题目中有一 ...
- 利用Windows资源监视器解决文件夹无法改名无法删除问题
在win7等Windows系统操作文件夹更名.删除时经常会报错,操作无法完成,balabala 这个时候仅凭在用的软件去一个一个找是很难的,即便软件全关了,还有后台进程,,,奔溃吧 好了,现在我们有了 ...
- 第7章—SpringMVC高级技术—不用web.xml,而使用java类配置SpringMVC
不用web.xml,而使用java类配置SpringMVC DispatcherServlet是Spring MVC的核心,按照传统方式, 需要把它配置到web.xml中. 我个人比较不喜欢XML配置 ...
- RocketMQ-Filer
一.搭建RocketMQ集群 我搭建的是2-master no slave模式,所以在${rocketmq}/conf/2m-noslave/下的 brokder-*.properties 中添加 f ...