侯体宗的博客
  • 首页
  • Hyperf版
  • beego仿版
  • 人生(杂谈)
  • 技术
  • 关于我
  • 更多分类
    • 文件下载
    • 文字修仙
    • 中国象棋ai
    • 群聊
    • 九宫格抽奖
    • 拼图
    • 消消乐
    • 相册

一个简单的Golang实现的HTTP Proxy方法

Go  /  管理员 发布于 7年前   184

最近因为换了Mac,以前的Linux基本上不再使用了,但是我的SS代理还得用。SS代理大家都了解,一个很NB的socks代理工具,但是就是因为他是Socks的,想用HTTP代理的时候很不方便。

以前在Linux下的时候,会安装一个Privoxy把socks代理转换为HTTP代理,开机启动,也比较方便。但是Mac下使用Brew安装的Privoxy就很难用,再加上以前一个有个想法,一个软件搞定socks和HTTP代理,这样就不用安装一个单独的软件做转换了。

想着就开始做吧,以前基本上没有搞过太多的网络编程,最近也正好在研究Go,正好练练手。

我们这里主要讲使用HTTP/1.1协议中的CONNECT方法建立起来的隧道连接,实现的HTTP Proxy。这种代理的好处就是不用知道客户端请求的数据,只需要原封不动的转发就可以了,对于处理HTTPS的请求就非常方便了,不用解析他的内容,就可以实现代理。

启动代理监听

要想做一个HTTP Proxy,我们需要启动一个服务器,监听一个端口,用于接收客户端的请求。Golang给我们提供了强大的net包供我们使用,我们启动一个代理服务器监听非常方便。

  l, err := net.Listen("tcp", ":8080")  if err != nil {    log.Panic(err)  }

以上代理我们就实现了一个在8080端口上监听的服务器,我们这里没有写ip地址,默认在所有ip地址上进行监听。如果你只想本机适用,可以使用127.0.0.1:8080,这样机器就访问不了你的代理服务器了。

监听接收代理请求

启动了代理服务器,就可以开始接受不了代理请求了,有了请求,我们才能做进一步的处理。

  for {    client, err := l.Accept()    if err != nil {      log.Panic(err)    }    go handleClientRequest(client)  }

Listener接口的Accept方法,会接受客户端发来的连接数据,这是一个阻塞型的方法,如果客户端没有连接数据发来,他就是阻塞等待。接收来的连接数据,会马上交给handleClientRequest方法进行处理,这里使用一个go关键字开一个goroutine的目的是不阻塞客户端的接收,代理服务器可以马上接收下一个连接请求。

解析请求,获取要访问的IP和端口

有了客户端的代理请求了,我们还得从请求里提取客户端要访问的远程主机的IP和端口,这样我们的代理服务器才可以建立和远程主机的连接,代理转发。

HTTP协议的头信息里就包含有我们需要的主机名(IP)和端口信息,并且是明文的,协议很规范,类似于:

CONNECT www.google.com:443 HTTP/1.1Host: www.google.com:443Proxy-Connection: keep-aliveUser-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.95 Safari/537.36

可以看到我们需要的在第一行,第一个行的信息以空格分开,第一部分CONNECT是请求方法,这里是CONNECT,除此之外还有GET,POST等,都是HTTP协议的标准方法。

第二部分是URL,https的请求只有host和port,http的请求是一个完成的url,等下会看个样例,就明白了。

第三部是HTTP的协议和版本,这个我们不用太关注。

以上是一个https的请求,我们看下http的:

GET http://www.flysnow.org/ HTTP/1.1Host: www.flysnow.orgProxy-Connection: keep-aliveUpgrade-Insecure-Requests: 1User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.95 Safari/537.36

可以看到htt的,没有端口号(默认是80);比https多了schameChttp://。

有了分析,下面我们就可以从HTTP头信息中获取请求的url和method信息了。

  var b [1024]byte  n, err := client.Read(b[:])  if err != nil {    log.Println(err)    return  }  var method, host, address string  fmt.Sscanf(string(b[:bytes.IndexByte(b[:], '\n')]), "%s%s", &method, &host)  hostPortURL, err := url.Parse(host)  if err != nil {    log.Println(err)    return    }

