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和域名传递 ...
随机推荐
- linux下程序编译出错解决方法
一 *** No rule to make target 'subscribe_pkg/CMakeFiles/subscribe_pkg_node.dir/build'. 停止.CMakeFiles/ ...
- FunDA(15)- 示范:任务并行运算 - user task parallel execution
FunDA的并行运算施用就是对用户自定义函数的并行运算.原理上就是把一个输入流截分成多个输入流并行地输入到一个自定义函数的多个运行实例.这些函数运行实例同时在各自不同的线程里同步运算直至耗尽所有输入. ...
- [JavaScript] css将footer置于页面最底部
<!-- 父层 --> <div id="wapper"> <!-- 主要内容 --> <div id="main-conten ...
- .Net Core命令行配置-配置介绍
1.使用VS2017 创建一个控制台应用程序,选中控制台应用(.NET Core) 2. 使用程序包管理控制台键入 Install-Package Microsoft.AspNetCore -Vers ...
- php内核为变量的值分配内存的几个宏
在php5.3之前,为某变量分配内存是用宏 MAKE_STD_ZVAL; 737 #define MAKE_STD_ZVAL(zv) \ # /Zend/zend.h738 ALLOC_ZVAL(zv ...
- Swift 基本数据类型与运算符表达式
// // main.swift // LessonSwift01 // // Created by lanouhn on 16/1/25. // Copyright © 2016年 齐彦坤. All ...
- SQL命令导入导出
利用cmd的操作命令导出,详情如下(备注:方法二是转载网上的教程):1:G:\Oracle\product\10.1.0\Client_1\NETWORK\ADMIN目录下有个tnsname.ora文 ...
- Vue的Computed的使用
Vue的Computed的使用 相关Html: <!DOCTYPE html> <html lang="en"> <head> <meta ...
- 【树】Construct Binary Tree from Preorder and Inorder Traversal
题目: Given preorder and inorder traversal of a tree, construct the binary tree. 思路: 线序序列的第一个元素就是树根,然后 ...
- 【链表】Linked List Cycle II
题目: Given a linked list, return the node where the cycle begins. If there is no cycle, return null. ...