Golang TCP服务器:如何写入HTTP2数据

Golang TCP服务器:如何写入HTTP2数据,go,tcp,http2,Go,Tcp,Http2,我是HTTP/2.0新手,我正在尝试设置一个TCP服务器,用Golang编写,它接收和写入HTTP/2.0帧。我无法将任何数据写回客户 下面的代码片段显示了如何处理请求 conn, err := l.Accept() if err != nil { log.Fatal("could not accept connection:", err) } defer conn.Close() // Every connection starts with a connection preface

我是HTTP/2.0新手,我正在尝试设置一个TCP服务器,用Golang编写,它接收和写入HTTP/2.0帧。我无法将任何数据写回客户

下面的代码片段显示了如何处理请求

conn, err := l.Accept()
if err != nil {
    log.Fatal("could not accept connection:", err)
}
defer conn.Close()

// Every connection starts with a connection preface send first, which has to be read prior
// to reading any frames (RFC 7540, section 3.5)
const preface = "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"
b := make([]byte, len(preface))
if _, err := io.ReadFull(conn, b); err != nil {
    log.Fatal("could not read from connection:", err)
}
if string(b) != preface {
    log.Fatal("invalid preface")
}

framer := http2.NewFramer(conn, conn)

// Read client request (SETTINGS and HEADERS)
readFrames(framer)

// Send empty SETTINGS frame to the client
framer.WriteRawFrame(http2.FrameSettings, 0, 0, []byte{})

// Read clients response (contains empty SETTINGS with END_STREAM flag)
readFrames(framer)

// Prepare HEADERS
hbuf := bytes.NewBuffer([]byte{})
encoder := hpack.NewEncoder(hbuf)
encoder.WriteField(hpack.HeaderField{Name: ":status:", Value: "200"})
encoder.WriteField(hpack.HeaderField{Name: "date", Value: time.Now().UTC().Format(http.TimeFormat)})
encoder.WriteField(hpack.HeaderField{Name: "content-length", Value: strconv.Itoa(len("ok"))})
encoder.WriteField(hpack.HeaderField{Name: "content-type", Value: "text/html"})

// Write HEADERS frame 
err = framer.WriteHeaders(http2.HeadersFrameParam{StreamID: 2, BlockFragment: hbuf.Bytes(), EndHeaders: true})
if err != nil {
    log.Fatal("could not write headers: ", err)
}

// Clients response contains GOAWAY
readFrames(framer)

framer.WriteData(2, true, []byte("ok"))
conn.Close()
可在此处找到完整的服务器:

通过执行卷曲来调用:

curl -kv https://127.0.0.1:8080 --http2
据我所知,在设置帧之后,连接应该准备好进行通信。应该通过发送头帧来打开流,然后可以在打开的流上发送数据帧。但是,在发送Header帧后,我收到以下错误消息:

curl: (16) Error in the HTTP2 framing layer

客户的回答是GOAWAY。

我自己发现了问题,如果事实上有两个问题的话。我在本文中发现了两个问题:

首先,包含状态代码的所需响应标题应为
:status
,而不是
:status:

最后,我没有对发出请求的相同流ID做出响应,而是尝试打开一个新流

因此,我应该在请求帧上循环,查找流ID,并使用它写出响应头

// Read client request (SETTINGS and HEADERS)
readFrames(framer)
var streamID uint32
for _, frame := range frames {
    if headersframe, ok := frame.(*http2.HeadersFrame); ok {
        streamID = headersframe.StreamID
    }
}

//... Prepare headers

// Write HEADERS frame 
err = framer.WriteHeaders(http2.HeadersFrameParam{StreamID: streamID, BlockFragment: hbuf.Bytes(), EndHeaders: true})
if err != nil {
    log.Fatal("could not write headers: ", err)
}

framer.WriteData(streamID, true, []byte("ok"))
conn.Close()

你为什么不直接使用标准库的
http
包,它已经为你提供了这个功能?@Flimzy我需要一个解决方案,它接受原始的头和请求体,并在请求体中以纯文本的形式返回。所以你在构建某种echo服务器?为什么标准库不适用于此?确实如此。目的是检查原始包装。一旦数据被解析成http.Request并再次转储,您就会丢失诸如头顺序之类的信息。我在挠头,为什么头的顺序会有任何不同。Iirc,任何应用程序都不得(在RFCnese中)依赖于头的顺序。如果键值对的顺序很重要,那么它们必须在一个报头中发送。例如,请参见Accept-*