mirror of
https://github.com/refraction-networking/uquic.git
synced 2025-04-03 20:27:35 +03:00
add support for the Extended CONNECT method (#3357)
Extended CONNECT is used by WebTransport.
This commit is contained in:
parent
85b495445e
commit
d065fb47e1
2 changed files with 74 additions and 30 deletions
|
@ -12,9 +12,9 @@ import (
|
|||
)
|
||||
|
||||
func requestFromHeaders(headers []qpack.HeaderField) (*http.Request, error) {
|
||||
var path, authority, method, contentLengthStr string
|
||||
httpHeaders := http.Header{}
|
||||
var path, authority, method, protocol, scheme, contentLengthStr string
|
||||
|
||||
httpHeaders := http.Header{}
|
||||
for _, h := range headers {
|
||||
switch h.Name {
|
||||
case ":path":
|
||||
|
@ -23,6 +23,10 @@ func requestFromHeaders(headers []qpack.HeaderField) (*http.Request, error) {
|
|||
method = h.Value
|
||||
case ":authority":
|
||||
authority = h.Value
|
||||
case ":protocol":
|
||||
protocol = h.Value
|
||||
case ":scheme":
|
||||
scheme = h.Value
|
||||
case "content-length":
|
||||
contentLengthStr = h.Value
|
||||
default:
|
||||
|
@ -39,7 +43,12 @@ func requestFromHeaders(headers []qpack.HeaderField) (*http.Request, error) {
|
|||
|
||||
isConnect := method == http.MethodConnect
|
||||
if isConnect {
|
||||
if path != "" || authority == "" {
|
||||
// Extended CONNECT, see https://datatracker.ietf.org/doc/html/rfc8441#section-4
|
||||
if protocol != "" {
|
||||
if scheme == "" || path == "" || authority == "" {
|
||||
return nil, errors.New("extended CONNECT: :scheme, :path and :authority must not be empty")
|
||||
}
|
||||
} else 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 {
|
||||
|
@ -51,9 +60,14 @@ func requestFromHeaders(headers []qpack.HeaderField) (*http.Request, error) {
|
|||
var err error
|
||||
|
||||
if isConnect {
|
||||
u = &url.URL{Host: authority}
|
||||
u = &url.URL{
|
||||
Scheme: scheme,
|
||||
Host: authority,
|
||||
Path: path,
|
||||
}
|
||||
requestURI = authority
|
||||
} else {
|
||||
protocol = "HTTP/3"
|
||||
u, err = url.ParseRequestURI(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -72,7 +86,7 @@ func requestFromHeaders(headers []qpack.HeaderField) (*http.Request, error) {
|
|||
return &http.Request{
|
||||
Method: method,
|
||||
URL: u,
|
||||
Proto: "HTTP/3",
|
||||
Proto: protocol,
|
||||
ProtoMajor: 3,
|
||||
ProtoMinor: 0,
|
||||
Header: httpHeaders,
|
||||
|
|
|
@ -81,17 +81,6 @@ var _ = Describe("Request", func() {
|
|||
}))
|
||||
})
|
||||
|
||||
It("handles CONNECT method", func() {
|
||||
headers := []qpack.HeaderField{
|
||||
{Name: ":authority", Value: "quic.clemente.io"},
|
||||
{Name: ":method", Value: http.MethodConnect},
|
||||
}
|
||||
req, err := requestFromHeaders(headers)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(req.Method).To(Equal(http.MethodConnect))
|
||||
Expect(req.RequestURI).To(Equal("quic.clemente.io"))
|
||||
})
|
||||
|
||||
It("errors with missing path", func() {
|
||||
headers := []qpack.HeaderField{
|
||||
{Name: ":authority", Value: "quic.clemente.io"},
|
||||
|
@ -119,22 +108,63 @@ var _ = Describe("Request", func() {
|
|||
Expect(err).To(MatchError(":path, :authority and :method must not be empty"))
|
||||
})
|
||||
|
||||
It("errors with missing authority in CONNECT method", func() {
|
||||
headers := []qpack.HeaderField{
|
||||
{Name: ":method", Value: http.MethodConnect},
|
||||
}
|
||||
_, err := requestFromHeaders(headers)
|
||||
Expect(err).To(MatchError(":path must be empty and :authority must not be empty"))
|
||||
Context("regular HTTP CONNECT", func() {
|
||||
It("handles CONNECT method", func() {
|
||||
headers := []qpack.HeaderField{
|
||||
{Name: ":authority", Value: "quic.clemente.io"},
|
||||
{Name: ":method", Value: http.MethodConnect},
|
||||
}
|
||||
req, err := requestFromHeaders(headers)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(req.Method).To(Equal(http.MethodConnect))
|
||||
Expect(req.RequestURI).To(Equal("quic.clemente.io"))
|
||||
})
|
||||
|
||||
It("errors with missing authority in CONNECT method", func() {
|
||||
headers := []qpack.HeaderField{
|
||||
{Name: ":method", Value: http.MethodConnect},
|
||||
}
|
||||
_, err := requestFromHeaders(headers)
|
||||
Expect(err).To(MatchError(":path must be empty and :authority must not be empty"))
|
||||
})
|
||||
|
||||
It("errors with extra path in CONNECT method", func() {
|
||||
headers := []qpack.HeaderField{
|
||||
{Name: ":path", Value: "/foo"},
|
||||
{Name: ":authority", Value: "quic.clemente.io"},
|
||||
{Name: ":method", Value: http.MethodConnect},
|
||||
}
|
||||
_, err := requestFromHeaders(headers)
|
||||
Expect(err).To(MatchError(":path must be empty and :authority must not be empty"))
|
||||
})
|
||||
})
|
||||
|
||||
It("errors with extra path in CONNECT method", func() {
|
||||
headers := []qpack.HeaderField{
|
||||
{Name: ":path", Value: "/foo"},
|
||||
{Name: ":authority", Value: "quic.clemente.io"},
|
||||
{Name: ":method", Value: http.MethodConnect},
|
||||
}
|
||||
_, err := requestFromHeaders(headers)
|
||||
Expect(err).To(MatchError(":path must be empty and :authority must not be empty"))
|
||||
Context("Extended CONNECT", func() {
|
||||
It("handles Extended CONNECT method", func() {
|
||||
headers := []qpack.HeaderField{
|
||||
{Name: ":protocol", Value: "webtransport"},
|
||||
{Name: ":scheme", Value: "ftp"},
|
||||
{Name: ":method", Value: http.MethodConnect},
|
||||
{Name: ":authority", Value: "quic.clemente.io"},
|
||||
{Name: ":path", Value: "/foo"},
|
||||
}
|
||||
req, err := requestFromHeaders(headers)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(req.Method).To(Equal(http.MethodConnect))
|
||||
Expect(req.Proto).To(Equal("webtransport"))
|
||||
Expect(req.URL.String()).To(Equal("ftp://quic.clemente.io/foo"))
|
||||
})
|
||||
|
||||
It("errors with missing scheme", func() {
|
||||
headers := []qpack.HeaderField{
|
||||
{Name: ":protocol", Value: "webtransport"},
|
||||
{Name: ":method", Value: http.MethodConnect},
|
||||
{Name: ":authority", Value: "quic.clemente.io"},
|
||||
{Name: ":path", Value: "/foo"},
|
||||
}
|
||||
_, err := requestFromHeaders(headers)
|
||||
Expect(err).To(MatchError("extended CONNECT: :scheme, :path and :authority must not be empty"))
|
||||
})
|
||||
})
|
||||
|
||||
Context("extracting the hostname from a request", func() {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue