package http3

import (
	
	
	
	
	
	
	
	
	

	

	
	
	
	
)

type qpackError struct{ err error }

func ( *qpackError) () string { return fmt.Sprintf("qpack: %v", .err) }
func ( *qpackError) () error { return .err }

var errHeaderTooLarge = errors.New("http3: headers too large")

type header struct {
	// Pseudo header fields defined in RFC 9114
	Path      string
	Method    string
	Authority string
	Scheme    string
	Status    string
	// for Extended connect
	Protocol string
	// parsed and deduplicated. -1 if no Content-Length header is sent
	ContentLength int64
	// all non-pseudo headers
	Headers http.Header
}

// connection-specific header fields must not be sent on HTTP/3
var invalidHeaderFields = [...]string{
	"connection",
	"keep-alive",
	"proxy-connection",
	"transfer-encoding",
	"upgrade",
}

func parseHeaders( qpack.DecodeFunc,  bool,  int,  *[]qpack.HeaderField) (header, error) {
	 := header{Headers: make(http.Header)}
	var ,  bool
	var  string
	for {
		,  := ()
		if  != nil {
			if  == io.EOF {
				break
			}
			return header{}, &qpackError{}
		}
		if  != nil {
			* = append(*, )
		}
		// RFC 9114, section 4.2.2:
		// The size of a field list is calculated based on the uncompressed size of fields,
		// including the length of the name and value in bytes plus an overhead of 32 bytes for each field.
		 -= len(.Name) + len(.Value) + 32
		if  < 0 {
			return header{}, errHeaderTooLarge
		}
		// field names need to be lowercase, see section 4.2 of RFC 9114
		if strings.ToLower(.Name) != .Name {
			return header{}, fmt.Errorf("header field is not lower-case: %s", .Name)
		}
		if !httpguts.ValidHeaderFieldValue(.Value) {
			return header{}, fmt.Errorf("invalid header field value for %s: %q", .Name, .Value)
		}
		if .IsPseudo() {
			if  {
				// all pseudo headers must appear before regular header fields, see section 4.3 of RFC 9114
				return header{}, fmt.Errorf("received pseudo header %s after a regular header field", .Name)
			}
			var  bool  // pseudo headers are either valid for requests or for responses
			var  bool // pseudo headers are allowed to appear exactly once
			switch .Name {
			case ":path":
				 = .Path != ""
				.Path = .Value
			case ":method":
				 = .Method != ""
				.Method = .Value
			case ":authority":
				 = .Authority != ""
				.Authority = .Value
			case ":protocol":
				 = .Protocol != ""
				.Protocol = .Value
			case ":scheme":
				 = .Scheme != ""
				.Scheme = .Value
			case ":status":
				 = .Status != ""
				.Status = .Value
				 = true
			default:
				return header{}, fmt.Errorf("unknown pseudo header: %s", .Name)
			}
			if  {
				return header{}, fmt.Errorf("duplicate pseudo header: %s", .Name)
			}
			if  &&  {
				return header{}, fmt.Errorf("invalid request pseudo header: %s", .Name)
			}
			if ! && ! {
				return header{}, fmt.Errorf("invalid response pseudo header: %s", .Name)
			}
		} else {
			if !httpguts.ValidHeaderFieldName(.Name) {
				return header{}, fmt.Errorf("invalid header field name: %q", .Name)
			}
			for ,  := range invalidHeaderFields {
				if .Name ==  {
					return header{}, fmt.Errorf("invalid header field name: %q", .Name)
				}
			}
			if .Name == "te" && .Value != "trailers" {
				return header{}, fmt.Errorf("invalid TE header field value: %q", .Value)
			}
			 = true
			switch .Name {
			case "content-length":
				// Ignore duplicate Content-Length headers.
				// Fail if the duplicates differ.
				if ! {
					 = true
					 = .Value
				} else if  != .Value {
					return header{}, fmt.Errorf("contradicting content lengths (%s and %s)", , .Value)
				}
			default:
				.Headers.Add(.Name, .Value)
			}
		}
	}
	.ContentLength = -1
	if len() > 0 {
		// use ParseUint instead of ParseInt, so that parsing fails on negative values
		,  := strconv.ParseUint(, 10, 63)
		if  != nil {
			return header{}, fmt.Errorf("invalid content length: %w", )
		}
		.Headers.Set("Content-Length", )
		.ContentLength = int64()
	}
	return , nil
}

func parseTrailers( qpack.DecodeFunc,  *[]qpack.HeaderField) (http.Header, error) {
	 := make(http.Header)
	for {
		,  := ()
		if  != nil {
			if  == io.EOF {
				break
			}
			return nil, &qpackError{}
		}
		if  != nil {
			* = append(*, )
		}
		if .IsPseudo() {
			return nil, fmt.Errorf("http3: received pseudo header in trailer: %s", .Name)
		}
		.Add(.Name, .Value)
	}
	return , nil
}

