英文:
How to set HTTP trailers in Go server?
问题
我想通过对响应主体进行哈希计算来生成响应的实体标签。但是当我计算实体标签时,已经太晚将实体标签添加到响应头中了。我想将实体标签添加到尾部。我看到 net/http 包支持编写尾部,但我不知道如何使用它们。
尾部的代码在 https://golang.org/src/pkg/net/http/transfer.go 中。我该如何从我的应用程序中设置尾部?
英文:
I want to compute the entity tag for a response by hashing the response body as it's written out. By the time I compute the entity tag, it's too late to add the entity tag to the response header. I'd like to add the entity tag to the trailer. I see the the net/http package has support for writing trailers, but I cannot figure out how to use them.
The trailer code is in https://golang.org/src/pkg/net/http/transfer.go. How do I set a trailer from my application?
答案1
得分: 3
2021年的回答(或者真的是go1.5+)
在第一次调用Write
之前,您需要预先设置尾部标头名称,然后可以稍后添加标头。
例如(从https://pkg.go.dev/net/http#example-ResponseWriter-Trailers复制):
// 在调用WriteHeader或Write之前,声明您将在HTTP响应期间设置的尾部。
w.Header().Set("Trailer", "AtEnd1, AtEnd2")
w.Header().Add("Trailer", "AtEnd3")
w.Header().Set("Content-Type", "text/plain; charset=utf-8") // 正常标头
w.WriteHeader(http.StatusOK)
w.Header().Set("AtEnd1", "value 1")
io.WriteString(w, "This HTTP response has both headers before this text and trailers at the end.\n")
w.Header().Set("AtEnd2", "value 2")
w.Header().Set("AtEnd3", "value 3") // 这些将显示为尾部。
原始回答(< go1.5)
使用bytes.Buffer
,同时将其包装到哈希中,类似于:
type HashedBuffer struct {
h hash.Hash
b bytes.Buffer
}
func NewHashedBuffer(h hash.Hash) *HashedBuffer {
return &HashedBuffer{h: h}
}
func (h *HashedBuffer) Write(p []byte) (n int, err error) {
n, err = h.b.Write(p)
h.h.Write(p)
return
}
func (h *HashedBuffer) Output(w http.ResponseWriter) {
w.Header().Set("ETag", hex.EncodeToString(h.h.Sum(nil)))
h.b.WriteTo(w)
}
//handler
func Handler(w http.ResponseWriter, r *http.Request) {
hb := NewHashedBuffer(sha256.New())
hb.Write([]byte("stuff"))
hb.Output(w)
}
截至目前,您无法设置尾部标头,有一个未解决的问题。
有一个解决方法,劫持连接(来自上述问题):
// TODO:目前服务器无法在未劫持的情况下设置尾部,因此现在只需劫持以测试客户端。
// 稍后,在Go 1.4中,如果在初始写入后对w.Header()进行任何更改,则应默认为要发送的尾部,但前提是它们以前已在w.Header().Set("Trailer", ..keys..)中声明。
w.(Flusher).Flush()
conn, buf, _ := w.(Hijacker).Hijack()
t := Header{}
t.Set("Server-Trailer-A", "valuea")
t.Set("Server-Trailer-C", "valuec") // 跳过B
buf.WriteString("0\r\n") // eof
t.Write(buf)
buf.WriteString("\r\n") // 尾部结束
buf.Flush()
conn.Close()
英文:
2021 answer (or really go1.5+)
You need to pre-set the trailer header names before the first Write
then you can add the headers later.
for example (copied from https://pkg.go.dev/net/http#example-ResponseWriter-Trailers):
// Before any call to WriteHeader or Write, declare
// the trailers you will set during the HTTP
// response. These three headers are actually sent in
// the trailer.
w.Header().Set("Trailer", "AtEnd1, AtEnd2")
w.Header().Add("Trailer", "AtEnd3")
w.Header().Set("Content-Type", "text/plain; charset=utf-8") // normal header
w.WriteHeader(http.StatusOK)
w.Header().Set("AtEnd1", "value 1")
io.WriteString(w, "This HTTP response has both headers before this text and trailers at the end.\n")
w.Header().Set("AtEnd2", "value 2")
w.Header().Set("AtEnd3", "value 3") // These will appear as trailers.
original answer (< go1.5)
use a bytes.Buffer, and wrap it to hash in the same time, something like:
type HashedBuffer struct {
h hash.Hash
b bytes.Buffer
}
func NewHashedBuffer(h hash.Hash) *HashedBuffer {
return &HashedBuffer{h: h}
}
func (h *HashedBuffer) Write(p []byte) (n int, err error) {
n, err = h.b.Write(p)
h.h.Write(p)
return
}
func (h *HashedBuffer) Output(w http.ResponseWriter) {
w.Header().Set("ETag", hex.EncodeToString(h.h.Sum(nil)))
h.b.WriteTo(w)
}
//handler
func Handler(w http.ResponseWriter, r *http.Request) {
hb := NewHashedBuffer(sha256.New())
hb.Write([]byte("stuff"))
hb.Output(w)
}
As of right now, you can't set trailer headers, there's an open issue about it.
There's a workaround, hijack the connection (from the above issue) :
// TODO: There's no way yet for the server to set trailers
// without hijacking, so do that for now, just to test the client.
// Later, in Go 1.4, it should be be implicit that any mutations
// to w.Header() after the initial write are the trailers to be
// sent, if and only if they were previously declared with
// w.Header().Set("Trailer", ..keys..)
w.(Flusher).Flush()
conn, buf, _ := w.(Hijacker).Hijack()
t := Header{}
t.Set("Server-Trailer-A", "valuea")
t.Set("Server-Trailer-C", "valuec") // skipping B
buf.WriteString("0\r\n") // eof
t.Write(buf)
buf.WriteString("\r\n") // end of trailers
buf.Flush()
conn.Close()
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论