Golang net/http标准库常用方法(三)

大家好,针对Go语言 net/http 标准库,将梳理的相关知识点分享给大家~~
围绕 net/http 标准库相关知识点还有许多章节,请大家多多关注。
文章中代码案例只有关键片段,完整代码请查看github仓库:https://github.com/hltfaith/go-example/tree/main/net-http

本章节案例,请大家以 go1.16+ 版本以上进行参考。

net/http标准库系列文章

  • Golang net/http标准库常用请求方法(一)
  • Golang net/http标准库常用方法(二)
  • Golang net/http标准库常用方法(三)

本节内容

  • ProxyFromEnvironment() 函数
  • ProxyURL() 函数
  • Serve() 函数
  • ServeContent() 函数
  • DetectContentType() 函数
  • MaxBytesReader() 函数

ProxyFromEnvironment()

ProxyFromEnvironment()函数,用于读取所在环境的环境变量返回代理地址。比如环境变量HTTP_PROXYHTTPS_PROXYNO_PROXY,如果在 NO_PROXY 排除的地址则不进行代理。
代理地址格式可以是完整的URL,也可以是host[:port]。支持 HTTPHTTPSSOCKS5代理。
如果环境中未定义代理,或者NO_PROXY定义的给定请求不应使用代理,则返回nil URLnil错误。如果 req.URL.Host 地址为 localhost 加或没加端口,都会返回 nil 错误。
函数原型

func ProxyFromEnvironment(req *Request) (*url.URL, error)

函数使用
proxyfromenvironment.go

func main() {
	os.Setenv("HTTP_PROXY", "http://127.0.0.1:12345")
	req, err := http.NewRequest("GET", "http://example.com", nil)

	if err != nil {
		panic(err)
	}
	url, err := http.ProxyFromEnvironment(req)
	if err != nil {
		panic(err)
	}
	fmt.Println(url)
}

案例中 http.ProxyFromEnvironment(req) 仅会把读取环境变量 HTTP_PROXY 的代理地址,在我们使用 http.NewRequest() 请求时,不会使用代理请求。
下面通过 ProxyURL() 函数案例,发起代理请求。

ProxyURL()

ProxyURL() 作用是返回一个代理函数主要用于在 Transport{} 类型中,其参数是代理地址。
函数原型

func ProxyURL(fixedURL *url.URL) func(*Request) (*url.URL, error)

举例:使用代理发送 HTTP 请求。
proxyurl.go

func main() {
	url, err := url.Parse("http://188.68.176.2:8080")
	if err != nil {
		panic(err)
	}
	client := http.Client{
		Transport: &http.Transport{
			Proxy:           http.ProxyURL(url),
			TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
		},
	}
	res, err := client.Get("http://baidu.com")
	if err != nil {
		panic(err)
	}
	b, _ := httputil.DumpRequest(res.Request, false)
	fmt.Println(string(b))
}

上述例子中, 将代理函数ProxyURL(url)通过Transport{}类型封装好后,向目标服务发送GET请求。
Client{}Transport{}类型后续文章将详细讲解。

注:代理地址,可以参考 https://www.kuaidaili.com/free/fps/ 用于测试使用。

上面案例,也可以将 http.ProxyURL() 函数改成 ProxyFromEnvironment() 用环境变量的方式。
proxyurl2.go

func main() {
	url, err := url.Parse("http://google.com")
	if err != nil {
		panic(err)
	}

	os.Setenv("HTTP_PROXY", "http://127.0.0.1:7890")
	client := http.Client{
		Transport: &http.Transport{
			Proxy:           http.ProxyFromEnvironment,
			TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, // 跳过https
		},
	}

	req := http.Request{
		Method: "GET",
		URL:    url,
		Header: map[string][]string{
			"Proxy-Connection": {"keep-alive"},
		},
	}
	res, err := client.Do(&req)
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()
}

这里是通过我本地环境的代理VPN所监听的端口 http://127.0.0.1:7890

下面我通过抓包,大家可以看到执行代理请求的时候源端口 50130是我们请求端,访问的谷歌网站目的端已经变成了 http://127.0.0.1:7890 地址也是我们的代理端,后面的响应也是由代理端给我们请求回应数据包。

Serve()

