package http3

import (
	
	
	
	
	
	

	
	

	
)

// A Stream is an HTTP/3 request stream.
// When writing to and reading from the stream, data is framed in HTTP/3 DATA frames.
type Stream interface {
	quic.Stream

	SendDatagram([]byte) error
	ReceiveDatagram(context.Context) ([]byte, error)
}

// A RequestStream is an HTTP/3 request stream.
// When writing to and reading from the stream, data is framed in HTTP/3 DATA frames.
type RequestStream interface {
	Stream

	// SendRequestHeader sends the HTTP request.
	// It is invalid to call it more than once.
	// It is invalid to call it after Write has been called.
	SendRequestHeader(req *http.Request) error

	// ReadResponse reads the HTTP response from the stream.
	// 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.
	ReadResponse() (*http.Response, error)
}

type stream struct {
	quic.Stream
	conn *connection

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

	bytesRemainingInFrame uint64

	datagrams *datagrammer

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

var _ Stream = &stream{}

func newStream( quic.Stream,  *connection,  *datagrammer,  func(io.Reader, uint64) error) *stream {
	return &stream{
		Stream:       ,
		conn:         ,
		buf:          make([]byte, 16),
		datagrams:    ,
		parseTrailer: ,
	}
}

func ( *stream) ( []byte) (int, error) {
	 := &frameParser{
		r:    .Stream,
		conn: .conn,
	}
	if .bytesRemainingInFrame == 0 {
	:
		for {
			,  := .ParseNext()
			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 .conn.perspective == protocol.PerspectiveServer {
					continue
				}
				if .parsedTrailer {
					return 0, errors.New("additional HEADERS frame received after trailers")
				}
				.parsedTrailer = true
				return 0, .parseTrailer(.Stream, .Length)
			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()) {
		,  = .Stream.Read([:.bytesRemainingInFrame])
	} else {
		,  = .Stream.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 ,  := .Stream.Write(.buf);  != nil {
		return 0, 
	}
	return .Stream.Write()
}

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

func ( *stream) () protocol.StreamID {
	return .Stream.StreamID()
}

// The stream conforms to the quic.Stream interface, but instead of writing to and reading directly
// from the QUIC stream, it writes to and reads from the HTTP stream.
type requestStream struct {
	*stream

	responseBody io.ReadCloser // set by ReadResponse

	decoder            *qpack.Decoder
	requestWriter      *requestWriter
	maxHeaderBytes     uint64
	reqDone            chan<- struct{}
	disableCompression bool
	response           *http.Response
	trace              *httptrace.ClientTrace

	sentRequest   bool
	requestedGzip bool
	isConnect     bool
	firstByte     bool
}

var _ RequestStream = &requestStream{}

func newRequestStream(
	 *stream,
	 *requestWriter,
	 chan<- struct{},
	 *qpack.Decoder,
	 bool,
	 uint64,
	 *http.Response,
	 *httptrace.ClientTrace,
) *requestStream {
	return &requestStream{
		stream:             ,
		requestWriter:      ,
		reqDone:            ,
		decoder:            ,
		disableCompression: ,
		maxHeaderBytes:     ,
		response:           ,
		trace:              ,
	}
}

func ( *requestStream) ( []byte) (int, error) {
	if .responseBody == nil {
		return 0, errors.New("http3: invalid use of RequestStream.Read: need to call ReadResponse first")
	}
	return .responseBody.Read()
}

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

func ( *requestStream) () (*http.Response, error) {
	 := &frameParser{
		conn: .conn,
		r: &tracingReader{
			Reader: .Stream,
			first:  &.firstByte,
			trace:  .trace,
		},
	}
	,  := .ParseNext()
	if  != nil {
		.CancelRead(quic.StreamErrorCode(ErrCodeFrameError))
		.CancelWrite(quic.StreamErrorCode(ErrCodeFrameError))
		return nil, fmt.Errorf("http3: parsing frame failed: %w", )
	}
	,  := .(*headersFrame)
	if ! {
		.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 > .maxHeaderBytes {
		.CancelRead(quic.StreamErrorCode(ErrCodeFrameError))
		.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(.Stream, );  != nil {
		.CancelRead(quic.StreamErrorCode(ErrCodeRequestIncomplete))
		.CancelWrite(quic.StreamErrorCode(ErrCodeRequestIncomplete))
		return nil, fmt.Errorf("http3: failed to read response headers: %w", )
	}
	,  := .decoder.DecodeFull()
	if  != nil {
		// TODO: use the right error code
		.conn.CloseWithError(quic.ApplicationErrorCode(ErrCodeGeneralProtocolError), "")
		return nil, fmt.Errorf("http3: failed to decode response headers: %w", )
	}
	 := .response
	if  := updateResponseFromHeaders(, );  != nil {
		.CancelRead(quic.StreamErrorCode(ErrCodeMessageError))
		.CancelWrite(quic.StreamErrorCode(ErrCodeMessageError))
		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(.stream, .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
}

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

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

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

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