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

2023-10-27

最近因为换了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包供我们使用,我们启动一个代理服务器监听非常方便。

1

2

3

4

l, err := net.Listen("tcp", ":8080")

if err != nil {

  log.Panic(err)

}

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

监听接收代理请求

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

1

2

3

4

5

6

7

8

for {

  client, err := l.Accept()

  if err != nil {

    log.Panic(err)

  }

 

  go handleClientRequest(client)

}

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

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

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

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

1

2

3

4

CONNECT www.google.com:443 HTTP/1.1

Host: www.google.com:443

Proxy-Connection: keep-alive

User-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的:

1

2

3

4

5

GET http://www.flysnow.org/ HTTP/1.1

Host: www.flysnow.org

Proxy-Connection: keep-alive

Upgrade-Insecure-Requests: 1

User-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多了schame–http://。

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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

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进行解析,获取我们需要的远程服务器信息

1

2

3

4

5

6

7

8

9

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

  }

}

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

1

2

3

ip:port

hostname:port

domainname:port

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

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

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

1

2

3

4

5

6

//获得了请求的host和port,就开始拨号吧

server, err := net.Dial("tcp", address)

if err != nil {

  log.Println(err)

  return

}

数据转发

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

1

2

3

4

5

6

7

8

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上

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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

package main

 

