求教怎么用go实现nginx https 反向代理代理

[Go]网络代理实现
| Go语言中文网 | Golang中文社区 | Golang中国
<meta name="author" content="polaris ">
[Go]网络代理实现
[Go]网络代理实现
在天朝做程序员比较让人蛋疼,比如你想用GOOGLE,你就很蛋疼。原因大家都懂。然后呢,一开始自己在用GOAGENT, VPN, SSH, ShadowSocks等程序,GOAGENT和SHADOWSOCKS都是非常优秀的。而自己在很早刚开始接触计算机的时候就有想法自己写一个代理程序,因为各种各样的原因总是没去做,或者说自己的需求总是能够被满足,所以没什么动力。但是自从学GO语言后,网络程序的开发变的没有之前做C/C++时那么蛋疼了,所以试着自己写一个代理程序,然后也贯彻的实践。这里把一些心得总结记录下。GITHUB地址: SOCKS5协议刚开始的时候,是想着做HTTP代理,但是HTTP协议比较复杂,牵扯到的概念比较多,自己又不熟悉,所以就没选中做HTTP通道方式的代理程序。而代理协议里用的最多的其实就是SOCKS协议了。SOCKS4,SOCKS5等。因为我一直在用CHROME, CHROME上的插件SwitchySharp也比较好用,也支持SOCKS5协议,更重要的是SOCKS5协议非常简单。SOCKS5协议主要分三步,第一步就是握手协商,协商双方的验证方式。我的做法简单粗暴:因为SOCKS5规定的握手协议中,规定了最大的数据长度就是258个直接,所以我在实现的时候,直接申请258字节的BUFFER,然后读取。读取后也不对验证方式进行判断是否合法,支持之类的,直接返回0X05 0X00,也就是不需要验证。第二步就是获取客户端发来的CMD协议。SOCKS5这里也规定了最长长度,263个字节。所以也一次申请出来以备后用。感谢GO语言封装的网络库,不管是啥域名,还是IPV4,IPV6之类的,调用net.Dial的时候传进去就好,net.Dial函数内部会自己搞定到IP的转换。然后就根据CMD里指定的目标地址链接,成功后返回对应的协议数据。目前我只支持了CONNECT,像UDP之类的就没做支持,遇到了UDP指令直接告诉客户端不支持,然后断掉链接。第三步就是开始接受客户端发送来的数据,然后发送到远端服务器,然后从远端服务器读数据再转发到客户端。这个就简单了,直接两行代码搞定。go io.Copy(dest, src)
io.Copy(src, dest) 部署写好程序后,就得部署起来。感谢亚马逊提供了免费一年的AWS服务。怎么申请之类的自行搜索解决。GO的移植性这里就体现出来了,因为我开发是在WINDOWS下,然后部署到AWS上的UBUNTU系统上,一个go build搞定。然后程序跑起来后,直接设置SwitchySharp到AWS上,效果显著。不过中间测试的时候,发现链接到 就不行了,抓包发现,从AWS上发来了很多RST,但是我的程序没有打印出对应的CLOSE信息。这个时候我就猜测应该是那个啥(你懂的)做了内容检测,解决的方法之一就是加密。所以我后面就做了加密的功能。加密增加加密功能就蛋疼了,因为我可能不只是要一种加密算法,可能这会想用RC4,后面就想用AES或者DES之类的。还有就是怎么把加密算法封装起来,使其对现有的代码影响较小。本来我是想自己手工封装成io.Reader和io.Writer的接口,这样的话就可以组成一个调用链,很自然的把网络数据进行加解密。然后我就去GO的标准库里找,有加密算法,RC4,DES,AES都有提供,然后有个叫crypto/cipher的包,这个包提供了俩接口,一个叫StreamRead,另外一个叫StreamWrite,这俩接口正好可以满足io.Reader和io.Write。这下子就简单多了。具体的代码如下(代码里没有做什么工厂模式之类的,因为实在是没啥必要,写的简单粗暴点也不会影响其他代码):func NewCipherStream(rwc io.ReadWriteCloser, cryptMethod string, password []byte) (*CipherStream, error) {
var stream *CipherStream
switch cryptMethod {
stream = &CipherStream{
writeCloser: rwc,
case &#34;rc4&#34;:
rc4CipherRead, err := rc4.NewCipher(password)
if err != nil {
return nil, err
rc4CipherWrite, err := rc4.NewCipher(password)
if err != nil {
return nil, err
stream = &CipherStream{
reader: &cipher.StreamReader{
S: rc4CipherRead,
writeCloser: &cipher.StreamWriter{
S: rc4CipherWrite,
case &#34;des&#34;:
block, err := des.NewCipher(password)
if err != nil {
return nil, err
desRead := cipher.NewCFBDecrypter(block, desIV[:])
desWrite := cipher.NewCFBEncrypter(block, desIV[:])
return &CipherStream{
reader: &cipher.StreamReader{
S: desRead,
writeCloser: &cipher.StreamWriter{
S: desWrite,
return stream, nil
}如果没有指定加密方式或者是不支持的加密方式,还是沿用原有的io.Reader和io.Writer接口。这样的话就可以减少对上层的影响。因为支持了多种加密方式,那么就得用配置文件了。这个就很简单,直接JSON格式搞定。另外一个问题是,浏览器只认SOCKS协议,你现在加了加密,那你得对浏览器隐藏掉,然后就得支持俩模式,一个模式是本地的SOCKS运行方式,对浏览器提供的,另外一个模式就是到远端SOCKS服务的数据要加密。本来是想再写一个本地SOCKS,但是想了下,实在是毫无必要,因为这个程序的大部分功能在现有的代码基础上都能满足,那么一种实现方式就是代码拷贝出来,但是我要维护两分;另外一种方式就是在原有的代码上改。决定在现有代码上改,无非是在客户端的请求来了后,要先链接到自己的远端SOCKS服务,然后再提供数据中转服务。这里就涉及到怎么和远端服务交互的问题。和远端SOCKS交互的解决方法为了解决这个问题,就得需要订协议。是否有必要增加协议呢?我第一个想法是好像没必要,第二个想法是真的没必要?真的没必要!因为SOCKS5的协议足够使用,我也不想因为要链接到远端还要增加一套额外的协议,而这个协议最终也会和SOCKS5差不多,实现出来的代码又会很蛋疼。所以决定使用SOCKS5协议,做成一个调用链的形式。客户端来的握手,本地SOCKS也到远端SOCKS上进行握手。客户端发来的CMD直接转发到远端SOCKS。然后中间走加密。这下子实现就变的非常简单了。定义了一个结构体RemoteSocks,匿名组合了*CipherStream,这样就可以直接走加密了。具体定义代码如下:type RemoteSocks struct {
conn net.Conn
*CipherStream
}然后同步代码到AWS上,编译开跑,然后发现很流畅。给基友狗眼坤分享了下,遇到了BUG,在他机器上链接YOUTUBE出现链接错误。然后想大概问题在哪,开始做代码REVIEW,发现自己实现的加密部分,RC4相关的部分读和写复用了同一个加密对象,然后这个对象又不是线程安全的,问题应该是这里了,修正掉,然后测试之,OK了。总结总算是有了自己的代理程序,用起来很开心,因为是自己做的!Eating your own dog food很重要。自己实现一个程序的时候,可以不用一下子跨很大的步子,那样容易扯到蛋,可以一点点来。
支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
支持 @ 本站用户;支持表情(输入 : 提示),见
记住登录状态
还不是会员求GO语言大能解答,go 1.1的http.Client.Do方法是不是有问题?
如下代码(非常简单的代理):
package main
&io/ioutil&
&net/http&
func handler(w http.ResponseWriter, r *http.Request) {
resp *http.Response
data []byte
resp, err = http.DefaultClient.Do(r)
defer resp.Body.Close()
if err != nil {
http.NotFound(w,r)
for i, j := range resp.Header {
for _, k := range j {
w.Header().Add(i, k)
w.WriteHeader(resp.StatusCode)
data, err = ioutil.ReadAll(resp.Body)
if err != nil && err != io.EOF {
http.NotFound(w,r)
w.Write(data)
func main() {
http.HandleFunc(&/&, handler)
log.Println(&Start serving on port 8888&)
http.ListenAndServe(&:8888&, nil)
os.Exit(0)
会导致panic,如果将第17句换成
resp, err = http.Get(r.URL.String())
就没有问题了,可是这样也就废掉了post方法
你这种方式写法有误。
Do方法里面传入http.Request,并且做了
http.Request.RequestURI的检查,详情查看:
下面是我写的一个Do使用的例子
package main
&io/ioutil&
&net/http&
func main() {
r, err := http.NewRequest(&GET&, &&, nil)
if err != nil {
log.Println(&http.NewRequest: &, err.Error())
resp, err := http.DefaultClient.Do(r)
if resp != nil {
defer resp.Body.Close()
if err != nil {
log.Println(&http.DefaultClient.Do: &, err.Error())
if resp.StatusCode != http.StatusOK {
log.Println(&resp.StatusCode!=http.StatusOK: &, resp.StatusCode)
data, err := ioutil.ReadAll(resp.Body)
if err != nil && err != io.EOF {
log.Println(&ioutil.ReadAll: &, err.Error())
log.Println(string(data))
嗯,我看了下pkg,然后把RequestURI字段设为&&就行了,但是貌似不能用来看视频
package main
&io/ioutil&
&net/http&
func handler(w http.ResponseWriter, r *http.Request) {
resp *http.Response
data []byte
r.RequestURI = &&
r.ParseForm()
resp, err = http.DefaultClient.Do(r)
if err != nil {
http.NotFound(w, r)
data, err = ioutil.ReadAll(resp.Body)
if err != nil && err != io.EOF {
http.NotFound(w, r)
for i, j := range resp.Header {
for _, k := range j {
w.Header().Add(i, k)
_, ok := resp.Header[&Content-Length&]
if !ok && resp.ContentLength&0 {
w.Header().Add(&Content-Length&,fmt.Sprint(resp.ContentLength))
w.WriteHeader(resp.StatusCode)
w.Write(data)
func main() {
http.HandleFunc(&/&, handler)
log.Println(&Start serving on port 8888&)
http.ListenAndServe(&:8888&, nil)
os.Exit(0)
} 添加上Content-Length字段后,优酷能看了(但是log里会报错),bilibili和acfun只能看到(旧版)的播放器界面,但是不会下载视频播放
不知道怎么回事
没有人知道原因嘛?
引用来自“刘地”的评论
嗯,我看了下pkg,然后把RequestURI字段设为&&就行了,但是貌似不能用来看视频
package main
&io/ioutil&
&net/http&
func handler(w http.ResponseWriter, r *http.Request) {
resp *http.Response
data []byte
r.RequestURI = &&
r.ParseForm()
resp, err = http.DefaultClient.Do(r)
if err != nil {
http.NotFound(w, r)
data, err = ioutil.ReadAll(resp.Body)
if err != nil && err != io.EOF {
http.NotFound(w, r)
for i, j := range resp.Header {
for _, k := range j {
w.Header().Add(i, k)
_, ok := resp.Header[&Content-Length&]
if !ok && resp.ContentLength&0 {
w.Header().Add(&Content-Length&,fmt.Sprint(resp.ContentLength))
w.WriteHeader(resp.StatusCode)
w.Write(data)
func main() {
http.HandleFunc(&/&, handler)
log.Println(&Start serving on port 8888&)
http.ListenAndServe(&:8888&, nil)
os.Exit(0)
} 添加上Content-Length字段后,优酷能看了(但是log里会报错),bilibili和acfun只能看到(旧版)的播放器界面,但是不会下载视频播放
不知道怎么回事 这个是对的使用Go语言实现的代理服务器
我的图书馆
使用Go语言实现的代理服务器
Google去年末发布的Go编程语言有着Python的简洁,C数量级的执行效率,类似Erlang的并发模型。我很是喜欢,但不知为何Go语言的官方站点golang.org在国内某些地区不能访问。我用Go写了个简单的代理服务器go-proxy来在一个未被国内屏蔽的网络空间上给golang.org建立了镜像,,在有一些go-proxy的相关信息。
发表评论:
TA的推荐TA的最新馆藏[转]&Pokemon go代理VPN如何防止被封号?用代理被封号了怎么解决
Pokemon go代理VPN如何防止被封号?用代理被封号了怎么解决?相信不少童鞋都不是很清楚吧,下面小编就给大家带来Pokemon go代理VPN防止被封号方法介绍,感兴趣的一起来看看吧!
由于目前pokemon go在国内还没有开通使用所以很多国服玩家使用了代理VPN也就是开飞机。
  官方检查你是否开飞机的机制很简单&&计算你一段时间内的移动速度,超过一个阈值,就暂停你账号功能。首先要解释一下这个暂停账号功能,这不是封号。是你一段时间之内拿不到补给,抓不到任何精灵。过4到6个小时就会解除限制。那怎么开飞机不被封号呢,当然就是只要保证自己的移动速度不会太高就可以了。
& & 由于每次登陆后固定出现在联合广场,你从联合广场出来后,随着你自己的移动,角色会离联合广场越来越远。如果走了一段距离后,你退出游戏了(闪退也算),再登陆还会出现在联合广场。登陆后,官方服务器会计算你的移动速度(从你上次断线的位置到这次登陆的当前位置 / 断线后重连的时间) 如果这个速度过高,就会封号。所以,如果你走的太远了,再登陆前,请等待一段时间(这段时间视你离开联合广场的距离而定)这样来保证你的移动速度合理化。
  以上就是小编和大家分享的关于pokemon go怎么防止用VPN被封号的方法介绍了,希望大家游戏玩的开心!
最新安卓游戏排行
类别:音乐游戏
类别:角色扮演
类别:角色扮演mixer: 一个用go实现的mysql proxy
| Go语言中文网 | Golang中文社区 | Golang中国
<meta name="author" content="polaris ">
mixer: 一个用go实现的mysql proxy
mixer: 一个用go实现的mysql proxy
siddontang
是一个用go实现的mysql proxy,支持基本的mysql代理功能。
mysql的中间件很多,对于市面上面现有的功能强大的proxy,我主要考察了如下几个:
mysql-proxy,mysql官方的代理,使用起来并不友好,需要进行lua定制,而且本人对其稳定性和性能存疑。Cobar,阿里的东西,品质没的说,但对于我们项目,有点杀鸡用牛刀的感觉,另外我们都不会java。Atlas,360出品的基于mysql-proxy的增强版,几乎用c重写了核心框架,性能和稳定性都没话说。
当然,还有很多强大的proxy,我不可能一一涉及,而现阶段我们项目中使用的是Atlas(这算不算给Atlas打了一个广告?)。
既然有这么多的proxy,为什么我还想自己实现一个呢?可能最主要的原因在于兴趣使然吧。
mysql功能支持
当开始着手进行开发的时候,我就知道,不是mysql,它不可能proxy所有mysql的功能。所以,我决定只支持如下mysql命令:
select, insert, update, delete, replaceset autocommitset namesbegin, commit, rollback
COM_PINGCOM_INIT_DBCOM_STMT_PREPARE, COM_STMT_EXEC等COM_STMT_*命令,仅支持上述COM_QUERY命令的prepare
[mixer](/siddontang/mixer不支持命令挺多的,列举一些:
set variable。如果支持,需要维护每一个变量的状态,增加了复杂度。但支持autocommit和names的设置。sql text模式的prepare statement。show命令。存储过程。
虽然很多功能现阶段没有,但不排除后续支持。
高可用方案
提供了一套mysql高可用使用方案,现阶段主要功能如下:
读写分离,将select发送到slave,其余发送到master执行,事物所有在master执行。现阶段只支持一主一备。主备自动切换,当主mysql不可用,根据相关规则切换到backup mysql执行。
还不完善,很多功能需要实现,后续优先需要实现的功能:
parser,将sql进行语法解析,构建AST,在proxy层面就防止一些mysql隐患,譬如注入攻击,delete没有where等。自定义路由,根据路由规则将sql路由到不同mysql执行。譬如根据主键将select语句hash到不同的slave上面执行。统计功能。
代码在这里。非常希望对proxy感兴趣的童鞋参与进来,共同完善,使其成为另一个mysql中间件解决方案。
支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
支持 @ 本站用户;支持表情(输入 : 提示),见
记住登录状态
还不是会员

我要回帖

更多关于 https代理 socket实现 的文章

 

随机推荐