go标准库httputil.ReverseProxy简单介绍和使用避坑

2023-05-16

很久没水博客了,今天就来水一篇,说说go标准库的httputil.ReverseProxy
httputil.ReverseProxy顾名思义,http的反向代理,可以类比nginx的反向代理功能

type ReverseProxy struct {
	// Director must be a function which modifies
	// the request into a new request to be sent
	// using Transport. Its response is then copied
	// back to the original client unmodified.
	// Director must not access the provided Request
	// after returning.
	Director func(*http.Request)

	// The transport used to perform proxy requests.
	// If nil, http.DefaultTransport is used.
	Transport http.RoundTripper

	// FlushInterval specifies the flush interval
	// to flush to the client while copying the
	// response body.
	// If zero, no periodic flushing is done.
	// A negative value means to flush immediately
	// after each write to the client.
	// The FlushInterval is ignored when ReverseProxy
	// recognizes a response as a streaming response, or
	// if its ContentLength is -1; for such responses, writes
	// are flushed to the client immediately.
	FlushInterval time.Duration

	// ErrorLog specifies an optional logger for errors
	// that occur when attempting to proxy the request.
	// If nil, logging is done via the log package's standard logger.
	ErrorLog *log.Logger

	// BufferPool optionally specifies a buffer pool to
	// get byte slices for use by io.CopyBuffer when
	// copying HTTP response bodies.
	BufferPool BufferPool

	// ModifyResponse is an optional function that modifies the
	// Response from the backend. It is called if the backend
	// returns a response at all, with any HTTP status code.
	// If the backend is unreachable, the optional ErrorHandler is
	// called without any call to ModifyResponse.
	//
	// If ModifyResponse returns an error, ErrorHandler is called
	// with its error value. If ErrorHandler is nil, its default
	// implementation is used.
	ModifyResponse func(*http.Response) error

	// ErrorHandler is an optional function that handles errors
	// reaching the backend or errors from ModifyResponse.
	//
	// If nil, the default is to log the provided error and return
	// a 502 Status Bad Gateway response.
	ErrorHandler func(http.ResponseWriter, *http.Request, error)
}

httputil.ReverseProxy 定义了一组方法让使用者去实现,主要有这几个

  • Director
    最核心的方法, 我们可以在这里对请求进行相应的修改,比如设置请求目标的地址,对原有请求头进行增删改,以及对请求体进行处理等等操作。

  • ModifyResponse
    可以让我们对响应的结果进行处理,比如修改、读取响应头和响应体。

  • ErrorHandler
    请求出错或者ModifyResponse返回error时会回调该方法,比如目标服务器无法连接,请求超时等等

看一个简单的示例代码

func main() {

	proxy := &httputil.ReverseProxy{

		Director: func(req *http.Request) {
			u, _ := url.Parse("https://www.qq.com/")
			req.URL = u
			req.Host = u.Host // 必须显示修改Host,否则转发可能失败
		},

		ModifyResponse: func(resp *http.Response) error {
			log.Println("resp status:", resp.Status)
			log.Println("resp headers:")
			for hk, hv := range resp.Header {
				log.Println(hk, ":", strings.Join(hv, ","))
			}
			return nil
		},

		ErrorLog: log.New(os.Stdout, "ReverseProxy:", log.LstdFlags | log.Lshortfile),

		ErrorHandler: func(w http.ResponseWriter, r *http.Request, err error) {
			if err != nil {
				log.Println("ErrorHandler catch err:", err)
				
				w.WriteHeader(http.StatusBadGateway)
				_, _ = fmt.Fprintf(w, err.Error())
			}
		},
	}

	http.Handle("/", proxy)

	if err := http.ListenAndServe(":1111", nil); err != nil {
		log.Fatal(err)
	}
}

访问效果
在这里插入图片描述

req.Host = u.Host 这句代码千万不要忘记,否则可能导致代理访问失败,因为在golang中修改http.Request.URL.Host并不等同于Host请求头,除非本身Host请求头是空的,这时会使用URL.Host的值。

摘录URL.Host 和 Request.Host 解释

// For client requests, the URL's Host specifies the server to
// connect to, while the Request's Host field optionally
// specifies the Host header value to send in the HTTP
// request.

// For client requests, Host optionally overrides the Host
// header to send. If empty, the Request.Write method uses
// the value of URL.Host. Host may contain an international
// domain name.

