追踪 HTTP

Go 支持用 net/http/httptraces标准包追踪 HTTP!这个包允许您追踪一个 HTTP 请求的解析。net/http/httptraces标准包的使用将在 httpTrace.go 中加以说明,它分为五部分进行讲解。

httpTrace.go 的第一部分如下:

  1. package main
  2. import (
  3. "fmt"
  4. "io"
  5. "net/http"
  6. "net/http/httptrace"
  7. "os"
  8. )

如您所料,您需要引入 net/http/httptrace包来追踪 HTTP。

httpTrace.go 的第二部分代码如下:

  1. func main() {
  2. if len(os.Args) != 2 {
  3. fmt.Println("Usage: URL\n")
  4. return
  5. }
  6. URL := os.Args[1]
  7. client := http.Client{}

在这部分,我们读取命令行参数并创建一个新的 http.Client变量。由于这是您第一次在执行中看到 http.Client 对象,所以关于它我们就多讲一些。http.Client 对象发送请求到服务器并获得一个响应。它的 Transport 字段允许您设置各种 HTTP 细节来替代默认值。

注意,您不应该在发布的软件中使用 http.Client 对象的默认值,因为这些值没有指定请求超时,它会影响程序的性能以及 goroutines的运行。而且,根据设计 http.Client 对象能够安全的用于并发程序。

httpTrace.go 的第三段代码如下:

  1. req, _ := http.NewRequest("GET", URL, nil)
  2. trace := &httptrace.ClientTrace{
  3. GotFirstResponseByte: func() {
  4. fmt.Println("First response byte!")
  5. },
  6. GotConn: func(connInfo httptrace.GotConnInfo) {
  7. fmt.Printf("Got Conn: %+v\n", connInfo)
  8. },
  9. DNSDone: func(dnsInfo httptrace.DNSDoneInfo) {
  10. fmt.Printf("DNS Info: %+v\n", dnsInfo)
  11. },
  12. ConnectStart: func(network, addr string) {
  13. fmt.Println("Dial start")
  14. },
  15. ConnectDone: func(network, addr string, err error) {
  16. fmt.Println("Dial done")
  17. },
  18. WroteHeaders: func() {
  19. fmt.Println("Wrote headers")
  20. },
  21. }

上面这段就是全部追踪 HTTP 请求的代码了。httptrace.ClientTrace 对象定义了使我们感兴趣的事件。当一个事件发生时,相关代码就会被执行。您能够在 net/http/httptrace 包的文档中找到更多信息,关于支持的事件和它们的目的。

httpTrace.go 的第四部分如下:

  1. req = req.WithContext(httptrace.WithClientTrace(req.Context(), trace))
  2. fmt.Println("Requesting data from server!")
  3. _, err := http.DefaultTransport.RoundTrip(req)
  4. if err != nil {
  5. fmt.Println(err)
  6. return
  7. }

httptrace.WithClientTrace() 函数基于给定的父上下文返回一个新的上下文;而 http.DefaultTransport.RoundTrip() 方法包裹 http.DefaultTransport.RoundTrip() 目的是告诉它要追踪当前的请求。注意,Go HTTP 跟踪被设计为追踪单个 http.Transport.RoundTrip 的事件。然而,当服务一个单独的HTTP 请求时,由于您可能有多个 URL 重定向,所以您需要能够识别出当前的请求。

httpTrace.go 的剩余代码如下:

  1. response, err := client.Do(req)
  2. if err != nil {
  3. fmt.Println(err)
  4. return
  5. }
  6. io.Copy(os.Stdout, response.Body)
  7. }

最后这部分是使用 Do() 执行实际的请求到 web 服务器,获取 HTTP 数据,并把它显示在屏幕上。

执行 httpTrace.go 将产生如下非常翔实的输出:

  1. $ go run httpTrace.go http://localhost:8001/
  2. Requesting data from server!
  3. DNS Info: {Addrs: [{IP:::1 Zone:} {IP:127.0.0.1 Zone:}] Err:<nil> Coalesced:false}
  4. Dial start
  5. Dial done
  6. Got Conn: {Conn:0xc42014200 Reused:false WasIdel:false IdleTime:0s}
  7. Wrote headers
  8. First response byte!
  9. DNS Info: {Addrs: [{IP:::1 Zone:} {IP:127.0.0.1 Zone:}] Err:<nil> Coalesced:false}
  10. Dial start
  11. Dial done
  12. Got Conn: {Conn:0xc420142008 Reused:false WasIdle:false IdleTime:0s}
  13. Wrote headers
  14. First response byte!
  15. Serving: /

考虑到 httpTrace.go 打印从 HTTP 服务器返回的全部 HTML 数据,当您用一个真实的 web 服务测试时,您可能会得到太多输出,所以在这我们就使用 www.go 这个 web 服务器了。

如果您有时间看 net/http/httptrace 包的源码,在 http://golang.org/src/net/http/httptrace/trace.go,您会立马意识到 net/http/httptrace 是个相当低级的包,它使用 context, reflectinternal/nettrace 包实现它的功能。