import (

  "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方法就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持脚本之家。

您可能感兴趣的文章:

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

一个简单的Golang实现的HTTP Proxy方法 的相关文章

  • Go语言函数

    http www jb51 net article 56831 htm Go语言中的函数有系统函数和自定义函数 1 系统函数 系统函数就是Go语言自带的函数 系统函数一般根据功能封装在不同的包内 比如Print Printf Println
  • CLI 命令行实用程序开发基础

    CLI 命令行实用程序开发基础 代码传送门 GoOnline平台 1 概述 CLI Command Line Interface 实用程序是Linux下应用开发的基础 正确的编写命令行程序让应用与操作系统融为一体 通过shell或scrip
  • Go基础(复杂类型):函数的闭包

    函数的闭包 Go 函数可以是一个闭包 闭包是一个函数值 它引用了其函数体之外的变量 该函数可以访问并赋予其引用的变量的值 换句话说 该函数被 绑定 在了这些变量上 例如 函数 adder 返回一个闭包 每个闭包都被绑定在其各自的 sum 变
  • 一个简单的Golang实现的HTTP Proxy方法

    最近因为换了Mac 以前的Linux基本上不再使用了 但是我的SS代理还得用 SS代理大家都了解 一个很NB的socks代理工具 但是就是因为他是Socks的 想用HTTP代理的时候很不方便 以前在Linux下的时候 会安装一个Privox
  • Go Web编程实战(7)----并发goroutine

    目录 什么是goroutine 使用方式 什么是goroutine 在Go语言中 每一个并发执行的活动被称为goroutine 使用go关键字可以创建goroutine 其完整定义如下 go func name 其中 go是关键字 需要放在
  • 在Windows上搭建Go开发环境

    Go语言是由谷歌开发的一个高性能 跨平台的编程语言 安装Go 首先先来安装一下Go语言的SDK 目前Go语言的最新版本为Go 1 8 3 Go下载页面列出了各种操作系统的安装包 如果选择Windows MSI安装包的话 会将Go安装到C G
  • 将切片作为参数传入函数并使用append方法遇到的问题

    切片的内部结构 type SliceHeader struct Data uintptr Len int Cap int 由切片的结构定义可知 切片的结构由三个信息组成 指针Data 指向底层数组中切片指定的开始位置 长度Len 即切片的长
  • Go语言学习5-切片类型

    切片类型 引言 1 切片 1 1 类型表示法 1 2 值表示法 1 3 属性和基本操作 1 4 切片使用的复杂用法 总结 引言 上篇我们介绍了 Go语言的数组类型 本篇将介绍Go语言的切片类型 主要如下 1 切片 切片可以看作是对数组的一种
  • CentOS 7下go环境配置(超多问题)

    文章目录 1 安装Visual Code 2 获得golang安装包并安装 3 第一个GO语言程序 hello go 4 安装必要工具与插件 1 安装Visual Code 在终端中安装VScode 使用以下命令 sudo rpm impo
  • Go编程技巧--io.Reader/Writer

    Go原生的pkg中有一些核心的interface 其中io Reader Writer是比较常用的接口 很多原生的结构都围绕这个系列的接口展开 在实际的开发过程中 你会发现通过这个接口可以在多种不同的io类型之间进行过渡和转化 本文结合实际
  • 开源Go语言数值算法库 An open numerical library purely based on Go programming language

    目录 关于 goNum https github com chfenger goNum 安装环境 安装方法 算法 许可证书 致谢 关于goNum goNum是一款完全以Go语言为基础的开源数值算法库 它可以使你像调用其它go函数一样使用其进
  • Go 语言运行时环境变量快速导览

    原文 http dave cheney net 2015 11 29 a whirlwind tour of gos runtime environment variables Go 语言运行时环境变量快速导览 介绍 Go Runtime除
  • pprof 性能分析

    pprof 是一个强大的性能分析工具 可以捕捉到多维度的运行状态的数据 在程序运行过程中可以获取cpu heap block traces等执行信息 一般情况下使用下列三种pprof分析 runtime pprof 采集程序 非 Serve
  • golang数据结构初探之动态数组slice

    动态数组slice slice 又称动态数组 依托于数组实现 可以方便的进行扩容和传递 实际使用时比数组更灵活 但正是因为灵活 实际使用时更容易出错 避免出错的最好方法便是了解其实现原理 特性速览 初始化 声明和初始化切片的方式主要有以下几
  • 【笔记】Go语言 Http-client 解析json后并插入数据库

    一 Http client 获取json 参考 Go标准库http Client的连接行为控制详解 原创手记 慕课网 因为 代码 resp err http Get http example com resp err http Post h
  • Go语言学习-基本

    命名 如果是在函数外部定义 那么将在当前包的所有文件中都可以访问 名字的开头字母的大小写决定了名字在包外的可见性 如果一个名字是大写字母开头的 译注 必须是在函数外部定义的包级名字 包级函数名本身也是包级名字 那么它将是导出的 也就是说可以
  • Go设置国内源

    Go设置国内源 如果你需要Beego这个框架 正常来说你需要安装Git之后go get不会报错 但由于网络问题 go get会非常慢 以至于没法使用 这个时候我们需要需要国内源来进行加速 首先需要我们开启Go的MODULL支持 SETX G
  • Go语言实现区块链与加密货币-Part1(基本原型、工作量证明、持久化)

    区块链 Blockchain 是21世纪最具革命性的技术之一 它仍然处于不断成长的阶段 而且还有很多潜力尚未显现 作为比特币的底层技术 它本质上只是一个分布式数据库 不过使它独一无二的是 区块链是一个公开的而不是私人的数据库 每个使用它的人
  • go语言exec包调用shell命令

    工程中需要用到ffmpeg 想直接用exec包调用shell命令 本来以为很简单 结果折腾了一下午 最后查到了解决方案 假如之前执行报错的语句为 cmd exec Command echo helloworld out err cmd Ou
  • Go语言学习4-数组类型

    数组类型 引言 1 数组 1 1 类型表示法 1 2 值表示法 1 3 属性和基本操作 总结 引言 上篇我们了解 Go语言的基本数据类型 现在开始介绍数组类型 主要如下 1 数组 在Go语言中 数组被称为Array 就是一个由若干相同类型的

随机推荐

  • (附源码)springboot大学生防疫封校管理系统 毕业设计 632124

    大学生防疫封校管理系统 摘 要 2019年12月19号中国武汉发生第一例新冠病毒的到来 大家都在听从政府的号召在居家隔离 不管是在城市还是在乡镇 农村 这引起我的注目 设计一套大学生防疫封校管理系统 疫情防控需要大家共同努力 团结对校园学生
  • 算法通过村第一关——链表青铜挑战笔记

    题目一 理解Java是如何构造出链表的 单链表的内部结构 单链表中就像铁链一样 每个元素 节点 之间互相连接 包含多个节点 每个节点都有一个指向后继元素 节点 的next指针 最后一个元素的next指针指向null 如下图所示 Java单链
  • vba使用win32 API(GetOpenFileName )实现打开文件对话框

    2007年10月30日 11 19 00 使用网上比较普遍的vb中使用WindowsAPI实现打开文件对话框代码 Declare Function GetOpenFileName Lib comdlg32 dll Alias GetOpen
  • SMbus和I2C异同

    关于I2C与SMBus 许多人很少去谈论与了解两者的细节差异 包括很多国外的简报 文章也经常将两者混写 交杂描述 交替运用 确实 在一般运用下 I2C Bus与SMBus没有太大的差别 从实际接线上看也几乎无差异 甚至两者直接相连多半也能相
  • 【小沐学NLP】Python实现聊天机器人(微软Azure机器人服务)

    NLP开发系列相关文章编写如下 1 小沐学NLP Python实现词云图 2 小沐学NLP Python实现图片文字识别 3 小沐学NLP Python实现中文 英文分词 4 小沐学NLP Python实现聊天机器人 ELIZA 5 小沐学
  • (Kubernetes)k8s简介 docker安装

    Kubernetes 简介 Kubernetes 是一个自动化部署 伸缩和操作应用程序容器的开源平台 使用 Kubernetes 你可以快速 高效地满足用户以下的需求 快速精准地部署应用程序 即时伸缩你的应用程序 无缝展现新特征 限制硬件用
  • js中计算两个日期之间的月份差

    开始时间startDate 结束时间endDate var startDate 2019 12 30 var endDate 2020 04 28 startDate startDate split 得到月数 startDate parse
  • Android模拟器的文件目录介绍

    文件存放在 avd文件夹下 ini为对应的配置文件 打开 avd文件夹 lock文件夹保存的是模拟器的一下数据 当模拟器正常关闭时这些文件夹都会被自动删除 当模拟器无法开启的时候可以检查一下这几个文件夹是否存在 把所以的 lock文件夹删除
  • 解决“from exceptions import PendingDeprecationWarning ModuleNotFoundError: No module named ‘exception”

    如图
  • 使用C语言结构体实现面向对象

    1 构造属性与方法 struct Animal char name 128 int age char six 成员属性 void peat 成员方法 void pbeat int num 2 构造类 int mian struct Anim
  • TypeScript(五)类型别名及类型符号

    目录 引言 类型别名 基本用法 字面量类型 数字字面量 字符串字面量 布尔字面量 空字面量 枚举字面量 类型符号 联合类型 交叉类型 类型断言 尖括号 as关键字 非空断言 类型保护 typeof instanceof 类型谓词 索引类型
  • chatgpt赋能python:Python编程:如何删除前面的代码?

    Python编程 如何删除前面的代码 在Python编程中 我们有时会需要删除之前写的一些代码 以便更好地组织我们的代码结构和逻辑 那么 Python中如何删除前面的代码呢 在本文章中 我们将为您详细介绍Python中删除前面代码的几种方法
  • Springboot Redisson Xxljob(Druid MySQL MybatisPlus) 对接使用

    前情提要 根据xxl job提供的springboot的基本对接代码 Github spingboot对接代码地址 根据业务需求 框架中需要接入druid mysql redis redisson等组件和框架 在springboot xxl
  • Python GUI案例之看图猜成语开发(第一篇)

    Python GUI案例之看图猜成语 第一篇 前言 爬取素材篇 看图猜成语小程序开发 第一篇 游戏首页 游戏首页完整代码 Python GUI案例之看图猜成语开发 第二篇 Python GUI案例之看图猜成语开发 第三篇 Python GU
  • 决策树分析例题经典案例_分析模型案例解析:决策树分析法

    1 1 基本概念 决策树分析法又称概率分析决策方法 是指将构成决策方案的有关因素 以树状图形的方式表现出来 并据以分析和选择决策方案的一种系统分析法 它是风险型决策最常用的方法之一 特别式勇敢于分析比较复杂的问题 她以损益值为依据 比较不同
  • python3安装mmseg库

    python3安装mmseg库 常见安装方法 错误的 正确的方法 常见安装方法 错误的 pip install mmseg 结果会出现问题 一直提示报错 具体报错示例如下图所示 网上很难找到有效地解决方法 可能不同的环境会有不同的报错信息
  • 【软件测试】测试用例八要素

    测试用例八要素 根据禅道 1 测试用例编号 编号是由字母和数字组合而成的 用例的编号应该具有唯一性 易识别性 有且于其和测试结果 错误报告等其他文档的链接 这样看到编号就可以知道是做的什么测试 测试的对象是什么 也方便维护 系统测试 产品编
  • appium、selenium、webdriver运行原理图解

    整理的appium selenium webdriver的运行原理图解 如果有不对的地方还请提醒 参考 Appium Selenium Webdriver 运行原理与机制 Appium实现原理 Appium原理整理总结
  • ORM框架概述

    现在各大公司比较流行的ORM框架有MyBatis Hibernate Spring Data JPA等 甚至一些公司会进行封装后成为自己公司专用的框架 相信后端方向的各位小伙伴都知道 ORM框架就是连接数据库的桥梁 比较官方的说法叫 对象关
  • 一个简单的Golang实现的HTTP Proxy方法

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