package http3

import (
	
	
	
	
	
	
	
	

	
	
	
)

// RawServerConn is an HTTP/3 server connection.
// It can be used for advanced use cases where the application wants to manage the QUIC connection lifecycle.
type RawServerConn struct {
	rawConn rawConn

	idleTimeout time.Duration
	idleTimer   *time.Timer

	serverContext  context.Context
	requestHandler http.Handler
	maxHeaderBytes int

	decoder *qpack.Decoder

	qlogger qlogwriter.Recorder
	logger  *slog.Logger
}

func newRawServerConn(
	 *quic.Conn,
	 bool,
	 time.Duration,
	 qlogwriter.Recorder,
	 *slog.Logger,
	 context.Context,
	 http.Handler,
	 int,
) *RawServerConn {
	 := &RawServerConn{
		idleTimeout:    ,
		serverContext:  ,
		requestHandler: ,
		maxHeaderBytes: ,
		decoder:        qpack.NewDecoder(),
		qlogger:        ,
		logger:         ,
	}
	.rawConn = *newRawConn(, , .onStreamsEmpty, nil, , )
	if  > 0 {
		.idleTimer = time.AfterFunc(, .onIdleTimer)
	}
	return 
}

func ( *RawServerConn) () {
	if .idleTimeout > 0 {
		.idleTimer.Reset(.idleTimeout)
	}
}

func ( *RawServerConn) () {
	.CloseWithError(quic.ApplicationErrorCode(ErrCodeNoError), "idle timeout")
}

// CloseWithError closes the connection with the given error code and message.
func ( *RawServerConn) ( quic.ApplicationErrorCode,  string) error {
	if .idleTimer != nil {
		.idleTimer.Stop()
	}
	return .rawConn.CloseWithError(, )
}

// HandleRequestStream handles an HTTP/3 request on a bidirectional request stream.
// The stream can either be obtained by calling AcceptStream on the underlying QUIC connection,
// or (internally) by using the server's stream accept loop.
func ( *RawServerConn) ( *quic.Stream) {
	 := .rawConn.TrackStream()
	.handleRequestStream()
}

func ( *RawServerConn) () int {
	if .maxHeaderBytes <= 0 {
		return http.DefaultMaxHeaderBytes
	}
	return .maxHeaderBytes
}

func ( *RawServerConn) ( *settingsFrame) (*quic.SendStream, error) {
	return .rawConn.openControlStream()
}

func ( *RawServerConn) ( *stateTrackingStream) {
	if .idleTimeout > 0 {
		// This only applies if the stream is the first active stream,
		// but it's ok to stop a stopped timer.
		.idleTimer.Stop()
	}

	 := &.rawConn
	 := .qlogger
	 := .decoder
	 := .serverContext
	 := .requestMaxHeaderBytes()

	 := &frameParser{closeConn: .CloseWithError, r: , streamID: .StreamID()}
	,  := .ParseNext()
	if  != nil {
		.CancelRead(quic.StreamErrorCode(ErrCodeRequestIncomplete))
		.CancelWrite(quic.StreamErrorCode(ErrCodeRequestIncomplete))
		return
	}
	,  := .(*headersFrame)
	if ! {
		.CloseWithError(quic.ApplicationErrorCode(ErrCodeFrameUnexpected), "expected first frame to be a HEADERS frame")
		return
	}
	if .Length > uint64() {
		maybeQlogInvalidHeadersFrame(, .StreamID(), .Length)
		// stop the client from sending more data
		.CancelRead(quic.StreamErrorCode(ErrCodeExcessiveLoad))
		// send a 431 Response (Request Header Fields Too Large)
		.rejectWithHeaderFieldsTooLarge()
		return
	}
	 := make([]byte, .Length)
	if ,  := io.ReadFull(, );  != nil {
		maybeQlogInvalidHeadersFrame(, .StreamID(), .Length)
		.CancelRead(quic.StreamErrorCode(ErrCodeRequestIncomplete))
		.CancelWrite(quic.StreamErrorCode(ErrCodeRequestIncomplete))
		return
	}
	 := .Decode()
	var  []qpack.HeaderField
	if  != nil {
		 = make([]qpack.HeaderField, 0, 16)
	}
	,  := requestFromHeaders(, , &)
	if  != nil {
		qlogParsedHeadersFrame(, .StreamID(), , )
	}
	if  != nil {
		if errors.Is(, errHeaderTooLarge) {
			// stop the client from sending more data
			.CancelRead(quic.StreamErrorCode(ErrCodeExcessiveLoad))
			// send a 431 Response (Request Header Fields Too Large)
			.rejectWithHeaderFieldsTooLarge()
			return
		}

		 := ErrCodeMessageError
		var  *qpackError
		if errors.As(, &) {
			 = ErrCodeQPACKDecompressionFailed
		}
		.CancelRead(quic.StreamErrorCode())
		.CancelWrite(quic.StreamErrorCode())
		return
	}

	 := .ConnectionState().TLS
	.TLS = &
	.RemoteAddr = .RemoteAddr().String()

	// Check that the client 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.
	 := int64(-1)
	if ,  := .Header["Content-Length"];  && .ContentLength >= 0 {
		 = .ContentLength
	}
	 := newStream(, , nil, func( io.Reader,  *headersFrame) error {
		,  := decodeTrailers(, , , , , .StreamID())
		if  != nil {
			return 
		}
		.Trailer = 
		return nil
	}, )
	 := newRequestBody(, , , .ReceivedSettings(), .Settings)
	.Body = 

	if .logger != nil {
		.logger.Debug("handling request", "method", .Method, "host", .Host, "uri", .RequestURI)
	}

	,  := context.WithCancel()
	 = .WithContext()
	context.AfterFunc(.Context(), )

	 := newResponseWriter(, , .Method == http.MethodHead, .logger)
	 := .requestHandler
	if  == nil {
		 = http.DefaultServeMux
	}

	// It's the client's responsibility to decide which requests are eligible for 0-RTT.
	var  bool
	func() {
		defer func() {
			if  := recover();  != nil {
				 = true
				if  == http.ErrAbortHandler {
					return
				}
				// Copied from net/http/server.go
				const  = 64 << 10
				 := make([]byte, )
				 = [:runtime.Stack(, false)]
				 := .logger
				if  == nil {
					 = slog.Default()
				}
				.Error("http3: panic serving", "arg", , "trace", string())
			}
		}()
		.ServeHTTP(, )
	}()

	if .wasStreamHijacked() {
		return
	}

	// abort the stream when there is a panic
	if  {
		.CancelRead(quic.StreamErrorCode(ErrCodeInternalError))
		.CancelWrite(quic.StreamErrorCode(ErrCodeInternalError))
		return
	}

	// response not written to the client yet, set Content-Length
	if !.headerWritten {
		if ,  := .header["Content-Length"]; ! {
			.header.Set("Content-Length", strconv.FormatInt(.numWritten, 10))
		}
	}
	.Flush()
	.flushTrailers()

	// If the EOF was read by the handler, CancelRead() is a no-op.
	.CancelRead(quic.StreamErrorCode(ErrCodeNoError))
	.Close()
}

func ( *RawServerConn) ( *stateTrackingStream) {
	 := newStream(, &.rawConn, nil, nil, .qlogger)
	defer .Close()
	 := newResponseWriter(, &.rawConn, false, .logger)
	.WriteHeader(http.StatusRequestHeaderFieldsTooLarge)
	.Flush()
}

// HandleUnidirectionalStream handles an incoming unidirectional stream.
func ( *RawServerConn) ( *quic.ReceiveStream) {
	.rawConn.handleUnidirectionalStream(, true)
}