// Copyright 2025 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package http3

import (
	"context"
	"crypto/tls"
	"fmt"
	"io"
	"maps"
	"net/http"
	"net/textproto"
	"slices"
	"strconv"
	"strings"
	"sync"
	"time"

	"golang.org/x/net/http/httpguts"
	"golang.org/x/net/internal/httpcommon"
	"golang.org/x/net/quic"
)

// A server is an HTTP/3 server.
// The zero value for server is a valid server.
type server struct {
	// handler to invoke for requests, http.DefaultServeMux if nil.
	handler http.Handler

	config *quic.Config

	listenQUIC func(addr string, config *quic.Config) (*quic.Endpoint, error)

	initOnce sync.Once

	serveCtx       context.Context
	serveCtxCancel context.CancelFunc

	// connClosed is used to signal that a connection has been unregistered
	// from activeConns. That way, when shutting down gracefully, the server
	// can avoid busy-waiting for activeConns to be empty.
	connClosed  chan any
	mu          sync.Mutex // Guards fields below.
	activeConns map[*serverConn]struct{}
}

// netHTTPHandler is an interface that is implemented by
// net/http.http3ServerHandler in std.
//
// It provides a way for information to be passed between x/net and net/http
// that would otherwise be inaccessible, such as the TLS configs that users
// have supplied to net/http servers.
//
// This allows us to integrate our HTTP/3 server implementation with the
// net/http server when RegisterServer is called.
type netHTTPHandler interface {
	http.Handler
	TLSConfig() *tls.Config
	BaseContext() context.Context
	Addr() string
	ListenErrHook(err error)
	ShutdownContext() context.Context
}

type ServerOpts struct {
	// ListenQUIC determines how the server will open a QUIC endpoint.
	// By default, quic.Listen("udp", addr, config) is used.
	ListenQUIC func(addr string, config *quic.Config) (*quic.Endpoint, error)

	// QUICConfig is the QUIC configuration used by the server.
	// QUICConfig may be nil and should not be modified after calling
	// RegisterServer.
	// If QUICConfig.TLSConfig is nil, the TLSConfig of the net/http Server
	// given to RegisterServer will be used.
	QUICConfig *quic.Config
}

// RegisterServer adds HTTP/3 support to a net/http Server.
//
// RegisterServer must be called before s begins serving, and only affects
// s.ListenAndServeTLS.
func RegisterServer(s *http.Server, opts ServerOpts) {
	if s.TLSNextProto == nil {
		s.TLSNextProto = make(map[string]func(*http.Server, *tls.Conn, http.Handler))
	}
	s.TLSNextProto["http/3"] = func(s *http.Server, c *tls.Conn, h http.Handler) {
		stdHandler, ok := h.(netHTTPHandler)
		if !ok {
			panic("RegisterServer was given a server that does not implement netHTTPHandler")
		}
		if opts.QUICConfig == nil {
			opts.QUICConfig = &quic.Config{}
		}
		if opts.QUICConfig.TLSConfig == nil {
			opts.QUICConfig.TLSConfig = stdHandler.TLSConfig()
		}
		s3 := &server{
			config:     opts.QUICConfig,
			listenQUIC: opts.ListenQUIC,
			handler:    stdHandler,
			serveCtx:   stdHandler.BaseContext(),
		}
		s3.init()
		s.RegisterOnShutdown(func() {
			s3.shutdown(stdHandler.ShutdownContext())
		})
		stdHandler.ListenErrHook(s3.listenAndServe(stdHandler.Addr()))
	}
}

func (s *server) init() {
	s.initOnce.Do(func() {
		s.config = initConfig(s.config)
		if s.handler == nil {
			s.handler = http.DefaultServeMux
		}
		if s.serveCtx == nil {
			s.serveCtx = context.Background()
		}
		if s.listenQUIC == nil {
			s.listenQUIC = func(addr string, config *quic.Config) (*quic.Endpoint, error) {
				return quic.Listen("udp", addr, config)
			}
		}
		s.serveCtx, s.serveCtxCancel = context.WithCancel(s.serveCtx)
		s.activeConns = make(map[*serverConn]struct{})
		s.connClosed = make(chan any, 1)
	})
}

