package http3

import (
	
	
	
	
	
	
	

	
	
	

	
)

type datagramStream interface {
	io.ReadWriteCloser
	CancelRead(quic.StreamErrorCode)
	CancelWrite(quic.StreamErrorCode)
	StreamID() quic.StreamID
	Context() context.Context
	SetDeadline(time.Time) error
	SetReadDeadline(time.Time) error
	SetWriteDeadline(time.Time) error
	SendDatagram(b []byte) error
	ReceiveDatagram(ctx context.Context) ([]byte, error)

	QUICStream() *quic.Stream
}

// A Stream is an HTTP/3 stream.
//
// When writing to and reading from the stream, data is framed in HTTP/3 DATA frames.
type Stream struct {
	datagramStream
	conn        *rawConn
	frameParser *frameParser

	buf []byte // used as a temporary buffer when writing the HTTP/3 frame headers

	bytesRemainingInFrame uint64

	qlogger qlogwriter.Recorder

	parseTrailer  func(io.Reader, *headersFrame) error
	parsedTrailer bool
}

func newStream(
	 datagramStream,
	 *rawConn,
	 *httptrace.ClientTrace,
	 func(io.Reader, *headersFrame) error,
	 qlogwriter.Recorder,
) *Stream {
	return &Stream{
		datagramStream: ,
		conn:           ,
		buf:            make([]byte, 16),
		qlogger:        ,
		parseTrailer:   ,
		frameParser: &frameParser{
			r:         &tracingReader{Reader: , trace: },
			streamID:  .StreamID(),
			closeConn: .CloseWithError,
		},
	}
}

func ( *Stream) ( []byte) (int, error) {
	if .bytesRemainingInFrame == 0 {
	:
		for {
			,  := .frameParser.ParseNext(.qlogger)
			if  != nil {
				return 0, 
			}
			switch f := .(type) {
			case *dataFrame:
				if .parsedTrailer {
					return 0, errors.New("DATA frame received after trailers")
				}
				.bytesRemainingInFrame = .Length
				break 
			case *headersFrame:
				if .parsedTrailer {
					maybeQlogInvalidHeadersFrame(.qlogger, .StreamID(), .Length)
					return 0, errors.New("additional HEADERS frame received after trailers")
				}
				.parsedTrailer = true
				return 0, .parseTrailer(.datagramStream, )
			default:
				.conn.CloseWithError(quic.ApplicationErrorCode(ErrCodeFrameUnexpected), "")
				// parseNextFrame skips over unknown frame types
				// Therefore, this condition is only entered when we parsed another known frame type.
				return 0, fmt.Errorf("peer sent an unexpected frame: %T", )
			}
		}
	}

	var  int
	var  error
	if .bytesRemainingInFrame < uint64(len()) {
		,  = .datagramStream.Read([:.bytesRemainingInFrame])
	} else {
		,  = .datagramStream.Read()
	}
	.bytesRemainingInFrame -= uint64()
	return , 
}

func ( *Stream) () bool {
	return .bytesRemainingInFrame > 0
}

func ( *Stream) ( []byte) (int, error) {
	.buf = .buf[:0]
	.buf = (&dataFrame{Length: uint64(len())}).Append(.buf)
	if .qlogger != nil {
		.qlogger.RecordEvent(qlog.FrameCreated{
			StreamID: .StreamID(),
			Raw: qlog.RawInfo{
				Length:        len(.buf) + len(),
				PayloadLength: len(),
			},
			Frame: qlog.Frame{Frame: qlog.DataFrame{}},
		})
	}
	if ,  := .datagramStream.Write(.buf);  != nil {
		return 0, 
	}
	return .datagramStream.Write()
}

func ( *Stream) ( []byte) (int, error) {
	return .datagramStream.Write()
}

func ( *Stream) () quic.StreamID {
	return .datagramStream.StreamID()
}

func ( *Stream) ( []byte) error {
	// TODO: reject if datagrams are not negotiated (yet)
	return .datagramStream.SendDatagram()
}

func ( *Stream) ( context.Context) ([]byte, error) {
	// TODO: reject if datagrams are not negotiated (yet)
	return .datagramStream.ReceiveDatagram()
}

