github.com/mikelsr/quic-go@v0.36.1-0.20230701132136-1d9415b66898/http3/request.go (about)

     1  package http3
     2  
     3  import (
     4  	"errors"
     5  	"net/http"
     6  	"net/url"
     7  	"strconv"
     8  	"strings"
     9  
    10  	"github.com/quic-go/qpack"
    11  )
    12  
    13  func requestFromHeaders(headers []qpack.HeaderField) (*http.Request, error) {
    14  	var path, authority, method, protocol, scheme, contentLengthStr string
    15  
    16  	httpHeaders := http.Header{}
    17  	for _, h := range headers {
    18  		switch h.Name {
    19  		case ":path":
    20  			path = h.Value
    21  		case ":method":
    22  			method = h.Value
    23  		case ":authority":
    24  			authority = h.Value
    25  		case ":protocol":
    26  			protocol = h.Value
    27  		case ":scheme":
    28  			scheme = h.Value
    29  		case "content-length":
    30  			contentLengthStr = h.Value
    31  		default:
    32  			if !h.IsPseudo() {
    33  				httpHeaders.Add(h.Name, h.Value)
    34  			}
    35  		}
    36  	}
    37  
    38  	// concatenate cookie headers, see https://tools.ietf.org/html/rfc6265#section-5.4
    39  	if len(httpHeaders["Cookie"]) > 0 {
    40  		httpHeaders.Set("Cookie", strings.Join(httpHeaders["Cookie"], "; "))
    41  	}
    42  
    43  	isConnect := method == http.MethodConnect
    44  	// Extended CONNECT, see https://datatracker.ietf.org/doc/html/rfc8441#section-4
    45  	isExtendedConnected := isConnect && protocol != ""
    46  	if isExtendedConnected {
    47  		if scheme == "" || path == "" || authority == "" {
    48  			return nil, errors.New("extended CONNECT: :scheme, :path and :authority must not be empty")
    49  		}
    50  	} else if isConnect {
    51  		if path != "" || authority == "" { // normal CONNECT
    52  			return nil, errors.New(":path must be empty and :authority must not be empty")
    53  		}
    54  	} else if len(path) == 0 || len(authority) == 0 || len(method) == 0 {
    55  		return nil, errors.New(":path, :authority and :method must not be empty")
    56  	}
    57  
    58  	var u *url.URL
    59  	var requestURI string
    60  	var err error
    61  
    62  	if isConnect {
    63  		u = &url.URL{}
    64  		if isExtendedConnected {
    65  			u, err = url.ParseRequestURI(path)
    66  			if err != nil {
    67  				return nil, err
    68  			}
    69  		} else {
    70  			u.Path = path
    71  		}
    72  		u.Scheme = scheme
    73  		u.Host = authority
    74  		requestURI = authority
    75  	} else {
    76  		protocol = "HTTP/3.0"
    77  		u, err = url.ParseRequestURI(path)
    78  		if err != nil {
    79  			return nil, err
    80  		}
    81  		requestURI = path
    82  	}
    83  
    84  	var contentLength int64
    85  	if len(contentLengthStr) > 0 {
    86  		contentLength, err = strconv.ParseInt(contentLengthStr, 10, 64)
    87  		if err != nil {
    88  			return nil, err
    89  		}
    90  	}
    91  
    92  	return &http.Request{
    93  		Method:        method,
    94  		URL:           u,
    95  		Proto:         protocol,
    96  		ProtoMajor:    3,
    97  		ProtoMinor:    0,
    98  		Header:        httpHeaders,
    99  		Body:          nil,
   100  		ContentLength: contentLength,
   101  		Host:          authority,
   102  		RequestURI:    requestURI,
   103  	}, nil
   104  }
   105  
   106  func hostnameFromRequest(req *http.Request) string {
   107  	if req.URL != nil {
   108  		return req.URL.Host
   109  	}
   110  	return ""
   111  }