// listenAndServe listens on the UDP network address addr
// and then calls Serve to handle requests on incoming connections.
func (s *server) listenAndServe(addr string) error {
	s.init()
	e, err := s.listenQUIC(addr, s.config)
	if err != nil {
		return err
	}
	go s.serve(e)
	return nil
}

// serve accepts incoming connections on the QUIC endpoint e,
// and handles requests from those connections.
func (s *server) serve(e *quic.Endpoint) error {
	s.init()
	defer e.Close(canceledCtx)
	for {
		qconn, err := e.Accept(s.serveCtx)
		if err != nil {
			return err
		}
		go s.newServerConn(qconn, s.handler)
	}
}

// shutdown attempts a graceful shutdown for the server.
func (s *server) shutdown(ctx context.Context) {
	// Set a reasonable default in case ctx is nil.
	if ctx == nil {
		var cancel context.CancelFunc
		ctx, cancel = context.WithTimeout(context.Background(), time.Second)
		defer cancel()
	}

	// Send GOAWAY frames to all active connections to give a chance for them
	// to gracefully terminate.
	s.mu.Lock()
	for sc := range s.activeConns {
		// TODO: Modify x/net/quic stream API so that write errors from context
		// deadline are sticky.
		go sc.sendGoaway()
	}
	s.mu.Unlock()

	// Complete shutdown as soon as there are no more active connections or ctx
	// is done, whichever comes first.
	defer func() {
		s.mu.Lock()
		defer s.mu.Unlock()
		s.serveCtxCancel()
		for sc := range s.activeConns {
			sc.abort(&connectionError{
				code:    errH3NoError,
				message: "server is shutting down",
			})
		}
	}()
	noMoreConns := func() bool {
		s.mu.Lock()
		defer s.mu.Unlock()
		return len(s.activeConns) == 0
	}
	for {
		if noMoreConns() {
			return
		}
		select {
		case <-ctx.Done():
			return
		case <-s.connClosed:
		}
	}
}

func (s *server) registerConn(sc *serverConn) {
	s.mu.Lock()
	defer s.mu.Unlock()
	s.activeConns[sc] = struct{}{}
}

func (s *server) unregisterConn(sc *serverConn) {
	s.mu.Lock()
	delete(s.activeConns, sc)
	s.mu.Unlock()
	select {
	case s.connClosed <- struct{}{}:
	default:
		// Channel already full. No need to send more values since we are just
		// using this channel as a simpler sync.Cond.
	}
}

type serverConn struct {
	qconn *quic.Conn

	genericConn // for handleUnidirectionalStream
	enc         qpackEncoder
	dec         qpackDecoder
	handler     http.Handler

	// For handling shutdown.
	controlStream      *stream
	mu                 sync.Mutex // Guards everything below.
	maxRequestStreamID int64
	goawaySent         bool
}

func (s *server) newServerConn(qconn *quic.Conn, handler http.Handler) {
	sc := &serverConn{
		qconn:   qconn,
		handler: handler,
	}
	s.registerConn(sc)
	defer s.unregisterConn(sc)
	sc.enc.init()

	// Create control stream and send SETTINGS frame.
	// TODO: Time out on creating stream.
	var err error
	sc.controlStream, err = newConnStream(context.Background(), sc.qconn, streamTypeControl)
	if err != nil {
		return
	}
	sc.controlStream.writeSettings()
	sc.controlStream.Flush()

	sc.acceptStreams(sc.qconn, sc)
}

