@@ -13,6 +13,7 @@ import (
1313 "strconv"
1414 "strings"
1515 "sync"
16+ "time"
1617
1718 "golang.org/x/net/http/httpguts"
1819 "golang.org/x/net/internal/httpcommon"
@@ -253,10 +254,11 @@ func (sc *serverConn) handleRequestStream(st *stream) error {
253254 defer req .Body .Close ()
254255
255256 rw := & responseWriter {
256- st : st ,
257- headers : make (http.Header ),
258- trailer : make (http.Header ),
259- isHeadResp : req .Method == "HEAD" ,
257+ st : st ,
258+ headers : make (http.Header ),
259+ trailer : make (http.Header ),
260+ bb : make (bodyBuffer , 0 , defaultBodyBufferCap ),
261+ cannotHaveBody : req .Method == "HEAD" ,
260262 bw : & bodyWriter {
261263 st : st ,
262264 remain : - 1 ,
@@ -268,8 +270,18 @@ func (sc *serverConn) handleRequestStream(st *stream) error {
268270 defer rw .close ()
269271 if reqInfo .NeedsContinue {
270272 req .Body .(* bodyReader ).send100Continue = func () {
271- rw .WriteHeader (http .StatusContinue )
272- rw .Flush ()
273+ rw .mu .Lock ()
274+ defer rw .mu .Unlock ()
275+ if rw .wroteHeader {
276+ return
277+ }
278+ encHeaders := rw .bw .enc .encode (func (f func (itype indexType , name , value string )) {
279+ f (mayIndex , ":status" , strconv .Itoa (http .StatusContinue ))
280+ })
281+ rw .st .writeVarint (int64 (frameTypeHeaders ))
282+ rw .st .writeVarint (int64 (len (encHeaders )))
283+ rw .st .Write (encHeaders )
284+ rw .st .Flush ()
273285 }
274286 }
275287
@@ -290,14 +302,31 @@ func (sc *serverConn) abort(err error) {
290302 }
291303}
292304
305+ // responseCanHaveBody reports whether a given response status code permits a
306+ // body. See RFC 7230, section 3.3.
307+ func responseCanHaveBody (status int ) bool {
308+ switch {
309+ case status >= 100 && status <= 199 :
310+ return false
311+ case status == 204 :
312+ return false
313+ case status == 304 :
314+ return false
315+ }
316+ return true
317+ }
318+
293319type responseWriter struct {
294- st * stream
295- bw * bodyWriter
296- mu sync.Mutex
297- headers http.Header
298- trailer http.Header
299- wroteHeader bool // Non-1xx header has been (logically) written.
300- isHeadResp bool // response is for a HEAD request.
320+ st * stream
321+ bw * bodyWriter
322+ mu sync.Mutex
323+ headers http.Header
324+ trailer http.Header
325+ bb bodyBuffer
326+ wroteHeader bool // Non-1xx header has been (logically) written.
327+ statusCode int // Status of the response that will be sent in HEADERS frame.
328+ statusCodeSet bool // Status of the response has been set via a call to WriteHeader.
329+ cannotHaveBody bool // Response should not have a body (e.g. response to a HEAD request).
301330}
302331
303332func (rw * responseWriter ) Header () http.Header {
@@ -322,11 +351,13 @@ func (rw *responseWriter) prepareTrailerForWriteLocked() {
322351
323352// Caller must hold rw.mu. If rw.wroteHeader is true, calling this method is a
324353// no-op.
325- func (rw * responseWriter ) writeHeaderLockedOnce (statusCode int ) {
354+ func (rw * responseWriter ) writeHeaderLockedOnce () {
326355 if rw .wroteHeader {
327356 return
328357 }
329-
358+ if ! responseCanHaveBody (rw .statusCode ) {
359+ rw .cannotHaveBody = true
360+ }
330361 // If there is any Trailer declared in headers, save them so we know which
331362 // trailers have been pre-declared. Also, write back the extracted value,
332363 // which is canonicalized, to rw.Header for consistency.
@@ -335,10 +366,9 @@ func (rw *responseWriter) writeHeaderLockedOnce(statusCode int) {
335366 rw .headers .Set ("Trailer" , strings .Join (slices .Sorted (maps .Keys (rw .trailer )), ", " ))
336367 }
337368
338- enc := & qpackEncoder {}
339- enc .init ()
340- encHeaders := enc .encode (func (f func (itype indexType , name , value string )) {
341- f (mayIndex , ":status" , strconv .Itoa (statusCode ))
369+ rw .bb .inferHeader (rw .headers , rw .statusCode )
370+ encHeaders := rw .bw .enc .encode (func (f func (itype indexType , name , value string )) {
371+ f (mayIndex , ":status" , strconv .Itoa (rw .statusCode ))
342372 for name , values := range rw .headers {
343373 if ! httpguts .ValidHeaderFieldName (name ) {
344374 continue
@@ -352,45 +382,128 @@ func (rw *responseWriter) writeHeaderLockedOnce(statusCode int) {
352382 }
353383 }
354384 })
385+
355386 rw .st .writeVarint (int64 (frameTypeHeaders ))
356387 rw .st .writeVarint (int64 (len (encHeaders )))
357388 rw .st .Write (encHeaders )
358- if statusCode >= http .StatusOK {
389+ if rw . statusCode >= http .StatusOK {
359390 rw .wroteHeader = true
360391 }
361392}
362393
363394func (rw * responseWriter ) WriteHeader (statusCode int ) {
395+ // TODO: handle sending informational status headers (e.g. 103).
364396 rw .mu .Lock ()
365397 defer rw .mu .Unlock ()
366- rw .writeHeaderLockedOnce (statusCode )
398+ if rw .statusCodeSet {
399+ return
400+ }
401+ rw .statusCodeSet = true
402+ rw .statusCode = statusCode
367403}
368404
369405func (rw * responseWriter ) Write (b []byte ) (int , error ) {
406+ // Calling Write implicitly calls WriteHeader(200) if WriteHeader has not
407+ // been called before.
408+ rw .WriteHeader (http .StatusOK )
370409 rw .mu .Lock ()
371410 defer rw .mu .Unlock ()
372- rw .writeHeaderLockedOnce (http .StatusOK )
373- if rw .isHeadResp {
374- return 0 , nil
411+
412+ // If b fits entirely in our body buffer, save it to the buffer and return
413+ // early so we can coalesce small writes.
414+ // As a special case, we always want to save b to the buffer even when b is
415+ // big if we had yet to write our header, so we can infer headers like
416+ // "Content-Type" with as much information as possible.
417+ initialBufLen := len (rw .bb )
418+ if ! rw .wroteHeader || len (b ) <= cap (rw .bb )- len (rw .bb ) {
419+ b = rw .bb .write (b )
420+ if len (b ) == 0 {
421+ return len (b ), nil
422+ }
423+ }
424+
425+ // Reaching this point means that our buffer has been sufficiently filled.
426+ // Therefore, we now want to:
427+ // 1. Infer and write response headers based on our body buffer, if not
428+ // done yet.
429+ // 2. Write our body buffer and the rest of b (if any).
430+ // 3. Reset the current body buffer so it can be used again.
431+ rw .writeHeaderLockedOnce ()
432+ if rw .cannotHaveBody {
433+ return len (b ), nil
434+ }
435+ if n , err := rw .bw .write (rw .bb , b ); err != nil {
436+ return max (0 , n - initialBufLen ), err
375437 }
376- return rw .bw .Write (b )
438+ rw .bb .discard ()
439+ return len (b ), nil
377440}
378441
379442func (rw * responseWriter ) Flush () {
443+ // Calling Flush implicitly calls WriteHeader(200) if WriteHeader has not
444+ // been called before.
445+ rw .WriteHeader (http .StatusOK )
380446 rw .mu .Lock ()
381- rw .writeHeaderLockedOnce (http .StatusOK )
447+ rw .writeHeaderLockedOnce ()
448+ if ! rw .cannotHaveBody {
449+ rw .bw .Write (rw .bb )
450+ rw .bb .discard ()
451+ }
382452 rw .mu .Unlock ()
383- rw .bw . st .Flush ()
453+ rw .st .Flush ()
384454}
385455
386456func (rw * responseWriter ) close () error {
457+ rw .Flush ()
387458 rw .mu .Lock ()
388459 defer rw .mu .Unlock ()
389- rw .writeHeaderLockedOnce (http .StatusOK )
390460 rw .prepareTrailerForWriteLocked ()
391-
392461 if err := rw .bw .Close (); err != nil {
393462 return err
394463 }
395464 return rw .st .stream .Close ()
396465}
466+
467+ // defaultBodyBufferCap is the default number of bytes of body that we are
468+ // willing to save in a buffer for the sake of inferring headers and coalescing
469+ // small writes. 512 was chosen to be consistent with how much
470+ // http.DetectContentType is willing to read.
471+ const defaultBodyBufferCap = 512
472+
473+ // bodyBuffer is a buffer used to store body content of a response.
474+ type bodyBuffer []byte
475+
476+ // write writes b to the buffer. It returns a new slice of b, which contains
477+ // any remaining data that could not be written to the buffer, if any.
478+ func (bb * bodyBuffer ) write (b []byte ) []byte {
479+ n := min (len (b ), cap (* bb )- len (* bb ))
480+ * bb = append (* bb , b [:n ]... )
481+ return b [n :]
482+ }
483+
484+ // discard resets the buffer so it can be used again.
485+ func (bb * bodyBuffer ) discard () {
486+ * bb = (* bb )[:0 ]
487+ }
488+
489+ // inferHeader populates h with the header values that we can infer from our
490+ // current buffer content, if not already explicitly set. This method should be
491+ // called only once with as much body content as possible in the buffer, before
492+ // a HEADERS frame is sent, and before discard has been called. Doing so
493+ // properly is the responsibility of the caller.
494+ func (bb * bodyBuffer ) inferHeader (h http.Header , status int ) {
495+ if _ , ok := h ["Date" ]; ! ok {
496+ h .Set ("Date" , time .Now ().UTC ().Format (http .TimeFormat ))
497+ }
498+ // If the Content-Encoding is non-blank, we shouldn't
499+ // sniff the body. See Issue golang.org/issue/31753.
500+ _ , hasCE := h ["Content-Encoding" ]
501+ _ , hasCT := h ["Content-Type" ]
502+ if ! hasCE && ! hasCT && responseCanHaveBody (status ) && len (* bb ) > 0 {
503+ h .Set ("Content-Type" , http .DetectContentType (* bb ))
504+ }
505+ // We can technically infer Content-Length too here, as long as the entire
506+ // response body fits within hi.buf and does not require flushing. However,
507+ // we have chosen not to do so for now as Content-Length is not very
508+ // important for HTTP/3, and such inconsistent behavior might be confusing.
509+ }
0 commit comments