造轮子 | golang | 简易http2拨测工具

yiekue · · 1784 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

最近需要进行http2相关的工作,但是开发环境和测试环境都的curl版本都太老了不支持http2,正好最近在学习golang,于是决定自己造个轮子:用go语言实现一个建议的http2客户端,以本文记录折腾过程。完整代码地址:https://github.com/yiekue/gh2c.

涉及内容:

  • flag包的使用
  • 标准库中http.Client的基本使用
  • golang中的http2

标准库的flag包

平时写程序中免不了根据输入的命令行参数来控制程序的行为,golang的标准库中提供了一个flag包,用于解析命令行输入的各种 - 开头的选项,使用比较方便,免去了自己挨个解析命令行参数的麻烦。

flag包支持boolstringint等多种类型的选项,使用过程比较简单:

  1. flag.Bool()flag.String()等函数定义一个flag,这些函数都有三个入参,依次是flag的名称默认值帮助信息,函数的返回值是一个对应类型的 指针指针指针
  2. 在使用变量之前调用flag.Parse()进行解析。如果解析失败,程序会退出,并且打印各个变量的帮助信息,包含名称、默认值、和之前定义的帮助信息。解析会在第一个非 - 开头的参数停止,在非flag参数后面的flag会被忽略
package main import ( "flag" "fmt" "os" ) // 首先定义需要flag的名称、默认值、帮助信息。需要注意的是这里的函数的返回值都是指针 var help = flag.Bool("help", false, "print help info") var version = flag.Int("v", 2, "http version 1/2") var method = flag.String("method", "GET", "http method, GET/POST...") func main() { // 首先调用Parse()函数进行解析。解析后,前面定义的各种变量就可以直接用了。 flag.Parse() if *help { // 打印选项的默认值和帮助信息 flag.PrintDefaults() os.Exit(0) } switch *version { case 1: fmt.Println("HTTP/1.1") case 2: fmt.Println("HTTP/2.0") default: flag.PrintDefaults() os.Exit(1) } fmt.Println("method:", *method) } 

上面这段代码的运行结果:

[~/code/test]$ go run test.go HTTP/2.0 method: GET 

由于设置了输出帮助信息的flag默认是false,因此默认不会打印帮助信息,而是打印了另外两个flag的默认值。指定输出帮助信息:

[~/code/test]$ go run test.go -help -help print help info -method string http method, GET/POST... (default "GET") -v int http version 1/2 (default 2) 

设置bool型的flag,只需要在命令行中添加这个flag即可,不需要指定它的值。而对于string之类的flag,就需要指定flag的值:

[~/code/test]$ go run test.go -v 1 -method "POST" HTTP/1.1 method: POST 

如果命令行中的flag在代码中没有定义或者flag的输入格式错误,程序会打印错误信息和已定义的flag信息并退出:

[~/code/test]$ go run test.go -v 1 -metho flag provided but not defined: -metho Usage of /tmp/go-build852894954/b001/exe/test: -help print help info -method string http method, GET/POST... (default "GET") -v int http version 1/2 (default 2) exit status 2 

http.Client && http2

golang的标准库net/http中提供了一个http的客户端,可以进行简单的http操作。但是如果要使用http2就需要额外的golang.org/x/net/http2,由于国内特殊的网络环境,golang.org无法直接访问到,可以到github的镜像仓库中下载使用。同时需要下载http2依赖的text.

使用Client的步骤一般如下:

  1. 新建一个http.Client
  2. 设置client的各项参数,例如tls参数,http版本等。
  3. 使用http.NewRequest(),新建一个请求,并设置请求的请求头等各项参数。
  4. 使用client.Do(req),发送一个请求。
  5. 处理请求的响应信息。

使用http.Client发起http请求的流程:

package main import ( "crypto/tls" "flag" "fmt" "golang.org/x/net/http2" "io/ioutil" "net/http" "os" ) var help = flag.Bool("help", false, "print help info") var version = flag.Int("v", 2, "http version 1/2") var method = flag.String("method", "GET", "http method, GET/POST...") func main() { flag.Parse() if *help { flag.PrintDefaults() os.Exit(0) } // 从命令行读取URL,URL需要在各种flag之后 url := flag.Arg(0) if "" == url { fmt.Println("error: please input URL") flag.PrintDefaults() } tlsConfig := &tls.Config{ InsecureSkipVerify: false, } // 新建一个client client := &http.Client{} // 设置http版本,默认使用http2 switch *version { case 1: client.Transport = &http.Transport{ TLSClientConfig: tlsConfig, } case 2: client.Transport = &http2.Transport{ TLSClientConfig: tlsConfig, } default: fmt.Println("error: unkown http version:", *version) flag.PrintDefaults() os.Exit(1) } // 使用参数输入的请求方法和url新建一个请求 req, err := http.NewRequest(*method, url, nil) if err != nil { fmt.Println("error: failed to create request,", err) flag.PrintDefaults() os.Exit(1) } // 设置User-Agent req.Header.Set("User-Agent", "GH2C") // 发送请求 resp, err := client.Do(req) if nil != err { fmt.Println("error: failed to do request,", err) flag.PrintDefaults() os.Exit(1) } defer resp.Body.Close() // 读取响应体信息 body, err := ioutil.ReadAll(resp.Body) if nil != err { fmt.Println("error: failed to read body.") flag.PrintDefaults() os.Exit(1) } // 答应响应头和响应体长度 fmt.Println(">", resp.Proto, resp.Status) for k, vs := range resp.Header { for _, v := range vs { fmt.Printf("> %s: %s\n", k, v) } } fmt.Println("body.length:", len(string(body))) } 

在网上百度一个支持http2的网站,测试一把,效果如下(域名侵删)

[~/code/test]$ go run test.go -v 2 https://www.chinacache.com/ > HTTP/2.0 200 OK > Content-Type: text/html > Expires: Mon, 17 Jun 2019 04:39:54 GMT > Accept-Ranges: bytes > Age: 19405 > Etag: W/"5cdbdbc8-2dc0" > Last-Modified: Wed, 15 May 2019 09:28:40 GMT > Date: Sun, 16 Jun 2019 04:39:54 GMT > Server: nginx > Powered-By-Chinacache: HIT from CMN-CD-b-3g3 > Cc_cache: TCP_HIT body.length: 11712 

gh2c

将在上一节的基础上增加更多的flag来增加更多的功能就成了支持http2的简易拨测工具gh2c

  • 支持自定义头域
  • 自定义是否忽略证书
  • 更友好的输出信息
*[master][~/code/gh2c]$ ./gh2c -help Usage: ./gh2c -[flags] url -H string custom headers -HKVsep string used for split a custom header key and value (default ":") -Hsep string used for split custom headers (default ";") -body output response body -debug print debug info -help print help info -host string custom Host to override default (default "defaltHost") -method string http method, GET/POST... (default "GET") -v int http version 1/2 (default 2) -verifyCert enable verification of the server certificate 

效果如下,默认不输出body:

*[master][~/code/gh2c]$ go run gh2c.go -v 2 -H "test:testheadker|test2:testheader2" -Hsep "|" https://example.com/ < GET HTTP/2.0 / < Host: www.chinacache.com < Test: testheadker < Test2: testheader2 < User-Agent: GH2C < > HTTP/2.0 200 OK > Etag: W/"5cdbdbc8-2dc0" > Last-Modified: Wed, 15 May 2019 09:28:40 GMT > Date: Sun, 16 Jun 2019 04:39:54 GMT > Server: nginx > Cc_cache: TCP_HIT > Accept-Ranges: bytes > Age: 11967 > Expires: Mon, 17 Jun 2019 04:39:54 GMT > Powered-By-Chinacache: HIT from CMN-CD-b-3g3 > Content-Type: text/html < 

FIXME

在把http2.Transport赋值给client.Transport之后,使用req.Proto获取到的仍然是HTTP/1.1,不知道怎么获取实际使用http版本。


有疑问加站长微信联系(非本文作者)

本文来自:简书

感谢作者:yiekue

查看原文:造轮子 | golang | 简易http2拨测工具

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

1784 次点击  
加入收藏 微博
1 回复  |  直到 2025-05-15 09:02:40
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传