Serve() 函数,接收监听 HTTP 连接请求,为每个连接创建一个新goroutinegoroutine读取请求,然后调用处理程序来回复它们。
官方建议 handlernil类型, 则默认使用 DefaultServerMux 全局锁机制。 (可以参考上篇文章中有所介绍)
只有当 Listener 返回tls的时候,才支持HTTP/2协议。
Serve() 函数返回非 nil 的报错。
函数原型

func Serve(l net.Listener, handler Handler) error

Serve()函数实际上是调用的 Server{} 类型中封装的一个方法。

func Serve(l net.Listener, handler Handler) error {
	srv := &Server{Handler: handler}
	return srv.Serve(l)
}

例如,上篇文章中介绍的 ListenAndServe()ListenAndServeTLS() 方法它们最终执行都是 Server{}类型中的 Serve() 方法。

函数使用
serve.go

func main() {
	ln, err := net.Listen("tcp", ":8080")
	if err != nil {
		panic(err)
	}
	http.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
		io.WriteString(w, "帽儿山的枪手!\n")
	})
	log.Panicln(http.Serve(ln, nil))
}

ServeContent()

ServeContent() 函数,使用ReadSeeker所读取的内容回复给用户请求。

ServeContentio.Copy更好的是,他能够合适的处理一批请求,设置MIME类型,并且能够处理文件是否修改的请求。

如果响应的内容类型头没有设置,该函数首先会尝试从文件的文件扩展名推断文件类型。 如果推断不出来,则会读取文件的第一个块并传送给DetectContentType来检测类型。

文件名称也可以不使用。 如果文字名称为空,则服务器不会传送给响应。 如果修改时间不为0,ServeContent会把它放在服务器响应的Last-Modified头里面。 如果客户端请求中包含了If-Modified-Since头,ServeContent会使用modtime来判断是否把内容传给客户端。
contentSeek方法必须能够工作。 ServeContent通过定位到文件结尾来确定文件大小。 *os.File中实现了io.ReadSeeker接口。

函数原型

func ServeContent(w ResponseWriter, req *Request, name string, modtime time.Time, content io.ReadSeeker)
  • 参数 w 服务器响应
  • 参数 req 客户端请求
  • 参数 name 文件名称
  • 参数 modtime 文件的修改时间
  • 参数 content 文件的内容,必须实现 io.ReadSeeker 这个接口中的方法

下面案例使用 ServeContent() 函数实现文件下载功能。
servecontent.go

func main() {
	http.HandleFunc("/download", func(w http.ResponseWriter, r *http.Request) {
		file := "servecontent.go"
		fileBytes, err := ioutil.ReadFile(file)
		if err != nil {
			panic(err)
		}

		mime := http.DetectContentType(fileBytes)
		fileSize := len(string(fileBytes))
		w.Header().Set("Content-Type", mime)
		w.Header().Set("Content-Disposition", "attachment; filename="+file)
		w.Header().Set("Content-Length", strconv.Itoa(fileSize))

		http.ServeContent(w, r, file, time.Now(), bytes.NewReader(fileBytes))
	})

	log.Fatal(http.ListenAndServe(":8080", nil))
}

首先通过 DetectContentType()函数获取了文件的 MIME 类型,然后将文件转换为 Byte 类型传入 ServeContent() 函数中实现下载功能。
结合上篇文章中介绍的 ServeFile()函数它实现起来更简洁仅需要一行代码实现文件下载,但前提需要知道文件上下文路径。
ServeContent() 函数更适用于当你只能拿到 byte[] 数据时,可以优先使用它。

DetectContentType()

DetectContentType() 该函数实现了一个算法,用来检测指定的数据是否符合该标准http://mimesniff.spec.whatwg.org

最多需要数据的前512个字节,DetectContentType()会返回一个有效的MIME类型。 如果它不能够识别数据,将会返回"application/octet-stream"
函数原型

func DetectContentType(data []byte) string

函数使用

func main() {
	// image/png
	fmt.Println(http.DetectContentType([]byte("\x89PNG\x0D\x0A\x1A\x0A")))
	// image/jpeg
	fmt.Println(http.DetectContentType([]byte("\xFF\xD8\xFF")))
}

注:一些类型的识别,可以参考go源码测试用例。