不修改 Request.Host 请求失败:
在这里插入图片描述
可以简单写个tcp服务查看经代理转发后的请求数据

func main() {

	listener, err := net.Listen("tcp4", ":1112")

	if err != nil {
		log.Fatal(err)
	}

	for {
		conn, err := listener.Accept()

		if err != nil {
			log.Println("accept err:", err)
			continue
		}

		// 另开一个协程处理tcp连接
		go func(conn net.Conn) {
			defer conn.Close()

			log.Println("build new connection")

			buf := make([]byte, 4096)
			sb := &strings.Builder{}

			for {
				n, err := conn.Read(buf)
				if err != nil {
					if errors.Is(err, os.ErrDeadlineExceeded) {
						log.Println("read timeout")
						break
					}

					if errors.Is(err, io.EOF) {
						log.Println("read eof")
						break
					}

					log.Println("read from client err:", err)
					return
				}
				sb.Write(buf[:n])
				if err := conn.SetReadDeadline(time.Now().Add(time.Millisecond * 10)); err != nil {
					log.Println("setTimeout err:", err)
					return
				}
			}

			log.Println(sb.String())

			if err := conn.SetWriteDeadline(time.Now().Add(time.Second * 3)); err != nil {
				log.Println("setTimeout err:", err)
				return
			}
			conn.Write([]byte("HTTP/1.1 200 OK\r\nContent-Length: 2\r\nConnection: Closed\r\n\r\nOK"))

		}(conn)
	}
}

设置代理访问上述服务 并注释掉 req.Host 设置代码,观察tcp服务日志输出:

func main() {

	proxy := &httputil.ReverseProxy{

		Director: func(req *http.Request) {
			u, _ := url.Parse("http://localhost:1112")
			req.URL = u
			//req.Host = u.Host // 必须显示修改Host,否则转发可能失败
		},

		ModifyResponse: func(resp *http.Response) error {
			log.Println("resp status:", resp.Status)
			log.Println("resp headers:")
			for hk, hv := range resp.Header {
				log.Println(hk, ":", strings.Join(hv, ","))
			}
			return nil
		},

		ErrorLog: log.New(os.Stdout, "ReverseProxy:", log.LstdFlags | log.Lshortfile),

		ErrorHandler: func(w http.ResponseWriter, r *http.Request, err error) {
			if err != nil {
				log.Println("ErrorHandler catch err:", err)
				
				w.WriteHeader(http.StatusBadGateway)
				_, _ = fmt.Fprintf(w, err.Error())
			}
		},
	}

	http.Handle("/", proxy)

	if err := http.ListenAndServe(":1111", nil); err != nil {
		log.Fatal(err)
	}
}
2022/06/12 18:32:12 build new connection
2022/06/12 18:32:12 read timeout
2022/06/12 18:32:12 GET / HTTP/1.1
Host: 127.0.0.1:1111
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Cache-Control: max-age=0
Cookie: ts_uid=2693745378; ariaDefaultTheme=undefined; ad_play_index=85
Sec-Ch-Ua: " Not A;Brand";v="99", "Chromium";v="102", "Google Chrome";v="102"
Sec-Ch-Ua-Mobile: ?0
Sec-Ch-Ua-Platform: "Windows"
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: none
Sec-Fetch-User: ?1
Upgrade-Insecure-Requests: 1
X-Forwarded-For: 127.0.0.1

转发后请求的地址是 localhost:1112,但Host还是原始请求的Host,而http服务器通常需要通过Host来区分不同的虚拟服务器,从而出现异常。

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