func requestFromHeaders( qpack.DecodeFunc,  int,  *[]qpack.HeaderField) (*http.Request, error) {
	,  := parseHeaders(, true, , )
	if  != nil {
		return nil, 
	}
	// concatenate cookie headers, see https://tools.ietf.org/html/rfc6265#section-5.4
	if len(.Headers["Cookie"]) > 0 {
		.Headers.Set("Cookie", strings.Join(.Headers["Cookie"], "; "))
	}

	 := .Method == http.MethodConnect
	// Extended CONNECT, see https://datatracker.ietf.org/doc/html/rfc8441#section-4
	 :=  && .Protocol != ""
	if  {
		if .Scheme == "" || .Path == "" || .Authority == "" {
			return nil, errors.New("extended CONNECT: :scheme, :path and :authority must not be empty")
		}
	} else if  {
		if .Path != "" || .Authority == "" { // normal CONNECT
			return nil, errors.New(":path must be empty and :authority must not be empty")
		}
	} else if len(.Path) == 0 || len(.Authority) == 0 || len(.Method) == 0 {
		return nil, errors.New(":path, :authority and :method must not be empty")
	}

	if ! && len(.Protocol) > 0 {
		return nil, errors.New(":protocol must be empty")
	}

	var  *url.URL
	var  string

	 := "HTTP/3.0"

	if  {
		 = &url.URL{}
		if  {
			,  = url.ParseRequestURI(.Path)
			if  != nil {
				return nil, 
			}
			 = .Protocol
		} else {
			.Path = .Path
		}
		.Scheme = .Scheme
		.Host = .Authority
		 = .Authority
	} else {
		,  = url.ParseRequestURI(.Path)
		if  != nil {
			return nil, fmt.Errorf("invalid content length: %w", )
		}
		 = .Path
	}

	 := &http.Request{
		Method:        .Method,
		URL:           ,
		Proto:         ,
		ProtoMajor:    3,
		ProtoMinor:    0,
		Header:        .Headers,
		Body:          nil,
		ContentLength: .ContentLength,
		Host:          .Authority,
		RequestURI:    ,
	}
	.Trailer = extractAnnouncedTrailers(.Header)
	return , nil
}

// updateResponseFromHeaders sets up http.Response as an HTTP/3 response,
// using the decoded qpack header filed.
// It is only called for the HTTP header (and not the HTTP trailer).
// It takes an http.Response as an argument to allow the caller to set the trailer later on.
func updateResponseFromHeaders( *http.Response,  qpack.DecodeFunc,  int,  *[]qpack.HeaderField) error {
	,  := parseHeaders(, false, , )
	if  != nil {
		return 
	}
	if .Status == "" {
		return errors.New("missing :status field")
	}
	.Proto = "HTTP/3.0"
	.ProtoMajor = 3
	.Header = .Headers
	.Trailer = extractAnnouncedTrailers(.Header)
	.ContentLength = .ContentLength

	,  := strconv.Atoi(.Status)
	if  != nil {
		return fmt.Errorf("invalid status code: %w", )
	}
	.StatusCode = 
	.Status = .Status + " " + http.StatusText()
	return nil
}

// extractAnnouncedTrailers extracts trailer keys from the "Trailer" header.
// It returns a map with the announced keys set to nil values, and removes the "Trailer" header.
// It handles both duplicate as well as comma-separated values for the Trailer header.
// For example:
//
//	Trailer: Trailer1, Trailer2
//	Trailer: Trailer3
//
// Will result in a map containing the keys "Trailer1", "Trailer2", "Trailer3" with nil values.
func extractAnnouncedTrailers( http.Header) http.Header {
	,  := ["Trailer"]
	if ! {
		return nil
	}

	 := make(http.Header)
	for ,  := range  {
		for ,  := range strings.Split(, ",") {
			[http.CanonicalHeaderKey(textproto.TrimString())] = nil
		}
	}
	delete(, "Trailer")
	return 
}

// writeTrailers encodes and writes HTTP trailers as a HEADERS frame.
// It returns true if trailers were written, false if there were no trailers to write.
func writeTrailers( io.Writer,  http.Header,  quic.StreamID,  qlogwriter.Recorder) (bool, error) {
	var  bool
	for ,  := range  {
		if httpguts.ValidTrailerHeader() && len() > 0 {
			 = true
			break
		}
	}
	if ! {
		return false, nil
	}

	var  bytes.Buffer
	 := qpack.NewEncoder(&)
	var  []qlog.HeaderField
	if  != nil {
		 = make([]qlog.HeaderField, 0, len())
	}

	for ,  := range  {
		if len() == 0 {
			continue
		}
		if !httpguts.ValidTrailerHeader() {
			continue
		}
		 := strings.ToLower()
		for ,  := range  {
			if  := .WriteField(qpack.HeaderField{Name: , Value: });  != nil {
				return false, 
			}
			if  != nil {
				 = append(, qlog.HeaderField{Name: , Value: })
			}
		}
	}

	 := make([]byte, 0, frameHeaderLen+.Len())
	 = (&headersFrame{Length: uint64(.Len())}).Append()
	 = append(, .Bytes()...)
	if  != nil {
		qlogCreatedHeadersFrame(, , len(), .Len(), )
	}
	,  := .Write()
	return true, 
}

func decodeTrailers( io.Reader,  *headersFrame,  int,  *qpack.Decoder,  qlogwriter.Recorder,  quic.StreamID) (http.Header, error) {
	if .Length > uint64() {
		maybeQlogInvalidHeadersFrame(, , .Length)
		return nil, fmt.Errorf("http3: HEADERS frame too large: %d bytes (max: %d)", .Length, )
	}

	 := make([]byte, .Length)
	if ,  := io.ReadFull(, );  != nil {
		return nil, 
	}
	 := .Decode()
	var  []qpack.HeaderField
	if  != nil {
		 = make([]qpack.HeaderField, 0, 16)
	}
	,  := parseTrailers(, &)
	if  != nil {
		maybeQlogInvalidHeadersFrame(, , .Length)
		return nil, 
	}
	if  != nil {
		qlogParsedHeadersFrame(, , , )
	}
	return , nil
}