Source file src/net/http/internal/http3/body.go

     1  // Copyright 2025 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package http3
     6  
     7  import (
     8  	"errors"
     9  	"fmt"
    10  	"io"
    11  	"net"
    12  	"net/http"
    13  	"net/textproto"
    14  	"strings"
    15  	"sync"
    16  
    17  	"golang.org/x/net/http/httpguts"
    18  )
    19  
    20  // extractTrailerFromHeader extracts the "Trailer" header values from a header
    21  // map, and populates a trailer map with those values as keys. The extracted
    22  // header values will be canonicalized.
    23  func extractTrailerFromHeader(header, trailer http.Header) {
    24  	for _, names := range header["Trailer"] {
    25  		names = textproto.TrimString(names)
    26  		for name := range strings.SplitSeq(names, ",") {
    27  			name = textproto.CanonicalMIMEHeaderKey(textproto.TrimString(name))
    28  			if !httpguts.ValidTrailerHeader(name) {
    29  				continue
    30  			}
    31  			trailer[name] = nil
    32  		}
    33  	}
    34  }
    35  
    36  // A bodyWriter writes a request or response body to a stream
    37  // as a series of DATA frames.
    38  type bodyWriter struct {
    39  	st      *stream
    40  	remain  int64         // -1 when content-length is not known
    41  	flush   bool          // flush the stream after every write
    42  	name    string        // "request" or "response"
    43  	trailer http.Header   // trailer headers that will be written once bodyWriter is closed.
    44  	enc     *qpackEncoder // QPACK encoder used by the connection.
    45  }
    46  
    47  func (w *bodyWriter) write(ps ...[]byte) (n int, err error) {
    48  	var size int64
    49  	for _, p := range ps {
    50  		size += int64(len(p))
    51  	}
    52  	// If write is called with empty byte slices, just return instead of
    53  	// sending out a DATA frame containing nothing.
    54  	if size == 0 {
    55  		return 0, nil
    56  	}
    57  	if w.remain >= 0 && size > w.remain {
    58  		return 0, &streamError{
    59  			code:    errH3InternalError,
    60  			message: w.name + " body longer than specified content length",
    61  		}
    62  	}
    63  	w.st.writeVarint(int64(frameTypeData))
    64  	w.st.writeVarint(size)
    65  	for _, p := range ps {
    66  		var n2 int
    67  		n2, err = w.st.Write(p)
    68  		n += n2
    69  		if w.remain >= 0 {
    70  			w.remain -= int64(n2)
    71  		}
    72  		if err != nil {
    73  			break
    74  		}
    75  	}
    76  	if w.flush && err == nil {
    77  		err = w.st.Flush()
    78  	}
    79  	if err != nil {
    80  		err = fmt.Errorf("writing %v body: %w", w.name, err)
    81  	}
    82  	return n, err
    83  }
    84  
    85  func (w *bodyWriter) Write(p []byte) (n int, err error) {
    86  	return w.write(p)
    87  }
    88  
    89  func (w *bodyWriter) Close() error {
    90  	if w.remain > 0 {
    91  		return errors.New(w.name + " body shorter than specified content length")
    92  	}
    93  	if len(w.trailer) > 0 {
    94  		encTrailer := w.enc.encode(func(f func(itype indexType, name, value string)) {
    95  			for name, values := range w.trailer {
    96  				if !httpguts.ValidHeaderFieldName(name) {
    97  					continue
    98  				}
    99  				for _, val := range values {
   100  					if !httpguts.ValidHeaderFieldValue(val) {
   101  						continue
   102  					}
   103  					f(mayIndex, name, val)
   104  				}
   105  			}
   106  		})
   107  		w.st.writeVarint(int64(frameTypeHeaders))
   108  		w.st.writeVarint(int64(len(encTrailer)))
   109  		w.st.Write(encTrailer)
   110  	}
   111  	if w.st != nil {
   112  		w.st.CloseWrite()
   113  	}
   114  	return nil
   115  }
   116  
   117  // A bodyReader reads a request or response body from a stream.
   118  type bodyReader struct {
   119  	st *stream
   120  
   121  	mu     sync.Mutex
   122  	remain int64
   123  	err    error
   124  	// A map where the key represents the trailer header names we expect. If
   125  	// there is a HEADERS frame after reading DATA frames to EOF, the value of
   126  	// the headers will be written here. Keys in the map are assumed to be
   127  	// canonicalized.
   128  	// If filterTrailer is true, headers that are not already in the map will
   129  	// be ignored; otherwise, all headers will be added to the map.
   130  	trailer             http.Header
   131  	filterTrailer       bool
   132  	maxHeaderBytes      int64
   133  	maxHeaderValueCount int64
   134  }
   135  
   136  func (r *bodyReader) Read(p []byte) (n int, err error) {
   137  	// The HTTP/1 and HTTP/2 implementations both permit concurrent reads from a body,
   138  	// in the sense that the race detector won't complain.
   139  	// Use a mutex here to provide the same behavior.
   140  	r.mu.Lock()
   141  	defer r.mu.Unlock()
   142  	if r.err != nil {
   143  		return 0, r.err
   144  	}
   145  	defer func() {
   146  		if err != nil {
   147  			r.err = err
   148  		}
   149  	}()
   150  	if r.st.lim == 0 {
   151  		// We've finished reading the previous DATA frame, so end it.
   152  		if err := r.st.endFrame(); err != nil {
   153  			return 0, err
   154  		}
   155  	}
   156  	// Read the next DATA frame header,
   157  	// if we aren't already in the middle of one.
   158  	for r.st.lim < 0 {
   159  		ftype, err := r.st.readFrameHeader()
   160  		if err == io.EOF && r.remain > 0 {
   161  			return 0, &streamError{
   162  				code:    errH3MessageError,
   163  				message: "body shorter than content-length",
   164  			}
   165  		}
   166  		if err != nil {
   167  			return 0, err
   168  		}
   169  		switch ftype {
   170  		case frameTypeData:
   171  			if r.remain >= 0 && r.st.lim > r.remain {
   172  				return 0, &streamError{
   173  					code:    errH3MessageError,
   174  					message: "body longer than content-length",
   175  				}
   176  			}
   177  			// Fall out of the loop and process the frame body below.
   178  		case frameTypeHeaders:
   179  			// This HEADERS frame contains the message trailers.
   180  			if r.remain > 0 {
   181  				return 0, &streamError{
   182  					code:    errH3MessageError,
   183  					message: "body shorter than content-length",
   184  				}
   185  			}
   186  			// The QPACK decoder will happily allocate an unbounded buffer, so
   187  			// check the stream read limit prior to decoding.
   188  			if r.maxHeaderBytes > 0 && r.st.lim > r.maxHeaderBytes {
   189  				return 0, errHeadersTooLarge
   190  			}
   191  			var dec qpackDecoder
   192  			var totalSize, valueCount int64
   193  			if err := dec.decode(r.st, func(_ indexType, name, value string) error {
   194  				totalSize += int64(len(name)) + int64(len(value)) + headerFieldOverhead
   195  				valueCount++
   196  				if (r.maxHeaderBytes > 0 && totalSize > r.maxHeaderBytes) ||
   197  					(r.maxHeaderValueCount > 0 && valueCount > r.maxHeaderValueCount) {
   198  					return errHeadersTooLarge
   199  				}
   200  				if r.trailer == nil {
   201  					return nil
   202  				}
   203  				if !validWireHeaderFieldName(name) || !httpguts.ValidHeaderFieldValue(value) {
   204  					return nil
   205  				}
   206  				name = textproto.CanonicalMIMEHeaderKey(textproto.TrimString(name))
   207  				if !r.filterTrailer {
   208  					r.trailer.Add(name, value)
   209  				} else if _, ok := r.trailer[name]; ok {
   210  					r.trailer.Add(name, value)
   211  				}
   212  				return nil
   213  			}); err != nil {
   214  				return 0, err
   215  			}
   216  			if err := r.st.discardFrame(); err != nil {
   217  				return 0, err
   218  			}
   219  			return 0, io.EOF
   220  		default:
   221  			if err := r.st.discardUnknownFrame(ftype); err != nil {
   222  				return 0, err
   223  			}
   224  		}
   225  	}
   226  	// We are now reading the content of a DATA frame.
   227  	// Fill the read buffer or read to the end of the frame,
   228  	// whichever comes first.
   229  	if int64(len(p)) > r.st.lim {
   230  		p = p[:r.st.lim]
   231  	}
   232  	n, err = r.st.Read(p)
   233  	if r.remain > 0 {
   234  		r.remain -= int64(n)
   235  	}
   236  	return n, err
   237  }
   238  
   239  func (r *bodyReader) Close() error {
   240  	// Unlike the HTTP/1 and HTTP/2 body readers (at the time of this comment being written),
   241  	// calling Close concurrently with Read will interrupt the read.
   242  	r.st.CloseRead(uint64(errH3NoError))
   243  	// Make sure that any data that has already been written to bodyReader
   244  	// cannot be read after it has been closed.
   245  	r.mu.Lock()
   246  	defer r.mu.Unlock()
   247  	r.err = net.ErrClosed
   248  	r.remain = 0
   249  	return nil
   250  }
   251  

View as plain text