如何从net/http请求中读取HTTP/2推送帧

cap*_*aig 7 go http2

我正在尝试编写一个Go客户端来测试我们的http/2基础架构.我想发一个http请求,https://mydomain.tld/somePage并希望收到一个html响应,以及几个推送资源.我想确保那些推动成功,如果不成功则失败.

我不清楚标准库的任何部分是否暴露了我需要的这个功能.

我可以查看响应并检查协议版本以检测http2.

我可以看到Linkhttps://http2-push.appspot.com/这样发送推送的网站的响应中的标题,但我不太清楚链接标头和实际推送承诺框架之间的关系.你可以通过http 1.1获得链接头,所以我不确定是否只能确保推送会发生.

http2包具有较低级别Framer的接口,我可能能够利用验证原始帧,但说实话,我不知道如何设置一个,并向其发出的初始请求.

是否有任何示例说明go客户端如何验证http2推送资源的正确配置?

Pet*_*ter 4

如果我们可以获得 http.Client 自然读取的字节的副本,那么使用 golang.org/x/net/http2 中的 Framer 并不难。我们可以通过实现我们自己的 net.Conn 来做到这一点。

我在下面的程序中取得了一些进展,但是我没有看到预期的 PUSH_PROMISE 帧。经过一番挖掘后,我发现Go 客户端明确禁用了 Push。在这种情况下,服务器不允许发送这些帧。我没有看到改变该设置的明显方法(除了破解 stdlib 之外)。

我以为我仍然分享我的代码。也许我终究错过了一些简单的事情来让它发挥作用。

package main

import (
    "bytes"
    "crypto/tls"
    "io"
    "io/ioutil"
    "log"
    "net"
    "net/http"
    "os"

    "golang.org/x/net/http2"
)

func main() {
    buf := &bytes.Buffer{}
    transport := &http2.Transport{DialTLS: dialT(buf)}
    client := &http.Client{Transport: transport}

    res, err := client.Get("https://http2-push.appspot.com/")
    if err != nil {
            log.Fatal(err)
    }

    res.Body.Close()
    res.Write(os.Stdout)

    framer := http2.NewFramer(ioutil.Discard, buf)
    for {
            f, err := framer.ReadFrame()
            if err == io.EOF || err == io.ErrUnexpectedEOF {
                    break
            }
            switch err.(type) {
            case nil:
                    log.Println(f)
            case http2.ConnectionError:
                    // Ignore. There will be many errors of type "PROTOCOL_ERROR, DATA
                    // frame with stream ID 0". Presumably we are abusing the framer.
            default:
                    log.Println(err, framer.ErrorDetail())
            }
    }
}

// dialT returns a connection that writes everything that is read to w.
func dialT(w io.Writer) func(network, addr string, cfg *tls.Config) (net.Conn, error) {
    return func(network, addr string, cfg *tls.Config) (net.Conn, error) {
            conn, err := tls.Dial(network, addr, cfg)
            return &tConn{conn, w}, err
    }
}

type tConn struct {
    net.Conn
    T io.Writer // receives everything that is read from Conn
}

func (w *tConn) Read(b []byte) (n int, err error) {
    n, err = w.Conn.Read(b)
    w.T.Write(b)
    return
}
Run Code Online (Sandbox Code Playgroud)