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

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

3天内不再提示

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

马哥Linux运维 来源:staight.github.io 作者:staight.github.io 2022-05-14 15:02 次阅读

本文详细介绍了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.1Host: staight.github.ioConnection: keep-alive
// http 代理GET http://staight.github.io/ HTTP/1.1Host: staight.github.ioProxy-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 协议交互过程如图:

79e26f24-d2c2-11ec-bce3-dac502259ad0.png

http 代理 http 协议

https 协议

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

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

		

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

  • 请求方法从GET变成CONNECT
  • url 没有 protocol 字段

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

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

7a2228b2-d2c2-11ec-bce3-dac502259ad0.png

http 代理 https 协议

代码实现

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

// tcp 连接,监听 8080 端口l, err := net.Listen("tcp", ":8080")if err != nil { log.Panic(err)}
// 死循环,每当遇到连接时,调用 handlefor { client, err := l.Accept() if err != nil {  log.Panic(err) }
 go handle(client)   }

		

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

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

		

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

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

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

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

		

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

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

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

		

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

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

		

完整的源代码:

package main
import ( "bytes" "fmt" "io" "log" "net" "net/url" "strings")
func main() { // tcp 连接,监听 8080 端口 l, err := net.Listen("tcp", ":8080") if err != nil {  log.Panic(err) }
 // 死循环,每当遇到连接时,调用 handle for {  client, err := l.Accept()  if err != nil {   log.Panic(err)  }
  go handle(client) }}
func handle(client net.Conn) { if client == nil {  return } defer client.Close()
 log.Printf("remote addr: %v
", client.RemoteAddr())
 // 用来存放客户端数据的缓冲区 var b [1024]byte //从客户端获取数据 n, err := client.Read(b[:]) if err != nil {  log.Println(err)  return }
 var method, URL, address string // 从客户端数据读入 method,url fmt.Sscanf(string(b[:bytes.IndexByte(b[:], '
')]), "%s%s", &method, &URL) hostPortURL, err := url.Parse(URL) if err != nil {  log.Println(err)  return }
 // 如果方法是 CONNECT,则为 https 协议 if method == "CONNECT" {  address = hostPortURL.Scheme + ":" + hostPortURL.Opaque } else { //否则为 http 协议  address = hostPortURL.Host  // 如果 host 不带端口,则默认为 80  if strings.Index(hostPortURL.Host, ":") == -1 { //host 不带端口, 默认 80   address = hostPortURL.Host + ":80"  } }
 //获得了请求的 host 和 port,向服务端发起 tcp 连接 server, err := net.Dial("tcp", address) if err != nil {  log.Println(err)  return } //如果使用 https 协议,需先向客户端表示连接建立完毕 if method == "CONNECT" {  fmt.Fprint(client, "HTTP/1.1 200 Connection established

") } else { //如果使用 http 协议,需将从客户端得到的 http 请求转发给服务端  server.Write(b[:n]) }
 //将客户端的请求转发至服务端,将服务端的响应转发给客户端。io.Copy 为阻塞函数,文件描述符不关闭就不停止 go io.Copy(server, client) io.Copy(client, server)}

		

添加代理,然后运行:

7ab1f91a-d2c2-11ec-bce3-dac502259ad0.png

7aeba5de-d2c2-11ec-bce3-dac502259ad0.png




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

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

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

    关注

    12

    文章

    8108

    浏览量

    82485
  • HTTP
    +关注

    关注

    0

    文章

    464

    浏览量

    30310
  • 代理
    +关注

    关注

    1

    文章

    18

    浏览量

    11149

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

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

收藏 人收藏

    评论

    相关推荐

    内存管理概述及原理

    记录一下,方便以后翻阅~主要内容:1) 内存管理概述及原理;2)相关实验代码解读。官方资料:《STM32中文参考手册_V10》-第19章 灵活的静态存储器控制器(FSMC)。实验要求:系统启动后
    发表于 02-23 06:15

    icepeak教程概述及工程应用

    icepeak教程概述及工程应用
    发表于 09-16 10:18 13次下载
    icepeak教程<b class='flag-5'>概述及</b>工程应用

    如何在java代码中使用HTTP代理IP

    如何在java代码中使用HTTP代理IP。
    的头像 发表于 08-04 15:38 1913次阅读

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

    如何再python代码中使用HTTP代理IP。 以下代码主要围绕第一次接触HTTP代理IP的py
    的头像 发表于 08-04 15:40 859次阅读

    如何在python代码中使用HTTP代理IP

    如何在python代码中使用HTTP代理IP。
    的头像 发表于 08-04 15:46 1035次阅读

    如何在PHP代码中使用HTTP代理IP

    如何在PHP代码中使用HTTP代理IP。
    的头像 发表于 08-04 16:08 2104次阅读

    go语言代码中使用HTTP代理IP的方法

    如何在go语言代码中使用HTTP代理IP。
    的头像 发表于 08-04 16:13 2883次阅读

    如何在易e语言代码中使用HTTP代理IP

    如何在易e语言代码中使用HTTP代理IP,示例代码demo直接可用(步骤注释清晰)
    的头像 发表于 08-05 16:29 6120次阅读

    如何在c语言代码中使用HTTP代理IP

    如何在c语言代码中使用HTTP代理IP,示例代码demo直接可用(步骤注释清晰)
    的头像 发表于 08-05 16:31 2026次阅读

    如何在c#语言代码中使用HTTP代理IP

    如何在c#语言代码中使用HTTP代理IP,示例代码demo直接可用(步骤注释清晰)
    的头像 发表于 08-05 16:33 2194次阅读

    c语言中怎么使用HTTP代理

    如何再c语言代码中使用HTTP代理IP。
    的头像 发表于 09-01 14:44 1110次阅读

    python代码中如何使用HTTP代理

    华益云HTTP代理API有效期是一年,也就是说一年内这1万IP用完就没了,如果你一年都用不完那到时候剩余IP才会被清零,对于调试代码来说时间充足灵活。
    的头像 发表于 09-01 14:50 715次阅读

    python代码中如何使用HTTP代理

    HTTP代理就是介于浏览器和web服务器之间的一台服务器,连接代理后,浏览器不再直接向web服务器取回网页,而是向代理服务器发出request信号,
    的头像 发表于 09-13 09:24 1051次阅读

    如何在python代码中使用HTTP代理IP

    如何再python代码中使用HTTP代理IP。
    的头像 发表于 09-13 09:25 767次阅读

    Golang实现一个简单的http代理

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