MaxBytesReader()

MaxBytesReader() 函数,用来保护服务器端,以避免客户端偶然或者恶意发送的长数据请求导致的服务端资源的浪费。

MaxBytesReader()io.LimitReader函数很像。但是它被设计来设置接收的请求体的最大大小。 跟io.LimitReader不同MaxBytesReader()的返回值是一个ReadCloser,当读取超过限制时会返回non-nil错误。 并且当它调用关闭方法的时候会把潜在的读取者(函数/进程)也关闭掉。

函数原型

func MaxBytesReader(w ResponseWriter, r io.ReadCloser, n int64) io.ReadCloser
  • 参数 w服务器响应
  • 参数 r可以指向 req.Body
  • 参数 n限制大小

案例,限制客户端上传数据为10个字节。
maxbytesreader.go

func main() {
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		r.Body = http.MaxBytesReader(w, r.Body, 10)
		_, err := io.Copy(ioutil.Discard, r.Body)
		if err != nil {
			panic(err)
		}
		io.WriteString(w, "200\n")
	})

	log.Fatal(http.ListenAndServe(":8080", nil))
}

下面我们通过 curl 命令模拟客户端请求, 其中body内容已经超出了10个字节

root@hc:~# curl --location --request POST 'http://127.0.0.1:8080' \
--header 'Content-Type: application/json' \
--data-raw '{
    "t": "1234567890"
}'

请求完成后,看到服务端已经提示 请求Body过大

技术文章持续更新,请大家多多关注呀~~

搜索微信公众号,关注我【 帽儿山的枪手 】

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/643305.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

面试八股之JVM篇3.6——垃圾回收——强引用、弱引用、虚引用、软引用

🌈hello,你好鸭,我是Ethan,一名不断学习的码农,很高兴你能来阅读。 ✔️目前博客主要更新Java系列、项目案例、计算机必学四件套等。 🏃人生之义,在于追求,不在成败,勤通…

LVS精益价值管理系统 LVS.Web.ashx SQL注入漏洞复现

0x01 产品简介 LVS精益价值管理系统是杭州吉拉科技有限公司研发的一款专注于企业精益化管理和价值流优化的解决方案。该系统通过集成先进的数据分析工具、可视化的价值流映射技术和灵活的流程改善机制,帮助企业实现高效、低耗、高质量的生产和服务。 0x02 漏洞概述 LVS精益…

全国数据库管理系统设计赛-人大金仓内核实训安排正式发布

作为数据库领域国家队,人大金仓积极响应国家战略,通过赛题设计、内核技术支撑及赛前培训等多方面,大力支持全国大学生计算机系统能力大赛-数据库管理系统设计大赛成功举办。目前第二届全国大赛正在火热报名中,各种奖项等你来拿&am…

RabbitMQ 发布订阅

RabbitMQ 发布订阅视频学习地址: 简单模式下RabbitMQ 发布者发布消息 消费者消费消息 Publist/Subscribe 发布订阅 在 RabbitMQ 中,发布订阅模式是一种消息传递方式,其中发送者(发布者)不会将消息直接发送到特 定的…

Linux文本处理三剑客(详解)

一、文本三剑客是什么? 1. 对于接触过Linux操作系统的人来说,应该都听过说Linux中的文本三剑客吧,即awk、grep、sed,也是必须要掌握的Linux命令之一,三者都是用来处理文本的,但侧重点各不相同,a…

Docker-镜像迁移的三种方式=>备份恢复公有仓库私有仓库

制作好的镜像要被别人使用,有三种方式: 1.先备份镜像,别人通过u盘或者其它方式拷贝后,再恢复镜像,这种方式比较麻烦 2.将制作的镜像上传到公共镜像仓库,被别人拉取后使用,但可能存在网络不通畅或…

嵩山为什么称为三水之源

三水指黄河、淮河、济河,这三条河流环绕在嵩山周边。 黄河横亘在嵩山北部,其支流伊洛河从西南方环绕嵩山,然后汇入黄河。济河,古称济水,源自济源王屋山,自身河道在东晋时代被黄河夺占,从此消失。…

【Spring MVC】_SpringMVC项目返回数据