func (sc *serverConn) handleControlStream(st *stream) error {
	// "A SETTINGS frame MUST be sent as the first frame of each control stream [...]"
	// https://www.rfc-editor.org/rfc/rfc9114.html#section-7.2.4-2
	if err := st.readSettings(func(settingsType, settingsValue int64) error {
		switch settingsType {
		case settingsMaxFieldSectionSize:
			_ = settingsValue // TODO
		case settingsQPACKMaxTableCapacity:
			_ = settingsValue // TODO
		case settingsQPACKBlockedStreams:
			_ = settingsValue // TODO
		default:
			// Unknown settings types are ignored.
		}
		return nil
	}); err != nil {
		return err
	}

	for {
		ftype, err := st.readFrameHeader()
		if err != nil {
			return err
		}
		switch ftype {
		case frameTypeCancelPush:
			// "If a server receives a CANCEL_PUSH frame for a push ID
			// that has not yet been mentioned by a PUSH_PROMISE frame,
			// this MUST be treated as a connection error of type H3_ID_ERROR."
			// https://www.rfc-editor.org/rfc/rfc9114.html#section-7.2.3-8
			return &connectionError{
				code:    errH3IDError,
				message: "CANCEL_PUSH for unsent push ID",
			}
		case frameTypeGoaway:
			return errH3NoError
		default:
			// Unknown frames are ignored.
			if err := st.discardUnknownFrame(ftype); err != nil {
				return err
			}
		}
	}
}

func (sc *serverConn) handleEncoderStream(*stream) error {
	// TODO
	return nil
}

func (sc *serverConn) handleDecoderStream(*stream) error {
	// TODO
	return nil
}

func (sc *serverConn) handlePushStream(*stream) error {
	// "[...] if a server receives a client-initiated push stream,
	// this MUST be treated as a connection error of type H3_STREAM_CREATION_ERROR."
	// https://www.rfc-editor.org/rfc/rfc9114.html#section-6.2.2-3
	return &connectionError{
		code:    errH3StreamCreationError,
		message: "client created push stream",
	}
}

// hasDisallowedConnectionHeader reports whether h contains connection headers
// that are not allowed in HTTP/3:
//
// "An endpoint MUST NOT generate an HTTP/3 field section containing
// connection-specific fields; any message containing connection-specific
// fields MUST be treated as malformed."
//
// "The only exception to this is the TE header field, which MAY be present in
// an HTTP/3 request header; when it is, it MUST NOT contain any value other
// than "trailers"."
func hasDisallowedConnectionHeader(h http.Header) bool {
	neverAllowed := []string{
		"Connection",
		"Keep-Alive",
		"Proxy-Connection",
		"Transfer-Encoding",
		"Upgrade",
	}
	for _, k := range neverAllowed {
		if _, ok := h[k]; ok {
			return true
		}
	}
	if te, ok := h["Te"]; ok && (len(te) != 1 || te[0] != "trailers") {
		return true
	}
	return false
}

type pseudoHeader struct {
	method    string
	scheme    string
	path      string
	authority string
}

func (sc *serverConn) parseHeader(st *stream) (http.Header, pseudoHeader, error) {
	ftype, err := st.readFrameHeader()
	if err != nil {
		return nil, pseudoHeader{}, err
	}
	if ftype != frameTypeHeaders {
		return nil, pseudoHeader{}, &streamError{errH3MessageError, "received other frames when expecting HEADERS"}
	}
	header := make(http.Header)
	var pHeader pseudoHeader
	var dec qpackDecoder
	var hasMethod, hasScheme, hasPath, hasAuthority bool
	if err := dec.decode(st, func(_ indexType, name, value string) error {
		if !httpguts.ValidHeaderFieldValue(value) {
			return &streamError{errH3MessageError, "invalid field value"}
		}
		switch name {
		case ":method":
			if hasMethod {
				return &streamError{errH3MessageError, "duplicate :method"}
			}
			hasMethod = true
			pHeader.method = value
		case ":scheme":
			if hasScheme {
				return &streamError{errH3MessageError, "duplicate :scheme"}
			}
			hasScheme = true
			pHeader.scheme = value
		case ":path":
			if hasPath {
				return &streamError{errH3MessageError, "duplicate :path"}
			}
			hasPath = true
			pHeader.path = value
		case ":authority":
			if hasAuthority {
				return &streamError{errH3MessageError, "duplicate :authority"}
			}
			hasAuthority = true
			pHeader.authority = value
		default:
			if !validWireHeaderFieldName(name) {
				return &streamError{errH3MessageError, "invalid field name"}
			}
			header.Add(name, value)
		}
		return nil
	}); err != nil {
		return nil, pseudoHeader{}, err
	}
	if err := st.endFrame(); err != nil {
		return nil, pseudoHeader{}, err
	}
	if hasDisallowedConnectionHeader(header) {
		return nil, pseudoHeader{}, &streamError{errH3MessageError, "invalid connection-related header"}
	}

	// "All HTTP/3 requests MUST include exactly one value for the :method,
	// :scheme, and :path pseudo-header fields, unless the request is a CONNECT
	// request"
	//
	// "A CONNECT request MUST be constructed as follows:
	// - The :method pseudo-header field is set to "CONNECT"
	// - The :scheme and :path pseudo-header fields are omitted
	// - The :authority pseudo-header field contains the host and port to connect to"
	if !hasMethod {
		return nil, pseudoHeader{}, &streamError{errH3MessageError, "missing :method"}
	}
	if pHeader.method != "CONNECT" && (!hasScheme || !hasPath) {
		return nil, pseudoHeader{}, &streamError{errH3MessageError, "missing :scheme or :path for non-CONNECT requests"}
	}
	if pHeader.method == "CONNECT" && (hasScheme || hasPath || !hasAuthority) {
		return nil, pseudoHeader{}, &streamError{
			errH3MessageError, "CONNECT request must only have :method and :authority pseudo-headers",
		}
	}
	return header, pHeader, nil
}