go标准库httputil.ReverseProxy简单介绍和使用避坑 的相关文章

  • 如果参数是指针,且仅作输入用,则应在类型前加 const,以防止该 指针在函数体内被意外修改...

    如果参数是指针 xff0c 且仅作输入用 xff0c 则应在类型前加 const xff0c 以防止该 指针在函数体内被意外修改 1 include lt iostream gt 2 3 run this program using the
  • 【ROS进阶】一文搞懂ROS话题通信机制与消息队列

    文章目录 一 话题通信机制解析1 话题通信机制简介2 消息队列分析3 使用技巧 二 实验验证 xff08 一 xff09 subscriber队列长度对数据传输影响 xff08 二 xff09 数据传输时间延迟 三 总结 一 话题通信机制解
  • stm32串口DMA方式发送数据

    该文档介绍stm32 uart1通过DMA方式发送和接收数据 xff0c 代码示例基于ucos ii操作系统 该文档参考https wenku baidu com view d44ef1380975f46526d3e1b5 html 中内容
  • opencv立体标定函数 stereoCalibrate()

    立体标定函数 nbsp stereoCalibrate nbsp nbsp stereoCalibrate nbsp 是用来标定一个立体摄像头的 也就是同时标定两个摄像头 标定的结果除了能够求出两个摄像头的内外参数矩阵 跟能够得出两个摄像头
  • postman好用的插件有哪些,给出实例和插件下载地址、使用方法

    postman好用的插件有哪些 xff0c 给出实例和插件下载地址 使用方法 Postman 是一个常用的高级 API测试工具 xff0c 也可以用来测试 Web 应用程序 它提供了许多内置的功能和工具 xff0c 同时还支持通过插件拓展其
  • STM32 HAL库实现多串口使用printf输出

    include lt stdlib h gt include lt stdio h gt include lt stdarg h gt 描述 xff1a HAL库实现多串口使用printf输出 参数 xff1a huart xff1a 对应
  • FIFO与DMA

    FIFO存储器是一个先入先出的双口缓冲器 xff0c 即第一个进入其内的数据第一个被移出 xff0c 其中一个存储器的输入口 xff0c 另一个口是存储器的输出口 主要有三个方面的作用 xff1a 1 xff09 对连续的数据流进行缓存 x
  • 两种常用压控电流源设计

    电流源是一种非常常用的电路 xff0c 如工业中4 20mA输出 xff0c LED恒流驱动 xff0c 以及一些传感器可能也需要恒流驱动等 本篇文章介绍两种常见的压控电流源电路的设计 废话不多说 xff0c 直接看图 xff1a 电路很简
  • 峰峰值-峰值-平均值-有效值之间有什么关系?

    首先 xff0c 这几个值一般针对交流信号说的 实际应用中 xff0c AD采集交流信号后 xff0c 可能需要各个值之间相互转换比较等 xff0c 就需要这几个值之间有什么关系 先说说这几个值的定义 峰峰值Vpp xff08 Peak t
  • 几种电平转换电路

    在电路设计时 xff0c 有时会遇到电压域不匹配的问题 xff0c 如芯片为1 8V供电 xff0c 而MCU为3 3V供电 这时候就需要进行电平转换才能通讯 今天就来介绍几种常用的电平转换电路 二 三极管单向电平转换 一些通讯模块 xff
  • STM32的串口硬件流控(RS232/RS485)

    流控的概念源于 RS232 这个标准 xff0c 在 RS232 标准里面包含了串口 流控的定义 RS232 中的 RS 是Recommend Standard 的缩写 xff0c 即 推荐标准 之意 xff0c 它并不像 IEEE 128
  • 头文件中写类的实现出现函数重复定义的问题

    先来做一个实验 xff0c 你在一个头文件中定义一个类 xff0c 然后把内中的一个函数的实现写在这个头文件当中 A test h ifndefine A TEST define A TEST class A void test void
  • 步进电机驱动代码

    步进电机简而言之就是能够通过输入脉冲的个数 xff0c 确定旋转的角位移 xff0c 一般用他来控制小车轮子的偏移角度等 步进电机由驱动芯片ULN2003驱动 xff0c 利用ULN2003与MCU引脚相连 xff0c 可以驱动步进电机 主
  • 全速下载百度云

    用CMD命令下载百度云的资源 听起来类似Linux或者我们在电影里常见的黑客下载数据 需要准备工具 xff1a Windows系统 xff08 我用的是Windows10 xff09 xff0c CMD命令行 xff0c BaiduPCS
  • 几种常用电流互感器采样电路

    我们知道 xff0c 采样电流信号最简单的方法就是通过采样电阻将电流信号转换为电压信号 xff0c 然后再进行放大 采样即可 直流信号一般都可以这样处理 xff0c 但是对于电流互感器出来的交流信号 xff0c 不能直接输入到单极性的AD进
  • 调试程序时怎样查看变量波形?看这里

    我们在调试单片机程序时 xff0c 经常会需要查看某个变量或数组的值 xff0c 一般情况下 xff0c 可以通过 Add xxx to Watch 来查看 xff0c 或直接查看内存 但有时候 xff0c 比如ADC采样时 xff0c 单
  • 几种常用的产生负电源的方法

    电源电路是电路设计的重要环节 xff0c 一般情况下 xff0c 单电源能实现功能的用单电源就行 xff0c 可选的方案很多 xff0c DC DC LDO等芯片很多 有时候 xff0c 单电源无法满足需求时 xff0c 就必须用到负电源
  • 盘点一些国产“有特点”的单片机

    自从芯片涨价潮以来 xff0c 国产芯片 xff0c 尤其是单片机类芯片犹如雨后春笋般发展起来 xff0c 其中也不乏一些优秀的产品 今天来盘点一下一些有特点的单片机 这里所说的单片机 xff0c 是指通用型的MCU xff0c 像ESP3
  • 不会写Bootloader?看这里,现成的

    前段时间要写一个BootLoader程序 xff0c 想起来好像在STM32的HAL库里面看到过相关的Demo xff0c 打算参考一下 打开相关的目录看了一下 xff0c 确实是有相关文件 xff1a 但是没找到工程文件 没办法 xff0
  • 一个IO挂多个按键怎么实现?

    有时候做设计时 xff0c 我们会遇到外部按键比较多 xff0c IO口不够用的情况 这时大部分人会考虑通过其它芯片扩展IO xff0c 或者直接换一个IO口足够的MCU 其实 xff0c 还有个方法可以实现一个IO上挂多个按键 即采用AD

