github.com/sagernet/quic-go@v0.43.1-beta.1/http3_ech/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/ech" 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 }