func (sc *serverConn) sendGoaway() {
	sc.mu.Lock()
	if sc.goawaySent || sc.controlStream == nil {
		sc.mu.Unlock()
		return
	}
	sc.goawaySent = true
	sc.mu.Unlock()

	// No lock in this section in case writing to stream blocks. This is safe
	// since sc.maxRequestStreamID is only updated when sc.goawaySent is false.
	sc.controlStream.writeVarint(int64(frameTypeGoaway))
	sc.controlStream.writeVarint(int64(sizeVarint(uint64(sc.maxRequestStreamID))))
	sc.controlStream.writeVarint(sc.maxRequestStreamID)
	sc.controlStream.Flush()
}

// requestShouldGoAway returns true if st has a stream ID that is equal or
// greater than the ID we have sent in a GOAWAY frame, if any.
func (sc *serverConn) requestShouldGoaway(st *stream) bool {
	sc.mu.Lock()
	defer sc.mu.Unlock()
	if sc.goawaySent {
		return st.stream.ID() >= sc.maxRequestStreamID
	} else {
		sc.maxRequestStreamID = max(sc.maxRequestStreamID, st.stream.ID())
		return false
	}
}

func (sc *serverConn) handleRequestStream(st *stream) error {
	if sc.requestShouldGoaway(st) {
		return &streamError{
			code:    errH3RequestRejected,
			message: "GOAWAY request with equal or lower ID than the stream has been sent",
		}
	}
	header, pHeader, err := sc.parseHeader(st)
	if err != nil {
		return err
	}

	reqInfo := httpcommon.NewServerRequest(httpcommon.ServerRequestParam{
		Method:    pHeader.method,
		Scheme:    pHeader.scheme,
		Authority: pHeader.authority,
		Path:      pHeader.path,
		Header:    header,
	})
	if reqInfo.InvalidReason != "" {
		return &streamError{
			code:    errH3MessageError,
			message: reqInfo.InvalidReason,
		}
	}

	var body io.ReadCloser
	contentLength := int64(-1)
	if n, err := strconv.Atoi(header.Get("Content-Length")); err == nil {
		contentLength = int64(n)
	}
	if contentLength != 0 || len(reqInfo.Trailer) != 0 {
		body = &bodyReader{
			st:            st,
			remain:        contentLength,
			trailer:       reqInfo.Trailer,
			filterTrailer: true,
		}
	} else {
		body = http.NoBody
	}

	req := &http.Request{
		Proto:         "HTTP/3.0",
		Method:        pHeader.method,
		Host:          pHeader.authority,
		URL:           reqInfo.URL,
		RequestURI:    reqInfo.RequestURI,
		Trailer:       reqInfo.Trailer,
		ProtoMajor:    3,
		RemoteAddr:    sc.qconn.RemoteAddr().String(),
		Body:          body,
		Header:        header,
		ContentLength: contentLength,
	}
	defer req.Body.Close()

	rw := &responseWriter{
		st:             st,
		headers:        make(http.Header),
		trailer:        make(http.Header),
		bb:             make(bodyBuffer, 0, defaultBodyBufferCap),
		cannotHaveBody: req.Method == "HEAD",
		bw: &bodyWriter{
			st:     st,
			remain: -1,
			flush:  false,
			name:   "response",
			enc:    &sc.enc,
		},
	}
	defer rw.close()
	if reqInfo.NeedsContinue {
		req.Body.(*bodyReader).send100Continue = func() {
			rw.WriteHeader(100)
		}
	}

	// TODO: handle panic coming from the HTTP handler.
	sc.handler.ServeHTTP(rw, req)
	return nil
}