随机推荐

  • SWM32系列教程8--SPI及其应用

    SPI接口是非常常用的一种数字外设 xff0c SWM32S单片机有2个SPI接口 xff0c 其特点如下 xff1a 全双工串行同步收发可编程时钟极性和相位支持 MASTER 模式和 SLAVE 模式MASTER 模式下最高传输速度支持主
  • STM32使用HAL库驱动W5500

    W5500 芯片是一款集成全硬件 TCP IP 协议栈的嵌入式以太网控制器 xff0c 为单片机提供了更加简单 快速 稳定 安全的以太网接入方案 采用标准4线SPI接口 xff0c 理论速率上可以达到 80MHz 硬件设计 原理图如下 xf
  • SWM32系列教程9-SDIO及FatFs文件系统

    SWM32S单片机有1个SDIO接口 xff0c 支持多媒体卡 xff08 MMC xff09 SD 存储卡 SDIO 卡等设备 xff0c 可以使用软件方法或者 DMA 方法 xff08 SDIO 模块内部 DMA xff0c 与芯片 D
  • SWM32系列教程10--SDRAM和LCD

    SWM32S单片机内部集成了8MB的SDRAM以及LCD控制器 xff0c 非常适合用于屏幕驱动 其中LCD控制器支持RGB565格式的接口 xff0c 最大支持1024 768分辨率 今天来介绍一下这两个外设的用法 SDRAM SWM32
  • VSCode的REST Client指南,超好用的HTTP客户端工具

    我最新最全的文章都在 南瓜慢说 www pkslow com xff0c 欢迎大家来喝茶 xff01 1 简介 在做Web应用开发的时候 xff0c 经常需要测试Web接口 xff0c 就需要一些客户端来发送HTTP请求到服务端 常用的客户
  • 开源自己做的4.3寸触摸屏,SWM32单片机+LVGL

    十一假期抽时间把SWM32S做的触摸屏板的综合程序弄了一下 xff0c 板子硬件资源如下 xff1a SWM32S单片机 xff0c LQFP 64封装 xff0c 内置8MB的SDRAM 4 3寸16位RBG接口电容触摸屏 xff0c 8
  • 盘点国产RISC-V内核的单片机

    RISC V就不必多说了 xff0c 它是一个基于精简指令集的开源指令集架构 与主流的主流的架构为x86与ARM架构不同 xff0c 其特点就是完全开源 今天跟大家一起盘点一下国产RISC V内核的单片机 1 GD32VF103系列 兆易创
  • 单片机通过WIFI模块(ESP8266)获取网络时间与天气预报

    前几天发布了开源4 3寸触摸屏的文章 开源4 3寸触摸屏 xff0c 里面有WIFI获取时间和天气预报相关的功能 xff0c 今天就来介绍一下这个功能是怎样实现的 1 底层驱动 首先 xff0c 硬件上 xff0c 单片机通过串口AT指令访
  • 开源贴片机OpenPnp使用体验

    平时做板子比较多 xff0c 一直想整一个贴片机 xff0c 但是据说国产的桌面贴片机用起来都不咋地 xff0c 而且价格也不菲 xff0c 带视觉的将近2W 思来想去选择了OpenPnP xff0c 一方面价格便宜 xff0c 另一方面开
  • 【干货】STM32通过ADC模拟看门狗实现掉电保存

    1 前言 很多时候我们需要将程序中的一些参数 数据等存储在EEPROM或者Flash中 xff0c 达到掉电保存的目的 但有些情况下 xff0c 程序需要频繁的修改这些参数 xff0c 如果每次修改参数都进行一次保存 xff0c 那将大大降
  • 【开源项目】SFUD--通用串口Flash驱动库的移植和使用

    1 简介 SFUD 是一款开源的串行 SPI Flash 通用驱动库 由于现有市面的串行 Flash 种类居多 xff0c 各个 Flash 的规格及命令存在差异 xff0c SFUD 就是为了解决这些 Flash 的差异现状而设计 xff
  • STM32F0系列中断向量映射问题

    最近用Cortex M0内核的STM32F030K6T6做个东西 xff0c 需要做IAP升级 xff0c 发现它的中断向量与M3 M4等内核的单片机不太一样 xff0c 这里分享给大家 IAP升级需要一个BootLoader程序 xff0
  • STM32定时器实现红外接收与解码

    1 NEC协议 红外遥控是一种比较常用的通讯方式 xff0c 目前红外遥控的编码方式中 xff0c 应用比较广泛的是NEC协议 NEC协议的特点如下 xff1a 载波频率为 38KHz 8 位地址和 8位指令长度 地址和命令2次传输 xff
  • SPI读写SD卡速度有多快?

    SD卡是一个嵌入式中非常常用的外设 xff0c 可以用于存储一些大容量的数据 但用单片机读写SD卡速度一般都有限 xff08 对于高速SD卡 xff0c 主要是受限于单片机本身的接口速度 xff09 xff0c 在高速 实时数据存储时可能会
  • SPI方式读写SD卡速度有多快?

    很久没有写公众号了 xff0c 一方面忙 xff0c 另一方面也不知道写些什么内容 xff0c 大家如果有想了解的 xff08 前提是我也懂 xff09 xff0c 可以后台发送给我 今天主要来测试一下SPI读写SD卡的速度 SD卡是一个嵌
  • 火狐浏览器添加脚本(可代替手动做一些操作)

    首先打开火狐浏览器的更多组件 xff0c 在扩展中搜索Greasemonkey 然后点击安装 安装成功后点立即重启 重新打开浏览器后浏览器右上角会有一个猴子的图标 点击下标按钮 xff0c 新建用户脚本 xff0c 新建用户脚本可以随意命名
  • SDIO读写SD卡速度有多快?

    前两天测试了SPI方式读写SD卡的速度 SPI方式读写SD卡速度测试 xff0c 今天来测试一下SDIO方式的读写速度 测试条件 xff1a 单片机 xff1a STM32F407VET6 编译环境 xff1a MDK 5 30 43 HA
  • STM32CubeMx+HAL库实现USB CDC+MSC复合设备

    之前的文章中介绍过STM32的USB应用 xff0c 包括虚拟串口 xff08 CDC xff09 和大容量存储设备 xff08 MSC xff09 今天来介绍USB实现CDC和MSC复合设备的方法 硬件 xff1a STM32F407VE
  • 基于STM32CubeMx的USB CDC+MSC复合设备

    之前的文章中介绍过STM32的USB应用 xff0c 包括虚拟串口 xff08 CDC xff09 和大容量存储设备 xff08 MSC xff09 今天来介绍USB实现CDC和MSC复合设备的方法 硬件 xff1a STM32F407VE
  • go标准库httputil.ReverseProxy简单介绍和使用避坑

    很久没水博客了 xff0c 今天就来水一篇 xff0c 说说go标准库的httputil ReverseProxy httputil ReverseProxy顾名思义 xff0c http的反向代理 xff0c 可以类比nginx的反向代理