0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

Golang实现一个简单的http代理

马哥Linux运维 来源:CSDN技术社区 2023-04-10 11:29 次阅读

本文详细介绍了Golang 实现 http 代理的实现,在实际业务中有需求的同学可以学起来了!

代理是网络中的一项重要的功能,其功能就是代理网络用户去取得网络信息。形象的说:它是网络信息的中转站,对于客户端来说,代理扮演的是服务器的角色,接收请求报文,返回响应报文;对于 web 服务器来说,代理扮演的是客户端的角色,发送请求报文,接收响应报文。

代理具有多种类型,如果是根据网络用户划分的话,可以划分为正向代理和反向代理:

正向代理:将客户端作为网络用户。客户端访问服务端时,先访问代理服务器,随后代理服务器再访问服务端。此过程需客户端进行代理配置,对服务端透明。

反向代理:将服务端作为网络用户。访问过程与正向代理相同,不过此过程对客户端透明,需服务端进行代理配置(也可不配置)。

针对正向代理和反向代理,分别有不同的代理协议,即代理服务器和网络用户之间通信所使用的协议:

正向代理:

http

https

socks4

socks5

vpn:就功能而言,vpn 也可以被认为是代理

反向代理:

tcp

udp

http

https

接下来我们就说说 http 代理。

http 代理概述

http 代理是正向代理中较为简单的代理方式,它使用 http 协议作为客户端和代理服务器的传输协议。

http 代理可以承载 http 协议,https 协议,ftp 协议等等。对于不同的协议,客户端和代理服务器间的数据格式略有不同。

http 协议

我们先来看看 http 协议下客户端发送给代理服务器的 HTTP Header:

//直接连接
GET/HTTP/1.1
Host:staight.github.io
Connection:keep-alive

//http代理
GEThttp://staight.github.io/HTTP/1.1
Host:staight.github.io
Proxy-Connection:keep-alive

可以看到,http 代理比起直接连接:

url 变成完整路径,/->http://staight.github.io/

Connection字段变成Proxy-Connection字段

其余保持原样

为什么使用完整路径?

为了识别目标服务器。如果没有完整路径,且没有 Host 字段的话,代理服务器将无法得知目标服务器的地址。

为什么使用 Proxy-Connection 字段代替 Connection 字段?

为了兼容使用 HTTP/1.0 协议的过时的代理服务器。HTTP/1.1 才开始有长连接功能,直接连接的情况下,客户端发送的 HTTP Header 中如果有Connection: keep-alive字段,表示使用长连接和服务端进行 http 通信,但如果中间有过时的代理服务器,该代理服务器将无法与客户端和服务端进行长连接,造成客户端和服务端一直等待,白白浪费时间。

因此使用Proxy-Connection字段代替Connection字段,如果代理服务器使用 HTTP/1.1 协议,能够识别Proxy-Connection字段,则将该字段转换成Connection再发送给服务端;如果不能识别,直接发送给服务端,因为服务端也无法识别,则使用短连接进行通信。

http 代理 http 协议交互过程如图:

6306faa6-d6f2-11ed-bfe3-dac502259ad0.png

http 代理 http 协议

https 协议

接下来我们来看看 https 协议下,客户端发送给代理服务器的 HTTP Header:

CONNECTstaight.github.io:443HTTP/1.1
Host:staight.github.io:443
Proxy-Connection:keep-alive

如上,https 协议和 http 协议相比:

请求方法从GET变成CONNECT

url 没有 protocol 字段

实际上,由于 https 下客户端和服务端的通信除了开头的协商以外都是密文,中间的代理服务器不再承担修改 http 报文再转发的功能,而是一开始就和客户端协商好服务端的地址,随后的 tcp 密文直接转发即可。

http 代理 https 协议交互过程如图:

6314f73c-d6f2-11ed-bfe3-dac502259ad0.png

代码实现

首先,创建 tcp 服务,并且对于每个 tcp 请求,均调用 handle 函数:

//tcp连接,监听8080端口
l,err:=net.Listen("tcp",":8080")
iferr!=nil{
log.Panic(err)
}

//死循环,每当遇到连接时,调用handle
for{
client,err:=l.Accept()
iferr!=nil{
log.Panic(err)
}

gohandle(client)
}