// abort closes the connection with an error.
func (sc *serverConn) abort(err error) {
	if e, ok := err.(*connectionError); ok {
		sc.qconn.Abort(&quic.ApplicationError{
			Code:   uint64(e.code),
			Reason: e.message,
		})
	} else {
		sc.qconn.Abort(err)
	}
}

// responseCanHaveBody reports whether a given response status code permits a
// body. See RFC 7230, section 3.3.
func responseCanHaveBody(status int) bool {
	switch {
	case status >= 100 && status <= 199:
		return false
	case status == 204:
		return false
	case status == 304:
		return false
	}
	return true
}

// trailerPrefix is a magic prefix for [responseWriter.Header] map keys that,
// if present, signals that the map entry is actually for the response
// trailers, and not the response headers. See [net/http.TrailerPrefix] for
// details.
const trailerPrefix = "Trailer:"

type responseWriter struct {
	st             *stream
	bw             *bodyWriter
	mu             sync.Mutex
	headers        http.Header
	snapHeaders    http.Header // Snapshot of headers at WriteHeader time
	trailer        http.Header
	bb             bodyBuffer
	wroteHeader    bool // Non-1xx header has been (logically) written.
	statusCode     int  // Status of the response that will be sent in HEADERS frame.
	statusCodeSet  bool // Status of the response has been set via a call to WriteHeader.
	cannotHaveBody bool // Response should not have a body (e.g. response to a HEAD request).
	bodyLenLeft    int  // How much of the content body is left to be sent, set via "Content-Length" header. -1 if unknown.
}

func (rw *responseWriter) Header() http.Header {
	return rw.headers
}

// prepareTrailerForWriteLocked populates any pre-declared trailer header with
// its value, and passes it to bodyWriter so it can be written after body EOF.
// Caller must hold rw.mu.
func (rw *responseWriter) prepareTrailerForWriteLocked() {
	for name := range rw.trailer {
		if val, ok := rw.headers[name]; ok {
			rw.trailer[name] = val
		} else {
			delete(rw.trailer, name)
		}
	}
	for name, vals := range rw.headers {
		if name, found := strings.CutPrefix(name, trailerPrefix); found {
			name = textproto.CanonicalMIMEHeaderKey(textproto.TrimString(name))
			rw.trailer[name] = vals
		}
	}
	if len(rw.trailer) > 0 {
		rw.bw.trailer = rw.trailer
	}
}

// writeHeaderLockedOnce writes the final response header. If rw.wroteHeader is
// true, calling this method is a no-op. Sending informational status headers
// should be done using writeInfoHeaderLocked, rather than this method.
// Caller must hold rw.mu.
func (rw *responseWriter) writeHeaderLockedOnce() {
	if rw.wroteHeader {
		return
	}
	if !responseCanHaveBody(rw.statusCode) {
		rw.cannotHaveBody = true
	}
	// If there is any Trailer declared, save them so we know which trailers
	// have been pre-declared. Also, write back the extracted value, which is
	// canonicalized, for consistency.
	if _, ok := rw.snapHeaders["Trailer"]; ok {
		extractTrailerFromHeader(rw.snapHeaders, rw.trailer)
		rw.snapHeaders.Set("Trailer", strings.Join(slices.Sorted(maps.Keys(rw.trailer)), ", "))
	}

	rw.bb.inferHeader(rw.snapHeaders, rw.statusCode)
	encHeaders := rw.bw.enc.encode(func(f func(itype indexType, name, value string)) {
		f(mayIndex, ":status", strconv.Itoa(rw.statusCode))
		for name, values := range rw.snapHeaders {
			if !httpguts.ValidHeaderFieldName(name) {
				continue
			}
			for _, val := range values {
				if !httpguts.ValidHeaderFieldValue(val) {
					continue
				}
				// Issue #71374: Consider supporting never-indexed fields.
				f(mayIndex, name, val)
			}
		}
	})

	rw.st.writeVarint(int64(frameTypeHeaders))
	rw.st.writeVarint(int64(len(encHeaders)))
	rw.st.Write(encHeaders)
	rw.wroteHeader = true
}

