如何在Go服务器中设置HTTP尾部?

huangapple go评论77阅读模式
英文:

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(&quot;Trailer&quot;, &quot;AtEnd1, AtEnd2&quot;)
		w.Header().Add(&quot;Trailer&quot;, &quot;AtEnd3&quot;)

		w.Header().Set(&quot;Content-Type&quot;, &quot;text/plain; charset=utf-8&quot;) // normal header
		w.WriteHeader(http.StatusOK)

		w.Header().Set(&quot;AtEnd1&quot;, &quot;value 1&quot;)
		io.WriteString(w, &quot;This HTTP response has both headers before this text and trailers at the end.\n&quot;)
		w.Header().Set(&quot;AtEnd2&quot;, &quot;value 2&quot;)
		w.Header().Set(&quot;AtEnd3&quot;, &quot;value 3&quot;) // 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 &amp;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(&quot;ETag&quot;, 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(&quot;stuff&quot;))
	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&#39;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(&quot;Trailer&quot;, ..keys..)
w.(Flusher).Flush()
conn, buf, _ := w.(Hijacker).Hijack()
t := Header{}
t.Set(&quot;Server-Trailer-A&quot;, &quot;valuea&quot;)
t.Set(&quot;Server-Trailer-C&quot;, &quot;valuec&quot;) // skipping B
buf.WriteString(&quot;0\r\n&quot;)            // eof
t.Write(buf)
buf.WriteString(&quot;\r\n&quot;) // end of trailers
buf.Flush()
conn.Close()

huangapple
  • 本文由 发表于 2014年9月28日 12:39:06
  • 转载请务必保留本文链接:https://go.coder-hub.com/26081673.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定