然后将获取的数据放入缓冲区:

//用来存放客户端数据的缓冲区
varb[1024]byte
//从客户端获取数据
n,err:=client.Read(b[:])
iferr!=nil{
log.Println(err)
return
}

从缓冲区读取 HTTP 请求方法,URL 等信息:

varmethod,URL,addressstring
//从客户端数据读入method,url
fmt.Sscanf(string(b[:bytes.IndexByte(b[:],'
')]),"%s%s",&method,&URL)
hostPortURL,err:=url.Parse(URL)
iferr!=nil{
log.Println(err)
return
}

http 协议和 https 协议获取地址的方式不同,分别处理:

//如果方法是CONNECT,则为https协议
ifmethod=="CONNECT"{
address=hostPortURL.Scheme+":"+hostPortURL.Opaque
}else{//否则为http协议
address=hostPortURL.Host
//如果host不带端口,则默认为80
ifstrings.Index(hostPortURL.Host,":")==-1{//host不带端口,默认80
address=hostPortURL.Host+":80"
}
}

用获取到的地址向服务端发起请求。如果是 http 协议,将客户端的请求直接转发给服务端;如果是 https 协议,发送 http 响应:

//获得了请求的host和port,向服务端发起tcp连接
server,err:=net.Dial("tcp",address)
iferr!=nil{
log.Println(err)
return
}
//如果使用https协议,需先向客户端表示连接建立完毕
ifmethod=="CONNECT"{
fmt.Fprint(client,"HTTP/1.1200Connectionestablished

")
}else{//如果使用http协议,需将从客户端得到的http请求转发给服务端
server.Write(b[:n])
}

最后,将所有客户端的请求转发至服务端,将所有服务端的响应转发给客户端:

//将客户端的请求转发至服务端,将服务端的响应转发给客户端。io.Copy 为阻塞函数,文件描述符不关闭就不停止
goio.Copy(server,client)
io.Copy(client,server

完整的源代码:

packagemain

import(
"bytes"
"fmt"
"io"
"log"
"net"
"net/url"
"strings"
)

funcmain(){
//tcp连接,监听8080端口
l,err:=net.Listen("tcp",":8080")
iferr!=nil{
log.Panic(err)
}

//死循环,每当遇到连接时,调用handle
for{
client,err:=l.Accept()
iferr!=nil{
log.Panic(err)
}

gohandle(client)
}
}

funchandle(clientnet.Conn){
ifclient==nil{
return
}
deferclient.Close()

log.Printf("remoteaddr:%v
",client.RemoteAddr())

//用来存放客户端数据的缓冲区
varb[1024]byte
//从客户端获取数据
n,err:=client.Read(b[:])
iferr!=nil{
log.Println(err)
return
}

varmethod,URL,addressstring
//从客户端数据读入method,url
fmt.Sscanf(string(b[:bytes.IndexByte(b[:],'
')]),"%s%s",&method,&URL)
hostPortURL,err:=url.Parse(URL)
iferr!=nil{
log.Println(err)
return
}

//如果方法是CONNECT,则为https协议
ifmethod=="CONNECT"{
address=hostPortURL.Scheme+":"+hostPortURL.Opaque
}else{//否则为http协议
address=hostPortURL.Host
//如果host不带端口,则默认为80
ifstrings.Index(hostPortURL.Host,":")==-1{//host不带端口,默认80
address=hostPortURL.Host+":80"
}
}

//获得了请求的host和port,向服务端发起tcp连接
server,err:=net.Dial("tcp",address)
iferr!=nil{
log.Println(err)
return
}
//如果使用https协议,需先向客户端表示连接建立完毕
ifmethod=="CONNECT"{
fmt.Fprint(client,"HTTP/1.1200Connectionestablished

")
}else{//如果使用http协议,需将从客户端得到的http请求转发给服务端
server.Write(b[:n])
}

//将客户端的请求转发至服务端,将服务端的响应转发给客户端。io.Copy 为阻塞函数,文件描述符不关闭就不停止
goio.Copy(server,client)
io.Copy(client,server)
}

添加代理,然后运行:

6321e8ac-d6f2-11ed-bfe3-dac502259ad0.png

632cae22-d6f2-11ed-bfe3-dac502259ad0.png

审核编辑:汤梓红
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • Web
    Web
    +关注

    关注

    2

    文章

    1255

    浏览量

    69322
  • 服务器
    +关注

    关注

    12

    文章

    9010

    浏览量

    85160
  • 网络
    +关注

    关注

    14

    文章

    7511

    浏览量

    88605
  • HTTP
    +关注

    关注

    0

    文章

    501

    浏览量

    31047
  • 客户端
    +关注

    关注

    1

    文章

    289

    浏览量

    16659

原文标题:Golang 实现一个简单的 http 代理

文章出处:【微信号:magedu-Linux,微信公众号:马哥Linux运维】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    Golang怎么实现UTS隔离

    Golang实现UTS隔离
    发表于 08-23 14:44

    http代理的分类

    有着不同的安全性,下面就随着亿牛云代理的脚步起看看HTTP代理是怎么按安全性分类的吧。透明代理(简单
    发表于 12-20 17:25

    Golang爬虫语言接入代理

    都是可以写出爬虫系统的。无论用什么爬虫语言框架,长期使用IP去采集数据,肯定会收到限制。这种时候就需要使用爬虫代理去解决问题。
    发表于 09-09 17:41

    HTTP反向代理服务器的编写程序

    在 Node.js 上实现简单HTTP 代理程序还是非常
    的头像 发表于 10-06 10:00 4083次阅读
    <b class='flag-5'>HTTP</b>反向<b class='flag-5'>代理</b>服务器的编写程序

    如何在Golang实现反向代理

    【导读】在本文中,我们将了解反向代理,它的应用场景以及如何在 Golang实现它。 反向代理是位于 Web 服务器前面并将客户端(例如 Web 浏览器)的请求转发到 Web 服务器
    的头像 发表于 08-23 10:22 2126次阅读

    http代理概述及代码实现方法

    本文详细介绍了Golang 实现 http 代理实现,在实际业务中有需求的同学可以学起来了!
    的头像 发表于 05-14 15:02 3814次阅读

    python代码中使用HTTP代理IP,demo注释清晰

    API链接,就可以跑起来了。 以下是示例代码,只是基础的演示,具体的代码还是要根据你业务的实际情况去写的。 示例代码中的HTTP
    的头像 发表于 08-04 15:40 1021次阅读

    java中怎么使用HTTP代理

    java中怎么使用HTTP代理,华益云python爬虫ip
    的头像 发表于 09-01 14:31 1653次阅读

    python中怎么使用HTTP代理

    python中怎么使用HTTP代理,华益云高匿爬虫代理ip
    的头像 发表于 09-01 14:36 1587次阅读

    PHP中怎么使用HTTP代理

    PHP中怎么使用HTTP代理,华益云高匿爬虫代理ip
    的头像 发表于 09-01 14:38 1715次阅读

    go语言中怎么使用HTTP代理

    go语言中怎么使用HTTP代理
    的头像 发表于 09-01 14:41 2387次阅读

    http代理的作用如下所示

    经常使用网络的人可能会常常听到这样的一个词:http代理。那么它真正的作用很多人都是模棱两可的,在这里给大家普及下在我们大部分人的网络活动中,ht
    发表于 09-15 13:00 468次阅读

    解析Golang定时任务库gron设计和原理

    正巧,最近看到了 gron 这个开源项目,它是用 Golang 实现并发安全的定时任务库。实现非常
    的头像 发表于 12-15 13:57 1281次阅读

    快速应用程序开发(RAD)工具(Golang版)

    SNMPAgent Builder(Golang版)是快速应用程序开发(RAD)工具,用于基于Golang 的 SNMP代理开发。提供了
    的头像 发表于 04-13 09:30 1492次阅读

    Golang配置代理方法

    由于些客观原因的存在,我们开发 Golang 项目的过程总会碰到无法下载某些依赖包的问题。这不是小问题,因为你的工作会被打断,即便你使用各种神通解决了问题,很可能这时你的线程已经
    的头像 发表于 11-11 11:17 121次阅读
    <b class='flag-5'>Golang</b>配置<b class='flag-5'>代理</b>方法