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