// A RequestStream is a low-level abstraction representing an HTTP/3 request stream.
// It decouples sending of the HTTP request from reading the HTTP response, allowing
// the application to optimistically use the stream (and, for example, send datagrams)
// before receiving the response.
//
// This is only needed for advanced use case, e.g. WebTransport and the various
// MASQUE proxying protocols.
type RequestStream struct {
	str *Stream

	responseBody io.ReadCloser // set by ReadResponse

	decoder            *qpack.Decoder
	requestWriter      *requestWriter
	maxHeaderBytes     int
	reqDone            chan<- struct{}
	disableCompression bool
	response           *http.Response

	sentRequest   bool
	requestedGzip bool
	isConnect     bool
}

func newRequestStream(
	 *Stream,
	 *requestWriter,
	 chan<- struct{},
	 *qpack.Decoder,
	 bool,
	 int,
	 *http.Response,
) *RequestStream {
	return &RequestStream{
		str:                ,
		requestWriter:      ,
		reqDone:            ,
		decoder:            ,
		disableCompression: ,
		maxHeaderBytes:     ,
		response:           ,
	}
}

// Read reads data from the underlying stream.
//
// It can only be used after the request has been sent (using SendRequestHeader)
// and the response has been consumed (using ReadResponse).
func ( *RequestStream) ( []byte) (int, error) {
	if .responseBody == nil {
		return 0, errors.New("http3: invalid use of RequestStream.Read before ReadResponse")
	}
	return .responseBody.Read()
}

// StreamID returns the QUIC stream ID of the underlying QUIC stream.
func ( *RequestStream) () quic.StreamID {
	return .str.StreamID()
}

// Write writes data to the stream.
//
// It can only be used after the request has been sent (using SendRequestHeader).
func ( *RequestStream) ( []byte) (int, error) {
	if !.sentRequest {
		return 0, errors.New("http3: invalid use of RequestStream.Write before SendRequestHeader")
	}
	return .str.Write()
}

// Close closes the send-direction of the stream.
// It does not close the receive-direction of the stream.
func ( *RequestStream) () error {
	return .str.Close()
}

// CancelRead aborts receiving on this stream.
// See [quic.Stream.CancelRead] for more details.
func ( *RequestStream) ( quic.StreamErrorCode) {
	.str.CancelRead()
}

// CancelWrite aborts sending on this stream.
// See [quic.Stream.CancelWrite] for more details.
func ( *RequestStream) ( quic.StreamErrorCode) {
	.str.CancelWrite()
}

// Context returns a context derived from the underlying QUIC stream's context.
// See [quic.Stream.Context] for more details.
func ( *RequestStream) () context.Context {
	return .str.Context()
}

// SetReadDeadline sets the deadline for Read calls.
func ( *RequestStream) ( time.Time) error {
	return .str.SetReadDeadline()
}

// SetWriteDeadline sets the deadline for Write calls.
func ( *RequestStream) ( time.Time) error {
	return .str.SetWriteDeadline()
}

// SetDeadline sets the read and write deadlines associated with the stream.
// It is equivalent to calling both SetReadDeadline and SetWriteDeadline.
func ( *RequestStream) ( time.Time) error {
	return .str.SetDeadline()
}

// SendDatagrams send a new HTTP Datagram (RFC 9297).
//
// It is only possible to send datagrams if the server enabled support for this extension.
// It is recommended (though not required) to send the request before calling this method,
// as the server might drop datagrams which it can't associate with an existing request.
func ( *RequestStream) ( []byte) error {
	return .str.SendDatagram()
}

// ReceiveDatagram receives HTTP Datagrams (RFC 9297).
//
// It is only possible if support for HTTP Datagrams was enabled, using the EnableDatagram
// option on the [Transport].
func ( *RequestStream) ( context.Context) ([]byte, error) {
	return .str.ReceiveDatagram()
}

// SendRequestHeader sends the HTTP request.
//
// It can only used for requests that don't have a request body.
// It is invalid to call it more than once.
// It is invalid to call it after Write has been called.
func ( *RequestStream) ( *http.Request) error {
	if .Body != nil && .Body != http.NoBody {
		return errors.New("http3: invalid use of RequestStream.SendRequestHeader with a request that has a request body")
	}
	return .sendRequestHeader()
}

func ( *RequestStream) ( *http.Request) error {
	if .sentRequest {
		return errors.New("http3: invalid duplicate use of RequestStream.SendRequestHeader")
	}
	if !.disableCompression && .Method != http.MethodHead &&
		.Header.Get("Accept-Encoding") == "" && .Header.Get("Range") == "" {
		.requestedGzip = true
	}
	.isConnect = .Method == http.MethodConnect
	.sentRequest = true
	return .requestWriter.WriteRequestHeader(.str.datagramStream, , .requestedGzip, .str.StreamID(), .str.qlogger)
}