// writeHeaderLocked writes informational status headers (i.e. status 1XX).
// If a non-informational status header has been written via
// writeHeaderLockedOnce, this method is a no-op.
// Caller must hold rw.mu.
func (rw *responseWriter) writeHeaderLocked(statusCode int) {
	if rw.wroteHeader {
		return
	}
	encHeaders := rw.bw.enc.encode(func(f func(itype indexType, name, value string)) {
		f(mayIndex, ":status", strconv.Itoa(statusCode))
		for name, values := range rw.headers {
			if name == "Content-Length" || name == "Transfer-Encoding" {
				continue
			}
			if !httpguts.ValidHeaderFieldName(name) {
				continue
			}
			for _, val := range values {
				if !httpguts.ValidHeaderFieldValue(val) {
					continue
				}
				// Issue #71374: Consider supporting never-indexed fields.
				f(mayIndex, name, val)
			}
		}
	})
	rw.st.writeVarint(int64(frameTypeHeaders))
	rw.st.writeVarint(int64(len(encHeaders)))
	rw.st.Write(encHeaders)
}

func isInfoStatus(status int) bool {
	return status >= 100 && status < 200
}

// checkWriteHeaderCode is a copy of net/http's checkWriteHeaderCode.
func checkWriteHeaderCode(code int) {
	// Issue 22880: require valid WriteHeader status codes.
	// For now we only enforce that it's three digits.
	// In the future we might block things over 599 (600 and above aren't defined
	// at http://httpwg.org/specs/rfc7231.html#status.codes).
	// But for now any three digits.
	//
	// We used to send "HTTP/1.1 000 0" on the wire in responses but there's
	// no equivalent bogus thing we can realistically send in HTTP/3,
	// so we'll consistently panic instead and help people find their bugs
	// early. (We can't return an error from WriteHeader even if we wanted to.)
	if code < 100 || code > 999 {
		panic(fmt.Sprintf("invalid WriteHeader code %v", code))
	}
}

func (rw *responseWriter) WriteHeader(statusCode int) {
	// TODO: handle sending informational status headers (e.g. 103).
	rw.mu.Lock()
	defer rw.mu.Unlock()
	if rw.statusCodeSet {
		return
	}
	checkWriteHeaderCode(statusCode)

	// Informational headers can be sent multiple times, and should be flushed
	// immediately.
	if isInfoStatus(statusCode) {
		rw.writeHeaderLocked(statusCode)
		rw.st.Flush()
		return
	}

	// Non-informational headers should only be set once, and should be
	// buffered.
	rw.statusCodeSet = true
	rw.statusCode = statusCode
	rw.snapHeaders = rw.headers.Clone()
	if n, err := strconv.Atoi(rw.Header().Get("Content-Length")); err == nil {
		rw.bodyLenLeft = n
	} else {
		rw.bodyLenLeft = -1 // Unknown.
	}
}

// trimWriteLocked trims a byte slice, b, such that the length of b will not
// exceed rw.bodyLenLeft. This method will update rw.bodyLenLeft when trimming
// b, and will also return whether b was trimmed or not.
// Caller must hold rw.mu.
func (rw *responseWriter) trimWriteLocked(b []byte) ([]byte, bool) {
	if rw.bodyLenLeft < 0 {
		return b, false
	}
	n := min(len(b), rw.bodyLenLeft)
	rw.bodyLenLeft -= n
	return b[:n], n != len(b)
}

