Skip to content

Commit cb3a992

Browse files
committed
allow sending 1xx responses
Currently, it's not possible to send informational responses such as 103 Early Hints or 102 Processing. This patch allows calling WriteHeader() multiple times in order to send informational responses before the final one. It follows the patch for HTTP/1 (golang/go#42597) and HTTP/2 (golang/net#96). In conformance with RFC 8297, if the status code is 103 the current content of the header map is also sent. Its content is not removed after the call to WriteHeader() because the headers must also be included in the final response. The Chrome and Fastly teams are starting a large-scale experiment to measure the real-life impact of the 103 status code. Using Early Hints is proposed as a (partial) alternative to Server Push, which are going to be removed from Chrome: https://groups.google.com/a/chromium.org/g/blink-dev/c/K3rYLvmQUBY/m/21anpFhxAQAJ Being able to send this status code from servers implemented using Go would help to see if implementing it in browsers is worth it.
1 parent dd9f8e4 commit cb3a992

File tree

2 files changed

+28
-1
lines changed

2 files changed

+28
-1
lines changed

http3/response_writer.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,10 @@ func (w *responseWriter) WriteHeader(status int) {
5757
if w.headerWritten {
5858
return
5959
}
60-
w.headerWritten = true
60+
61+
if status < 100 || status >= 200 {
62+
w.headerWritten = true
63+
}
6164
w.status = status
6265

6366
var headers bytes.Buffer

http3/response_writer_test.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,30 @@ var _ = Describe("Response Writer", func() {
117117
Expect(fields).To(HaveKeyWithValue(":status", []string{"200"}))
118118
})
119119

120+
It("allows calling WriteHeader() several times when using the 103 status code", func() {
121+
rw.Header().Add("Link", "</style.css>; rel=preload; as=style")
122+
rw.Header().Add("Link", "</script.js>; rel=preload; as=script")
123+
rw.WriteHeader(http.StatusEarlyHints)
124+
125+
n, err := rw.Write([]byte("foobar"))
126+
Expect(n).To(Equal(6))
127+
Expect(err).ToNot(HaveOccurred())
128+
129+
// Early Hints must have been received
130+
fields := decodeHeader(strBuf)
131+
Expect(fields).To(HaveLen(2))
132+
Expect(fields).To(HaveKeyWithValue(":status", []string{"103"}))
133+
Expect(fields).To(HaveKeyWithValue("link", []string{"</style.css>; rel=preload; as=style", "</script.js>; rel=preload; as=script"}))
134+
135+
// According to the spec, headers sent in the informational response must also be included in the final response
136+
fields = decodeHeader(strBuf)
137+
Expect(fields).To(HaveLen(2))
138+
Expect(fields).To(HaveKeyWithValue(":status", []string{"200"}))
139+
Expect(fields).To(HaveKeyWithValue("link", []string{"</style.css>; rel=preload; as=style", "</script.js>; rel=preload; as=script"}))
140+
141+
Expect(getData(strBuf)).To(Equal([]byte("foobar")))
142+
})
143+
120144
It("doesn't allow writes if the status code doesn't allow a body", func() {
121145
rw.WriteHeader(304)
122146
n, err := rw.Write([]byte("foobar"))

0 commit comments

Comments
 (0)