1. HTTP.Transport介绍
HTTP.Transport是Go语言中用于HTTP客户端和HTTP服务器通信的核心组件之一,它提供了基于TCP连接的、可恢复的、并发安全的HTTP客户端请求方法。它可以帮助我们处理HTTP请求与响应之间的底层细节,比如连接的绑定、连接池的管理、代理的设置、重定向的响应等等。因此,我们可以通过对HTTP.Transport的灵活配置和定制,使得HTTP客户端的使用更加智能化,适应更多场景的使用需求。
2. Transport请求拦截实现
HTTP.Transport在实现上比较自由,我们可以通过一定的手段对HTTP请求进行拦截,从而在实际应用场景中实现更多的功能。
2.1. 使用Transport.Request给请求添加元数据
通过使用Transport.Request方法,我们可以给请求添加元数据,方便在响应中进行数据标识和处理。下面的示例展示了如何给HTTP请求添加元数据:
package main
import (
"net"
"net/http"
"fmt"
"log"
)
type MyTransport struct {
http.Transport
}
func (t *MyTransport) RoundTrip(req *http.Request) (*http.Response, error) {
req.Header.Add("X-My-Header", "my custom value")
return t.Transport.RoundTrip(req)
}
func main() {
transport := &MyTransport{}
client := &http.Client{
Transport: transport,
}
res, err := client.Get("http://example.com")
if err != nil {
log.Fatal(err)
}
defer res.Body.Close()
fmt.Println(res)
}
在上述示例中,我们实现了一个自定义的Transport,通过修改RoundTrip方法,我们在请求中添加了一个名为“X-My-Header”的请求头,可以自定义请求头字段,以方便后期业务处理中的识别和调用。
2.2. 使用Transport.Dial给请求添加代理
在一些需要翻墙或访问内网资源的场景下,我们需要为HTTP请求添加代理配置,以确保请求能够正确地到达目标服务器。我们可以通过重写Dial方法,使用指定的代理服务器来实现该功能。下面的示例展示了如何使用Transport.Dial添加代理配置:
package main
import (
"net"
"net/http"
"fmt"
"log"
)
type MyTransport struct {
http.Transport
}
func (t *MyTransport) Dial(network, addr string) (net.Conn, error) {
proxyAddr := "127.0.0.1:1080"
conn, err := net.Dial("tcp", proxyAddr)
if err != nil {
return nil, err
}
return conn, nil
}
func main() {
transport := &MyTransport{}
client := &http.Client{
Transport: transport,
}
res, err := client.Get("http://example.com")
if err != nil {
log.Fatal(err)
}
defer res.Body.Close()
fmt.Println(res)
}
在上述示例中,我们实现了一个自定义的Transport,通过修改Dial方法,我们使用了“127.0.0.1:1080”作为代理服务器的地址,将请求传输到代理服务器,再由代理服务器向实际目标服务器发起请求。
3. Transport请求过滤实现
HTTP.Transport还可以通过一定的手段对HTTP请求进行过滤,从而可以对请求进行更细致的控制和限制。
3.1. 使用Transport.Proxy给代理请求进行过滤
在某些场景下,我们需要对请求进行黑名单或白名单的过滤限制,这时可以使用Transport.Proxy方法,通过匹配目标URL的Host字段,向白名单中的服务器发起请求,过滤掉不符合要求的请求。下面的示例展示了如何使用Transport.Proxy实现黑名单过滤:
package main
import (
"net"
"net/http"
"fmt"
"log"
)
type MyTransport struct {
http.Transport
}
func (t *MyTransport) Proxy(req *http.Request) (*url.URL, error) {
forbiddenHosts := []string{"example.com", "test.com"}
if contains(forbiddenHosts, req.URL.Host) {
return nil, fmt.Errorf("forbidden hostname: %s", req.URL.Host)
}
return t.Transport.Proxy(req)
}
func main() {
transport := &MyTransport{}
client := &http.Client{
Transport: transport,
}
res, err := client.Get("http://example.com")
if err != nil {
log.Fatal(err)
}
defer res.Body.Close()
fmt.Println(res)
}
func contains(strs []string, str string) bool {
for _, s := range strs {
if s == str {
return true
}
}
return false
}
在上述示例中,我们实现了一个自定义的Transport,通过修改Proxy方法,我们使用了一个名为forbiddenHosts的黑名单,如果请求的Host字段在名单中,则返回错误信息,否则继续使用默认的Transport.Proxy实现。
3.2. 使用Transport.DialTLS给HTTPS请求进行过滤
在HTTPS请求中,我们可以使用Transport.DialTLS方法,通过重新定义TLS验证的逻辑,来实现过滤TLS证书信任链不完整的请求。下面的示例展示了如何使用Transport.DialTLS实现HTTPS证书过滤:
package main
import (
"net"
"net/http"
"fmt"
"log"
)
type MyTransport struct {
http.Transport
}
func (t *MyTransport) DialTLS(network, addr string) (net.Conn, error) {
conn, err := tls.Dial("tcp", addr, &tls.Config{
InsecureSkipVerify: true,
})
if err != nil {
return nil, err
}
certChain := conn.ConnectionState().PeerCertificates
checkResult := // 自定义的证书链验证方法
if !checkResult {
return nil, fmt.Errorf("untrusted certificate chain")
}
return conn, nil
}
func main() {
transport := &MyTransport{}
client := &http.Client{
Transport: transport,
}
res, err := client.Get("https://example.com")
if err != nil {
log.Fatal(err)
}
defer res.Body.Close()
fmt.Println(res)
}
在上述示例中,我们实现了一个自定义的Transport,通过修改DialTLS方法,我们增加了一个自定义的证书链验证方法,如果验证结果不符,则返回错误信息,中断请求。