func (rw *responseWriter) Write(b []byte) (n int, err error) {
	// Calling Write implicitly calls WriteHeader(200) if WriteHeader has not
	// been called before.
	rw.WriteHeader(http.StatusOK)
	rw.mu.Lock()
	defer rw.mu.Unlock()

	if rw.statusCode == http.StatusNotModified {
		return 0, http.ErrBodyNotAllowed
	}

	b, trimmed := rw.trimWriteLocked(b)
	if trimmed {
		defer func() {
			err = http.ErrContentLength
		}()
	}

	// If b fits entirely in our body buffer, save it to the buffer and return
	// early so we can coalesce small writes.
	// As a special case, we always want to save b to the buffer even when b is
	// big if we had yet to write our header, so we can infer headers like
	// "Content-Type" with as much information as possible.
	initialBLen := len(b)
	initialBufLen := len(rw.bb)
	if !rw.wroteHeader || len(b) <= cap(rw.bb)-len(rw.bb) {
		b = rw.bb.write(b)
		if len(b) == 0 {
			return initialBLen, nil
		}
	}

	// Reaching this point means that our buffer has been sufficiently filled.
	// Therefore, we now want to:
	// 1. Infer and write response headers based on our body buffer, if not
	// done yet.
	// 2. Write our body buffer and the rest of b (if any).
	// 3. Reset the current body buffer so it can be used again.
	rw.writeHeaderLockedOnce()
	if rw.cannotHaveBody {
		return initialBLen, nil
	}
	if n, err := rw.bw.write(rw.bb, b); err != nil {
		return max(0, n-initialBufLen), err
	}
	rw.bb.discard()
	return initialBLen, nil
}

func (rw *responseWriter) Flush() {
	// Calling Flush implicitly calls WriteHeader(200) if WriteHeader has not
	// been called before.
	rw.WriteHeader(http.StatusOK)
	rw.mu.Lock()
	defer rw.mu.Unlock()
	rw.writeHeaderLockedOnce()
	if !rw.cannotHaveBody {
		rw.bw.Write(rw.bb)
		rw.bb.discard()
	}
	rw.st.Flush()
}

func (rw *responseWriter) close() error {
	rw.Flush()
	rw.mu.Lock()
	defer rw.mu.Unlock()
	rw.prepareTrailerForWriteLocked()
	if err := rw.bw.Close(); err != nil {
		return err
	}
	return rw.st.stream.Close()
}

// defaultBodyBufferCap is the default number of bytes of body that we are
// willing to save in a buffer for the sake of inferring headers and coalescing
// small writes. 512 was chosen to be consistent with how much
// http.DetectContentType is willing to read.
const defaultBodyBufferCap = 512

// bodyBuffer is a buffer used to store body content of a response.
type bodyBuffer []byte

// write writes b to the buffer. It returns a new slice of b, which contains
// any remaining data that could not be written to the buffer, if any.
func (bb *bodyBuffer) write(b []byte) []byte {
	n := min(len(b), cap(*bb)-len(*bb))
	*bb = append(*bb, b[:n]...)
	return b[n:]
}

// discard resets the buffer so it can be used again.
func (bb *bodyBuffer) discard() {
	*bb = (*bb)[:0]
}

// inferHeader populates h with the header values that we can infer from our
// current buffer content, if not already explicitly set. This method should be
// called only once with as much body content as possible in the buffer, before
// a HEADERS frame is sent, and before discard has been called. Doing so
// properly is the responsibility of the caller.
func (bb *bodyBuffer) inferHeader(h http.Header, status int) {
	if _, ok := h["Date"]; !ok {
		h.Set("Date", time.Now().UTC().Format(http.TimeFormat))
	}
	// If the Content-Encoding is non-blank, we shouldn't
	// sniff the body. See Issue golang.org/issue/31753.
	_, hasCE := h["Content-Encoding"]
	_, hasCT := h["Content-Type"]
	if !hasCE && !hasCT && responseCanHaveBody(status) && len(*bb) > 0 {
		h.Set("Content-Type", http.DetectContentType(*bb))
	}
	// We can technically infer Content-Length too here, as long as the entire
	// response body fits within hi.buf and does not require flushing. However,
	// we have chosen not to do so for now as Content-Length is not very
	// important for HTTP/3, and such inconsistent behavior might be confusing.
}
