github.com/sagernet/quic-go@v0.43.1-beta.1/http3/http_stream.go (about)

     1  package http3
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"io"
     8  	"net/http"
     9  	"strconv"
    10  
    11  	"github.com/quic-go/qpack"
    12  	"github.com/sagernet/quic-go"
    13  	"github.com/sagernet/quic-go/internal/protocol"
    14  )
    15  
    16  // A Stream is an HTTP/3 request stream.
    17  // When writing to and reading from the stream, data is framed in HTTP/3 DATA frames.
    18  type Stream interface {
    19  	quic.Stream
    20  
    21  	SendDatagram([]byte) error
    22  	ReceiveDatagram(context.Context) ([]byte, error)
    23  }
    24  
    25  // A RequestStream is an HTTP/3 request stream.
    26  // When writing to and reading from the stream, data is framed in HTTP/3 DATA frames.
    27  type RequestStream interface {
    28  	Stream
    29  
    30  	// SendRequestHeader sends the HTTP request.
    31  	// It is invalid to call it more than once.
    32  	// It is invalid to call it after Write has been called.
    33  	SendRequestHeader(req *http.Request) error
    34  
    35  	// ReadResponse reads the HTTP response from the stream.
    36  	// It is invalid to call it more than once.
    37  	// It doesn't set Response.Request and Response.TLS.
    38  	// It is invalid to call it after Read has been called.
    39  	ReadResponse() (*http.Response, error)
    40  }
    41  
    42  type stream struct {
    43  	quic.Stream
    44  	conn *connection
    45  
    46  	buf []byte // used as a temporary buffer when writing the HTTP/3 frame headers
    47  
    48  	bytesRemainingInFrame uint64
    49  
    50  	datagrams *datagrammer
    51  }
    52  
    53  var _ Stream = &stream{}
    54  
    55  func newStream(str quic.Stream, conn *connection, datagrams *datagrammer) *stream {
    56  	return &stream{
    57  		Stream:    str,
    58  		conn:      conn,
    59  		buf:       make([]byte, 16),
    60  		datagrams: datagrams,
    61  	}
    62  }
    63  
    64  func (s *stream) Read(b []byte) (int, error) {
    65  	if s.bytesRemainingInFrame == 0 {
    66  	parseLoop:
    67  		for {
    68  			frame, err := parseNextFrame(s.Stream, nil)
    69  			if err != nil {
    70  				return 0, err
    71  			}
    72  			switch f := frame.(type) {
    73  			case *headersFrame:
    74  				// skip HEADERS frames
    75  				continue
    76  			case *dataFrame:
    77  				s.bytesRemainingInFrame = f.Length
    78  				break parseLoop
    79  			default:
    80  				s.conn.CloseWithError(quic.ApplicationErrorCode(ErrCodeFrameUnexpected), "")
    81  				// parseNextFrame skips over unknown frame types
    82  				// Therefore, this condition is only entered when we parsed another known frame type.
    83  				return 0, fmt.Errorf("peer sent an unexpected frame: %T", f)
    84  			}
    85  		}
    86  	}
    87  
    88  	var n int
    89  	var err error
    90  	if s.bytesRemainingInFrame < uint64(len(b)) {
    91  		n, err = s.Stream.Read(b[:s.bytesRemainingInFrame])
    92  	} else {
    93  		n, err = s.Stream.Read(b)
    94  	}
    95  	s.bytesRemainingInFrame -= uint64(n)
    96  	return n, err
    97  }
    98  
    99  func (s *stream) hasMoreData() bool {
   100  	return s.bytesRemainingInFrame > 0
   101  }
   102  
   103  func (s *stream) Write(b []byte) (int, error) {
   104  	s.buf = s.buf[:0]
   105  	s.buf = (&dataFrame{Length: uint64(len(b))}).Append(s.buf)
   106  	if _, err := s.Stream.Write(s.buf); err != nil {
   107  		return 0, err
   108  	}
   109  	return s.Stream.Write(b)
   110  }
   111  
   112  func (s *stream) writeUnframed(b []byte) (int, error) {
   113  	return s.Stream.Write(b)
   114  }
   115  
   116  func (s *stream) StreamID() protocol.StreamID {
   117  	return s.Stream.StreamID()
   118  }
   119  
   120  // The stream conforms to the quic.Stream interface, but instead of writing to and reading directly
   121  // from the QUIC stream, it writes to and reads from the HTTP stream.
   122  type requestStream struct {
   123  	*stream
   124  
   125  	responseBody io.ReadCloser // set by ReadResponse
   126  
   127  	decoder            *qpack.Decoder
   128  	requestWriter      *requestWriter
   129  	maxHeaderBytes     uint64
   130  	reqDone            chan<- struct{}
   131  	disableCompression bool
   132  
   133  	sentRequest   bool
   134  	requestedGzip bool
   135  	isConnect     bool
   136  }
   137  
   138  var _ RequestStream = &requestStream{}
   139  
   140  func newRequestStream(
   141  	str *stream,
   142  	requestWriter *requestWriter,
   143  	reqDone chan<- struct{},
   144  	decoder *qpack.Decoder,
   145  	disableCompression bool,
   146  	maxHeaderBytes uint64,
   147  ) *requestStream {
   148  	return &requestStream{
   149  		stream:             str,
   150  		requestWriter:      requestWriter,
   151  		reqDone:            reqDone,
   152  		decoder:            decoder,
   153  		disableCompression: disableCompression,
   154  		maxHeaderBytes:     maxHeaderBytes,
   155  	}
   156  }
   157  
   158  func (s *requestStream) Read(b []byte) (int, error) {
   159  	if s.responseBody == nil {
   160  		return 0, errors.New("http3: invalid use of RequestStream.Read: need to call ReadResponse first")
   161  	}
   162  	return s.responseBody.Read(b)
   163  }
   164  
   165  func (s *requestStream) SendRequestHeader(req *http.Request) error {
   166  	if s.sentRequest {
   167  		return errors.New("http3: invalid duplicate use of SendRequestHeader")
   168  	}
   169  	if !s.disableCompression && req.Method != http.MethodHead &&
   170  		req.Header.Get("Accept-Encoding") == "" && req.Header.Get("Range") == "" {
   171  		s.requestedGzip = true
   172  	}
   173  	s.isConnect = req.Method == http.MethodConnect
   174  	s.sentRequest = true
   175  	return s.requestWriter.WriteRequestHeader(s.Stream, req, s.requestedGzip)
   176  }
   177  
   178  func (s *requestStream) ReadResponse() (*http.Response, error) {
   179  	frame, err := parseNextFrame(s.Stream, nil)
   180  	if err != nil {
   181  		s.Stream.CancelRead(quic.StreamErrorCode(ErrCodeFrameError))
   182  		s.Stream.CancelWrite(quic.StreamErrorCode(ErrCodeFrameError))
   183  		return nil, fmt.Errorf("http3: parsing frame failed: %w", err)
   184  	}
   185  	hf, ok := frame.(*headersFrame)
   186  	if !ok {
   187  		s.conn.CloseWithError(quic.ApplicationErrorCode(ErrCodeFrameUnexpected), "expected first frame to be a HEADERS frame")
   188  		return nil, errors.New("http3: expected first frame to be a HEADERS frame")
   189  	}
   190  	if hf.Length > s.maxHeaderBytes {
   191  		s.Stream.CancelRead(quic.StreamErrorCode(ErrCodeFrameError))
   192  		s.Stream.CancelWrite(quic.StreamErrorCode(ErrCodeFrameError))
   193  		return nil, fmt.Errorf("http3: HEADERS frame too large: %d bytes (max: %d)", hf.Length, s.maxHeaderBytes)
   194  	}
   195  	headerBlock := make([]byte, hf.Length)
   196  	if _, err := io.ReadFull(s.Stream, headerBlock); err != nil {
   197  		s.Stream.CancelRead(quic.StreamErrorCode(ErrCodeRequestIncomplete))
   198  		s.Stream.CancelWrite(quic.StreamErrorCode(ErrCodeRequestIncomplete))
   199  		return nil, fmt.Errorf("http3: failed to read response headers: %w", err)
   200  	}
   201  	hfs, err := s.decoder.DecodeFull(headerBlock)
   202  	if err != nil {
   203  		// TODO: use the right error code
   204  		s.conn.CloseWithError(quic.ApplicationErrorCode(ErrCodeGeneralProtocolError), "")
   205  		return nil, fmt.Errorf("http3: failed to decode response headers: %w", err)
   206  	}
   207  
   208  	res, err := responseFromHeaders(hfs)
   209  	if err != nil {
   210  		s.Stream.CancelRead(quic.StreamErrorCode(ErrCodeMessageError))
   211  		s.Stream.CancelWrite(quic.StreamErrorCode(ErrCodeMessageError))
   212  		return nil, fmt.Errorf("http3: invalid response: %w", err)
   213  	}
   214  
   215  	// Check that the server doesn't send more data in DATA frames than indicated by the Content-Length header (if set).
   216  	// See section 4.1.2 of RFC 9114.
   217  	contentLength := int64(-1)
   218  	if _, ok := res.Header["Content-Length"]; ok && res.ContentLength >= 0 {
   219  		contentLength = res.ContentLength
   220  	}
   221  	respBody := newResponseBody(s.stream, contentLength, s.reqDone)
   222  
   223  	// Rules for when to set Content-Length are defined in https://tools.ietf.org/html/rfc7230#section-3.3.2.
   224  	_, hasTransferEncoding := res.Header["Transfer-Encoding"]
   225  	isInformational := res.StatusCode >= 100 && res.StatusCode < 200
   226  	isNoContent := res.StatusCode == http.StatusNoContent
   227  	isSuccessfulConnect := s.isConnect && res.StatusCode >= 200 && res.StatusCode < 300
   228  	if !hasTransferEncoding && !isInformational && !isNoContent && !isSuccessfulConnect {
   229  		res.ContentLength = -1
   230  		if clens, ok := res.Header["Content-Length"]; ok && len(clens) == 1 {
   231  			if clen64, err := strconv.ParseInt(clens[0], 10, 64); err == nil {
   232  				res.ContentLength = clen64
   233  			}
   234  		}
   235  	}
   236  
   237  	if s.requestedGzip && res.Header.Get("Content-Encoding") == "gzip" {
   238  		res.Header.Del("Content-Encoding")
   239  		res.Header.Del("Content-Length")
   240  		res.ContentLength = -1
   241  		s.responseBody = newGzipReader(respBody)
   242  		res.Uncompressed = true
   243  	} else {
   244  		s.responseBody = respBody
   245  	}
   246  	res.Body = s.responseBody
   247  	return res, nil
   248  }
   249  
   250  func (s *stream) SendDatagram(b []byte) error {
   251  	// TODO: reject if datagrams are not negotiated (yet)
   252  	return s.conn.sendDatagram(s.Stream.StreamID(), b)
   253  }
   254  
   255  func (s *stream) ReceiveDatagram(ctx context.Context) ([]byte, error) {
   256  	// TODO: reject if datagrams are not negotiated (yet)
   257  	return s.datagrams.Receive(ctx)
   258  }