目录 1. 注解使用示例 1.1 使用Controller注解 1.2 使用RestController注解 1.3 使用Controller与ResponseBody注解 2. 关于ResponseBody注解 前文已经介绍过使用Controller注解向前端返回一个HTML页面,接下来将介绍向前端返回数据。 关于Controller和RestCon…

算法金 | Dask,一个超强的 python 库

本文来源公众号“算法金”,仅用于学术分享,侵权删,干货满满。 原文链接:Dask,一个超强的 python 库 1 Dask 概览 在数据科学和大数据处理的领域,高效处理海量数据一直是一项挑战。 为了应对这一挑战&am…

初学者都能掌握的操作符(中)

(1)位操作符(& | ^) &:(按二进制位“与”) 也就是两个数的每一位二进制数按照 “与” 的算法,如下: int a 3 ,b 5 ; c a & b; 我们首先写出a和b的二进…

Java面试八股之Synchronized和ReentrantLock的区别

Synchronized和ReentrantLock的区别 实现级别: synchronized是Java的一个关键字,属于JVM层面的原生支持,它通过监视器锁(Monitor)来实现同步控制,无需手动获取和释放锁。 ReentrantLock是java.util.conc…

【Linux网络编程】传输层中的TCP和UDP(TCP篇)

【Linux网络编程】传输层中的TCP和UDP(TCP篇) 目录 【Linux网络编程】传输层中的TCP和UDP(TCP篇)TCP协议TCP协议段格式确认应答(ACK)机制(保证可靠性)超时重传机制连接管理机制理解T…

aws msk加密方式和问控制连接方式

msk加密方式 msk提供了两种加密方式 静态加密传输中加密 创建集群时可以指定加密方式,参数如下 aws kafka create-cluster --cluster-name "ExampleClusterName" --broker-node-group-info file://brokernodegroupinfo.json --encryption-info file:/…

ASP+ACCESS公司门户网站建设

【摘 要】随着计算机科学的发展,数据库技术在Internet中的应用越来越广泛,为广大网络用户提供了更加周到和人性化的服务。本文讲解了一个公司的网站的建设,它基于数据关联规则的公司个性化页面及动态数据生成案例,在网页方面&…

Kubeadm安装部署k8s集群、踩坑日常

背景 ​ Docker是一个非常流行的容器化平台,它可以让我们方便构建、打包、发布和运行容器化应用程序。但是,在生产环境中,我们可能需要处理成百上千个容器,需要更好的管理这些容器,这就是Kubernetes(K8S)的用武之地。…

利用大语言模型增强网络抓取:一种现代化的方法

本文将探讨大语言模型(LLMs)与网络抓取的集成,以及如何利用LLMs高效地将复杂的HTML转换为结构化的JSON。 作为一名数据工程师,我的职业生涯可以追溯到2016年。那时,我的主要职责是利用自动化工具从不同网站上获取海量数据,这个过…

网络模型-策略路由配置

在实际网络应用中,策略路由也是一种重要的技术手段。尽管在考试并不注重策略路由,但是实际上应用较多建议考生除了掌握基本的静态路由协议IP route-static,动态路由协议RIP、还要掌握如何配置策略路由。策略路由的基本原理:根据ACL定义的不同…

云界洞见:移动云服务开启技术创新与问题解决的新篇章

一、什么是移动云 移动云以“央企保障、安全智慧、算网一体、属地服务”为品牌支撑,聚焦智能算力建设,打造一朵智能、智慧、安全可信可控的云,提供更优质的算力服务,引领云计算产业发展。 那么下面博主带领大家了解移动云的优势所…

Golang单元测试

文章目录 传统测试方法基本介绍主要缺点 单元测试基本介绍测试函数基准测试示例函数 传统测试方法 基本介绍 基本介绍 代码测试是软件开发中的一项重要实践,用于验证代码的正确性、可靠性和预期行为。通过代码测试,开发者可以发现和修复潜在的错误、确保…

【vue-cli搭建vue项目的过程2.x】

vue-cli搭建vue项目 vue-cli搭建vue项目安装node安装vue-cli脚手架并创建项目安装 Ant Design Vue或element-ui(笔者使用Ant-design-vue组件,并全局引入)开发安装三方库包1、Package.json文件---引入如下package.json文件执行npm i或npm install命令即可下载如下依赖…