// sendRequestTrailer sends request trailers to the stream.
// It should be called after the request body has been fully written.
func ( *RequestStream) ( *http.Request) error {
	return .requestWriter.WriteRequestTrailer(.str.datagramStream, , .str.StreamID(), .str.qlogger)
}

// ReadResponse reads the HTTP response from the stream.
//
// It must be called after sending the request (using SendRequestHeader).
// It is invalid to call it more than once.
// It doesn't set Response.Request and Response.TLS.
// It is invalid to call it after Read has been called.
func ( *RequestStream) () (*http.Response, error) {
	if !.sentRequest {
		return nil, errors.New("http3: invalid use of RequestStream.ReadResponse before SendRequestHeader")
	}
	,  := .str.frameParser.ParseNext(.str.qlogger)
	if  != nil {
		.str.CancelRead(quic.StreamErrorCode(ErrCodeFrameError))
		.str.CancelWrite(quic.StreamErrorCode(ErrCodeFrameError))
		return nil, fmt.Errorf("http3: parsing frame failed: %w", )
	}
	,  := .(*headersFrame)
	if ! {
		.str.conn.CloseWithError(quic.ApplicationErrorCode(ErrCodeFrameUnexpected), "expected first frame to be a HEADERS frame")
		return nil, errors.New("http3: expected first frame to be a HEADERS frame")
	}
	if .Length > uint64(.maxHeaderBytes) {
		maybeQlogInvalidHeadersFrame(.str.qlogger, .str.StreamID(), .Length)
		.str.CancelRead(quic.StreamErrorCode(ErrCodeFrameError))
		.str.CancelWrite(quic.StreamErrorCode(ErrCodeFrameError))
		return nil, fmt.Errorf("http3: HEADERS frame too large: %d bytes (max: %d)", .Length, .maxHeaderBytes)
	}
	 := make([]byte, .Length)
	if ,  := io.ReadFull(.str.datagramStream, );  != nil {
		maybeQlogInvalidHeadersFrame(.str.qlogger, .str.StreamID(), .Length)
		.str.CancelRead(quic.StreamErrorCode(ErrCodeRequestIncomplete))
		.str.CancelWrite(quic.StreamErrorCode(ErrCodeRequestIncomplete))
		return nil, fmt.Errorf("http3: failed to read response headers: %w", )
	}
	 := .decoder.Decode()
	var  []qpack.HeaderField
	if .str.qlogger != nil {
		 = make([]qpack.HeaderField, 0, 16)
	}
	 := .response
	 = updateResponseFromHeaders(, , .maxHeaderBytes, &)
	if .str.qlogger != nil {
		qlogParsedHeadersFrame(.str.qlogger, .str.StreamID(), , )
	}
	if  != nil {
		 := ErrCodeMessageError
		var  *qpackError
		if errors.As(, &) {
			 = ErrCodeQPACKDecompressionFailed
		}
		.str.CancelRead(quic.StreamErrorCode())
		.str.CancelWrite(quic.StreamErrorCode())
		return nil, fmt.Errorf("http3: invalid response: %w", )
	}

	// Check that the server doesn't send more data in DATA frames than indicated by the Content-Length header (if set).
	// See section 4.1.2 of RFC 9114.
	 := newResponseBody(.str, .ContentLength, .reqDone)

	// Rules for when to set Content-Length are defined in https://tools.ietf.org/html/rfc7230#section-3.3.2.
	 := .StatusCode >= 100 && .StatusCode < 200
	 := .StatusCode == http.StatusNoContent
	 := .isConnect && .StatusCode >= 200 && .StatusCode < 300
	if ( ||  || ) && .ContentLength == -1 {
		.ContentLength = 0
	}
	if .requestedGzip && .Header.Get("Content-Encoding") == "gzip" {
		.Header.Del("Content-Encoding")
		.Header.Del("Content-Length")
		.ContentLength = -1
		.responseBody = newGzipReader()
		.Uncompressed = true
	} else {
		.responseBody = 
	}
	.Body = .responseBody
	return , nil
}

type tracingReader struct {
	io.Reader
	readFirst bool
	trace     *httptrace.ClientTrace
}

func ( *tracingReader) ( []byte) (int, error) {
	,  := .Reader.Read()
	if  > 0 && !.readFirst {
		traceGotFirstResponseByte(.trace)
		.readFirst = true
	}
	return , 
}