然后需要进一步对url进行解析,获取我们需要的远程服务器信息

  if hostPortURL.Opaque == "443" { //https访问    address = hostPortURL.Scheme + ":443"  } else { //http访问    if strings.Index(hostPortURL.Host, ":") == -1 { //host不带端口, 默认80      address = hostPortURL.Host + ":80"    } else {      address = hostPortURL.Host    }  }

这样就完整了获取了要请求服务器的信息,他们可能是以下几种格式

ip:porthostname:portdomainname:port

就是有可能是ip(v4orv6),有可能是主机名(内网),有可能是域名(dns解析)

代理服务器和远程服务器建立连接

有了远程服务器的信息了,就可以进行拨号建立连接了,有了连接,才可以通信。

  //获得了请求的host和port,就开始拨号吧  server, err := net.Dial("tcp", address)  if err != nil {    log.Println(err)    return  }

数据转发

拨号成功后,就可以进行数据代理传输了

if method == "CONNECT" {    fmt.Fprint(client, "HTTP/1.1 200 Connection established\r\n")  } else {    server.Write(b[:n])  }  //进行转发  go io.Copy(server, client)  io.Copy(client, server)

其中对CONNECT方法有单独的回应,客户端说要建立连接,代理服务器要回应建立好了,然后才可以像HTTP一样请求访问。

运行外国外VPS上

到这里,我们的代理服务器全部开发完成了,下面是完整的源代码:

package mainimport (  "bytes"  "fmt"  "io"  "log"  "net"  "net/url"  "strings")func main() {  log.SetFlags(log.LstdFlags|log.Lshortfile)  l, err := net.Listen("tcp", ":8081")  if err != nil {    log.Panic(err)  }  for {    client, err := l.Accept()    if err != nil {      log.Panic(err)    }    go handleClientRequest(client)  }}func handleClientRequest(client net.Conn) {  if client == nil {    return  }  defer client.Close()  var b [1024]byte  n, err := client.Read(b[:])  if err != nil {    log.Println(err)    return  }  var method, host, address string  fmt.Sscanf(string(b[:bytes.IndexByte(b[:], '\n')]), "%s%s", &method, &host)  hostPortURL, err := url.Parse(host)  if err != nil {    log.Println(err)    return  }  if hostPortURL.Opaque == "443" { //https访问    address = hostPortURL.Scheme + ":443"  } else { //http访问    if strings.Index(hostPortURL.Host, ":") == -1 { //host不带端口, 默认80      address = hostPortURL.Host + ":80"    } else {      address = hostPortURL.Host    }  }  //获得了请求的host和port,就开始拨号吧  server, err := net.Dial("tcp", address)  if err != nil {    log.Println(err)    return  }  if method == "CONNECT" {    fmt.Fprint(client, "HTTP/1.1 200 Connection established\r\n")  } else {    server.Write(b[:n])  }  //进行转发  go io.Copy(server, client)  io.Copy(client, server)}

把源代码编译,然后放到你国外的VPS上,在自己机器上配置好HTTP代理,就可以到处访问,自由自在了。

以上这篇一个简单的Golang实现的HTTP Proxy方法就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持。


  • 上一条:
    golang解析html网页的方法
    下一条:
    一百行Golang代码实现简单并发聊天室
  • 昵称:

    邮箱:

    0条评论 (评论内容有缓存机制,请悉知!)
    最新最热
    • 分类目录
    • 人生(杂谈)
    • 技术
    • linux
    • Java
    • php
    • 框架(架构)
    • 前端
    • ThinkPHP
    • 数据库
    • 微信(小程序)
    • Laravel
    • Redis
    • Docker
    • Go
    • swoole
    • Windows
    • Python
    • 苹果(mac/ios)
    • 相关文章
    • 在go中实现一个常用的先进先出的缓存淘汰算法示例代码(0个评论)
    • 在go+gin中使用"github.com/skip2/go-qrcode"实现url转二维码功能(0个评论)
    • 在go语言中使用api.geonames.org接口实现根据国际邮政编码获取地址信息功能(1个评论)
    • 在go语言中使用github.com/signintech/gopdf实现生成pdf分页文件功能(0个评论)
    • 在go语言中使用github.com/signintech/gopdf实现生成pdf文件功能(0个评论)
    • 近期文章
    • 在go中实现一个常用的先进先出的缓存淘汰算法示例代码(0个评论)
    • 在go+gin中使用"github.com/skip2/go-qrcode"实现url转二维码功能(0个评论)
    • 在go语言中使用api.geonames.org接口实现根据国际邮政编码获取地址信息功能(1个评论)
    • 在go语言中使用github.com/signintech/gopdf实现生成pdf分页文件功能(0个评论)
    • gmail发邮件报错:534 5.7.9 Application-specific password required...解决方案(0个评论)
    • 欧盟关于强迫劳动的规定的官方举报渠道及官方举报网站(0个评论)
    • 在go语言中使用github.com/signintech/gopdf实现生成pdf文件功能(0个评论)
    • Laravel从Accel获得5700万美元A轮融资(0个评论)
    • 在go + gin中gorm实现指定搜索/区间搜索分页列表功能接口实例(0个评论)
    • 在go语言中实现IP/CIDR的ip和netmask互转及IP段形式互转及ip是否存在IP/CIDR(0个评论)
    • 近期评论
    • 122 在

      学历:一种延缓就业设计,生活需求下的权衡之选中评论 工作几年后,报名考研了,到现在还没认真学习备考,迷茫中。作为一名北漂互联网打工人..
    • 123 在

      Clash for Windows作者删库跑路了,github已404中评论 按理说只要你在国内,所有的流量进出都在监控范围内,不管你怎么隐藏也没用,想搞你分..
    • 原梓番博客 在

      在Laravel框架中使用模型Model分表最简单的方法中评论 好久好久都没看友情链接申请了,今天刚看,已经添加。..
    • 博主 在

      佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 @1111老铁这个不行了,可以看看近期评论的其他文章..
    • 1111 在

      佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 网站不能打开,博主百忙中能否发个APP下载链接,佛跳墙或极光..
    • 2016-10
    • 2017-09
    • 2020-03
    • 2020-05
    • 2020-06
    • 2020-07
    • 2020-12
    • 2021-01
    • 2021-05
    • 2021-06
    • 2021-07
    • 2021-08
    • 2021-10
    • 2021-11
    • 2021-12
    • 2022-01
    • 2022-02
    • 2022-03
    • 2022-04
    • 2022-05
    • 2022-06
    • 2022-07
    • 2022-08
    • 2022-09
    • 2022-10
    • 2022-11
    • 2022-12
    • 2023-01
    • 2023-02
    • 2023-03
    • 2023-04
    • 2023-05
    • 2023-06
    • 2023-07
    • 2023-08
    • 2023-09
    • 2023-10
    • 2023-11
    • 2023-12
    • 2024-01
    • 2024-02
    • 2024-03
    • 2024-04
    • 2024-05
    • 2024-06
    • 2024-07
    • 2024-08
    • 2024-11
    • 2025-02
    • 2025-04
    • 2025-05
    • 2025-06
    Top

    Copyright·© 2019 侯体宗版权所有· 粤ICP备20027696号 PHP交流群

    侯体宗的博客