TUN-6191: Update quic-go to v0.27.1 and with custom patch to allow keep alive period to be configurable
The idle period is set to 5sec. We now also ping every second since last activity. This makes the quic.Connection less prone to being closed with no network activity, since we send multiple pings per idle period, and thus a single packet loss cannot cause the problem.
This commit is contained in:
parent
4ccef23dbc
commit
475939a77f
|
@ -35,7 +35,7 @@ const (
|
||||||
|
|
||||||
// QUICConnection represents the type that facilitates Proxying via QUIC streams.
|
// QUICConnection represents the type that facilitates Proxying via QUIC streams.
|
||||||
type QUICConnection struct {
|
type QUICConnection struct {
|
||||||
session quic.Session
|
session quic.Connection
|
||||||
logger *zerolog.Logger
|
logger *zerolog.Logger
|
||||||
orchestrator Orchestrator
|
orchestrator Orchestrator
|
||||||
sessionManager datagramsession.Manager
|
sessionManager datagramsession.Manager
|
||||||
|
|
|
@ -31,7 +31,7 @@ import (
|
||||||
var (
|
var (
|
||||||
testTLSServerConfig = quicpogs.GenerateTLSConfig()
|
testTLSServerConfig = quicpogs.GenerateTLSConfig()
|
||||||
testQUICConfig = &quic.Config{
|
testQUICConfig = &quic.Config{
|
||||||
KeepAlive: true,
|
KeepAlivePeriod: 5 * time.Second,
|
||||||
EnableDatagrams: true,
|
EnableDatagrams: true,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -502,7 +502,7 @@ func TestServeUDPSession(t *testing.T) {
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
|
||||||
// Establish QUIC connection with edge
|
// Establish QUIC connection with edge
|
||||||
edgeQUICSessionChan := make(chan quic.Session)
|
edgeQUICSessionChan := make(chan quic.Connection)
|
||||||
go func() {
|
go func() {
|
||||||
earlyListener, err := quic.Listen(udpListener, testTLSServerConfig, testQUICConfig)
|
earlyListener, err := quic.Listen(udpListener, testTLSServerConfig, testQUICConfig)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
@ -522,7 +522,7 @@ func TestServeUDPSession(t *testing.T) {
|
||||||
cancel()
|
cancel()
|
||||||
}
|
}
|
||||||
|
|
||||||
func serveSession(ctx context.Context, qc *QUICConnection, edgeQUICSession quic.Session, closeType closeReason, expectedReason string, t *testing.T) {
|
func serveSession(ctx context.Context, qc *QUICConnection, edgeQUICSession quic.Connection, closeType closeReason, expectedReason string, t *testing.T) {
|
||||||
var (
|
var (
|
||||||
payload = []byte(t.Name())
|
payload = []byte(t.Name())
|
||||||
)
|
)
|
||||||
|
@ -583,7 +583,7 @@ const (
|
||||||
closedByTimeout
|
closedByTimeout
|
||||||
)
|
)
|
||||||
|
|
||||||
func runRPCServer(ctx context.Context, session quic.Session, sessionRPCServer tunnelpogs.SessionManager, configRPCServer tunnelpogs.ConfigurationManager, t *testing.T) {
|
func runRPCServer(ctx context.Context, session quic.Connection, sessionRPCServer tunnelpogs.SessionManager, configRPCServer tunnelpogs.ConfigurationManager, t *testing.T) {
|
||||||
stream, err := session.AcceptStream(ctx)
|
stream, err := session.AcceptStream(ctx)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
|
10
go.mod
10
go.mod
|
@ -17,7 +17,7 @@ require (
|
||||||
github.com/gorilla/mux v1.8.0
|
github.com/gorilla/mux v1.8.0
|
||||||
github.com/gorilla/websocket v1.4.2
|
github.com/gorilla/websocket v1.4.2
|
||||||
github.com/json-iterator/go v1.1.12
|
github.com/json-iterator/go v1.1.12
|
||||||
github.com/lucas-clemente/quic-go v0.24.0
|
github.com/lucas-clemente/quic-go v0.27.1
|
||||||
github.com/mattn/go-colorable v0.1.8
|
github.com/mattn/go-colorable v0.1.8
|
||||||
github.com/miekg/dns v1.1.45
|
github.com/miekg/dns v1.1.45
|
||||||
github.com/mitchellh/go-homedir v1.1.0
|
github.com/mitchellh/go-homedir v1.1.0
|
||||||
|
@ -74,9 +74,9 @@ require (
|
||||||
github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645 // indirect
|
github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645 // indirect
|
||||||
github.com/kylelemons/godebug v1.1.0 // indirect
|
github.com/kylelemons/godebug v1.1.0 // indirect
|
||||||
github.com/lucasb-eyer/go-colorful v1.0.3 // indirect
|
github.com/lucasb-eyer/go-colorful v1.0.3 // indirect
|
||||||
github.com/marten-seemann/qtls-go1-16 v0.1.4 // indirect
|
github.com/marten-seemann/qtls-go1-16 v0.1.5 // indirect
|
||||||
github.com/marten-seemann/qtls-go1-17 v0.1.0 // indirect
|
github.com/marten-seemann/qtls-go1-17 v0.1.1 // indirect
|
||||||
github.com/marten-seemann/qtls-go1-18 v0.1.0-beta.1 // indirect
|
github.com/marten-seemann/qtls-go1-18 v0.1.1 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.12 // indirect
|
github.com/mattn/go-isatty v0.0.12 // indirect
|
||||||
github.com/mattn/go-runewidth v0.0.8 // indirect
|
github.com/mattn/go-runewidth v0.0.8 // indirect
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
|
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
|
||||||
|
@ -105,7 +105,7 @@ require (
|
||||||
|
|
||||||
replace github.com/urfave/cli/v2 => github.com/ipostelnik/cli/v2 v2.3.1-0.20210324024421-b6ea8234fe3d
|
replace github.com/urfave/cli/v2 => github.com/ipostelnik/cli/v2 v2.3.1-0.20210324024421-b6ea8234fe3d
|
||||||
|
|
||||||
replace github.com/lucas-clemente/quic-go => github.com/chungthuang/quic-go v0.24.1-0.20220110095058-981dc498cb62
|
replace github.com/lucas-clemente/quic-go => github.com/chungthuang/quic-go v0.27.1-0.20220607112311-13144fbde8da
|
||||||
|
|
||||||
// Avoid 'CVE-2022-21698'
|
// Avoid 'CVE-2022-21698'
|
||||||
replace github.com/prometheus/golang_client => github.com/prometheus/golang_client v1.12.1
|
replace github.com/prometheus/golang_client => github.com/prometheus/golang_client v1.12.1
|
||||||
|
|
17
go.sum
17
go.sum
|
@ -111,8 +111,8 @@ github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cb
|
||||||
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
github.com/cheekybits/genny v1.0.0 h1:uGGa4nei+j20rOSeDeP5Of12XVm7TGUd4dJA9RDitfE=
|
github.com/cheekybits/genny v1.0.0 h1:uGGa4nei+j20rOSeDeP5Of12XVm7TGUd4dJA9RDitfE=
|
||||||
github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ=
|
github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ=
|
||||||
github.com/chungthuang/quic-go v0.24.1-0.20220110095058-981dc498cb62 h1:PLTB4iA6sOgAItzQY642tYdcGKfG/7i2gu93JQGgUcM=
|
github.com/chungthuang/quic-go v0.27.1-0.20220607112311-13144fbde8da h1:FmuwbQ8RU/ftTKnfz5diawqvQFH1KDB9wN2Q8S2wqds=
|
||||||
github.com/chungthuang/quic-go v0.24.1-0.20220110095058-981dc498cb62/go.mod h1:YtzP8bxRVCBlO77yRanE264+fY/T2U9ZlW1AaHOsMOg=
|
github.com/chungthuang/quic-go v0.27.1-0.20220607112311-13144fbde8da/go.mod h1:AzgQoPda7N+3IqMMMkywBKggIFo2KT6pfnlrQ2QieeI=
|
||||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||||
|
@ -405,13 +405,12 @@ github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN
|
||||||
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||||
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||||
github.com/marten-seemann/qpack v0.2.1/go.mod h1:F7Gl5L1jIgN1D11ucXefiuJS9UMVP2opoCp2jDKb7wc=
|
github.com/marten-seemann/qpack v0.2.1/go.mod h1:F7Gl5L1jIgN1D11ucXefiuJS9UMVP2opoCp2jDKb7wc=
|
||||||
github.com/marten-seemann/qtls-go1-15 v0.1.4/go.mod h1:GyFwywLKkRt+6mfU99csTEY1joMZz5vmB1WNZH3P81I=
|
github.com/marten-seemann/qtls-go1-16 v0.1.5 h1:o9JrYPPco/Nukd/HpOHMHZoBDXQqoNtUCmny98/1uqQ=
|
||||||
github.com/marten-seemann/qtls-go1-16 v0.1.4 h1:xbHbOGGhrenVtII6Co8akhLEdrawwB2iHl5yhJRpnco=
|
github.com/marten-seemann/qtls-go1-16 v0.1.5/go.mod h1:gNpI2Ol+lRS3WwSOtIUUtRwZEQMXjYK+dQSBFbethAk=
|
||||||
github.com/marten-seemann/qtls-go1-16 v0.1.4/go.mod h1:gNpI2Ol+lRS3WwSOtIUUtRwZEQMXjYK+dQSBFbethAk=
|
github.com/marten-seemann/qtls-go1-17 v0.1.1 h1:DQjHPq+aOzUeh9/lixAGunn6rIOQyWChPSI4+hgW7jc=
|
||||||
github.com/marten-seemann/qtls-go1-17 v0.1.0 h1:P9ggrs5xtwiqXv/FHNwntmuLMNq3KaSIG93AtAZ48xk=
|
github.com/marten-seemann/qtls-go1-17 v0.1.1/go.mod h1:C2ekUKcDdz9SDWxec1N/MvcXBpaX9l3Nx67XaR84L5s=
|
||||||
github.com/marten-seemann/qtls-go1-17 v0.1.0/go.mod h1:fz4HIxByo+LlWcreM4CZOYNuz3taBQ8rN2X6FqvaWo8=
|
github.com/marten-seemann/qtls-go1-18 v0.1.1 h1:qp7p7XXUFL7fpBvSS1sWD+uSqPvzNQK43DH+/qEkj0Y=
|
||||||
github.com/marten-seemann/qtls-go1-18 v0.1.0-beta.1 h1:EnzzN9fPUkUck/1CuY1FlzBaIYMoiBsdwTNmNGkwUUM=
|
github.com/marten-seemann/qtls-go1-18 v0.1.1/go.mod h1:mJttiymBAByA49mhlNZZGrH5u1uXYZJ+RW28Py7f4m4=
|
||||||
github.com/marten-seemann/qtls-go1-18 v0.1.0-beta.1/go.mod h1:PUhIQk19LoFt2174H4+an8TYvWOGjb/hHwphBeaDHwI=
|
|
||||||
github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
|
github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
|
||||||
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||||
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
||||||
|
|
|
@ -14,11 +14,11 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
type DatagramMuxer struct {
|
type DatagramMuxer struct {
|
||||||
session quic.Session
|
session quic.Connection
|
||||||
logger *zerolog.Logger
|
logger *zerolog.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDatagramMuxer(quicSession quic.Session, logger *zerolog.Logger) (*DatagramMuxer, error) {
|
func NewDatagramMuxer(quicSession quic.Connection, logger *zerolog.Logger) (*DatagramMuxer, error) {
|
||||||
return &DatagramMuxer{
|
return &DatagramMuxer{
|
||||||
session: quicSession,
|
session: quicSession,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
|
|
|
@ -56,7 +56,7 @@ func TestMaxDatagramPayload(t *testing.T) {
|
||||||
payload := make([]byte, maxDatagramPayloadSize)
|
payload := make([]byte, maxDatagramPayloadSize)
|
||||||
|
|
||||||
quicConfig := &quic.Config{
|
quicConfig := &quic.Config{
|
||||||
KeepAlive: true,
|
KeepAlivePeriod: 5 * time.Millisecond,
|
||||||
EnableDatagrams: true,
|
EnableDatagrams: true,
|
||||||
MaxDatagramFrameSize: MaxDatagramFrameSize,
|
MaxDatagramFrameSize: MaxDatagramFrameSize,
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,7 +37,8 @@ const (
|
||||||
protocolVersionLength = 2
|
protocolVersionLength = 2
|
||||||
|
|
||||||
HandshakeIdleTimeout = 5 * time.Second
|
HandshakeIdleTimeout = 5 * time.Second
|
||||||
MaxIdleTimeout = 15 * time.Second
|
MaxIdleTimeout = 5 * time.Second
|
||||||
|
MaxIdlePingPeriod = 1 * time.Second
|
||||||
)
|
)
|
||||||
|
|
||||||
// RequestServerStream is a stream to serve requests
|
// RequestServerStream is a stream to serve requests
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"net"
|
"net"
|
||||||
"sync"
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/lucas-clemente/quic-go"
|
"github.com/lucas-clemente/quic-go"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
@ -15,7 +16,7 @@ import (
|
||||||
var (
|
var (
|
||||||
testTLSServerConfig = GenerateTLSConfig()
|
testTLSServerConfig = GenerateTLSConfig()
|
||||||
testQUICConfig = &quic.Config{
|
testQUICConfig = &quic.Config{
|
||||||
KeepAlive: true,
|
KeepAlivePeriod: 5 * time.Second,
|
||||||
EnableDatagrams: true,
|
EnableDatagrams: true,
|
||||||
}
|
}
|
||||||
exchanges = 1000
|
exchanges = 1000
|
||||||
|
|
|
@ -550,9 +550,9 @@ func ServeQUIC(
|
||||||
quicConfig := &quic.Config{
|
quicConfig := &quic.Config{
|
||||||
HandshakeIdleTimeout: quicpogs.HandshakeIdleTimeout,
|
HandshakeIdleTimeout: quicpogs.HandshakeIdleTimeout,
|
||||||
MaxIdleTimeout: quicpogs.MaxIdleTimeout,
|
MaxIdleTimeout: quicpogs.MaxIdleTimeout,
|
||||||
|
KeepAlivePeriod: quicpogs.MaxIdlePingPeriod,
|
||||||
MaxIncomingStreams: connection.MaxConcurrentStreams,
|
MaxIncomingStreams: connection.MaxConcurrentStreams,
|
||||||
MaxIncomingUniStreams: connection.MaxConcurrentStreams,
|
MaxIncomingUniStreams: connection.MaxConcurrentStreams,
|
||||||
KeepAlive: true,
|
|
||||||
EnableDatagrams: true,
|
EnableDatagrams: true,
|
||||||
MaxDatagramFrameSize: quicpogs.MaxDatagramFrameSize,
|
MaxDatagramFrameSize: quicpogs.MaxDatagramFrameSize,
|
||||||
Tracer: quicpogs.NewClientTracer(connLogger.Logger(), connIndex),
|
Tracer: quicpogs.NewClientTracer(connLogger.Logger(), connIndex),
|
||||||
|
|
|
@ -28,7 +28,6 @@ linters:
|
||||||
- ineffassign
|
- ineffassign
|
||||||
- misspell
|
- misspell
|
||||||
- prealloc
|
- prealloc
|
||||||
- scopelint
|
|
||||||
- staticcheck
|
- staticcheck
|
||||||
- stylecheck
|
- stylecheck
|
||||||
- structcheck
|
- structcheck
|
||||||
|
|
|
@ -3,9 +3,6 @@
|
||||||
<img src="docs/quic.png" width=303 height=124>
|
<img src="docs/quic.png" width=303 height=124>
|
||||||
|
|
||||||
[![PkgGoDev](https://pkg.go.dev/badge/github.com/lucas-clemente/quic-go)](https://pkg.go.dev/github.com/lucas-clemente/quic-go)
|
[![PkgGoDev](https://pkg.go.dev/badge/github.com/lucas-clemente/quic-go)](https://pkg.go.dev/github.com/lucas-clemente/quic-go)
|
||||||
[![Travis Build Status](https://img.shields.io/travis/lucas-clemente/quic-go/master.svg?style=flat-square&label=Travis+build)](https://travis-ci.org/lucas-clemente/quic-go)
|
|
||||||
[![CircleCI Build Status](https://img.shields.io/circleci/project/github/lucas-clemente/quic-go.svg?style=flat-square&label=CircleCI+build)](https://circleci.com/gh/lucas-clemente/quic-go)
|
|
||||||
[![Windows Build Status](https://img.shields.io/appveyor/ci/lucas-clemente/quic-go/master.svg?style=flat-square&label=windows+build)](https://ci.appveyor.com/project/lucas-clemente/quic-go/branch/master)
|
|
||||||
[![Code Coverage](https://img.shields.io/codecov/c/github/lucas-clemente/quic-go/master.svg?style=flat-square)](https://codecov.io/gh/lucas-clemente/quic-go/)
|
[![Code Coverage](https://img.shields.io/codecov/c/github/lucas-clemente/quic-go/master.svg?style=flat-square)](https://codecov.io/gh/lucas-clemente/quic-go/)
|
||||||
|
|
||||||
quic-go is an implementation of the [QUIC protocol, RFC 9000](https://datatracker.ietf.org/doc/html/rfc9000) protocol in Go.
|
quic-go is an implementation of the [QUIC protocol, RFC 9000](https://datatracker.ietf.org/doc/html/rfc9000) protocol in Go.
|
||||||
|
@ -13,7 +10,7 @@ In addition to RFC 9000, it currently implements the [IETF QUIC draft-29](https:
|
||||||
|
|
||||||
## Guides
|
## Guides
|
||||||
|
|
||||||
*We currently support Go 1.16.x and Go 1.17.x.*
|
*We currently support Go 1.16.x, Go 1.17.x, and Go 1.18.x.*
|
||||||
|
|
||||||
Running tests:
|
Running tests:
|
||||||
|
|
||||||
|
@ -51,10 +48,11 @@ http.Client{
|
||||||
| [algernon](https://github.com/xyproto/algernon) | Small self-contained pure-Go web server with Lua, Markdown, HTTP/2, QUIC, Redis and PostgreSQL support | ![GitHub Repo stars](https://img.shields.io/github/stars/xyproto/algernon?style=flat-square) |
|
| [algernon](https://github.com/xyproto/algernon) | Small self-contained pure-Go web server with Lua, Markdown, HTTP/2, QUIC, Redis and PostgreSQL support | ![GitHub Repo stars](https://img.shields.io/github/stars/xyproto/algernon?style=flat-square) |
|
||||||
| [caddy](https://github.com/caddyserver/caddy/) | Fast, multi-platform web server with automatic HTTPS | ![GitHub Repo stars](https://img.shields.io/github/stars/caddyserver/caddy?style=flat-square) |
|
| [caddy](https://github.com/caddyserver/caddy/) | Fast, multi-platform web server with automatic HTTPS | ![GitHub Repo stars](https://img.shields.io/github/stars/caddyserver/caddy?style=flat-square) |
|
||||||
| [go-ipfs](https://github.com/ipfs/go-ipfs) | IPFS implementation in go | ![GitHub Repo stars](https://img.shields.io/github/stars/ipfs/go-ipfs?style=flat-square) |
|
| [go-ipfs](https://github.com/ipfs/go-ipfs) | IPFS implementation in go | ![GitHub Repo stars](https://img.shields.io/github/stars/ipfs/go-ipfs?style=flat-square) |
|
||||||
| [nextdns](https://github.com/nextdns/nextdns) | NextDNS CLI client (DoH Proxy) | ![GitHub Repo stars](https://img.shields.io/github/stars/nextdns/nextdns?style=flat-square) |
|
|
||||||
| [syncthing](https://github.com/syncthing/syncthing/) | Open Source Continuous File Synchronization | ![GitHub Repo stars](https://img.shields.io/github/stars/syncthing/syncthing?style=flat-square) |
|
| [syncthing](https://github.com/syncthing/syncthing/) | Open Source Continuous File Synchronization | ![GitHub Repo stars](https://img.shields.io/github/stars/syncthing/syncthing?style=flat-square) |
|
||||||
| [traefik](https://github.com/traefik/traefik) | The Cloud Native Application Proxy | ![GitHub Repo stars](https://img.shields.io/github/stars/traefik/traefik?style=flat-square) |
|
| [traefik](https://github.com/traefik/traefik) | The Cloud Native Application Proxy | ![GitHub Repo stars](https://img.shields.io/github/stars/traefik/traefik?style=flat-square) |
|
||||||
| [v2ray-core](https://github.com/v2fly/v2ray-core) | A platform for building proxies to bypass network restrictions | ![GitHub Repo stars](https://img.shields.io/github/stars/v2fly/v2ray-core?style=flat-square) |
|
| [v2ray-core](https://github.com/v2fly/v2ray-core) | A platform for building proxies to bypass network restrictions | ![GitHub Repo stars](https://img.shields.io/github/stars/v2fly/v2ray-core?style=flat-square) |
|
||||||
|
| [cloudflared](https://github.com/cloudflare/cloudflared) | A tunneling daemon that proxies traffic from the Cloudflare network to your origins | ![GitHub Repo stars](https://img.shields.io/github/stars/cloudflare/cloudflared?style=flat-square) |
|
||||||
|
| [OONI Probe](https://github.com/ooni/probe-cli) | The Open Observatory of Network Interference (OONI) aims to empower decentralized efforts in documenting Internet censorship around the world. | ![GitHub Repo stars](https://img.shields.io/github/stars/ooni/probe-cli?style=flat-square) |
|
||||||
|
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
|
@ -14,7 +14,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type client struct {
|
type client struct {
|
||||||
conn sendConn
|
sconn sendConn
|
||||||
// If the client is created with DialAddr, we create a packet conn.
|
// If the client is created with DialAddr, we create a packet conn.
|
||||||
// If it is started with Dial, we take a packet conn as a parameter.
|
// If it is started with Dial, we take a packet conn as a parameter.
|
||||||
createdPacketConn bool
|
createdPacketConn bool
|
||||||
|
@ -35,7 +35,7 @@ type client struct {
|
||||||
|
|
||||||
handshakeChan chan struct{}
|
handshakeChan chan struct{}
|
||||||
|
|
||||||
session quicSession
|
conn quicConn
|
||||||
|
|
||||||
tracer logging.ConnectionTracer
|
tracer logging.ConnectionTracer
|
||||||
tracingID uint64
|
tracingID uint64
|
||||||
|
@ -49,26 +49,26 @@ var (
|
||||||
)
|
)
|
||||||
|
|
||||||
// DialAddr establishes a new QUIC connection to a server.
|
// DialAddr establishes a new QUIC connection to a server.
|
||||||
// It uses a new UDP connection and closes this connection when the QUIC session is closed.
|
// It uses a new UDP connection and closes this connection when the QUIC connection is closed.
|
||||||
// The hostname for SNI is taken from the given address.
|
// The hostname for SNI is taken from the given address.
|
||||||
// The tls.Config.CipherSuites allows setting of TLS 1.3 cipher suites.
|
// The tls.Config.CipherSuites allows setting of TLS 1.3 cipher suites.
|
||||||
func DialAddr(
|
func DialAddr(
|
||||||
addr string,
|
addr string,
|
||||||
tlsConf *tls.Config,
|
tlsConf *tls.Config,
|
||||||
config *Config,
|
config *Config,
|
||||||
) (Session, error) {
|
) (Connection, error) {
|
||||||
return DialAddrContext(context.Background(), addr, tlsConf, config)
|
return DialAddrContext(context.Background(), addr, tlsConf, config)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DialAddrEarly establishes a new 0-RTT QUIC connection to a server.
|
// DialAddrEarly establishes a new 0-RTT QUIC connection to a server.
|
||||||
// It uses a new UDP connection and closes this connection when the QUIC session is closed.
|
// It uses a new UDP connection and closes this connection when the QUIC connection is closed.
|
||||||
// The hostname for SNI is taken from the given address.
|
// The hostname for SNI is taken from the given address.
|
||||||
// The tls.Config.CipherSuites allows setting of TLS 1.3 cipher suites.
|
// The tls.Config.CipherSuites allows setting of TLS 1.3 cipher suites.
|
||||||
func DialAddrEarly(
|
func DialAddrEarly(
|
||||||
addr string,
|
addr string,
|
||||||
tlsConf *tls.Config,
|
tlsConf *tls.Config,
|
||||||
config *Config,
|
config *Config,
|
||||||
) (EarlySession, error) {
|
) (EarlyConnection, error) {
|
||||||
return DialAddrEarlyContext(context.Background(), addr, tlsConf, config)
|
return DialAddrEarlyContext(context.Background(), addr, tlsConf, config)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,13 +79,13 @@ func DialAddrEarlyContext(
|
||||||
addr string,
|
addr string,
|
||||||
tlsConf *tls.Config,
|
tlsConf *tls.Config,
|
||||||
config *Config,
|
config *Config,
|
||||||
) (EarlySession, error) {
|
) (EarlyConnection, error) {
|
||||||
sess, err := dialAddrContext(ctx, addr, tlsConf, config, true)
|
conn, err := dialAddrContext(ctx, addr, tlsConf, config, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
utils.Logger.WithPrefix(utils.DefaultLogger, "client").Debugf("Returning early session")
|
utils.Logger.WithPrefix(utils.DefaultLogger, "client").Debugf("Returning early connection")
|
||||||
return sess, nil
|
return conn, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// DialAddrContext establishes a new QUIC connection to a server using the provided context.
|
// DialAddrContext establishes a new QUIC connection to a server using the provided context.
|
||||||
|
@ -95,7 +95,7 @@ func DialAddrContext(
|
||||||
addr string,
|
addr string,
|
||||||
tlsConf *tls.Config,
|
tlsConf *tls.Config,
|
||||||
config *Config,
|
config *Config,
|
||||||
) (Session, error) {
|
) (Connection, error) {
|
||||||
return dialAddrContext(ctx, addr, tlsConf, config, false)
|
return dialAddrContext(ctx, addr, tlsConf, config, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -105,7 +105,7 @@ func dialAddrContext(
|
||||||
tlsConf *tls.Config,
|
tlsConf *tls.Config,
|
||||||
config *Config,
|
config *Config,
|
||||||
use0RTT bool,
|
use0RTT bool,
|
||||||
) (quicSession, error) {
|
) (quicConn, error) {
|
||||||
udpAddr, err := net.ResolveUDPAddr("udp", addr)
|
udpAddr, err := net.ResolveUDPAddr("udp", addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -131,7 +131,7 @@ func Dial(
|
||||||
host string,
|
host string,
|
||||||
tlsConf *tls.Config,
|
tlsConf *tls.Config,
|
||||||
config *Config,
|
config *Config,
|
||||||
) (Session, error) {
|
) (Connection, error) {
|
||||||
return dialContext(context.Background(), pconn, remoteAddr, host, tlsConf, config, false, false)
|
return dialContext(context.Background(), pconn, remoteAddr, host, tlsConf, config, false, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -146,7 +146,7 @@ func DialEarly(
|
||||||
host string,
|
host string,
|
||||||
tlsConf *tls.Config,
|
tlsConf *tls.Config,
|
||||||
config *Config,
|
config *Config,
|
||||||
) (EarlySession, error) {
|
) (EarlyConnection, error) {
|
||||||
return DialEarlyContext(context.Background(), pconn, remoteAddr, host, tlsConf, config)
|
return DialEarlyContext(context.Background(), pconn, remoteAddr, host, tlsConf, config)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -159,7 +159,7 @@ func DialEarlyContext(
|
||||||
host string,
|
host string,
|
||||||
tlsConf *tls.Config,
|
tlsConf *tls.Config,
|
||||||
config *Config,
|
config *Config,
|
||||||
) (EarlySession, error) {
|
) (EarlyConnection, error) {
|
||||||
return dialContext(ctx, pconn, remoteAddr, host, tlsConf, config, true, false)
|
return dialContext(ctx, pconn, remoteAddr, host, tlsConf, config, true, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -172,7 +172,7 @@ func DialContext(
|
||||||
host string,
|
host string,
|
||||||
tlsConf *tls.Config,
|
tlsConf *tls.Config,
|
||||||
config *Config,
|
config *Config,
|
||||||
) (Session, error) {
|
) (Connection, error) {
|
||||||
return dialContext(ctx, pconn, remoteAddr, host, tlsConf, config, false, false)
|
return dialContext(ctx, pconn, remoteAddr, host, tlsConf, config, false, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -185,7 +185,7 @@ func dialContext(
|
||||||
config *Config,
|
config *Config,
|
||||||
use0RTT bool,
|
use0RTT bool,
|
||||||
createdPacketConn bool,
|
createdPacketConn bool,
|
||||||
) (quicSession, error) {
|
) (quicConn, error) {
|
||||||
if tlsConf == nil {
|
if tlsConf == nil {
|
||||||
return nil, errors.New("quic: tls.Config not set")
|
return nil, errors.New("quic: tls.Config not set")
|
||||||
}
|
}
|
||||||
|
@ -203,21 +203,21 @@ func dialContext(
|
||||||
}
|
}
|
||||||
c.packetHandlers = packetHandlers
|
c.packetHandlers = packetHandlers
|
||||||
|
|
||||||
c.tracingID = nextSessionTracingID()
|
c.tracingID = nextConnTracingID()
|
||||||
if c.config.Tracer != nil {
|
if c.config.Tracer != nil {
|
||||||
c.tracer = c.config.Tracer.TracerForConnection(
|
c.tracer = c.config.Tracer.TracerForConnection(
|
||||||
context.WithValue(ctx, SessionTracingKey, c.tracingID),
|
context.WithValue(ctx, ConnectionTracingKey, c.tracingID),
|
||||||
protocol.PerspectiveClient,
|
protocol.PerspectiveClient,
|
||||||
c.destConnID,
|
c.destConnID,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if c.tracer != nil {
|
if c.tracer != nil {
|
||||||
c.tracer.StartedConnection(c.conn.LocalAddr(), c.conn.RemoteAddr(), c.srcConnID, c.destConnID)
|
c.tracer.StartedConnection(c.sconn.LocalAddr(), c.sconn.RemoteAddr(), c.srcConnID, c.destConnID)
|
||||||
}
|
}
|
||||||
if err := c.dial(ctx); err != nil {
|
if err := c.dial(ctx); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return c.session, nil
|
return c.conn, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func newClient(
|
func newClient(
|
||||||
|
@ -265,7 +265,7 @@ func newClient(
|
||||||
c := &client{
|
c := &client{
|
||||||
srcConnID: srcConnID,
|
srcConnID: srcConnID,
|
||||||
destConnID: destConnID,
|
destConnID: destConnID,
|
||||||
conn: newSendPconn(pconn, remoteAddr),
|
sconn: newSendPconn(pconn, remoteAddr),
|
||||||
createdPacketConn: createdPacketConn,
|
createdPacketConn: createdPacketConn,
|
||||||
use0RTT: use0RTT,
|
use0RTT: use0RTT,
|
||||||
tlsConf: tlsConf,
|
tlsConf: tlsConf,
|
||||||
|
@ -278,10 +278,10 @@ func newClient(
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *client) dial(ctx context.Context) error {
|
func (c *client) dial(ctx context.Context) error {
|
||||||
c.logger.Infof("Starting new connection to %s (%s -> %s), source connection ID %s, destination connection ID %s, version %s", c.tlsConf.ServerName, c.conn.LocalAddr(), c.conn.RemoteAddr(), c.srcConnID, c.destConnID, c.version)
|
c.logger.Infof("Starting new connection to %s (%s -> %s), source connection ID %s, destination connection ID %s, version %s", c.tlsConf.ServerName, c.sconn.LocalAddr(), c.sconn.RemoteAddr(), c.srcConnID, c.destConnID, c.version)
|
||||||
|
|
||||||
c.session = newClientSession(
|
c.conn = newClientConnection(
|
||||||
c.conn,
|
c.sconn,
|
||||||
c.packetHandlers,
|
c.packetHandlers,
|
||||||
c.destConnID,
|
c.destConnID,
|
||||||
c.srcConnID,
|
c.srcConnID,
|
||||||
|
@ -295,11 +295,11 @@ func (c *client) dial(ctx context.Context) error {
|
||||||
c.logger,
|
c.logger,
|
||||||
c.version,
|
c.version,
|
||||||
)
|
)
|
||||||
c.packetHandlers.Add(c.srcConnID, c.session)
|
c.packetHandlers.Add(c.srcConnID, c.conn)
|
||||||
|
|
||||||
errorChan := make(chan error, 1)
|
errorChan := make(chan error, 1)
|
||||||
go func() {
|
go func() {
|
||||||
err := c.session.run() // returns as soon as the session is closed
|
err := c.conn.run() // returns as soon as the connection is closed
|
||||||
|
|
||||||
if e := (&errCloseForRecreating{}); !errors.As(err, &e) && c.createdPacketConn {
|
if e := (&errCloseForRecreating{}); !errors.As(err, &e) && c.createdPacketConn {
|
||||||
c.packetHandlers.Destroy()
|
c.packetHandlers.Destroy()
|
||||||
|
@ -308,15 +308,15 @@ func (c *client) dial(ctx context.Context) error {
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// only set when we're using 0-RTT
|
// only set when we're using 0-RTT
|
||||||
// Otherwise, earlySessionChan will be nil. Receiving from a nil chan blocks forever.
|
// Otherwise, earlyConnChan will be nil. Receiving from a nil chan blocks forever.
|
||||||
var earlySessionChan <-chan struct{}
|
var earlyConnChan <-chan struct{}
|
||||||
if c.use0RTT {
|
if c.use0RTT {
|
||||||
earlySessionChan = c.session.earlySessionReady()
|
earlyConnChan = c.conn.earlyConnReady()
|
||||||
}
|
}
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
c.session.shutdown()
|
c.conn.shutdown()
|
||||||
return ctx.Err()
|
return ctx.Err()
|
||||||
case err := <-errorChan:
|
case err := <-errorChan:
|
||||||
var recreateErr *errCloseForRecreating
|
var recreateErr *errCloseForRecreating
|
||||||
|
@ -327,10 +327,10 @@ func (c *client) dial(ctx context.Context) error {
|
||||||
return c.dial(ctx)
|
return c.dial(ctx)
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
case <-earlySessionChan:
|
case <-earlyConnChan:
|
||||||
// ready to send 0-RTT data
|
// ready to send 0-RTT data
|
||||||
return nil
|
return nil
|
||||||
case <-c.session.HandshakeComplete().Done():
|
case <-c.conn.HandshakeComplete().Done():
|
||||||
// handshake successfully completed
|
// handshake successfully completed
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,15 +7,15 @@ import (
|
||||||
"github.com/lucas-clemente/quic-go/internal/utils"
|
"github.com/lucas-clemente/quic-go/internal/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
// A closedLocalSession is a session that we closed locally.
|
// A closedLocalConn is a connection that we closed locally.
|
||||||
// When receiving packets for such a session, we need to retransmit the packet containing the CONNECTION_CLOSE frame,
|
// When receiving packets for such a connection, we need to retransmit the packet containing the CONNECTION_CLOSE frame,
|
||||||
// with an exponential backoff.
|
// with an exponential backoff.
|
||||||
type closedLocalSession struct {
|
type closedLocalConn struct {
|
||||||
conn sendConn
|
conn sendConn
|
||||||
connClosePacket []byte
|
connClosePacket []byte
|
||||||
|
|
||||||
closeOnce sync.Once
|
closeOnce sync.Once
|
||||||
closeChan chan struct{} // is closed when the session is closed or destroyed
|
closeChan chan struct{} // is closed when the connection is closed or destroyed
|
||||||
|
|
||||||
receivedPackets chan *receivedPacket
|
receivedPackets chan *receivedPacket
|
||||||
counter uint64 // number of packets received
|
counter uint64 // number of packets received
|
||||||
|
@ -25,16 +25,16 @@ type closedLocalSession struct {
|
||||||
logger utils.Logger
|
logger utils.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ packetHandler = &closedLocalSession{}
|
var _ packetHandler = &closedLocalConn{}
|
||||||
|
|
||||||
// newClosedLocalSession creates a new closedLocalSession and runs it.
|
// newClosedLocalConn creates a new closedLocalConn and runs it.
|
||||||
func newClosedLocalSession(
|
func newClosedLocalConn(
|
||||||
conn sendConn,
|
conn sendConn,
|
||||||
connClosePacket []byte,
|
connClosePacket []byte,
|
||||||
perspective protocol.Perspective,
|
perspective protocol.Perspective,
|
||||||
logger utils.Logger,
|
logger utils.Logger,
|
||||||
) packetHandler {
|
) packetHandler {
|
||||||
s := &closedLocalSession{
|
s := &closedLocalConn{
|
||||||
conn: conn,
|
conn: conn,
|
||||||
connClosePacket: connClosePacket,
|
connClosePacket: connClosePacket,
|
||||||
perspective: perspective,
|
perspective: perspective,
|
||||||
|
@ -46,7 +46,7 @@ func newClosedLocalSession(
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *closedLocalSession) run() {
|
func (s *closedLocalConn) run() {
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case p := <-s.receivedPackets:
|
case p := <-s.receivedPackets:
|
||||||
|
@ -57,14 +57,14 @@ func (s *closedLocalSession) run() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *closedLocalSession) handlePacket(p *receivedPacket) {
|
func (s *closedLocalConn) handlePacket(p *receivedPacket) {
|
||||||
select {
|
select {
|
||||||
case s.receivedPackets <- p:
|
case s.receivedPackets <- p:
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *closedLocalSession) handlePacketImpl(_ *receivedPacket) {
|
func (s *closedLocalConn) handlePacketImpl(_ *receivedPacket) {
|
||||||
s.counter++
|
s.counter++
|
||||||
// exponential backoff
|
// exponential backoff
|
||||||
// only send a CONNECTION_CLOSE for the 1st, 2nd, 4th, 8th, 16th, ... packet arriving
|
// only send a CONNECTION_CLOSE for the 1st, 2nd, 4th, 8th, 16th, ... packet arriving
|
||||||
|
@ -79,34 +79,34 @@ func (s *closedLocalSession) handlePacketImpl(_ *receivedPacket) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *closedLocalSession) shutdown() {
|
func (s *closedLocalConn) shutdown() {
|
||||||
s.destroy(nil)
|
s.destroy(nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *closedLocalSession) destroy(error) {
|
func (s *closedLocalConn) destroy(error) {
|
||||||
s.closeOnce.Do(func() {
|
s.closeOnce.Do(func() {
|
||||||
close(s.closeChan)
|
close(s.closeChan)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *closedLocalSession) getPerspective() protocol.Perspective {
|
func (s *closedLocalConn) getPerspective() protocol.Perspective {
|
||||||
return s.perspective
|
return s.perspective
|
||||||
}
|
}
|
||||||
|
|
||||||
// A closedRemoteSession is a session that was closed remotely.
|
// A closedRemoteConn is a connection that was closed remotely.
|
||||||
// For such a session, we might receive reordered packets that were sent before the CONNECTION_CLOSE.
|
// For such a connection, we might receive reordered packets that were sent before the CONNECTION_CLOSE.
|
||||||
// We can just ignore those packets.
|
// We can just ignore those packets.
|
||||||
type closedRemoteSession struct {
|
type closedRemoteConn struct {
|
||||||
perspective protocol.Perspective
|
perspective protocol.Perspective
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ packetHandler = &closedRemoteSession{}
|
var _ packetHandler = &closedRemoteConn{}
|
||||||
|
|
||||||
func newClosedRemoteSession(pers protocol.Perspective) packetHandler {
|
func newClosedRemoteConn(pers protocol.Perspective) packetHandler {
|
||||||
return &closedRemoteSession{perspective: pers}
|
return &closedRemoteConn{perspective: pers}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *closedRemoteSession) handlePacket(*receivedPacket) {}
|
func (s *closedRemoteConn) handlePacket(*receivedPacket) {}
|
||||||
func (s *closedRemoteSession) shutdown() {}
|
func (s *closedRemoteConn) shutdown() {}
|
||||||
func (s *closedRemoteSession) destroy(error) {}
|
func (s *closedRemoteConn) destroy(error) {}
|
||||||
func (s *closedRemoteSession) getPerspective() protocol.Perspective { return s.perspective }
|
func (s *closedRemoteConn) getPerspective() protocol.Perspective { return s.perspective }
|
|
@ -109,11 +109,12 @@ func populateConfig(config *Config) *Config {
|
||||||
HandshakeIdleTimeout: handshakeIdleTimeout,
|
HandshakeIdleTimeout: handshakeIdleTimeout,
|
||||||
MaxIdleTimeout: idleTimeout,
|
MaxIdleTimeout: idleTimeout,
|
||||||
AcceptToken: config.AcceptToken,
|
AcceptToken: config.AcceptToken,
|
||||||
KeepAlive: config.KeepAlive,
|
KeepAlivePeriod: config.KeepAlivePeriod,
|
||||||
InitialStreamReceiveWindow: initialStreamReceiveWindow,
|
InitialStreamReceiveWindow: initialStreamReceiveWindow,
|
||||||
MaxStreamReceiveWindow: maxStreamReceiveWindow,
|
MaxStreamReceiveWindow: maxStreamReceiveWindow,
|
||||||
InitialConnectionReceiveWindow: initialConnectionReceiveWindow,
|
InitialConnectionReceiveWindow: initialConnectionReceiveWindow,
|
||||||
MaxConnectionReceiveWindow: maxConnectionReceiveWindow,
|
MaxConnectionReceiveWindow: maxConnectionReceiveWindow,
|
||||||
|
AllowConnectionWindowIncrease: config.AllowConnectionWindowIncrease,
|
||||||
MaxIncomingStreams: maxIncomingStreams,
|
MaxIncomingStreams: maxIncomingStreams,
|
||||||
MaxIncomingUniStreams: maxIncomingUniStreams,
|
MaxIncomingUniStreams: maxIncomingUniStreams,
|
||||||
ConnectionIDLength: config.ConnectionIDLength,
|
ConnectionIDLength: config.ConnectionIDLength,
|
||||||
|
|
|
@ -90,7 +90,7 @@ func (p *receivedPacket) Clone() *receivedPacket {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type sessionRunner interface {
|
type connRunner interface {
|
||||||
Add(protocol.ConnectionID, packetHandler) bool
|
Add(protocol.ConnectionID, packetHandler) bool
|
||||||
GetStatelessResetToken(protocol.ConnectionID) protocol.StatelessResetToken
|
GetStatelessResetToken(protocol.ConnectionID) protocol.StatelessResetToken
|
||||||
Retire(protocol.ConnectionID)
|
Retire(protocol.ConnectionID)
|
||||||
|
@ -124,18 +124,14 @@ type errCloseForRecreating struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *errCloseForRecreating) Error() string {
|
func (e *errCloseForRecreating) Error() string {
|
||||||
return "closing session in order to recreate it"
|
return "closing connection in order to recreate it"
|
||||||
}
|
}
|
||||||
|
|
||||||
var sessionTracingID uint64 // to be accessed atomically
|
var connTracingID uint64 // to be accessed atomically
|
||||||
func nextSessionTracingID() uint64 { return atomic.AddUint64(&sessionTracingID, 1) }
|
func nextConnTracingID() uint64 { return atomic.AddUint64(&connTracingID, 1) }
|
||||||
|
|
||||||
func pathMTUDiscoveryEnabled(config *Config) bool {
|
// A Connection is a QUIC connection
|
||||||
return !disablePathMTUDiscovery && !config.DisablePathMTUDiscovery
|
type connection struct {
|
||||||
}
|
|
||||||
|
|
||||||
// A Session is a QUIC session
|
|
||||||
type session struct {
|
|
||||||
// Destination connection ID used during the handshake.
|
// Destination connection ID used during the handshake.
|
||||||
// Used to check source connection ID on incoming packets.
|
// Used to check source connection ID on incoming packets.
|
||||||
handshakeDestConnID protocol.ConnectionID
|
handshakeDestConnID protocol.ConnectionID
|
||||||
|
@ -192,7 +188,7 @@ type session struct {
|
||||||
undecryptablePacketsToProcess []*receivedPacket
|
undecryptablePacketsToProcess []*receivedPacket
|
||||||
|
|
||||||
clientHelloWritten <-chan *wire.TransportParameters
|
clientHelloWritten <-chan *wire.TransportParameters
|
||||||
earlySessionReadyChan chan struct{}
|
earlyConnReadyChan chan struct{}
|
||||||
handshakeCompleteChan chan struct{} // is closed when the handshake completes
|
handshakeCompleteChan chan struct{} // is closed when the handshake completes
|
||||||
handshakeComplete bool
|
handshakeComplete bool
|
||||||
handshakeConfirmed bool
|
handshakeConfirmed bool
|
||||||
|
@ -201,8 +197,8 @@ type session struct {
|
||||||
versionNegotiated bool
|
versionNegotiated bool
|
||||||
receivedFirstPacket bool
|
receivedFirstPacket bool
|
||||||
|
|
||||||
idleTimeout time.Duration
|
idleTimeout time.Duration
|
||||||
sessionCreationTime time.Time
|
creationTime time.Time
|
||||||
// The idle timeout is set based on the max of the time we received the last packet...
|
// The idle timeout is set based on the max of the time we received the last packet...
|
||||||
lastPacketReceivedTime time.Time
|
lastPacketReceivedTime time.Time
|
||||||
// ... and the time we sent a new ack-eliciting packet after receiving a packet.
|
// ... and the time we sent a new ack-eliciting packet after receiving a packet.
|
||||||
|
@ -226,15 +222,15 @@ type session struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
_ Session = &session{}
|
_ Connection = &connection{}
|
||||||
_ EarlySession = &session{}
|
_ EarlyConnection = &connection{}
|
||||||
_ streamSender = &session{}
|
_ streamSender = &connection{}
|
||||||
deadlineSendImmediately = time.Time{}.Add(42 * time.Millisecond) // any value > time.Time{} and before time.Now() is fine
|
deadlineSendImmediately = time.Time{}.Add(42 * time.Millisecond) // any value > time.Time{} and before time.Now() is fine
|
||||||
)
|
)
|
||||||
|
|
||||||
var newSession = func(
|
var newConnection = func(
|
||||||
conn sendConn,
|
conn sendConn,
|
||||||
runner sessionRunner,
|
runner connRunner,
|
||||||
origDestConnID protocol.ConnectionID,
|
origDestConnID protocol.ConnectionID,
|
||||||
retrySrcConnID *protocol.ConnectionID,
|
retrySrcConnID *protocol.ConnectionID,
|
||||||
clientDestConnID protocol.ConnectionID,
|
clientDestConnID protocol.ConnectionID,
|
||||||
|
@ -249,8 +245,8 @@ var newSession = func(
|
||||||
tracingID uint64,
|
tracingID uint64,
|
||||||
logger utils.Logger,
|
logger utils.Logger,
|
||||||
v protocol.VersionNumber,
|
v protocol.VersionNumber,
|
||||||
) quicSession {
|
) quicConn {
|
||||||
s := &session{
|
s := &connection{
|
||||||
conn: conn,
|
conn: conn,
|
||||||
config: conf,
|
config: conf,
|
||||||
handshakeDestConnID: destConnID,
|
handshakeDestConnID: destConnID,
|
||||||
|
@ -286,7 +282,7 @@ var newSession = func(
|
||||||
s.version,
|
s.version,
|
||||||
)
|
)
|
||||||
s.preSetup()
|
s.preSetup()
|
||||||
s.ctx, s.ctxCancel = context.WithCancel(context.WithValue(context.Background(), SessionTracingKey, tracingID))
|
s.ctx, s.ctxCancel = context.WithCancel(context.WithValue(context.Background(), ConnectionTracingKey, tracingID))
|
||||||
s.sentPacketHandler, s.receivedPacketHandler = ackhandler.NewAckHandler(
|
s.sentPacketHandler, s.receivedPacketHandler = ackhandler.NewAckHandler(
|
||||||
0,
|
0,
|
||||||
getMaxPacketSize(s.conn.RemoteAddr()),
|
getMaxPacketSize(s.conn.RemoteAddr()),
|
||||||
|
@ -369,9 +365,9 @@ var newSession = func(
|
||||||
}
|
}
|
||||||
|
|
||||||
// declare this as a variable, such that we can it mock it in the tests
|
// declare this as a variable, such that we can it mock it in the tests
|
||||||
var newClientSession = func(
|
var newClientConnection = func(
|
||||||
conn sendConn,
|
conn sendConn,
|
||||||
runner sessionRunner,
|
runner connRunner,
|
||||||
destConnID protocol.ConnectionID,
|
destConnID protocol.ConnectionID,
|
||||||
srcConnID protocol.ConnectionID,
|
srcConnID protocol.ConnectionID,
|
||||||
conf *Config,
|
conf *Config,
|
||||||
|
@ -383,8 +379,8 @@ var newClientSession = func(
|
||||||
tracingID uint64,
|
tracingID uint64,
|
||||||
logger utils.Logger,
|
logger utils.Logger,
|
||||||
v protocol.VersionNumber,
|
v protocol.VersionNumber,
|
||||||
) quicSession {
|
) quicConn {
|
||||||
s := &session{
|
s := &connection{
|
||||||
conn: conn,
|
conn: conn,
|
||||||
config: conf,
|
config: conf,
|
||||||
origDestConnID: destConnID,
|
origDestConnID: destConnID,
|
||||||
|
@ -416,7 +412,7 @@ var newClientSession = func(
|
||||||
s.version,
|
s.version,
|
||||||
)
|
)
|
||||||
s.preSetup()
|
s.preSetup()
|
||||||
s.ctx, s.ctxCancel = context.WithCancel(context.WithValue(context.Background(), SessionTracingKey, tracingID))
|
s.ctx, s.ctxCancel = context.WithCancel(context.WithValue(context.Background(), ConnectionTracingKey, tracingID))
|
||||||
s.sentPacketHandler, s.receivedPacketHandler = ackhandler.NewAckHandler(
|
s.sentPacketHandler, s.receivedPacketHandler = ackhandler.NewAckHandler(
|
||||||
initialPacketNumber,
|
initialPacketNumber,
|
||||||
getMaxPacketSize(s.conn.RemoteAddr()),
|
getMaxPacketSize(s.conn.RemoteAddr()),
|
||||||
|
@ -500,7 +496,7 @@ var newClientSession = func(
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *session) preSetup() {
|
func (s *connection) preSetup() {
|
||||||
s.sendQueue = newSendQueue(s.conn)
|
s.sendQueue = newSendQueue(s.conn)
|
||||||
s.retransmissionQueue = newRetransmissionQueue(s.version)
|
s.retransmissionQueue = newRetransmissionQueue(s.version)
|
||||||
s.frameParser = wire.NewFrameParser(s.config.EnableDatagrams, s.version)
|
s.frameParser = wire.NewFrameParser(s.config.EnableDatagrams, s.version)
|
||||||
|
@ -509,10 +505,16 @@ func (s *session) preSetup() {
|
||||||
protocol.ByteCount(s.config.InitialConnectionReceiveWindow),
|
protocol.ByteCount(s.config.InitialConnectionReceiveWindow),
|
||||||
protocol.ByteCount(s.config.MaxConnectionReceiveWindow),
|
protocol.ByteCount(s.config.MaxConnectionReceiveWindow),
|
||||||
s.onHasConnectionWindowUpdate,
|
s.onHasConnectionWindowUpdate,
|
||||||
|
func(size protocol.ByteCount) bool {
|
||||||
|
if s.config.AllowConnectionWindowIncrease == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return s.config.AllowConnectionWindowIncrease(s, uint64(size))
|
||||||
|
},
|
||||||
s.rttStats,
|
s.rttStats,
|
||||||
s.logger,
|
s.logger,
|
||||||
)
|
)
|
||||||
s.earlySessionReadyChan = make(chan struct{})
|
s.earlyConnReadyChan = make(chan struct{})
|
||||||
s.streamsMap = newStreamsMap(
|
s.streamsMap = newStreamsMap(
|
||||||
s,
|
s,
|
||||||
s.newFlowController,
|
s.newFlowController,
|
||||||
|
@ -522,14 +524,14 @@ func (s *session) preSetup() {
|
||||||
s.version,
|
s.version,
|
||||||
)
|
)
|
||||||
s.framer = newFramer(s.streamsMap, s.version)
|
s.framer = newFramer(s.streamsMap, s.version)
|
||||||
s.receivedPackets = make(chan *receivedPacket, protocol.MaxSessionUnprocessedPackets)
|
s.receivedPackets = make(chan *receivedPacket, protocol.MaxConnUnprocessedPackets)
|
||||||
s.closeChan = make(chan closeError, 1)
|
s.closeChan = make(chan closeError, 1)
|
||||||
s.sendingScheduled = make(chan struct{}, 1)
|
s.sendingScheduled = make(chan struct{}, 1)
|
||||||
s.handshakeCtx, s.handshakeCtxCancel = context.WithCancel(context.Background())
|
s.handshakeCtx, s.handshakeCtxCancel = context.WithCancel(context.Background())
|
||||||
|
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
s.lastPacketReceivedTime = now
|
s.lastPacketReceivedTime = now
|
||||||
s.sessionCreationTime = now
|
s.creationTime = now
|
||||||
|
|
||||||
s.windowUpdateQueue = newWindowUpdateQueue(s.streamsMap, s.connFlowController, s.framer.QueueControlFrame)
|
s.windowUpdateQueue = newWindowUpdateQueue(s.streamsMap, s.connFlowController, s.framer.QueueControlFrame)
|
||||||
if s.config.EnableDatagrams {
|
if s.config.EnableDatagrams {
|
||||||
|
@ -537,8 +539,8 @@ func (s *session) preSetup() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// run the session main loop
|
// run the connection main loop
|
||||||
func (s *session) run() error {
|
func (s *connection) run() error {
|
||||||
defer s.ctxCancel()
|
defer s.ctxCancel()
|
||||||
|
|
||||||
s.timer = utils.NewTimer()
|
s.timer = utils.NewTimer()
|
||||||
|
@ -556,7 +558,7 @@ func (s *session) run() error {
|
||||||
s.scheduleSending()
|
s.scheduleSending()
|
||||||
if zeroRTTParams != nil {
|
if zeroRTTParams != nil {
|
||||||
s.restoreTransportParameters(zeroRTTParams)
|
s.restoreTransportParameters(zeroRTTParams)
|
||||||
close(s.earlySessionReadyChan)
|
close(s.earlyConnReadyChan)
|
||||||
}
|
}
|
||||||
case closeErr := <-s.closeChan:
|
case closeErr := <-s.closeChan:
|
||||||
// put the close error back into the channel, so that the run loop can receive it
|
// put the close error back into the channel, so that the run loop can receive it
|
||||||
|
@ -590,7 +592,7 @@ runLoop:
|
||||||
if processed := s.handlePacketImpl(p); processed {
|
if processed := s.handlePacketImpl(p); processed {
|
||||||
processedUndecryptablePacket = true
|
processedUndecryptablePacket = true
|
||||||
}
|
}
|
||||||
// Don't set timers and send packets if the packet made us close the session.
|
// Don't set timers and send packets if the packet made us close the connection.
|
||||||
select {
|
select {
|
||||||
case closeErr = <-s.closeChan:
|
case closeErr = <-s.closeChan:
|
||||||
break runLoop
|
break runLoop
|
||||||
|
@ -613,7 +615,7 @@ runLoop:
|
||||||
case <-sendQueueAvailable:
|
case <-sendQueueAvailable:
|
||||||
case firstPacket := <-s.receivedPackets:
|
case firstPacket := <-s.receivedPackets:
|
||||||
wasProcessed := s.handlePacketImpl(firstPacket)
|
wasProcessed := s.handlePacketImpl(firstPacket)
|
||||||
// Don't set timers and send packets if the packet made us close the session.
|
// Don't set timers and send packets if the packet made us close the connection.
|
||||||
select {
|
select {
|
||||||
case closeErr = <-s.closeChan:
|
case closeErr = <-s.closeChan:
|
||||||
break runLoop
|
break runLoop
|
||||||
|
@ -662,11 +664,11 @@ runLoop:
|
||||||
}
|
}
|
||||||
|
|
||||||
if keepAliveTime := s.nextKeepAliveTime(); !keepAliveTime.IsZero() && !now.Before(keepAliveTime) {
|
if keepAliveTime := s.nextKeepAliveTime(); !keepAliveTime.IsZero() && !now.Before(keepAliveTime) {
|
||||||
// send a PING frame since there is no activity in the session
|
// send a PING frame since there is no activity in the connection
|
||||||
s.logger.Debugf("Sending a keep-alive PING to keep the connection alive.")
|
s.logger.Debugf("Sending a keep-alive PING to keep the connection alive.")
|
||||||
s.framer.QueueControlFrame(&wire.PingFrame{})
|
s.framer.QueueControlFrame(&wire.PingFrame{})
|
||||||
s.keepAlivePingSent = true
|
s.keepAlivePingSent = true
|
||||||
} else if !s.handshakeComplete && now.Sub(s.sessionCreationTime) >= s.config.handshakeTimeout() {
|
} else if !s.handshakeComplete && now.Sub(s.creationTime) >= s.config.handshakeTimeout() {
|
||||||
s.destroyImpl(qerr.ErrHandshakeTimeout)
|
s.destroyImpl(qerr.ErrHandshakeTimeout)
|
||||||
continue
|
continue
|
||||||
} else {
|
} else {
|
||||||
|
@ -705,24 +707,24 @@ runLoop:
|
||||||
return closeErr.err
|
return closeErr.err
|
||||||
}
|
}
|
||||||
|
|
||||||
// blocks until the early session can be used
|
// blocks until the early connection can be used
|
||||||
func (s *session) earlySessionReady() <-chan struct{} {
|
func (s *connection) earlyConnReady() <-chan struct{} {
|
||||||
return s.earlySessionReadyChan
|
return s.earlyConnReadyChan
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *session) HandshakeComplete() context.Context {
|
func (s *connection) HandshakeComplete() context.Context {
|
||||||
return s.handshakeCtx
|
return s.handshakeCtx
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *session) Context() context.Context {
|
func (s *connection) Context() context.Context {
|
||||||
return s.ctx
|
return s.ctx
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *session) supportsDatagrams() bool {
|
func (s *connection) supportsDatagrams() bool {
|
||||||
return s.peerParams.MaxDatagramFrameSize != protocol.InvalidByteCount
|
return s.peerParams.MaxDatagramFrameSize != protocol.InvalidByteCount
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *session) ConnectionState() ConnectionState {
|
func (s *connection) ConnectionState() ConnectionState {
|
||||||
return ConnectionState{
|
return ConnectionState{
|
||||||
TLS: s.cryptoStreamHandler.ConnectionState(),
|
TLS: s.cryptoStreamHandler.ConnectionState(),
|
||||||
SupportsDatagrams: s.supportsDatagrams(),
|
SupportsDatagrams: s.supportsDatagrams(),
|
||||||
|
@ -731,18 +733,18 @@ func (s *session) ConnectionState() ConnectionState {
|
||||||
|
|
||||||
// Time when the next keep-alive packet should be sent.
|
// Time when the next keep-alive packet should be sent.
|
||||||
// It returns a zero time if no keep-alive should be sent.
|
// It returns a zero time if no keep-alive should be sent.
|
||||||
func (s *session) nextKeepAliveTime() time.Time {
|
func (s *connection) nextKeepAliveTime() time.Time {
|
||||||
if !s.config.KeepAlive || s.keepAlivePingSent || !s.firstAckElicitingPacketAfterIdleSentTime.IsZero() {
|
if s.config.KeepAlivePeriod == 0 || s.keepAlivePingSent || !s.firstAckElicitingPacketAfterIdleSentTime.IsZero() {
|
||||||
return time.Time{}
|
return time.Time{}
|
||||||
}
|
}
|
||||||
return s.lastPacketReceivedTime.Add(s.keepAliveInterval)
|
return s.lastPacketReceivedTime.Add(s.keepAliveInterval)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *session) maybeResetTimer() {
|
func (s *connection) maybeResetTimer() {
|
||||||
var deadline time.Time
|
var deadline time.Time
|
||||||
if !s.handshakeComplete {
|
if !s.handshakeComplete {
|
||||||
deadline = utils.MinTime(
|
deadline = utils.MinTime(
|
||||||
s.sessionCreationTime.Add(s.config.handshakeTimeout()),
|
s.creationTime.Add(s.config.handshakeTimeout()),
|
||||||
s.idleTimeoutStartTime().Add(s.config.HandshakeIdleTimeout),
|
s.idleTimeoutStartTime().Add(s.config.HandshakeIdleTimeout),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
|
@ -752,11 +754,6 @@ func (s *session) maybeResetTimer() {
|
||||||
deadline = s.idleTimeoutStartTime().Add(s.idleTimeout)
|
deadline = s.idleTimeoutStartTime().Add(s.idleTimeout)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if s.handshakeConfirmed && pathMTUDiscoveryEnabled(s.config) {
|
|
||||||
if probeTime := s.mtuDiscoverer.NextProbeTime(); !probeTime.IsZero() {
|
|
||||||
deadline = utils.MinTime(deadline, probeTime)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ackAlarm := s.receivedPacketHandler.GetAlarmTimeout(); !ackAlarm.IsZero() {
|
if ackAlarm := s.receivedPacketHandler.GetAlarmTimeout(); !ackAlarm.IsZero() {
|
||||||
deadline = utils.MinTime(deadline, ackAlarm)
|
deadline = utils.MinTime(deadline, ackAlarm)
|
||||||
|
@ -771,11 +768,11 @@ func (s *session) maybeResetTimer() {
|
||||||
s.timer.Reset(deadline)
|
s.timer.Reset(deadline)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *session) idleTimeoutStartTime() time.Time {
|
func (s *connection) idleTimeoutStartTime() time.Time {
|
||||||
return utils.MaxTime(s.lastPacketReceivedTime, s.firstAckElicitingPacketAfterIdleSentTime)
|
return utils.MaxTime(s.lastPacketReceivedTime, s.firstAckElicitingPacketAfterIdleSentTime)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *session) handleHandshakeComplete() {
|
func (s *connection) handleHandshakeComplete() {
|
||||||
s.handshakeComplete = true
|
s.handshakeComplete = true
|
||||||
s.handshakeCompleteChan = nil // prevent this case from ever being selected again
|
s.handshakeCompleteChan = nil // prevent this case from ever being selected again
|
||||||
defer s.handshakeCtxCancel()
|
defer s.handshakeCtxCancel()
|
||||||
|
@ -811,12 +808,12 @@ func (s *session) handleHandshakeComplete() {
|
||||||
s.queueControlFrame(&wire.HandshakeDoneFrame{})
|
s.queueControlFrame(&wire.HandshakeDoneFrame{})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *session) handleHandshakeConfirmed() {
|
func (s *connection) handleHandshakeConfirmed() {
|
||||||
s.handshakeConfirmed = true
|
s.handshakeConfirmed = true
|
||||||
s.sentPacketHandler.SetHandshakeConfirmed()
|
s.sentPacketHandler.SetHandshakeConfirmed()
|
||||||
s.cryptoStreamHandler.SetHandshakeConfirmed()
|
s.cryptoStreamHandler.SetHandshakeConfirmed()
|
||||||
|
|
||||||
if pathMTUDiscoveryEnabled(s.config) {
|
if !s.config.DisablePathMTUDiscovery {
|
||||||
maxPacketSize := s.peerParams.MaxUDPPayloadSize
|
maxPacketSize := s.peerParams.MaxUDPPayloadSize
|
||||||
if maxPacketSize == 0 {
|
if maxPacketSize == 0 {
|
||||||
maxPacketSize = protocol.MaxByteCount
|
maxPacketSize = protocol.MaxByteCount
|
||||||
|
@ -834,7 +831,7 @@ func (s *session) handleHandshakeConfirmed() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *session) handlePacketImpl(rp *receivedPacket) bool {
|
func (s *connection) handlePacketImpl(rp *receivedPacket) bool {
|
||||||
s.sentPacketHandler.ReceivedBytes(rp.Size())
|
s.sentPacketHandler.ReceivedBytes(rp.Size())
|
||||||
|
|
||||||
if wire.IsVersionNegotiationPacket(rp.data) {
|
if wire.IsVersionNegotiationPacket(rp.data) {
|
||||||
|
@ -902,7 +899,7 @@ func (s *session) handlePacketImpl(rp *receivedPacket) bool {
|
||||||
return processed
|
return processed
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *session) handleSinglePacket(p *receivedPacket, hdr *wire.Header) bool /* was the packet successfully processed */ {
|
func (s *connection) handleSinglePacket(p *receivedPacket, hdr *wire.Header) bool /* was the packet successfully processed */ {
|
||||||
var wasQueued bool
|
var wasQueued bool
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
|
@ -994,7 +991,7 @@ func (s *session) handleSinglePacket(p *receivedPacket, hdr *wire.Header) bool /
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *session) handleRetryPacket(hdr *wire.Header, data []byte) bool /* was this a valid Retry */ {
|
func (s *connection) handleRetryPacket(hdr *wire.Header, data []byte) bool /* was this a valid Retry */ {
|
||||||
if s.perspective == protocol.PerspectiveServer {
|
if s.perspective == protocol.PerspectiveServer {
|
||||||
if s.tracer != nil {
|
if s.tracer != nil {
|
||||||
s.tracer.DroppedPacket(logging.PacketTypeRetry, protocol.ByteCount(len(data)), logging.PacketDropUnexpectedPacket)
|
s.tracer.DroppedPacket(logging.PacketTypeRetry, protocol.ByteCount(len(data)), logging.PacketDropUnexpectedPacket)
|
||||||
|
@ -1056,7 +1053,7 @@ func (s *session) handleRetryPacket(hdr *wire.Header, data []byte) bool /* was t
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *session) handleVersionNegotiationPacket(p *receivedPacket) {
|
func (s *connection) handleVersionNegotiationPacket(p *receivedPacket) {
|
||||||
if s.perspective == protocol.PerspectiveServer || // servers never receive version negotiation packets
|
if s.perspective == protocol.PerspectiveServer || // servers never receive version negotiation packets
|
||||||
s.receivedFirstPacket || s.versionNegotiated { // ignore delayed / duplicated version negotiation packets
|
s.receivedFirstPacket || s.versionNegotiated { // ignore delayed / duplicated version negotiation packets
|
||||||
if s.tracer != nil {
|
if s.tracer != nil {
|
||||||
|
@ -1110,7 +1107,7 @@ func (s *session) handleVersionNegotiationPacket(p *receivedPacket) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *session) handleUnpackedPacket(
|
func (s *connection) handleUnpackedPacket(
|
||||||
packet *unpackedPacket,
|
packet *unpackedPacket,
|
||||||
ecn protocol.ECN,
|
ecn protocol.ECN,
|
||||||
rcvTime time.Time,
|
rcvTime time.Time,
|
||||||
|
@ -1142,10 +1139,10 @@ func (s *session) handleUnpackedPacket(
|
||||||
s.handshakeDestConnID = cid
|
s.handshakeDestConnID = cid
|
||||||
s.connIDManager.ChangeInitialConnID(cid)
|
s.connIDManager.ChangeInitialConnID(cid)
|
||||||
}
|
}
|
||||||
// We create the session as soon as we receive the first packet from the client.
|
// We create the connection as soon as we receive the first packet from the client.
|
||||||
// We do that before authenticating the packet.
|
// We do that before authenticating the packet.
|
||||||
// That means that if the source connection ID was corrupted,
|
// That means that if the source connection ID was corrupted,
|
||||||
// we might have create a session with an incorrect source connection ID.
|
// we might have create a connection with an incorrect source connection ID.
|
||||||
// Once we authenticate the first packet, we need to update it.
|
// Once we authenticate the first packet, we need to update it.
|
||||||
if s.perspective == protocol.PerspectiveServer {
|
if s.perspective == protocol.PerspectiveServer {
|
||||||
if !packet.hdr.SrcConnectionID.Equal(s.handshakeDestConnID) {
|
if !packet.hdr.SrcConnectionID.Equal(s.handshakeDestConnID) {
|
||||||
|
@ -1210,7 +1207,7 @@ func (s *session) handleUnpackedPacket(
|
||||||
return s.receivedPacketHandler.ReceivedPacket(packet.packetNumber, ecn, packet.encryptionLevel, rcvTime, isAckEliciting)
|
return s.receivedPacketHandler.ReceivedPacket(packet.packetNumber, ecn, packet.encryptionLevel, rcvTime, isAckEliciting)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *session) handleFrame(f wire.Frame, encLevel protocol.EncryptionLevel, destConnID protocol.ConnectionID) error {
|
func (s *connection) handleFrame(f wire.Frame, encLevel protocol.EncryptionLevel, destConnID protocol.ConnectionID) error {
|
||||||
var err error
|
var err error
|
||||||
wire.LogFrame(s.logger, f, false)
|
wire.LogFrame(s.logger, f, false)
|
||||||
switch frame := f.(type) {
|
switch frame := f.(type) {
|
||||||
|
@ -1258,9 +1255,9 @@ func (s *session) handleFrame(f wire.Frame, encLevel protocol.EncryptionLevel, d
|
||||||
}
|
}
|
||||||
|
|
||||||
// handlePacket is called by the server with a new packet
|
// handlePacket is called by the server with a new packet
|
||||||
func (s *session) handlePacket(p *receivedPacket) {
|
func (s *connection) handlePacket(p *receivedPacket) {
|
||||||
// Discard packets once the amount of queued packets is larger than
|
// Discard packets once the amount of queued packets is larger than
|
||||||
// the channel size, protocol.MaxSessionUnprocessedPackets
|
// the channel size, protocol.MaxConnUnprocessedPackets
|
||||||
select {
|
select {
|
||||||
case s.receivedPackets <- p:
|
case s.receivedPackets <- p:
|
||||||
default:
|
default:
|
||||||
|
@ -1270,7 +1267,7 @@ func (s *session) handlePacket(p *receivedPacket) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *session) handleConnectionCloseFrame(frame *wire.ConnectionCloseFrame) {
|
func (s *connection) handleConnectionCloseFrame(frame *wire.ConnectionCloseFrame) {
|
||||||
if frame.IsApplicationError {
|
if frame.IsApplicationError {
|
||||||
s.closeRemote(&qerr.ApplicationError{
|
s.closeRemote(&qerr.ApplicationError{
|
||||||
Remote: true,
|
Remote: true,
|
||||||
|
@ -1287,7 +1284,7 @@ func (s *session) handleConnectionCloseFrame(frame *wire.ConnectionCloseFrame) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *session) handleCryptoFrame(frame *wire.CryptoFrame, encLevel protocol.EncryptionLevel) error {
|
func (s *connection) handleCryptoFrame(frame *wire.CryptoFrame, encLevel protocol.EncryptionLevel) error {
|
||||||
encLevelChanged, err := s.cryptoStreamManager.HandleCryptoFrame(frame, encLevel)
|
encLevelChanged, err := s.cryptoStreamManager.HandleCryptoFrame(frame, encLevel)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -1300,7 +1297,7 @@ func (s *session) handleCryptoFrame(frame *wire.CryptoFrame, encLevel protocol.E
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *session) handleStreamFrame(frame *wire.StreamFrame) error {
|
func (s *connection) handleStreamFrame(frame *wire.StreamFrame) error {
|
||||||
str, err := s.streamsMap.GetOrOpenReceiveStream(frame.StreamID)
|
str, err := s.streamsMap.GetOrOpenReceiveStream(frame.StreamID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -1313,11 +1310,11 @@ func (s *session) handleStreamFrame(frame *wire.StreamFrame) error {
|
||||||
return str.handleStreamFrame(frame)
|
return str.handleStreamFrame(frame)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *session) handleMaxDataFrame(frame *wire.MaxDataFrame) {
|
func (s *connection) handleMaxDataFrame(frame *wire.MaxDataFrame) {
|
||||||
s.connFlowController.UpdateSendWindow(frame.MaximumData)
|
s.connFlowController.UpdateSendWindow(frame.MaximumData)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *session) handleMaxStreamDataFrame(frame *wire.MaxStreamDataFrame) error {
|
func (s *connection) handleMaxStreamDataFrame(frame *wire.MaxStreamDataFrame) error {
|
||||||
str, err := s.streamsMap.GetOrOpenSendStream(frame.StreamID)
|
str, err := s.streamsMap.GetOrOpenSendStream(frame.StreamID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -1330,11 +1327,11 @@ func (s *session) handleMaxStreamDataFrame(frame *wire.MaxStreamDataFrame) error
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *session) handleMaxStreamsFrame(frame *wire.MaxStreamsFrame) {
|
func (s *connection) handleMaxStreamsFrame(frame *wire.MaxStreamsFrame) {
|
||||||
s.streamsMap.HandleMaxStreamsFrame(frame)
|
s.streamsMap.HandleMaxStreamsFrame(frame)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *session) handleResetStreamFrame(frame *wire.ResetStreamFrame) error {
|
func (s *connection) handleResetStreamFrame(frame *wire.ResetStreamFrame) error {
|
||||||
str, err := s.streamsMap.GetOrOpenReceiveStream(frame.StreamID)
|
str, err := s.streamsMap.GetOrOpenReceiveStream(frame.StreamID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -1346,7 +1343,7 @@ func (s *session) handleResetStreamFrame(frame *wire.ResetStreamFrame) error {
|
||||||
return str.handleResetStreamFrame(frame)
|
return str.handleResetStreamFrame(frame)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *session) handleStopSendingFrame(frame *wire.StopSendingFrame) error {
|
func (s *connection) handleStopSendingFrame(frame *wire.StopSendingFrame) error {
|
||||||
str, err := s.streamsMap.GetOrOpenSendStream(frame.StreamID)
|
str, err := s.streamsMap.GetOrOpenSendStream(frame.StreamID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -1359,11 +1356,11 @@ func (s *session) handleStopSendingFrame(frame *wire.StopSendingFrame) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *session) handlePathChallengeFrame(frame *wire.PathChallengeFrame) {
|
func (s *connection) handlePathChallengeFrame(frame *wire.PathChallengeFrame) {
|
||||||
s.queueControlFrame(&wire.PathResponseFrame{Data: frame.Data})
|
s.queueControlFrame(&wire.PathResponseFrame{Data: frame.Data})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *session) handleNewTokenFrame(frame *wire.NewTokenFrame) error {
|
func (s *connection) handleNewTokenFrame(frame *wire.NewTokenFrame) error {
|
||||||
if s.perspective == protocol.PerspectiveServer {
|
if s.perspective == protocol.PerspectiveServer {
|
||||||
return &qerr.TransportError{
|
return &qerr.TransportError{
|
||||||
ErrorCode: qerr.ProtocolViolation,
|
ErrorCode: qerr.ProtocolViolation,
|
||||||
|
@ -1376,15 +1373,15 @@ func (s *session) handleNewTokenFrame(frame *wire.NewTokenFrame) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *session) handleNewConnectionIDFrame(f *wire.NewConnectionIDFrame) error {
|
func (s *connection) handleNewConnectionIDFrame(f *wire.NewConnectionIDFrame) error {
|
||||||
return s.connIDManager.Add(f)
|
return s.connIDManager.Add(f)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *session) handleRetireConnectionIDFrame(f *wire.RetireConnectionIDFrame, destConnID protocol.ConnectionID) error {
|
func (s *connection) handleRetireConnectionIDFrame(f *wire.RetireConnectionIDFrame, destConnID protocol.ConnectionID) error {
|
||||||
return s.connIDGenerator.Retire(f.SequenceNumber, destConnID)
|
return s.connIDGenerator.Retire(f.SequenceNumber, destConnID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *session) handleHandshakeDoneFrame() error {
|
func (s *connection) handleHandshakeDoneFrame() error {
|
||||||
if s.perspective == protocol.PerspectiveServer {
|
if s.perspective == protocol.PerspectiveServer {
|
||||||
return &qerr.TransportError{
|
return &qerr.TransportError{
|
||||||
ErrorCode: qerr.ProtocolViolation,
|
ErrorCode: qerr.ProtocolViolation,
|
||||||
|
@ -1397,7 +1394,7 @@ func (s *session) handleHandshakeDoneFrame() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *session) handleAckFrame(frame *wire.AckFrame, encLevel protocol.EncryptionLevel) error {
|
func (s *connection) handleAckFrame(frame *wire.AckFrame, encLevel protocol.EncryptionLevel) error {
|
||||||
acked1RTTPacket, err := s.sentPacketHandler.ReceivedAck(frame, encLevel, s.lastPacketReceivedTime)
|
acked1RTTPacket, err := s.sentPacketHandler.ReceivedAck(frame, encLevel, s.lastPacketReceivedTime)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -1411,7 +1408,7 @@ func (s *session) handleAckFrame(frame *wire.AckFrame, encLevel protocol.Encrypt
|
||||||
return s.cryptoStreamHandler.SetLargest1RTTAcked(frame.LargestAcked())
|
return s.cryptoStreamHandler.SetLargest1RTTAcked(frame.LargestAcked())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *session) handleDatagramFrame(f *wire.DatagramFrame) error {
|
func (s *connection) handleDatagramFrame(f *wire.DatagramFrame) error {
|
||||||
if f.Length(s.version) > protocol.ByteCount(s.config.MaxDatagramFrameSize) {
|
if f.Length(s.version) > protocol.ByteCount(s.config.MaxDatagramFrameSize) {
|
||||||
return &qerr.TransportError{
|
return &qerr.TransportError{
|
||||||
ErrorCode: qerr.ProtocolViolation,
|
ErrorCode: qerr.ProtocolViolation,
|
||||||
|
@ -1422,50 +1419,50 @@ func (s *session) handleDatagramFrame(f *wire.DatagramFrame) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// closeLocal closes the session and send a CONNECTION_CLOSE containing the error
|
// closeLocal closes the connection and send a CONNECTION_CLOSE containing the error
|
||||||
func (s *session) closeLocal(e error) {
|
func (s *connection) closeLocal(e error) {
|
||||||
s.closeOnce.Do(func() {
|
s.closeOnce.Do(func() {
|
||||||
if e == nil {
|
if e == nil {
|
||||||
s.logger.Infof("Closing session.")
|
s.logger.Infof("Closing connection.")
|
||||||
} else {
|
} else {
|
||||||
s.logger.Errorf("Closing session with error: %s", e)
|
s.logger.Errorf("Closing connection with error: %s", e)
|
||||||
}
|
}
|
||||||
s.closeChan <- closeError{err: e, immediate: false, remote: false}
|
s.closeChan <- closeError{err: e, immediate: false, remote: false}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// destroy closes the session without sending the error on the wire
|
// destroy closes the connection without sending the error on the wire
|
||||||
func (s *session) destroy(e error) {
|
func (s *connection) destroy(e error) {
|
||||||
s.destroyImpl(e)
|
s.destroyImpl(e)
|
||||||
<-s.ctx.Done()
|
<-s.ctx.Done()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *session) destroyImpl(e error) {
|
func (s *connection) destroyImpl(e error) {
|
||||||
s.closeOnce.Do(func() {
|
s.closeOnce.Do(func() {
|
||||||
if nerr, ok := e.(net.Error); ok && nerr.Timeout() {
|
if nerr, ok := e.(net.Error); ok && nerr.Timeout() {
|
||||||
s.logger.Errorf("Destroying session: %s", e)
|
s.logger.Errorf("Destroying connection: %s", e)
|
||||||
} else {
|
} else {
|
||||||
s.logger.Errorf("Destroying session with error: %s", e)
|
s.logger.Errorf("Destroying connection with error: %s", e)
|
||||||
}
|
}
|
||||||
s.closeChan <- closeError{err: e, immediate: true, remote: false}
|
s.closeChan <- closeError{err: e, immediate: true, remote: false}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *session) closeRemote(e error) {
|
func (s *connection) closeRemote(e error) {
|
||||||
s.closeOnce.Do(func() {
|
s.closeOnce.Do(func() {
|
||||||
s.logger.Errorf("Peer closed session with error: %s", e)
|
s.logger.Errorf("Peer closed connection with error: %s", e)
|
||||||
s.closeChan <- closeError{err: e, immediate: true, remote: true}
|
s.closeChan <- closeError{err: e, immediate: true, remote: true}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close the connection. It sends a NO_ERROR application error.
|
// Close the connection. It sends a NO_ERROR application error.
|
||||||
// It waits until the run loop has stopped before returning
|
// It waits until the run loop has stopped before returning
|
||||||
func (s *session) shutdown() {
|
func (s *connection) shutdown() {
|
||||||
s.closeLocal(nil)
|
s.closeLocal(nil)
|
||||||
<-s.ctx.Done()
|
<-s.ctx.Done()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *session) CloseWithError(code ApplicationErrorCode, desc string) error {
|
func (s *connection) CloseWithError(code ApplicationErrorCode, desc string) error {
|
||||||
s.closeLocal(&qerr.ApplicationError{
|
s.closeLocal(&qerr.ApplicationError{
|
||||||
ErrorCode: code,
|
ErrorCode: code,
|
||||||
ErrorMessage: desc,
|
ErrorMessage: desc,
|
||||||
|
@ -1474,7 +1471,7 @@ func (s *session) CloseWithError(code ApplicationErrorCode, desc string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *session) handleCloseError(closeErr *closeError) {
|
func (s *connection) handleCloseError(closeErr *closeError) {
|
||||||
e := closeErr.err
|
e := closeErr.err
|
||||||
if e == nil {
|
if e == nil {
|
||||||
e = &qerr.ApplicationError{}
|
e = &qerr.ApplicationError{}
|
||||||
|
@ -1518,7 +1515,7 @@ func (s *session) handleCloseError(closeErr *closeError) {
|
||||||
|
|
||||||
// If this is a remote close we're done here
|
// If this is a remote close we're done here
|
||||||
if closeErr.remote {
|
if closeErr.remote {
|
||||||
s.connIDGenerator.ReplaceWithClosed(newClosedRemoteSession(s.perspective))
|
s.connIDGenerator.ReplaceWithClosed(newClosedRemoteConn(s.perspective))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if closeErr.immediate {
|
if closeErr.immediate {
|
||||||
|
@ -1529,11 +1526,11 @@ func (s *session) handleCloseError(closeErr *closeError) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.logger.Debugf("Error sending CONNECTION_CLOSE: %s", err)
|
s.logger.Debugf("Error sending CONNECTION_CLOSE: %s", err)
|
||||||
}
|
}
|
||||||
cs := newClosedLocalSession(s.conn, connClosePacket, s.perspective, s.logger)
|
cs := newClosedLocalConn(s.conn, connClosePacket, s.perspective, s.logger)
|
||||||
s.connIDGenerator.ReplaceWithClosed(cs)
|
s.connIDGenerator.ReplaceWithClosed(cs)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *session) dropEncryptionLevel(encLevel protocol.EncryptionLevel) {
|
func (s *connection) dropEncryptionLevel(encLevel protocol.EncryptionLevel) {
|
||||||
s.sentPacketHandler.DropPackets(encLevel)
|
s.sentPacketHandler.DropPackets(encLevel)
|
||||||
s.receivedPacketHandler.DropPackets(encLevel)
|
s.receivedPacketHandler.DropPackets(encLevel)
|
||||||
if s.tracer != nil {
|
if s.tracer != nil {
|
||||||
|
@ -1551,7 +1548,7 @@ func (s *session) dropEncryptionLevel(encLevel protocol.EncryptionLevel) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// is called for the client, when restoring transport parameters saved for 0-RTT
|
// is called for the client, when restoring transport parameters saved for 0-RTT
|
||||||
func (s *session) restoreTransportParameters(params *wire.TransportParameters) {
|
func (s *connection) restoreTransportParameters(params *wire.TransportParameters) {
|
||||||
if s.logger.Debug() {
|
if s.logger.Debug() {
|
||||||
s.logger.Debugf("Restoring Transport Parameters: %s", params)
|
s.logger.Debugf("Restoring Transport Parameters: %s", params)
|
||||||
}
|
}
|
||||||
|
@ -1562,7 +1559,7 @@ func (s *session) restoreTransportParameters(params *wire.TransportParameters) {
|
||||||
s.streamsMap.UpdateLimits(params)
|
s.streamsMap.UpdateLimits(params)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *session) handleTransportParameters(params *wire.TransportParameters) {
|
func (s *connection) handleTransportParameters(params *wire.TransportParameters) {
|
||||||
if err := s.checkTransportParameters(params); err != nil {
|
if err := s.checkTransportParameters(params); err != nil {
|
||||||
s.closeLocal(&qerr.TransportError{
|
s.closeLocal(&qerr.TransportError{
|
||||||
ErrorCode: qerr.TransportParameterError,
|
ErrorCode: qerr.TransportParameterError,
|
||||||
|
@ -1574,13 +1571,13 @@ func (s *session) handleTransportParameters(params *wire.TransportParameters) {
|
||||||
// During a 0-RTT connection, we are only allowed to use the new transport parameters for 1-RTT packets.
|
// During a 0-RTT connection, we are only allowed to use the new transport parameters for 1-RTT packets.
|
||||||
if s.perspective == protocol.PerspectiveServer {
|
if s.perspective == protocol.PerspectiveServer {
|
||||||
s.applyTransportParameters()
|
s.applyTransportParameters()
|
||||||
// On the server side, the early session is ready as soon as we processed
|
// On the server side, the early connection is ready as soon as we processed
|
||||||
// the client's transport parameters.
|
// the client's transport parameters.
|
||||||
close(s.earlySessionReadyChan)
|
close(s.earlyConnReadyChan)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *session) checkTransportParameters(params *wire.TransportParameters) error {
|
func (s *connection) checkTransportParameters(params *wire.TransportParameters) error {
|
||||||
if s.logger.Debug() {
|
if s.logger.Debug() {
|
||||||
s.logger.Debugf("Processed Transport Parameters: %s", params)
|
s.logger.Debugf("Processed Transport Parameters: %s", params)
|
||||||
}
|
}
|
||||||
|
@ -1613,11 +1610,11 @@ func (s *session) checkTransportParameters(params *wire.TransportParameters) err
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *session) applyTransportParameters() {
|
func (s *connection) applyTransportParameters() {
|
||||||
params := s.peerParams
|
params := s.peerParams
|
||||||
// Our local idle timeout will always be > 0.
|
// Our local idle timeout will always be > 0.
|
||||||
s.idleTimeout = utils.MinNonZeroDuration(s.config.MaxIdleTimeout, params.MaxIdleTimeout)
|
s.idleTimeout = utils.MinNonZeroDuration(s.config.MaxIdleTimeout, params.MaxIdleTimeout)
|
||||||
s.keepAliveInterval = utils.MinDuration(s.idleTimeout/2, protocol.MaxKeepAliveInterval)
|
s.keepAliveInterval = utils.MinDuration(s.config.KeepAlivePeriod, utils.MinDuration(s.idleTimeout/2, protocol.MaxKeepAliveInterval))
|
||||||
s.streamsMap.UpdateLimits(params)
|
s.streamsMap.UpdateLimits(params)
|
||||||
s.packer.HandleTransportParameters(params)
|
s.packer.HandleTransportParameters(params)
|
||||||
s.frameParser.SetAckDelayExponent(params.AckDelayExponent)
|
s.frameParser.SetAckDelayExponent(params.AckDelayExponent)
|
||||||
|
@ -1634,7 +1631,7 @@ func (s *session) applyTransportParameters() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *session) sendPackets() error {
|
func (s *connection) sendPackets() error {
|
||||||
s.pacingDeadline = time.Time{}
|
s.pacingDeadline = time.Time{}
|
||||||
|
|
||||||
var sentPacket bool // only used in for packets sent in send mode SendAny
|
var sentPacket bool // only used in for packets sent in send mode SendAny
|
||||||
|
@ -1700,7 +1697,7 @@ func (s *session) sendPackets() error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *session) maybeSendAckOnlyPacket() error {
|
func (s *connection) maybeSendAckOnlyPacket() error {
|
||||||
packet, err := s.packer.MaybePackAckPacket(s.handshakeConfirmed)
|
packet, err := s.packer.MaybePackAckPacket(s.handshakeConfirmed)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -1712,7 +1709,7 @@ func (s *session) maybeSendAckOnlyPacket() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *session) sendProbePacket(encLevel protocol.EncryptionLevel) error {
|
func (s *connection) sendProbePacket(encLevel protocol.EncryptionLevel) error {
|
||||||
// Queue probe packets until we actually send out a packet,
|
// Queue probe packets until we actually send out a packet,
|
||||||
// or until there are no more packets to queue.
|
// or until there are no more packets to queue.
|
||||||
var packet *packedPacket
|
var packet *packedPacket
|
||||||
|
@ -1748,13 +1745,13 @@ func (s *session) sendProbePacket(encLevel protocol.EncryptionLevel) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if packet == nil || packet.packetContents == nil {
|
if packet == nil || packet.packetContents == nil {
|
||||||
return fmt.Errorf("session BUG: couldn't pack %s probe packet", encLevel)
|
return fmt.Errorf("connection BUG: couldn't pack %s probe packet", encLevel)
|
||||||
}
|
}
|
||||||
s.sendPackedPacket(packet, time.Now())
|
s.sendPackedPacket(packet, time.Now())
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *session) sendPacket() (bool, error) {
|
func (s *connection) sendPacket() (bool, error) {
|
||||||
if isBlocked, offset := s.connFlowController.IsNewlyBlocked(); isBlocked {
|
if isBlocked, offset := s.connFlowController.IsNewlyBlocked(); isBlocked {
|
||||||
s.framer.QueueControlFrame(&wire.DataBlockedFrame{MaximumData: offset})
|
s.framer.QueueControlFrame(&wire.DataBlockedFrame{MaximumData: offset})
|
||||||
}
|
}
|
||||||
|
@ -1777,7 +1774,7 @@ func (s *session) sendPacket() (bool, error) {
|
||||||
s.sendQueue.Send(packet.buffer)
|
s.sendQueue.Send(packet.buffer)
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
if pathMTUDiscoveryEnabled(s.config) && s.mtuDiscoverer.ShouldSendProbe(now) {
|
if !s.config.DisablePathMTUDiscovery && s.mtuDiscoverer.ShouldSendProbe(now) {
|
||||||
packet, err := s.packer.PackMTUProbePacket(s.mtuDiscoverer.GetPing())
|
packet, err := s.packer.PackMTUProbePacket(s.mtuDiscoverer.GetPing())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
|
@ -1793,7 +1790,7 @@ func (s *session) sendPacket() (bool, error) {
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *session) sendPackedPacket(packet *packedPacket, now time.Time) {
|
func (s *connection) sendPackedPacket(packet *packedPacket, now time.Time) {
|
||||||
if s.firstAckElicitingPacketAfterIdleSentTime.IsZero() && packet.IsAckEliciting() {
|
if s.firstAckElicitingPacketAfterIdleSentTime.IsZero() && packet.IsAckEliciting() {
|
||||||
s.firstAckElicitingPacketAfterIdleSentTime = now
|
s.firstAckElicitingPacketAfterIdleSentTime = now
|
||||||
}
|
}
|
||||||
|
@ -1803,7 +1800,7 @@ func (s *session) sendPackedPacket(packet *packedPacket, now time.Time) {
|
||||||
s.sendQueue.Send(packet.buffer)
|
s.sendQueue.Send(packet.buffer)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *session) sendConnectionClose(e error) ([]byte, error) {
|
func (s *connection) sendConnectionClose(e error) ([]byte, error) {
|
||||||
var packet *coalescedPacket
|
var packet *coalescedPacket
|
||||||
var err error
|
var err error
|
||||||
var transportErr *qerr.TransportError
|
var transportErr *qerr.TransportError
|
||||||
|
@ -1815,7 +1812,7 @@ func (s *session) sendConnectionClose(e error) ([]byte, error) {
|
||||||
} else {
|
} else {
|
||||||
packet, err = s.packer.PackConnectionClose(&qerr.TransportError{
|
packet, err = s.packer.PackConnectionClose(&qerr.TransportError{
|
||||||
ErrorCode: qerr.InternalError,
|
ErrorCode: qerr.InternalError,
|
||||||
ErrorMessage: fmt.Sprintf("session BUG: unspecified error type (msg: %s)", e.Error()),
|
ErrorMessage: fmt.Sprintf("connection BUG: unspecified error type (msg: %s)", e.Error()),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -1825,7 +1822,7 @@ func (s *session) sendConnectionClose(e error) ([]byte, error) {
|
||||||
return packet.buffer.Data, s.conn.Write(packet.buffer.Data)
|
return packet.buffer.Data, s.conn.Write(packet.buffer.Data)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *session) logPacketContents(p *packetContents) {
|
func (s *connection) logPacketContents(p *packetContents) {
|
||||||
// tracing
|
// tracing
|
||||||
if s.tracer != nil {
|
if s.tracer != nil {
|
||||||
frames := make([]logging.Frame, 0, len(p.frames))
|
frames := make([]logging.Frame, 0, len(p.frames))
|
||||||
|
@ -1848,7 +1845,7 @@ func (s *session) logPacketContents(p *packetContents) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *session) logCoalescedPacket(packet *coalescedPacket) {
|
func (s *connection) logCoalescedPacket(packet *coalescedPacket) {
|
||||||
if s.logger.Debug() {
|
if s.logger.Debug() {
|
||||||
if len(packet.packets) > 1 {
|
if len(packet.packets) > 1 {
|
||||||
s.logger.Debugf("-> Sending coalesced packet (%d parts, %d bytes) for connection %s", len(packet.packets), packet.buffer.Len(), s.logID)
|
s.logger.Debugf("-> Sending coalesced packet (%d parts, %d bytes) for connection %s", len(packet.packets), packet.buffer.Len(), s.logID)
|
||||||
|
@ -1861,7 +1858,7 @@ func (s *session) logCoalescedPacket(packet *coalescedPacket) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *session) logPacket(packet *packedPacket) {
|
func (s *connection) logPacket(packet *packedPacket) {
|
||||||
if s.logger.Debug() {
|
if s.logger.Debug() {
|
||||||
s.logger.Debugf("-> Sending packet %d (%d bytes) for connection %s, %s", packet.header.PacketNumber, packet.buffer.Len(), s.logID, packet.EncryptionLevel())
|
s.logger.Debugf("-> Sending packet %d (%d bytes) for connection %s, %s", packet.header.PacketNumber, packet.buffer.Len(), s.logID, packet.EncryptionLevel())
|
||||||
}
|
}
|
||||||
|
@ -1869,32 +1866,32 @@ func (s *session) logPacket(packet *packedPacket) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// AcceptStream returns the next stream openend by the peer
|
// AcceptStream returns the next stream openend by the peer
|
||||||
func (s *session) AcceptStream(ctx context.Context) (Stream, error) {
|
func (s *connection) AcceptStream(ctx context.Context) (Stream, error) {
|
||||||
return s.streamsMap.AcceptStream(ctx)
|
return s.streamsMap.AcceptStream(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *session) AcceptUniStream(ctx context.Context) (ReceiveStream, error) {
|
func (s *connection) AcceptUniStream(ctx context.Context) (ReceiveStream, error) {
|
||||||
return s.streamsMap.AcceptUniStream(ctx)
|
return s.streamsMap.AcceptUniStream(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
// OpenStream opens a stream
|
// OpenStream opens a stream
|
||||||
func (s *session) OpenStream() (Stream, error) {
|
func (s *connection) OpenStream() (Stream, error) {
|
||||||
return s.streamsMap.OpenStream()
|
return s.streamsMap.OpenStream()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *session) OpenStreamSync(ctx context.Context) (Stream, error) {
|
func (s *connection) OpenStreamSync(ctx context.Context) (Stream, error) {
|
||||||
return s.streamsMap.OpenStreamSync(ctx)
|
return s.streamsMap.OpenStreamSync(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *session) OpenUniStream() (SendStream, error) {
|
func (s *connection) OpenUniStream() (SendStream, error) {
|
||||||
return s.streamsMap.OpenUniStream()
|
return s.streamsMap.OpenUniStream()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *session) OpenUniStreamSync(ctx context.Context) (SendStream, error) {
|
func (s *connection) OpenUniStreamSync(ctx context.Context) (SendStream, error) {
|
||||||
return s.streamsMap.OpenUniStreamSync(ctx)
|
return s.streamsMap.OpenUniStreamSync(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *session) newFlowController(id protocol.StreamID) flowcontrol.StreamFlowController {
|
func (s *connection) newFlowController(id protocol.StreamID) flowcontrol.StreamFlowController {
|
||||||
initialSendWindow := s.peerParams.InitialMaxStreamDataUni
|
initialSendWindow := s.peerParams.InitialMaxStreamDataUni
|
||||||
if id.Type() == protocol.StreamTypeBidi {
|
if id.Type() == protocol.StreamTypeBidi {
|
||||||
if id.InitiatedBy() == s.perspective {
|
if id.InitiatedBy() == s.perspective {
|
||||||
|
@ -1916,14 +1913,14 @@ func (s *session) newFlowController(id protocol.StreamID) flowcontrol.StreamFlow
|
||||||
}
|
}
|
||||||
|
|
||||||
// scheduleSending signals that we have data for sending
|
// scheduleSending signals that we have data for sending
|
||||||
func (s *session) scheduleSending() {
|
func (s *connection) scheduleSending() {
|
||||||
select {
|
select {
|
||||||
case s.sendingScheduled <- struct{}{}:
|
case s.sendingScheduled <- struct{}{}:
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *session) tryQueueingUndecryptablePacket(p *receivedPacket, hdr *wire.Header) {
|
func (s *connection) tryQueueingUndecryptablePacket(p *receivedPacket, hdr *wire.Header) {
|
||||||
if s.handshakeComplete {
|
if s.handshakeComplete {
|
||||||
panic("shouldn't queue undecryptable packets after handshake completion")
|
panic("shouldn't queue undecryptable packets after handshake completion")
|
||||||
}
|
}
|
||||||
|
@ -1941,33 +1938,33 @@ func (s *session) tryQueueingUndecryptablePacket(p *receivedPacket, hdr *wire.He
|
||||||
s.undecryptablePackets = append(s.undecryptablePackets, p)
|
s.undecryptablePackets = append(s.undecryptablePackets, p)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *session) queueControlFrame(f wire.Frame) {
|
func (s *connection) queueControlFrame(f wire.Frame) {
|
||||||
s.framer.QueueControlFrame(f)
|
s.framer.QueueControlFrame(f)
|
||||||
s.scheduleSending()
|
s.scheduleSending()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *session) onHasStreamWindowUpdate(id protocol.StreamID) {
|
func (s *connection) onHasStreamWindowUpdate(id protocol.StreamID) {
|
||||||
s.windowUpdateQueue.AddStream(id)
|
s.windowUpdateQueue.AddStream(id)
|
||||||
s.scheduleSending()
|
s.scheduleSending()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *session) onHasConnectionWindowUpdate() {
|
func (s *connection) onHasConnectionWindowUpdate() {
|
||||||
s.windowUpdateQueue.AddConnection()
|
s.windowUpdateQueue.AddConnection()
|
||||||
s.scheduleSending()
|
s.scheduleSending()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *session) onHasStreamData(id protocol.StreamID) {
|
func (s *connection) onHasStreamData(id protocol.StreamID) {
|
||||||
s.framer.AddActiveStream(id)
|
s.framer.AddActiveStream(id)
|
||||||
s.scheduleSending()
|
s.scheduleSending()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *session) onStreamCompleted(id protocol.StreamID) {
|
func (s *connection) onStreamCompleted(id protocol.StreamID) {
|
||||||
if err := s.streamsMap.DeleteStream(id); err != nil {
|
if err := s.streamsMap.DeleteStream(id); err != nil {
|
||||||
s.closeLocal(err)
|
s.closeLocal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *session) SendMessage(p []byte) error {
|
func (s *connection) SendMessage(p []byte) error {
|
||||||
f := &wire.DatagramFrame{DataLenPresent: true}
|
f := &wire.DatagramFrame{DataLenPresent: true}
|
||||||
if protocol.ByteCount(len(p)) > f.MaxDataLen(s.peerParams.MaxDatagramFrameSize, s.version) {
|
if protocol.ByteCount(len(p)) > f.MaxDataLen(s.peerParams.MaxDatagramFrameSize, s.version) {
|
||||||
return errors.New("message too large")
|
return errors.New("message too large")
|
||||||
|
@ -1977,27 +1974,27 @@ func (s *session) SendMessage(p []byte) error {
|
||||||
return s.datagramQueue.AddAndWait(f)
|
return s.datagramQueue.AddAndWait(f)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *session) ReceiveMessage() ([]byte, error) {
|
func (s *connection) ReceiveMessage() ([]byte, error) {
|
||||||
return s.datagramQueue.Receive()
|
return s.datagramQueue.Receive()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *session) LocalAddr() net.Addr {
|
func (s *connection) LocalAddr() net.Addr {
|
||||||
return s.conn.LocalAddr()
|
return s.conn.LocalAddr()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *session) RemoteAddr() net.Addr {
|
func (s *connection) RemoteAddr() net.Addr {
|
||||||
return s.conn.RemoteAddr()
|
return s.conn.RemoteAddr()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *session) getPerspective() protocol.Perspective {
|
func (s *connection) getPerspective() protocol.Perspective {
|
||||||
return s.perspective
|
return s.perspective
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *session) GetVersion() protocol.VersionNumber {
|
func (s *connection) GetVersion() protocol.VersionNumber {
|
||||||
return s.version
|
return s.version
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *session) NextSession() Session {
|
func (s *connection) NextConnection() Connection {
|
||||||
<-s.HandshakeComplete().Done()
|
<-s.HandshakeComplete().Done()
|
||||||
s.streamsMap.UseResetMaps()
|
s.streamsMap.UseResetMaps()
|
||||||
return s
|
return s
|
|
@ -59,15 +59,15 @@ type TokenStore interface {
|
||||||
// when the server rejects a 0-RTT connection attempt.
|
// when the server rejects a 0-RTT connection attempt.
|
||||||
var Err0RTTRejected = errors.New("0-RTT rejected")
|
var Err0RTTRejected = errors.New("0-RTT rejected")
|
||||||
|
|
||||||
// SessionTracingKey can be used to associate a ConnectionTracer with a Session.
|
// ConnectionTracingKey can be used to associate a ConnectionTracer with a Connection.
|
||||||
// It is set on the Session.Context() context,
|
// It is set on the Connection.Context() context,
|
||||||
// as well as on the context passed to logging.Tracer.NewConnectionTracer.
|
// as well as on the context passed to logging.Tracer.NewConnectionTracer.
|
||||||
var SessionTracingKey = sessionTracingCtxKey{}
|
var ConnectionTracingKey = connTracingCtxKey{}
|
||||||
|
|
||||||
type sessionTracingCtxKey struct{}
|
type connTracingCtxKey struct{}
|
||||||
|
|
||||||
// Stream is the interface implemented by QUIC streams
|
// Stream is the interface implemented by QUIC streams
|
||||||
// In addition to the errors listed on the Session,
|
// In addition to the errors listed on the Connection,
|
||||||
// calls to stream functions can return a StreamError if the stream is canceled.
|
// calls to stream functions can return a StreamError if the stream is canceled.
|
||||||
type Stream interface {
|
type Stream interface {
|
||||||
ReceiveStream
|
ReceiveStream
|
||||||
|
@ -87,7 +87,7 @@ type ReceiveStream interface {
|
||||||
// after a fixed time limit; see SetDeadline and SetReadDeadline.
|
// after a fixed time limit; see SetDeadline and SetReadDeadline.
|
||||||
// If the stream was canceled by the peer, the error implements the StreamError
|
// If the stream was canceled by the peer, the error implements the StreamError
|
||||||
// interface, and Canceled() == true.
|
// interface, and Canceled() == true.
|
||||||
// If the session was closed due to a timeout, the error satisfies
|
// If the connection was closed due to a timeout, the error satisfies
|
||||||
// the net.Error interface, and Timeout() will be true.
|
// the net.Error interface, and Timeout() will be true.
|
||||||
io.Reader
|
io.Reader
|
||||||
// CancelRead aborts receiving on this stream.
|
// CancelRead aborts receiving on this stream.
|
||||||
|
@ -111,7 +111,7 @@ type SendStream interface {
|
||||||
// after a fixed time limit; see SetDeadline and SetWriteDeadline.
|
// after a fixed time limit; see SetDeadline and SetWriteDeadline.
|
||||||
// If the stream was canceled by the peer, the error implements the StreamError
|
// If the stream was canceled by the peer, the error implements the StreamError
|
||||||
// interface, and Canceled() == true.
|
// interface, and Canceled() == true.
|
||||||
// If the session was closed due to a timeout, the error satisfies
|
// If the connection was closed due to a timeout, the error satisfies
|
||||||
// the net.Error interface, and Timeout() will be true.
|
// the net.Error interface, and Timeout() will be true.
|
||||||
io.Writer
|
io.Writer
|
||||||
// Close closes the write-direction of the stream.
|
// Close closes the write-direction of the stream.
|
||||||
|
@ -124,7 +124,7 @@ type SendStream interface {
|
||||||
// Write will unblock immediately, and future calls to Write will fail.
|
// Write will unblock immediately, and future calls to Write will fail.
|
||||||
// When called multiple times or after closing the stream it is a no-op.
|
// When called multiple times or after closing the stream it is a no-op.
|
||||||
CancelWrite(StreamErrorCode)
|
CancelWrite(StreamErrorCode)
|
||||||
// The context is canceled as soon as the write-side of the stream is closed.
|
// The Context is canceled as soon as the write-side of the stream is closed.
|
||||||
// This happens when Close() or CancelWrite() is called, or when the peer
|
// This happens when Close() or CancelWrite() is called, or when the peer
|
||||||
// cancels the read-side of their stream.
|
// cancels the read-side of their stream.
|
||||||
// Warning: This API should not be considered stable and might change soon.
|
// Warning: This API should not be considered stable and might change soon.
|
||||||
|
@ -132,26 +132,26 @@ type SendStream interface {
|
||||||
// SetWriteDeadline sets the deadline for future Write calls
|
// SetWriteDeadline sets the deadline for future Write calls
|
||||||
// and any currently-blocked Write call.
|
// and any currently-blocked Write call.
|
||||||
// Even if write times out, it may return n > 0, indicating that
|
// Even if write times out, it may return n > 0, indicating that
|
||||||
// some of the data was successfully written.
|
// some data was successfully written.
|
||||||
// A zero value for t means Write will not time out.
|
// A zero value for t means Write will not time out.
|
||||||
SetWriteDeadline(t time.Time) error
|
SetWriteDeadline(t time.Time) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// A Session is a QUIC connection between two peers.
|
// A Connection is a QUIC connection between two peers.
|
||||||
// Calls to the session (and to streams) can return the following types of errors:
|
// Calls to the connection (and to streams) can return the following types of errors:
|
||||||
// * ApplicationError: for errors triggered by the application running on top of QUIC
|
// * ApplicationError: for errors triggered by the application running on top of QUIC
|
||||||
// * TransportError: for errors triggered by the QUIC transport (in many cases a misbehaving peer)
|
// * TransportError: for errors triggered by the QUIC transport (in many cases a misbehaving peer)
|
||||||
// * IdleTimeoutError: when the peer goes away unexpectedly (this is a net.Error timeout error)
|
// * IdleTimeoutError: when the peer goes away unexpectedly (this is a net.Error timeout error)
|
||||||
// * HandshakeTimeoutError: when the cryptographic handshake takes too long (this is a net.Error timeout error)
|
// * HandshakeTimeoutError: when the cryptographic handshake takes too long (this is a net.Error timeout error)
|
||||||
// * StatelessResetError: when we receive a stateless reset (this is a net.Error temporary error)
|
// * StatelessResetError: when we receive a stateless reset (this is a net.Error temporary error)
|
||||||
// * VersionNegotiationError: returned by the client, when there's no version overlap between the peers
|
// * VersionNegotiationError: returned by the client, when there's no version overlap between the peers
|
||||||
type Session interface {
|
type Connection interface {
|
||||||
// AcceptStream returns the next stream opened by the peer, blocking until one is available.
|
// AcceptStream returns the next stream opened by the peer, blocking until one is available.
|
||||||
// If the session was closed due to a timeout, the error satisfies
|
// If the connection was closed due to a timeout, the error satisfies
|
||||||
// the net.Error interface, and Timeout() will be true.
|
// the net.Error interface, and Timeout() will be true.
|
||||||
AcceptStream(context.Context) (Stream, error)
|
AcceptStream(context.Context) (Stream, error)
|
||||||
// AcceptUniStream returns the next unidirectional stream opened by the peer, blocking until one is available.
|
// AcceptUniStream returns the next unidirectional stream opened by the peer, blocking until one is available.
|
||||||
// If the session was closed due to a timeout, the error satisfies
|
// If the connection was closed due to a timeout, the error satisfies
|
||||||
// the net.Error interface, and Timeout() will be true.
|
// the net.Error interface, and Timeout() will be true.
|
||||||
AcceptUniStream(context.Context) (ReceiveStream, error)
|
AcceptUniStream(context.Context) (ReceiveStream, error)
|
||||||
// OpenStream opens a new bidirectional QUIC stream.
|
// OpenStream opens a new bidirectional QUIC stream.
|
||||||
|
@ -159,22 +159,22 @@ type Session interface {
|
||||||
// The peer can only accept the stream after data has been sent on the stream.
|
// The peer can only accept the stream after data has been sent on the stream.
|
||||||
// If the error is non-nil, it satisfies the net.Error interface.
|
// If the error is non-nil, it satisfies the net.Error interface.
|
||||||
// When reaching the peer's stream limit, err.Temporary() will be true.
|
// When reaching the peer's stream limit, err.Temporary() will be true.
|
||||||
// If the session was closed due to a timeout, Timeout() will be true.
|
// If the connection was closed due to a timeout, Timeout() will be true.
|
||||||
OpenStream() (Stream, error)
|
OpenStream() (Stream, error)
|
||||||
// OpenStreamSync opens a new bidirectional QUIC stream.
|
// OpenStreamSync opens a new bidirectional QUIC stream.
|
||||||
// It blocks until a new stream can be opened.
|
// It blocks until a new stream can be opened.
|
||||||
// If the error is non-nil, it satisfies the net.Error interface.
|
// If the error is non-nil, it satisfies the net.Error interface.
|
||||||
// If the session was closed due to a timeout, Timeout() will be true.
|
// If the connection was closed due to a timeout, Timeout() will be true.
|
||||||
OpenStreamSync(context.Context) (Stream, error)
|
OpenStreamSync(context.Context) (Stream, error)
|
||||||
// OpenUniStream opens a new outgoing unidirectional QUIC stream.
|
// OpenUniStream opens a new outgoing unidirectional QUIC stream.
|
||||||
// If the error is non-nil, it satisfies the net.Error interface.
|
// If the error is non-nil, it satisfies the net.Error interface.
|
||||||
// When reaching the peer's stream limit, Temporary() will be true.
|
// When reaching the peer's stream limit, Temporary() will be true.
|
||||||
// If the session was closed due to a timeout, Timeout() will be true.
|
// If the connection was closed due to a timeout, Timeout() will be true.
|
||||||
OpenUniStream() (SendStream, error)
|
OpenUniStream() (SendStream, error)
|
||||||
// OpenUniStreamSync opens a new outgoing unidirectional QUIC stream.
|
// OpenUniStreamSync opens a new outgoing unidirectional QUIC stream.
|
||||||
// It blocks until a new stream can be opened.
|
// It blocks until a new stream can be opened.
|
||||||
// If the error is non-nil, it satisfies the net.Error interface.
|
// If the error is non-nil, it satisfies the net.Error interface.
|
||||||
// If the session was closed due to a timeout, Timeout() will be true.
|
// If the connection was closed due to a timeout, Timeout() will be true.
|
||||||
OpenUniStreamSync(context.Context) (SendStream, error)
|
OpenUniStreamSync(context.Context) (SendStream, error)
|
||||||
// LocalAddr returns the local address.
|
// LocalAddr returns the local address.
|
||||||
LocalAddr() net.Addr
|
LocalAddr() net.Addr
|
||||||
|
@ -183,7 +183,7 @@ type Session interface {
|
||||||
// CloseWithError closes the connection with an error.
|
// CloseWithError closes the connection with an error.
|
||||||
// The error string will be sent to the peer.
|
// The error string will be sent to the peer.
|
||||||
CloseWithError(ApplicationErrorCode, string) error
|
CloseWithError(ApplicationErrorCode, string) error
|
||||||
// The context is cancelled when the session is closed.
|
// The context is cancelled when the connection is closed.
|
||||||
// Warning: This API should not be considered stable and might change soon.
|
// Warning: This API should not be considered stable and might change soon.
|
||||||
Context() context.Context
|
Context() context.Context
|
||||||
// ConnectionState returns basic details about the QUIC connection.
|
// ConnectionState returns basic details about the QUIC connection.
|
||||||
|
@ -199,19 +199,19 @@ type Session interface {
|
||||||
ReceiveMessage() ([]byte, error)
|
ReceiveMessage() ([]byte, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// An EarlySession is a session that is handshaking.
|
// An EarlyConnection is a connection that is handshaking.
|
||||||
// Data sent during the handshake is encrypted using the forward secure keys.
|
// Data sent during the handshake is encrypted using the forward secure keys.
|
||||||
// When using client certificates, the client's identity is only verified
|
// When using client certificates, the client's identity is only verified
|
||||||
// after completion of the handshake.
|
// after completion of the handshake.
|
||||||
type EarlySession interface {
|
type EarlyConnection interface {
|
||||||
Session
|
Connection
|
||||||
|
|
||||||
// Blocks until the handshake completes (or fails).
|
// HandshakeComplete blocks until the handshake completes (or fails).
|
||||||
// Data sent before completion of the handshake is encrypted with 1-RTT keys.
|
// Data sent before completion of the handshake is encrypted with 1-RTT keys.
|
||||||
// Note that the client's identity hasn't been verified yet.
|
// Note that the client's identity hasn't been verified yet.
|
||||||
HandshakeComplete() context.Context
|
HandshakeComplete() context.Context
|
||||||
|
|
||||||
NextSession() Session
|
NextConnection() Connection
|
||||||
}
|
}
|
||||||
|
|
||||||
// Config contains all configuration data needed for a QUIC server or client.
|
// Config contains all configuration data needed for a QUIC server or client.
|
||||||
|
@ -266,6 +266,13 @@ type Config struct {
|
||||||
// MaxConnectionReceiveWindow is the connection-level flow control window for receiving data.
|
// MaxConnectionReceiveWindow is the connection-level flow control window for receiving data.
|
||||||
// If this value is zero, it will default to 15 MB.
|
// If this value is zero, it will default to 15 MB.
|
||||||
MaxConnectionReceiveWindow uint64
|
MaxConnectionReceiveWindow uint64
|
||||||
|
// AllowConnectionWindowIncrease is called every time the connection flow controller attempts
|
||||||
|
// to increase the connection flow control window.
|
||||||
|
// If set, the caller can prevent an increase of the window. Typically, it would do so to
|
||||||
|
// limit the memory usage.
|
||||||
|
// To avoid deadlocks, it is not valid to call other functions on the connection or on streams
|
||||||
|
// in this callback.
|
||||||
|
AllowConnectionWindowIncrease func(sess Connection, delta uint64) bool
|
||||||
// MaxIncomingStreams is the maximum number of concurrent bidirectional streams that a peer is allowed to open.
|
// MaxIncomingStreams is the maximum number of concurrent bidirectional streams that a peer is allowed to open.
|
||||||
// Values above 2^60 are invalid.
|
// Values above 2^60 are invalid.
|
||||||
// If not set, it will default to 100.
|
// If not set, it will default to 100.
|
||||||
|
@ -279,11 +286,13 @@ type Config struct {
|
||||||
// The StatelessResetKey is used to generate stateless reset tokens.
|
// The StatelessResetKey is used to generate stateless reset tokens.
|
||||||
// If no key is configured, sending of stateless resets is disabled.
|
// If no key is configured, sending of stateless resets is disabled.
|
||||||
StatelessResetKey []byte
|
StatelessResetKey []byte
|
||||||
// KeepAlive defines whether this peer will periodically send a packet to keep the connection alive.
|
// KeepAlivePeriod defines whether this peer will periodically send a packet to keep the connection alive.
|
||||||
KeepAlive bool
|
// If set to 0, then no keep alive is sent. Otherwise, the keep alive is sent on that period (or at most
|
||||||
|
// every half of MaxIdleTimeout, whichever is smaller).
|
||||||
|
KeepAlivePeriod time.Duration
|
||||||
// DisablePathMTUDiscovery disables Path MTU Discovery (RFC 8899).
|
// DisablePathMTUDiscovery disables Path MTU Discovery (RFC 8899).
|
||||||
// Packets will then be at most 1252 (IPv4) / 1232 (IPv6) bytes in size.
|
// Packets will then be at most 1252 (IPv4) / 1232 (IPv6) bytes in size.
|
||||||
// Note that Path MTU discovery is always disabled on Windows, see https://github.com/lucas-clemente/quic-go/issues/3273.
|
// Note that if Path MTU discovery is causing issues on your system, please open a new issue
|
||||||
DisablePathMTUDiscovery bool
|
DisablePathMTUDiscovery bool
|
||||||
// DisableVersionNegotiationPackets disables the sending of Version Negotiation packets.
|
// DisableVersionNegotiationPackets disables the sending of Version Negotiation packets.
|
||||||
// This can be useful if version information is exchanged out-of-band.
|
// This can be useful if version information is exchanged out-of-band.
|
||||||
|
@ -304,21 +313,21 @@ type ConnectionState struct {
|
||||||
|
|
||||||
// A Listener for incoming QUIC connections
|
// A Listener for incoming QUIC connections
|
||||||
type Listener interface {
|
type Listener interface {
|
||||||
// Close the server. All active sessions will be closed.
|
// Close the server. All active connections will be closed.
|
||||||
Close() error
|
Close() error
|
||||||
// Addr returns the local network addr that the server is listening on.
|
// Addr returns the local network addr that the server is listening on.
|
||||||
Addr() net.Addr
|
Addr() net.Addr
|
||||||
// Accept returns new sessions. It should be called in a loop.
|
// Accept returns new connections. It should be called in a loop.
|
||||||
Accept(context.Context) (Session, error)
|
Accept(context.Context) (Connection, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// An EarlyListener listens for incoming QUIC connections,
|
// An EarlyListener listens for incoming QUIC connections,
|
||||||
// and returns them before the handshake completes.
|
// and returns them before the handshake completes.
|
||||||
type EarlyListener interface {
|
type EarlyListener interface {
|
||||||
// Close the server. All active sessions will be closed.
|
// Close the server. All active connections will be closed.
|
||||||
Close() error
|
Close() error
|
||||||
// Addr returns the local network addr that the server is listening on.
|
// Addr returns the local network addr that the server is listening on.
|
||||||
Addr() net.Addr
|
Addr() net.Addr
|
||||||
// Accept returns new early sessions. It should be called in a loop.
|
// Accept returns new early connections. It should be called in a loop.
|
||||||
Accept(context.Context) (EarlySession, error)
|
Accept(context.Context) (EarlyConnection, error)
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,8 @@ type baseFlowController struct {
|
||||||
receiveWindowSize protocol.ByteCount
|
receiveWindowSize protocol.ByteCount
|
||||||
maxReceiveWindowSize protocol.ByteCount
|
maxReceiveWindowSize protocol.ByteCount
|
||||||
|
|
||||||
|
allowWindowIncrease func(size protocol.ByteCount) bool
|
||||||
|
|
||||||
epochStartTime time.Time
|
epochStartTime time.Time
|
||||||
epochStartOffset protocol.ByteCount
|
epochStartOffset protocol.ByteCount
|
||||||
rttStats *utils.RTTStats
|
rttStats *utils.RTTStats
|
||||||
|
@ -105,7 +107,10 @@ func (c *baseFlowController) maybeAdjustWindowSize() {
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
if now.Sub(c.epochStartTime) < time.Duration(4*fraction*float64(rtt)) {
|
if now.Sub(c.epochStartTime) < time.Duration(4*fraction*float64(rtt)) {
|
||||||
// window is consumed too fast, try to increase the window size
|
// window is consumed too fast, try to increase the window size
|
||||||
c.receiveWindowSize = utils.MinByteCount(2*c.receiveWindowSize, c.maxReceiveWindowSize)
|
newSize := utils.MinByteCount(2*c.receiveWindowSize, c.maxReceiveWindowSize)
|
||||||
|
if newSize > c.receiveWindowSize && (c.allowWindowIncrease == nil || c.allowWindowIncrease(newSize-c.receiveWindowSize)) {
|
||||||
|
c.receiveWindowSize = newSize
|
||||||
|
}
|
||||||
}
|
}
|
||||||
c.startNewAutoTuningEpoch(now)
|
c.startNewAutoTuningEpoch(now)
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,11 +19,12 @@ type connectionFlowController struct {
|
||||||
var _ ConnectionFlowController = &connectionFlowController{}
|
var _ ConnectionFlowController = &connectionFlowController{}
|
||||||
|
|
||||||
// NewConnectionFlowController gets a new flow controller for the connection
|
// NewConnectionFlowController gets a new flow controller for the connection
|
||||||
// It is created before we receive the peer's transport paramenters, thus it starts with a sendWindow of 0.
|
// It is created before we receive the peer's transport parameters, thus it starts with a sendWindow of 0.
|
||||||
func NewConnectionFlowController(
|
func NewConnectionFlowController(
|
||||||
receiveWindow protocol.ByteCount,
|
receiveWindow protocol.ByteCount,
|
||||||
maxReceiveWindow protocol.ByteCount,
|
maxReceiveWindow protocol.ByteCount,
|
||||||
queueWindowUpdate func(),
|
queueWindowUpdate func(),
|
||||||
|
allowWindowIncrease func(size protocol.ByteCount) bool,
|
||||||
rttStats *utils.RTTStats,
|
rttStats *utils.RTTStats,
|
||||||
logger utils.Logger,
|
logger utils.Logger,
|
||||||
) ConnectionFlowController {
|
) ConnectionFlowController {
|
||||||
|
@ -33,6 +34,7 @@ func NewConnectionFlowController(
|
||||||
receiveWindow: receiveWindow,
|
receiveWindow: receiveWindow,
|
||||||
receiveWindowSize: receiveWindow,
|
receiveWindowSize: receiveWindow,
|
||||||
maxReceiveWindowSize: maxReceiveWindow,
|
maxReceiveWindowSize: maxReceiveWindow,
|
||||||
|
allowWindowIncrease: allowWindowIncrease,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
},
|
},
|
||||||
queueWindowUpdate: queueWindowUpdate,
|
queueWindowUpdate: queueWindowUpdate,
|
||||||
|
@ -85,13 +87,16 @@ func (c *connectionFlowController) EnsureMinimumWindowSize(inc protocol.ByteCoun
|
||||||
c.mutex.Lock()
|
c.mutex.Lock()
|
||||||
if inc > c.receiveWindowSize {
|
if inc > c.receiveWindowSize {
|
||||||
c.logger.Debugf("Increasing receive flow control window for the connection to %d kB, in response to stream flow control window increase", c.receiveWindowSize/(1<<10))
|
c.logger.Debugf("Increasing receive flow control window for the connection to %d kB, in response to stream flow control window increase", c.receiveWindowSize/(1<<10))
|
||||||
c.receiveWindowSize = utils.MinByteCount(inc, c.maxReceiveWindowSize)
|
newSize := utils.MinByteCount(inc, c.maxReceiveWindowSize)
|
||||||
|
if delta := newSize - c.receiveWindowSize; delta > 0 && c.allowWindowIncrease(delta) {
|
||||||
|
c.receiveWindowSize = newSize
|
||||||
|
}
|
||||||
c.startNewAutoTuningEpoch(time.Now())
|
c.startNewAutoTuningEpoch(time.Now())
|
||||||
}
|
}
|
||||||
c.mutex.Unlock()
|
c.mutex.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
// The flow controller is reset when 0-RTT is rejected.
|
// Reset rests the flow controller. This happens when 0-RTT is rejected.
|
||||||
// All stream data is invalidated, it's if we had never opened a stream and never sent any data.
|
// All stream data is invalidated, it's if we had never opened a stream and never sent any data.
|
||||||
// At that point, we only have sent stream data, but we didn't have the keys to open 1-RTT keys yet.
|
// At that point, we only have sent stream data, but we didn't have the keys to open 1-RTT keys yet.
|
||||||
func (c *connectionFlowController) Reset() error {
|
func (c *connectionFlowController) Reset() error {
|
||||||
|
|
|
@ -14,7 +14,7 @@ const InitialPacketSizeIPv6 = 1232
|
||||||
// MaxCongestionWindowPackets is the maximum congestion window in packet.
|
// MaxCongestionWindowPackets is the maximum congestion window in packet.
|
||||||
const MaxCongestionWindowPackets = 10000
|
const MaxCongestionWindowPackets = 10000
|
||||||
|
|
||||||
// MaxUndecryptablePackets limits the number of undecryptable packets that are queued in the session.
|
// MaxUndecryptablePackets limits the number of undecryptable packets that are queued in the connection.
|
||||||
const MaxUndecryptablePackets = 32
|
const MaxUndecryptablePackets = 32
|
||||||
|
|
||||||
// ConnectionFlowControlMultiplier determines how much larger the connection flow control windows needs to be relative to any stream's flow control window
|
// ConnectionFlowControlMultiplier determines how much larger the connection flow control windows needs to be relative to any stream's flow control window
|
||||||
|
@ -45,8 +45,8 @@ const DefaultMaxIncomingUniStreams = 100
|
||||||
// MaxServerUnprocessedPackets is the max number of packets stored in the server that are not yet processed.
|
// MaxServerUnprocessedPackets is the max number of packets stored in the server that are not yet processed.
|
||||||
const MaxServerUnprocessedPackets = 1024
|
const MaxServerUnprocessedPackets = 1024
|
||||||
|
|
||||||
// MaxSessionUnprocessedPackets is the max number of packets stored in each session that are not yet processed.
|
// MaxConnUnprocessedPackets is the max number of packets stored in each connection that are not yet processed.
|
||||||
const MaxSessionUnprocessedPackets = 256
|
const MaxConnUnprocessedPackets = 256
|
||||||
|
|
||||||
// SkipPacketInitialPeriod is the initial period length used for packet number skipping to prevent an Optimistic ACK attack.
|
// SkipPacketInitialPeriod is the initial period length used for packet number skipping to prevent an Optimistic ACK attack.
|
||||||
// Every time a packet number is skipped, the period is doubled, up to SkipPacketMaxPeriod.
|
// Every time a packet number is skipped, the period is doubled, up to SkipPacketMaxPeriod.
|
||||||
|
@ -55,7 +55,7 @@ const SkipPacketInitialPeriod PacketNumber = 256
|
||||||
// SkipPacketMaxPeriod is the maximum period length used for packet number skipping.
|
// SkipPacketMaxPeriod is the maximum period length used for packet number skipping.
|
||||||
const SkipPacketMaxPeriod PacketNumber = 128 * 1024
|
const SkipPacketMaxPeriod PacketNumber = 128 * 1024
|
||||||
|
|
||||||
// MaxAcceptQueueSize is the maximum number of sessions that the server queues for accepting.
|
// MaxAcceptQueueSize is the maximum number of connections that the server queues for accepting.
|
||||||
// If the queue is full, new connection attempts will be rejected.
|
// If the queue is full, new connection attempts will be rejected.
|
||||||
const MaxAcceptQueueSize = 32
|
const MaxAcceptQueueSize = 32
|
||||||
|
|
||||||
|
@ -112,7 +112,7 @@ const DefaultHandshakeTimeout = 10 * time.Second
|
||||||
// It should be shorter than the time that NATs clear their mapping.
|
// It should be shorter than the time that NATs clear their mapping.
|
||||||
const MaxKeepAliveInterval = 20 * time.Second
|
const MaxKeepAliveInterval = 20 * time.Second
|
||||||
|
|
||||||
// RetiredConnectionIDDeleteTimeout is the time we keep closed sessions around in order to retransmit the CONNECTION_CLOSE.
|
// RetiredConnectionIDDeleteTimeout is the time we keep closed connections around in order to retransmit the CONNECTION_CLOSE.
|
||||||
// after this time all information about the old connection will be deleted
|
// after this time all information about the old connection will be deleted
|
||||||
const RetiredConnectionIDDeleteTimeout = 5 * time.Second
|
const RetiredConnectionIDDeleteTimeout = 5 * time.Second
|
||||||
|
|
||||||
|
@ -189,7 +189,7 @@ const Max0RTTQueueingDuration = 100 * time.Millisecond
|
||||||
const Max0RTTQueues = 32
|
const Max0RTTQueues = 32
|
||||||
|
|
||||||
// Max0RTTQueueLen is the maximum number of 0-RTT packets that we buffer for each connection.
|
// Max0RTTQueueLen is the maximum number of 0-RTT packets that we buffer for each connection.
|
||||||
// When a new session is created, all buffered packets are passed to the session immediately.
|
// When a new connection is created, all buffered packets are passed to the connection immediately.
|
||||||
// To avoid blocking, this value has to be smaller than MaxSessionUnprocessedPackets.
|
// To avoid blocking, this value has to be smaller than MaxConnUnprocessedPackets.
|
||||||
// To avoid packets being dropped as undecryptable by the session, this value has to be smaller than MaxUndecryptablePackets.
|
// To avoid packets being dropped as undecryptable by the connection, this value has to be smaller than MaxUndecryptablePackets.
|
||||||
const Max0RTTQueueLen = 31
|
const Max0RTTQueueLen = 31
|
||||||
|
|
|
@ -3,4 +3,4 @@
|
||||||
|
|
||||||
package qtls
|
package qtls
|
||||||
|
|
||||||
var _ int = "quic-go doesn't build on Go 1.19 yet."
|
var _ int = "The version of quic-go you're using can't be built on Go 1.19 yet. For more details, please see https://github.com/lucas-clemente/quic-go/wiki/quic-go-and-Go-versions."
|
||||||
|
|
7
vendor/github.com/lucas-clemente/quic-go/internal/qtls/go_oldversion.go
generated
vendored
Normal file
7
vendor/github.com/lucas-clemente/quic-go/internal/qtls/go_oldversion.go
generated
vendored
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
//go:build (go1.9 || go1.10 || go1.11 || go1.12 || go1.13 || go1.14 || go1.15) && !go1.16
|
||||||
|
// +build go1.9 go1.10 go1.11 go1.12 go1.13 go1.14 go1.15
|
||||||
|
// +build !go1.16
|
||||||
|
|
||||||
|
package qtls
|
||||||
|
|
||||||
|
var _ int = "The version of quic-go you're using can't be built using outdated Go versions. For more details, please see https://github.com/lucas-clemente/quic-go/wiki/quic-go-and-Go-versions."
|
|
@ -68,14 +68,14 @@ const (
|
||||||
TimerTypePTO
|
TimerTypePTO
|
||||||
)
|
)
|
||||||
|
|
||||||
// TimeoutReason is the reason why a session is closed
|
// TimeoutReason is the reason why a connection is closed
|
||||||
type TimeoutReason uint8
|
type TimeoutReason uint8
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// TimeoutReasonHandshake is used when the session is closed due to a handshake timeout
|
// TimeoutReasonHandshake is used when the connection is closed due to a handshake timeout
|
||||||
// This reason is not defined in the qlog draft, but very useful for debugging.
|
// This reason is not defined in the qlog draft, but very useful for debugging.
|
||||||
TimeoutReasonHandshake TimeoutReason = iota
|
TimeoutReasonHandshake TimeoutReason = iota
|
||||||
// TimeoutReasonIdle is used when the session is closed due to an idle timeout
|
// TimeoutReasonIdle is used when the connection is closed due to an idle timeout
|
||||||
// This reason is not defined in the qlog draft, but very useful for debugging.
|
// This reason is not defined in the qlog draft, but very useful for debugging.
|
||||||
TimeoutReasonIdle
|
TimeoutReasonIdle
|
||||||
)
|
)
|
||||||
|
@ -87,7 +87,7 @@ const (
|
||||||
CongestionStateSlowStart CongestionState = iota
|
CongestionStateSlowStart CongestionState = iota
|
||||||
// CongestionStateCongestionAvoidance is the slow start phase of Reno / Cubic
|
// CongestionStateCongestionAvoidance is the slow start phase of Reno / Cubic
|
||||||
CongestionStateCongestionAvoidance
|
CongestionStateCongestionAvoidance
|
||||||
// CongestionStateCongestionAvoidance is the recovery phase of Reno / Cubic
|
// CongestionStateRecovery is the recovery phase of Reno / Cubic
|
||||||
CongestionStateRecovery
|
CongestionStateRecovery
|
||||||
// CongestionStateApplicationLimited means that the congestion controller is application limited
|
// CongestionStateApplicationLimited means that the congestion controller is application limited
|
||||||
CongestionStateApplicationLimited
|
CongestionStateApplicationLimited
|
||||||
|
|
|
@ -16,8 +16,8 @@ package quic
|
||||||
//go:generate sh -c "./mockgen_private.sh quic mock_unpacker_test.go github.com/lucas-clemente/quic-go unpacker"
|
//go:generate sh -c "./mockgen_private.sh quic mock_unpacker_test.go github.com/lucas-clemente/quic-go unpacker"
|
||||||
//go:generate sh -c "./mockgen_private.sh quic mock_packer_test.go github.com/lucas-clemente/quic-go packer"
|
//go:generate sh -c "./mockgen_private.sh quic mock_packer_test.go github.com/lucas-clemente/quic-go packer"
|
||||||
//go:generate sh -c "./mockgen_private.sh quic mock_mtu_discoverer_test.go github.com/lucas-clemente/quic-go mtuDiscoverer"
|
//go:generate sh -c "./mockgen_private.sh quic mock_mtu_discoverer_test.go github.com/lucas-clemente/quic-go mtuDiscoverer"
|
||||||
//go:generate sh -c "./mockgen_private.sh quic mock_session_runner_test.go github.com/lucas-clemente/quic-go sessionRunner"
|
//go:generate sh -c "./mockgen_private.sh quic mock_conn_runner_test.go github.com/lucas-clemente/quic-go connRunner"
|
||||||
//go:generate sh -c "./mockgen_private.sh quic mock_quic_session_test.go github.com/lucas-clemente/quic-go quicSession"
|
//go:generate sh -c "./mockgen_private.sh quic mock_quic_conn_test.go github.com/lucas-clemente/quic-go quicConn"
|
||||||
//go:generate sh -c "./mockgen_private.sh quic mock_packet_handler_test.go github.com/lucas-clemente/quic-go packetHandler"
|
//go:generate sh -c "./mockgen_private.sh quic mock_packet_handler_test.go github.com/lucas-clemente/quic-go packetHandler"
|
||||||
//go:generate sh -c "./mockgen_private.sh quic mock_unknown_packet_handler_test.go github.com/lucas-clemente/quic-go unknownPacketHandler"
|
//go:generate sh -c "./mockgen_private.sh quic mock_unknown_packet_handler_test.go github.com/lucas-clemente/quic-go unknownPacketHandler"
|
||||||
//go:generate sh -c "./mockgen_private.sh quic mock_packet_handler_manager_test.go github.com/lucas-clemente/quic-go packetHandlerManager"
|
//go:generate sh -c "./mockgen_private.sh quic mock_packet_handler_manager_test.go github.com/lucas-clemente/quic-go packetHandlerManager"
|
||||||
|
|
|
@ -11,7 +11,6 @@ import (
|
||||||
|
|
||||||
type mtuDiscoverer interface {
|
type mtuDiscoverer interface {
|
||||||
ShouldSendProbe(now time.Time) bool
|
ShouldSendProbe(now time.Time) bool
|
||||||
NextProbeTime() time.Time
|
|
||||||
GetPing() (ping ackhandler.Frame, datagramSize protocol.ByteCount)
|
GetPing() (ping ackhandler.Frame, datagramSize protocol.ByteCount)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,16 +52,7 @@ func (f *mtuFinder) ShouldSendProbe(now time.Time) bool {
|
||||||
if f.probeInFlight || f.done() {
|
if f.probeInFlight || f.done() {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return !now.Before(f.NextProbeTime())
|
return !now.Before(f.lastProbeTime.Add(mtuProbeDelay * f.rttStats.SmoothedRTT()))
|
||||||
}
|
|
||||||
|
|
||||||
// NextProbeTime returns the time when the next probe packet should be sent.
|
|
||||||
// It returns the zero value if no probe packet should be sent.
|
|
||||||
func (f *mtuFinder) NextProbeTime() time.Time {
|
|
||||||
if f.probeInFlight || f.done() {
|
|
||||||
return time.Time{}
|
|
||||||
}
|
|
||||||
return f.lastProbeTime.Add(mtuProbeDelay * f.rttStats.SmoothedRTT())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *mtuFinder) GetPing() (ackhandler.Frame, protocol.ByteCount) {
|
func (f *mtuFinder) GetPing() (ackhandler.Frame, protocol.ByteCount) {
|
||||||
|
|
|
@ -32,7 +32,7 @@ type connManager struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// The connMultiplexer listens on multiple net.PacketConns and dispatches
|
// The connMultiplexer listens on multiple net.PacketConns and dispatches
|
||||||
// incoming packets to the session handler.
|
// incoming packets to the connection handler.
|
||||||
type connMultiplexer struct {
|
type connMultiplexer struct {
|
||||||
mutex sync.Mutex
|
mutex sync.Mutex
|
||||||
|
|
||||||
|
|
|
@ -7,8 +7,12 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"hash"
|
"hash"
|
||||||
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -45,6 +49,14 @@ func (h *zeroRTTQueue) Clear() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// rawConn is a connection that allow reading of a receivedPacket.
|
||||||
|
type rawConn interface {
|
||||||
|
ReadPacket() (*receivedPacket, error)
|
||||||
|
WritePacket(b []byte, addr net.Addr, oob []byte) (int, error)
|
||||||
|
LocalAddr() net.Addr
|
||||||
|
io.Closer
|
||||||
|
}
|
||||||
|
|
||||||
type packetHandlerMapEntry struct {
|
type packetHandlerMapEntry struct {
|
||||||
packetHandler packetHandler
|
packetHandler packetHandler
|
||||||
is0RTTQueue bool
|
is0RTTQueue bool
|
||||||
|
@ -52,12 +64,12 @@ type packetHandlerMapEntry struct {
|
||||||
|
|
||||||
// The packetHandlerMap stores packetHandlers, identified by connection ID.
|
// The packetHandlerMap stores packetHandlers, identified by connection ID.
|
||||||
// It is used:
|
// It is used:
|
||||||
// * by the server to store sessions
|
// * by the server to store connections
|
||||||
// * when multiplexing outgoing connections to store clients
|
// * when multiplexing outgoing connections to store clients
|
||||||
type packetHandlerMap struct {
|
type packetHandlerMap struct {
|
||||||
mutex sync.Mutex
|
mutex sync.Mutex
|
||||||
|
|
||||||
conn connection
|
conn rawConn
|
||||||
connIDLen int
|
connIDLen int
|
||||||
|
|
||||||
handlers map[string] /* string(ConnectionID)*/ packetHandlerMapEntry
|
handlers map[string] /* string(ConnectionID)*/ packetHandlerMapEntry
|
||||||
|
@ -68,8 +80,8 @@ type packetHandlerMap struct {
|
||||||
listening chan struct{} // is closed when listen returns
|
listening chan struct{} // is closed when listen returns
|
||||||
closed bool
|
closed bool
|
||||||
|
|
||||||
deleteRetiredSessionsAfter time.Duration
|
deleteRetiredConnsAfter time.Duration
|
||||||
zeroRTTQueueDuration time.Duration
|
zeroRTTQueueDuration time.Duration
|
||||||
|
|
||||||
statelessResetEnabled bool
|
statelessResetEnabled bool
|
||||||
statelessResetMutex sync.Mutex
|
statelessResetMutex sync.Mutex
|
||||||
|
@ -110,7 +122,7 @@ func setReceiveBuffer(c net.PacketConn, logger utils.Logger) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// only print warnings about the UPD receive buffer size once
|
// only print warnings about the UDP receive buffer size once
|
||||||
var receiveBufferWarningOnce sync.Once
|
var receiveBufferWarningOnce sync.Once
|
||||||
|
|
||||||
func newPacketHandlerMap(
|
func newPacketHandlerMap(
|
||||||
|
@ -121,26 +133,31 @@ func newPacketHandlerMap(
|
||||||
logger utils.Logger,
|
logger utils.Logger,
|
||||||
) (packetHandlerManager, error) {
|
) (packetHandlerManager, error) {
|
||||||
if err := setReceiveBuffer(c, logger); err != nil {
|
if err := setReceiveBuffer(c, logger); err != nil {
|
||||||
receiveBufferWarningOnce.Do(func() {
|
if !strings.Contains(err.Error(), "use of closed network connection") {
|
||||||
log.Printf("%s. See https://github.com/lucas-clemente/quic-go/wiki/UDP-Receive-Buffer-Size for details.", err)
|
receiveBufferWarningOnce.Do(func() {
|
||||||
})
|
if disable, _ := strconv.ParseBool(os.Getenv("QUIC_GO_DISABLE_RECEIVE_BUFFER_WARNING")); disable {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Printf("%s. See https://github.com/lucas-clemente/quic-go/wiki/UDP-Receive-Buffer-Size for details.", err)
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
conn, err := wrapConn(c)
|
conn, err := wrapConn(c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
m := &packetHandlerMap{
|
m := &packetHandlerMap{
|
||||||
conn: conn,
|
conn: conn,
|
||||||
connIDLen: connIDLen,
|
connIDLen: connIDLen,
|
||||||
listening: make(chan struct{}),
|
listening: make(chan struct{}),
|
||||||
handlers: make(map[string]packetHandlerMapEntry),
|
handlers: make(map[string]packetHandlerMapEntry),
|
||||||
resetTokens: make(map[protocol.StatelessResetToken]packetHandler),
|
resetTokens: make(map[protocol.StatelessResetToken]packetHandler),
|
||||||
deleteRetiredSessionsAfter: protocol.RetiredConnectionIDDeleteTimeout,
|
deleteRetiredConnsAfter: protocol.RetiredConnectionIDDeleteTimeout,
|
||||||
zeroRTTQueueDuration: protocol.Max0RTTQueueingDuration,
|
zeroRTTQueueDuration: protocol.Max0RTTQueueingDuration,
|
||||||
statelessResetEnabled: len(statelessResetKey) > 0,
|
statelessResetEnabled: len(statelessResetKey) > 0,
|
||||||
statelessResetHasher: hmac.New(sha256.New, statelessResetKey),
|
statelessResetHasher: hmac.New(sha256.New, statelessResetKey),
|
||||||
tracer: tracer,
|
tracer: tracer,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
}
|
}
|
||||||
go m.listen()
|
go m.listen()
|
||||||
|
|
||||||
|
@ -196,7 +213,7 @@ func (h *packetHandlerMap) AddWithConnID(clientDestConnID, newConnID protocol.Co
|
||||||
var q *zeroRTTQueue
|
var q *zeroRTTQueue
|
||||||
if entry, ok := h.handlers[string(clientDestConnID)]; ok {
|
if entry, ok := h.handlers[string(clientDestConnID)]; ok {
|
||||||
if !entry.is0RTTQueue {
|
if !entry.is0RTTQueue {
|
||||||
h.logger.Debugf("Not adding connection ID %s for a new session, as it already exists.", clientDestConnID)
|
h.logger.Debugf("Not adding connection ID %s for a new connection, as it already exists.", clientDestConnID)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
q = entry.packetHandler.(*zeroRTTQueue)
|
q = entry.packetHandler.(*zeroRTTQueue)
|
||||||
|
@ -212,7 +229,7 @@ func (h *packetHandlerMap) AddWithConnID(clientDestConnID, newConnID protocol.Co
|
||||||
}
|
}
|
||||||
h.handlers[string(clientDestConnID)] = packetHandlerMapEntry{packetHandler: sess}
|
h.handlers[string(clientDestConnID)] = packetHandlerMapEntry{packetHandler: sess}
|
||||||
h.handlers[string(newConnID)] = packetHandlerMapEntry{packetHandler: sess}
|
h.handlers[string(newConnID)] = packetHandlerMapEntry{packetHandler: sess}
|
||||||
h.logger.Debugf("Adding connection IDs %s and %s for a new session.", clientDestConnID, newConnID)
|
h.logger.Debugf("Adding connection IDs %s and %s for a new connection.", clientDestConnID, newConnID)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -224,8 +241,8 @@ func (h *packetHandlerMap) Remove(id protocol.ConnectionID) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *packetHandlerMap) Retire(id protocol.ConnectionID) {
|
func (h *packetHandlerMap) Retire(id protocol.ConnectionID) {
|
||||||
h.logger.Debugf("Retiring connection ID %s in %s.", id, h.deleteRetiredSessionsAfter)
|
h.logger.Debugf("Retiring connection ID %s in %s.", id, h.deleteRetiredConnsAfter)
|
||||||
time.AfterFunc(h.deleteRetiredSessionsAfter, func() {
|
time.AfterFunc(h.deleteRetiredConnsAfter, func() {
|
||||||
h.mutex.Lock()
|
h.mutex.Lock()
|
||||||
delete(h.handlers, string(id))
|
delete(h.handlers, string(id))
|
||||||
h.mutex.Unlock()
|
h.mutex.Unlock()
|
||||||
|
@ -237,14 +254,14 @@ func (h *packetHandlerMap) ReplaceWithClosed(id protocol.ConnectionID, handler p
|
||||||
h.mutex.Lock()
|
h.mutex.Lock()
|
||||||
h.handlers[string(id)] = packetHandlerMapEntry{packetHandler: handler}
|
h.handlers[string(id)] = packetHandlerMapEntry{packetHandler: handler}
|
||||||
h.mutex.Unlock()
|
h.mutex.Unlock()
|
||||||
h.logger.Debugf("Replacing session for connection ID %s with a closed session.", id)
|
h.logger.Debugf("Replacing connection for connection ID %s with a closed connection.", id)
|
||||||
|
|
||||||
time.AfterFunc(h.deleteRetiredSessionsAfter, func() {
|
time.AfterFunc(h.deleteRetiredConnsAfter, func() {
|
||||||
h.mutex.Lock()
|
h.mutex.Lock()
|
||||||
handler.shutdown()
|
handler.shutdown()
|
||||||
delete(h.handlers, string(id))
|
delete(h.handlers, string(id))
|
||||||
h.mutex.Unlock()
|
h.mutex.Unlock()
|
||||||
h.logger.Debugf("Removing connection ID %s for a closed session after it has been retired.", id)
|
h.logger.Debugf("Removing connection ID %s for a closed connection after it has been retired.", id)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -289,7 +306,7 @@ func (h *packetHandlerMap) CloseServer() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Destroy closes the underlying connection and waits until listen() has returned.
|
// Destroy closes the underlying connection and waits until listen() has returned.
|
||||||
// It does not close active sessions.
|
// It does not close active connections.
|
||||||
func (h *packetHandlerMap) Destroy() error {
|
func (h *packetHandlerMap) Destroy() error {
|
||||||
if err := h.conn.Close(); err != nil {
|
if err := h.conn.Close(); err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -327,6 +344,10 @@ func (h *packetHandlerMap) listen() {
|
||||||
defer close(h.listening)
|
defer close(h.listening)
|
||||||
for {
|
for {
|
||||||
p, err := h.conn.ReadPacket()
|
p, err := h.conn.ReadPacket()
|
||||||
|
//nolint:staticcheck // SA1019 ignore this!
|
||||||
|
// TODO: This code is used to ignore wsa errors on Windows.
|
||||||
|
// Since net.Error.Temporary is deprecated as of Go 1.18, we should find a better solution.
|
||||||
|
// See https://github.com/lucas-clemente/quic-go/issues/1737 for details.
|
||||||
if nerr, ok := err.(net.Error); ok && nerr.Temporary() {
|
if nerr, ok := err.(net.Error); ok && nerr.Temporary() {
|
||||||
h.logger.Debugf("Temporary error reading from conn: %w", err)
|
h.logger.Debugf("Temporary error reading from conn: %w", err)
|
||||||
continue
|
continue
|
||||||
|
@ -363,7 +384,7 @@ func (h *packetHandlerMap) handlePacket(p *receivedPacket) {
|
||||||
entry.packetHandler.handlePacket(p)
|
entry.packetHandler.handlePacket(p)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
} else { // existing session
|
} else { // existing connection
|
||||||
entry.packetHandler.handlePacket(p)
|
entry.packetHandler.handlePacket(p)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -389,7 +410,7 @@ func (h *packetHandlerMap) handlePacket(p *receivedPacket) {
|
||||||
queue.retireTimer = time.AfterFunc(h.zeroRTTQueueDuration, func() {
|
queue.retireTimer = time.AfterFunc(h.zeroRTTQueueDuration, func() {
|
||||||
h.mutex.Lock()
|
h.mutex.Lock()
|
||||||
defer h.mutex.Unlock()
|
defer h.mutex.Unlock()
|
||||||
// The entry might have been replaced by an actual session.
|
// The entry might have been replaced by an actual connection.
|
||||||
// Only delete it if it's still a 0-RTT queue.
|
// Only delete it if it's still a 0-RTT queue.
|
||||||
if entry, ok := h.handlers[string(connID)]; ok && entry.is0RTTQueue {
|
if entry, ok := h.handlers[string(connID)]; ok && entry.is0RTTQueue {
|
||||||
delete(h.handlers, string(connID))
|
delete(h.handlers, string(connID))
|
||||||
|
@ -421,7 +442,7 @@ func (h *packetHandlerMap) maybeHandleStatelessReset(data []byte) bool {
|
||||||
var token protocol.StatelessResetToken
|
var token protocol.StatelessResetToken
|
||||||
copy(token[:], data[len(data)-16:])
|
copy(token[:], data[len(data)-16:])
|
||||||
if sess, ok := h.resetTokens[token]; ok {
|
if sess, ok := h.resetTokens[token]; ok {
|
||||||
h.logger.Debugf("Received a stateless reset with token %#x. Closing session.", token)
|
h.logger.Debugf("Received a stateless reset with token %#x. Closing connection.", token)
|
||||||
go sess.destroy(&StatelessResetError{Token: token})
|
go sess.destroy(&StatelessResetError{Token: token})
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@ type sendConn interface {
|
||||||
}
|
}
|
||||||
|
|
||||||
type sconn struct {
|
type sconn struct {
|
||||||
connection
|
rawConn
|
||||||
|
|
||||||
remoteAddr net.Addr
|
remoteAddr net.Addr
|
||||||
info *packetInfo
|
info *packetInfo
|
||||||
|
@ -22,9 +22,9 @@ type sconn struct {
|
||||||
|
|
||||||
var _ sendConn = &sconn{}
|
var _ sendConn = &sconn{}
|
||||||
|
|
||||||
func newSendConn(c connection, remote net.Addr, info *packetInfo) sendConn {
|
func newSendConn(c rawConn, remote net.Addr, info *packetInfo) sendConn {
|
||||||
return &sconn{
|
return &sconn{
|
||||||
connection: c,
|
rawConn: c,
|
||||||
remoteAddr: remote,
|
remoteAddr: remote,
|
||||||
info: info,
|
info: info,
|
||||||
oob: info.OOB(),
|
oob: info.OOB(),
|
||||||
|
@ -41,7 +41,7 @@ func (c *sconn) RemoteAddr() net.Addr {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *sconn) LocalAddr() net.Addr {
|
func (c *sconn) LocalAddr() net.Addr {
|
||||||
addr := c.connection.LocalAddr()
|
addr := c.rawConn.LocalAddr()
|
||||||
if c.info != nil {
|
if c.info != nil {
|
||||||
if udpAddr, ok := addr.(*net.UDPAddr); ok {
|
if udpAddr, ok := addr.(*net.UDPAddr); ok {
|
||||||
addrCopy := *udpAddr
|
addrCopy := *udpAddr
|
||||||
|
|
|
@ -64,7 +64,13 @@ func (h *sendQueue) Run() error {
|
||||||
shouldClose = true
|
shouldClose = true
|
||||||
case p := <-h.queue:
|
case p := <-h.queue:
|
||||||
if err := h.conn.Write(p.Data); err != nil {
|
if err := h.conn.Write(p.Data); err != nil {
|
||||||
return err
|
// This additional check enables:
|
||||||
|
// 1. Checking for "datagram too large" message from the kernel, as such,
|
||||||
|
// 2. Path MTU discovery,and
|
||||||
|
// 3. Eventual detection of loss PingFrame.
|
||||||
|
if !isMsgSizeErr(err) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
p.Release()
|
p.Release()
|
||||||
select {
|
select {
|
||||||
|
|
|
@ -36,14 +36,14 @@ type unknownPacketHandler interface {
|
||||||
type packetHandlerManager interface {
|
type packetHandlerManager interface {
|
||||||
AddWithConnID(protocol.ConnectionID, protocol.ConnectionID, func() packetHandler) bool
|
AddWithConnID(protocol.ConnectionID, protocol.ConnectionID, func() packetHandler) bool
|
||||||
Destroy() error
|
Destroy() error
|
||||||
sessionRunner
|
connRunner
|
||||||
SetServer(unknownPacketHandler)
|
SetServer(unknownPacketHandler)
|
||||||
CloseServer()
|
CloseServer()
|
||||||
}
|
}
|
||||||
|
|
||||||
type quicSession interface {
|
type quicConn interface {
|
||||||
EarlySession
|
EarlyConnection
|
||||||
earlySessionReady() <-chan struct{}
|
earlyConnReady() <-chan struct{}
|
||||||
handlePacket(*receivedPacket)
|
handlePacket(*receivedPacket)
|
||||||
GetVersion() protocol.VersionNumber
|
GetVersion() protocol.VersionNumber
|
||||||
getPerspective() protocol.Perspective
|
getPerspective() protocol.Perspective
|
||||||
|
@ -56,26 +56,26 @@ type quicSession interface {
|
||||||
type baseServer struct {
|
type baseServer struct {
|
||||||
mutex sync.Mutex
|
mutex sync.Mutex
|
||||||
|
|
||||||
acceptEarlySessions bool
|
acceptEarlyConns bool
|
||||||
|
|
||||||
tlsConf *tls.Config
|
tlsConf *tls.Config
|
||||||
config *Config
|
config *Config
|
||||||
|
|
||||||
conn connection
|
conn rawConn
|
||||||
// If the server is started with ListenAddr, we create a packet conn.
|
// If the server is started with ListenAddr, we create a packet conn.
|
||||||
// If it is started with Listen, we take a packet conn as a parameter.
|
// If it is started with Listen, we take a packet conn as a parameter.
|
||||||
createdPacketConn bool
|
createdPacketConn bool
|
||||||
|
|
||||||
tokenGenerator *handshake.TokenGenerator
|
tokenGenerator *handshake.TokenGenerator
|
||||||
|
|
||||||
sessionHandler packetHandlerManager
|
connHandler packetHandlerManager
|
||||||
|
|
||||||
receivedPackets chan *receivedPacket
|
receivedPackets chan *receivedPacket
|
||||||
|
|
||||||
// set as a member, so they can be set in the tests
|
// set as a member, so they can be set in the tests
|
||||||
newSession func(
|
newConn func(
|
||||||
sendConn,
|
sendConn,
|
||||||
sessionRunner,
|
connRunner,
|
||||||
protocol.ConnectionID, /* original dest connection ID */
|
protocol.ConnectionID, /* original dest connection ID */
|
||||||
*protocol.ConnectionID, /* retry src connection ID */
|
*protocol.ConnectionID, /* retry src connection ID */
|
||||||
protocol.ConnectionID, /* client dest connection ID */
|
protocol.ConnectionID, /* client dest connection ID */
|
||||||
|
@ -90,15 +90,15 @@ type baseServer struct {
|
||||||
uint64,
|
uint64,
|
||||||
utils.Logger,
|
utils.Logger,
|
||||||
protocol.VersionNumber,
|
protocol.VersionNumber,
|
||||||
) quicSession
|
) quicConn
|
||||||
|
|
||||||
serverError error
|
serverError error
|
||||||
errorChan chan struct{}
|
errorChan chan struct{}
|
||||||
closed bool
|
closed bool
|
||||||
running chan struct{} // closed as soon as run() returns
|
running chan struct{} // closed as soon as run() returns
|
||||||
|
|
||||||
sessionQueue chan quicSession
|
connQueue chan quicConn
|
||||||
sessionQueueLen int32 // to be used as an atomic
|
connQueueLen int32 // to be used as an atomic
|
||||||
|
|
||||||
logger utils.Logger
|
logger utils.Logger
|
||||||
}
|
}
|
||||||
|
@ -112,7 +112,7 @@ type earlyServer struct{ *baseServer }
|
||||||
|
|
||||||
var _ EarlyListener = &earlyServer{}
|
var _ EarlyListener = &earlyServer{}
|
||||||
|
|
||||||
func (s *earlyServer) Accept(ctx context.Context) (EarlySession, error) {
|
func (s *earlyServer) Accept(ctx context.Context) (EarlyConnection, error) {
|
||||||
return s.baseServer.accept(ctx)
|
return s.baseServer.accept(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -123,7 +123,7 @@ func ListenAddr(addr string, tlsConf *tls.Config, config *Config) (Listener, err
|
||||||
return listenAddr(addr, tlsConf, config, false)
|
return listenAddr(addr, tlsConf, config, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListenAddrEarly works like ListenAddr, but it returns sessions before the handshake completes.
|
// ListenAddrEarly works like ListenAddr, but it returns connections before the handshake completes.
|
||||||
func ListenAddrEarly(addr string, tlsConf *tls.Config, config *Config) (EarlyListener, error) {
|
func ListenAddrEarly(addr string, tlsConf *tls.Config, config *Config) (EarlyListener, error) {
|
||||||
s, err := listenAddr(addr, tlsConf, config, true)
|
s, err := listenAddr(addr, tlsConf, config, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -164,7 +164,7 @@ func Listen(conn net.PacketConn, tlsConf *tls.Config, config *Config) (Listener,
|
||||||
return listen(conn, tlsConf, config, false)
|
return listen(conn, tlsConf, config, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListenEarly works like Listen, but it returns sessions before the handshake completes.
|
// ListenEarly works like Listen, but it returns connections before the handshake completes.
|
||||||
func ListenEarly(conn net.PacketConn, tlsConf *tls.Config, config *Config) (EarlyListener, error) {
|
func ListenEarly(conn net.PacketConn, tlsConf *tls.Config, config *Config) (EarlyListener, error) {
|
||||||
s, err := listen(conn, tlsConf, config, true)
|
s, err := listen(conn, tlsConf, config, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -187,7 +187,7 @@ func listen(conn net.PacketConn, tlsConf *tls.Config, config *Config, acceptEarl
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sessionHandler, err := getMultiplexer().AddConn(conn, config.ConnectionIDLength, config.StatelessResetKey, config.Tracer)
|
connHandler, err := getMultiplexer().AddConn(conn, config.ConnectionIDLength, config.StatelessResetKey, config.Tracer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -200,21 +200,21 @@ func listen(conn net.PacketConn, tlsConf *tls.Config, config *Config, acceptEarl
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
s := &baseServer{
|
s := &baseServer{
|
||||||
conn: c,
|
conn: c,
|
||||||
tlsConf: tlsConf,
|
tlsConf: tlsConf,
|
||||||
config: config,
|
config: config,
|
||||||
tokenGenerator: tokenGenerator,
|
tokenGenerator: tokenGenerator,
|
||||||
sessionHandler: sessionHandler,
|
connHandler: connHandler,
|
||||||
sessionQueue: make(chan quicSession),
|
connQueue: make(chan quicConn),
|
||||||
errorChan: make(chan struct{}),
|
errorChan: make(chan struct{}),
|
||||||
running: make(chan struct{}),
|
running: make(chan struct{}),
|
||||||
receivedPackets: make(chan *receivedPacket, protocol.MaxServerUnprocessedPackets),
|
receivedPackets: make(chan *receivedPacket, protocol.MaxServerUnprocessedPackets),
|
||||||
newSession: newSession,
|
newConn: newConnection,
|
||||||
logger: utils.DefaultLogger.WithPrefix("server"),
|
logger: utils.DefaultLogger.WithPrefix("server"),
|
||||||
acceptEarlySessions: acceptEarly,
|
acceptEarlyConns: acceptEarly,
|
||||||
}
|
}
|
||||||
go s.run()
|
go s.run()
|
||||||
sessionHandler.SetServer(s)
|
connHandler.SetServer(s)
|
||||||
s.logger.Debugf("Listening for %s connections on %s", conn.LocalAddr().Network(), conn.LocalAddr().String())
|
s.logger.Debugf("Listening for %s connections on %s", conn.LocalAddr().Network(), conn.LocalAddr().String())
|
||||||
return s, nil
|
return s, nil
|
||||||
}
|
}
|
||||||
|
@ -258,19 +258,19 @@ var defaultAcceptToken = func(clientAddr net.Addr, token *Token) bool {
|
||||||
return sourceAddr == token.RemoteAddr
|
return sourceAddr == token.RemoteAddr
|
||||||
}
|
}
|
||||||
|
|
||||||
// Accept returns sessions that already completed the handshake.
|
// Accept returns connections that already completed the handshake.
|
||||||
// It is only valid if acceptEarlySessions is false.
|
// It is only valid if acceptEarlyConns is false.
|
||||||
func (s *baseServer) Accept(ctx context.Context) (Session, error) {
|
func (s *baseServer) Accept(ctx context.Context) (Connection, error) {
|
||||||
return s.accept(ctx)
|
return s.accept(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *baseServer) accept(ctx context.Context) (quicSession, error) {
|
func (s *baseServer) accept(ctx context.Context) (quicConn, error) {
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
return nil, ctx.Err()
|
return nil, ctx.Err()
|
||||||
case sess := <-s.sessionQueue:
|
case conn := <-s.connQueue:
|
||||||
atomic.AddInt32(&s.sessionQueueLen, -1)
|
atomic.AddInt32(&s.connQueueLen, -1)
|
||||||
return sess, nil
|
return conn, nil
|
||||||
case <-s.errorChan:
|
case <-s.errorChan:
|
||||||
return nil, s.serverError
|
return nil, s.serverError
|
||||||
}
|
}
|
||||||
|
@ -294,9 +294,9 @@ func (s *baseServer) Close() error {
|
||||||
s.mutex.Unlock()
|
s.mutex.Unlock()
|
||||||
|
|
||||||
<-s.running
|
<-s.running
|
||||||
s.sessionHandler.CloseServer()
|
s.connHandler.CloseServer()
|
||||||
if createdPacketConn {
|
if createdPacketConn {
|
||||||
return s.sessionHandler.Destroy()
|
return s.connHandler.Destroy()
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -336,7 +336,7 @@ func (s *baseServer) handlePacketImpl(p *receivedPacket) bool /* is the buffer s
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
// If we're creating a new session, the packet will be passed to the session.
|
// If we're creating a new connection, the packet will be passed to the connection.
|
||||||
// The header will then be parsed again.
|
// The header will then be parsed again.
|
||||||
hdr, _, _, err := wire.ParsePacket(p.data, s.config.ConnectionIDLength)
|
hdr, _, _, err := wire.ParsePacket(p.data, s.config.ConnectionIDLength)
|
||||||
if err != nil && err != wire.ErrUnsupportedVersion {
|
if err != nil && err != wire.ErrUnsupportedVersion {
|
||||||
|
@ -436,7 +436,7 @@ func (s *baseServer) handleInitialImpl(p *receivedPacket, hdr *wire.Header) erro
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if queueLen := atomic.LoadInt32(&s.sessionQueueLen); queueLen >= protocol.MaxAcceptQueueSize {
|
if queueLen := atomic.LoadInt32(&s.connQueueLen); queueLen >= protocol.MaxAcceptQueueSize {
|
||||||
s.logger.Debugf("Rejecting new connection. Server currently busy. Accept queue length: %d (max %d)", queueLen, protocol.MaxAcceptQueueSize)
|
s.logger.Debugf("Rejecting new connection. Server currently busy. Accept queue length: %d (max %d)", queueLen, protocol.MaxAcceptQueueSize)
|
||||||
go func() {
|
go func() {
|
||||||
defer p.buffer.Release()
|
defer p.buffer.Release()
|
||||||
|
@ -452,9 +452,9 @@ func (s *baseServer) handleInitialImpl(p *receivedPacket, hdr *wire.Header) erro
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
s.logger.Debugf("Changing connection ID to %s.", connID)
|
s.logger.Debugf("Changing connection ID to %s.", connID)
|
||||||
var sess quicSession
|
var conn quicConn
|
||||||
tracingID := nextSessionTracingID()
|
tracingID := nextConnTracingID()
|
||||||
if added := s.sessionHandler.AddWithConnID(hdr.DestConnectionID, connID, func() packetHandler {
|
if added := s.connHandler.AddWithConnID(hdr.DestConnectionID, connID, func() packetHandler {
|
||||||
var tracer logging.ConnectionTracer
|
var tracer logging.ConnectionTracer
|
||||||
if s.config.Tracer != nil {
|
if s.config.Tracer != nil {
|
||||||
// Use the same connection ID that is passed to the client's GetLogWriter callback.
|
// Use the same connection ID that is passed to the client's GetLogWriter callback.
|
||||||
|
@ -463,74 +463,74 @@ func (s *baseServer) handleInitialImpl(p *receivedPacket, hdr *wire.Header) erro
|
||||||
connID = origDestConnID
|
connID = origDestConnID
|
||||||
}
|
}
|
||||||
tracer = s.config.Tracer.TracerForConnection(
|
tracer = s.config.Tracer.TracerForConnection(
|
||||||
context.WithValue(context.Background(), SessionTracingKey, tracingID),
|
context.WithValue(context.Background(), ConnectionTracingKey, tracingID),
|
||||||
protocol.PerspectiveServer,
|
protocol.PerspectiveServer,
|
||||||
connID,
|
connID,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
sess = s.newSession(
|
conn = s.newConn(
|
||||||
newSendConn(s.conn, p.remoteAddr, p.info),
|
newSendConn(s.conn, p.remoteAddr, p.info),
|
||||||
s.sessionHandler,
|
s.connHandler,
|
||||||
origDestConnID,
|
origDestConnID,
|
||||||
retrySrcConnID,
|
retrySrcConnID,
|
||||||
hdr.DestConnectionID,
|
hdr.DestConnectionID,
|
||||||
hdr.SrcConnectionID,
|
hdr.SrcConnectionID,
|
||||||
connID,
|
connID,
|
||||||
s.sessionHandler.GetStatelessResetToken(connID),
|
s.connHandler.GetStatelessResetToken(connID),
|
||||||
s.config,
|
s.config,
|
||||||
s.tlsConf,
|
s.tlsConf,
|
||||||
s.tokenGenerator,
|
s.tokenGenerator,
|
||||||
s.acceptEarlySessions,
|
s.acceptEarlyConns,
|
||||||
tracer,
|
tracer,
|
||||||
tracingID,
|
tracingID,
|
||||||
s.logger,
|
s.logger,
|
||||||
hdr.Version,
|
hdr.Version,
|
||||||
)
|
)
|
||||||
sess.handlePacket(p)
|
conn.handlePacket(p)
|
||||||
return sess
|
return conn
|
||||||
}); !added {
|
}); !added {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
go sess.run()
|
go conn.run()
|
||||||
go s.handleNewSession(sess)
|
go s.handleNewConn(conn)
|
||||||
if sess == nil {
|
if conn == nil {
|
||||||
p.buffer.Release()
|
p.buffer.Release()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *baseServer) handleNewSession(sess quicSession) {
|
func (s *baseServer) handleNewConn(conn quicConn) {
|
||||||
sessCtx := sess.Context()
|
connCtx := conn.Context()
|
||||||
if s.acceptEarlySessions {
|
if s.acceptEarlyConns {
|
||||||
// wait until the early session is ready (or the handshake fails)
|
// wait until the early connection is ready (or the handshake fails)
|
||||||
select {
|
select {
|
||||||
case <-sess.earlySessionReady():
|
case <-conn.earlyConnReady():
|
||||||
case <-sessCtx.Done():
|
case <-connCtx.Done():
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// wait until the handshake is complete (or fails)
|
// wait until the handshake is complete (or fails)
|
||||||
select {
|
select {
|
||||||
case <-sess.HandshakeComplete().Done():
|
case <-conn.HandshakeComplete().Done():
|
||||||
case <-sessCtx.Done():
|
case <-connCtx.Done():
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
atomic.AddInt32(&s.sessionQueueLen, 1)
|
atomic.AddInt32(&s.connQueueLen, 1)
|
||||||
select {
|
select {
|
||||||
case s.sessionQueue <- sess:
|
case s.connQueue <- conn:
|
||||||
// blocks until the session is accepted
|
// blocks until the connection is accepted
|
||||||
case <-sessCtx.Done():
|
case <-connCtx.Done():
|
||||||
atomic.AddInt32(&s.sessionQueueLen, -1)
|
atomic.AddInt32(&s.connQueueLen, -1)
|
||||||
// don't pass sessions that were already closed to Accept()
|
// don't pass connections that were already closed to Accept()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *baseServer) sendRetry(remoteAddr net.Addr, hdr *wire.Header, info *packetInfo) error {
|
func (s *baseServer) sendRetry(remoteAddr net.Addr, hdr *wire.Header, info *packetInfo) error {
|
||||||
// Log the Initial packet now.
|
// Log the Initial packet now.
|
||||||
// If no Retry is sent, the packet will be logged by the session.
|
// If no Retry is sent, the packet will be logged by the connection.
|
||||||
(&wire.ExtendedHeader{Header: *hdr}).Log(s.logger)
|
(&wire.ExtendedHeader{Header: *hdr}).Log(s.logger)
|
||||||
srcConnID, err := protocol.GenerateConnectionID(s.config.ConnectionIDLength)
|
srcConnID, err := protocol.GenerateConnectionID(s.config.ConnectionIDLength)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package quic
|
package quic
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io"
|
|
||||||
"net"
|
"net"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
@ -10,14 +9,8 @@ import (
|
||||||
"github.com/lucas-clemente/quic-go/internal/utils"
|
"github.com/lucas-clemente/quic-go/internal/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
type connection interface {
|
// OOBCapablePacketConn is a connection that allows the reading of ECN bits from the IP header.
|
||||||
ReadPacket() (*receivedPacket, error)
|
// If the PacketConn passed to Dial or Listen satisfies this interface, quic-go will use it.
|
||||||
WritePacket(b []byte, addr net.Addr, oob []byte) (int, error)
|
|
||||||
LocalAddr() net.Addr
|
|
||||||
io.Closer
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the PacketConn passed to Dial or Listen satisfies this interface, quic-go will read the ECN bits from the IP header.
|
|
||||||
// In this case, ReadMsgUDP() will be used instead of ReadFrom() to read packets.
|
// In this case, ReadMsgUDP() will be used instead of ReadFrom() to read packets.
|
||||||
type OOBCapablePacketConn interface {
|
type OOBCapablePacketConn interface {
|
||||||
net.PacketConn
|
net.PacketConn
|
||||||
|
@ -28,7 +21,20 @@ type OOBCapablePacketConn interface {
|
||||||
|
|
||||||
var _ OOBCapablePacketConn = &net.UDPConn{}
|
var _ OOBCapablePacketConn = &net.UDPConn{}
|
||||||
|
|
||||||
func wrapConn(pc net.PacketConn) (connection, error) {
|
func wrapConn(pc net.PacketConn) (rawConn, error) {
|
||||||
|
conn, ok := pc.(interface {
|
||||||
|
SyscallConn() (syscall.RawConn, error)
|
||||||
|
})
|
||||||
|
if ok {
|
||||||
|
rawConn, err := conn.SyscallConn()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
err = setDF(rawConn)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
c, ok := pc.(OOBCapablePacketConn)
|
c, ok := pc.(OOBCapablePacketConn)
|
||||||
if !ok {
|
if !ok {
|
||||||
utils.DefaultLogger.Infof("PacketConn is not a net.UDPConn. Disabling optimizations possible on UDP connections.")
|
utils.DefaultLogger.Infof("PacketConn is not a net.UDPConn. Disabling optimizations possible on UDP connections.")
|
||||||
|
@ -37,11 +43,16 @@ func wrapConn(pc net.PacketConn) (connection, error) {
|
||||||
return newConn(c)
|
return newConn(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The basicConn is the most trivial implementation of a connection.
|
||||||
|
// It reads a single packet from the underlying net.PacketConn.
|
||||||
|
// It is used when
|
||||||
|
// * the net.PacketConn is not a OOBCapablePacketConn, and
|
||||||
|
// * when the OS doesn't support OOB.
|
||||||
type basicConn struct {
|
type basicConn struct {
|
||||||
net.PacketConn
|
net.PacketConn
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ connection = &basicConn{}
|
var _ rawConn = &basicConn{}
|
||||||
|
|
||||||
func (c *basicConn) ReadPacket() (*receivedPacket, error) {
|
func (c *basicConn) ReadPacket() (*receivedPacket, error) {
|
||||||
buffer := getPacketBuffer()
|
buffer := getPacketBuffer()
|
|
@ -0,0 +1,16 @@
|
||||||
|
//go:build !linux && !windows
|
||||||
|
// +build !linux,!windows
|
||||||
|
|
||||||
|
package quic
|
||||||
|
|
||||||
|
import "syscall"
|
||||||
|
|
||||||
|
func setDF(rawConn syscall.RawConn) error {
|
||||||
|
// no-op on unsupported platforms
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func isMsgSizeErr(err error) bool {
|
||||||
|
// to be implemented for more specific platforms
|
||||||
|
return false
|
||||||
|
}
|
|
@ -0,0 +1,41 @@
|
||||||
|
//go:build linux
|
||||||
|
// +build linux
|
||||||
|
|
||||||
|
package quic
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
|
||||||
|
"github.com/lucas-clemente/quic-go/internal/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
func setDF(rawConn syscall.RawConn) error {
|
||||||
|
// Enabling IP_MTU_DISCOVER will force the kernel to return "sendto: message too long"
|
||||||
|
// and the datagram will not be fragmented
|
||||||
|
var errDFIPv4, errDFIPv6 error
|
||||||
|
if err := rawConn.Control(func(fd uintptr) {
|
||||||
|
errDFIPv4 = unix.SetsockoptInt(int(fd), unix.IPPROTO_IP, unix.IP_MTU_DISCOVER, unix.IP_PMTUDISC_DO)
|
||||||
|
errDFIPv6 = unix.SetsockoptInt(int(fd), unix.IPPROTO_IPV6, unix.IPV6_MTU_DISCOVER, unix.IPV6_PMTUDISC_DO)
|
||||||
|
}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
switch {
|
||||||
|
case errDFIPv4 == nil && errDFIPv6 == nil:
|
||||||
|
utils.DefaultLogger.Debugf("Setting DF for IPv4 and IPv6.")
|
||||||
|
case errDFIPv4 == nil && errDFIPv6 != nil:
|
||||||
|
utils.DefaultLogger.Debugf("Setting DF for IPv4.")
|
||||||
|
case errDFIPv4 != nil && errDFIPv6 == nil:
|
||||||
|
utils.DefaultLogger.Debugf("Setting DF for IPv6.")
|
||||||
|
case errDFIPv4 != nil && errDFIPv6 != nil:
|
||||||
|
utils.DefaultLogger.Errorf("setting DF failed for both IPv4 and IPv6")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func isMsgSizeErr(err error) bool {
|
||||||
|
// https://man7.org/linux/man-pages/man7/udp.7.html
|
||||||
|
return errors.Is(err, unix.EMSGSIZE)
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
//go:build windows
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
package quic
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/lucas-clemente/quic-go/internal/utils"
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// same for both IPv4 and IPv6 on Windows
|
||||||
|
// https://microsoft.github.io/windows-docs-rs/doc/windows/Win32/Networking/WinSock/constant.IP_DONTFRAG.html
|
||||||
|
// https://microsoft.github.io/windows-docs-rs/doc/windows/Win32/Networking/WinSock/constant.IPV6_DONTFRAG.html
|
||||||
|
IP_DONTFRAGMENT = 14
|
||||||
|
IPV6_DONTFRAG = 14
|
||||||
|
)
|
||||||
|
|
||||||
|
func setDF(rawConn syscall.RawConn) error {
|
||||||
|
var errDFIPv4, errDFIPv6 error
|
||||||
|
if err := rawConn.Control(func(fd uintptr) {
|
||||||
|
errDFIPv4 = windows.SetsockoptInt(windows.Handle(fd), windows.IPPROTO_IP, IP_DONTFRAGMENT, 1)
|
||||||
|
errDFIPv6 = windows.SetsockoptInt(windows.Handle(fd), windows.IPPROTO_IPV6, IPV6_DONTFRAG, 1)
|
||||||
|
}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
switch {
|
||||||
|
case errDFIPv4 == nil && errDFIPv6 == nil:
|
||||||
|
utils.DefaultLogger.Debugf("Setting DF for IPv4 and IPv6.")
|
||||||
|
case errDFIPv4 == nil && errDFIPv6 != nil:
|
||||||
|
utils.DefaultLogger.Debugf("Setting DF for IPv4.")
|
||||||
|
case errDFIPv4 != nil && errDFIPv6 == nil:
|
||||||
|
utils.DefaultLogger.Debugf("Setting DF for IPv6.")
|
||||||
|
case errDFIPv4 != nil && errDFIPv6 != nil:
|
||||||
|
return errors.New("setting DF failed for both IPv4 and IPv6")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func isMsgSizeErr(err error) bool {
|
||||||
|
// https://docs.microsoft.com/en-us/windows/win32/winsock/windows-sockets-error-codes-2
|
||||||
|
return errors.Is(err, windows.WSAEMSGSIZE)
|
||||||
|
}
|
|
@ -5,10 +5,7 @@ package quic
|
||||||
|
|
||||||
import "golang.org/x/sys/unix"
|
import "golang.org/x/sys/unix"
|
||||||
|
|
||||||
const (
|
const msgTypeIPTOS = unix.IP_RECVTOS
|
||||||
msgTypeIPTOS = unix.IP_RECVTOS
|
|
||||||
disablePathMTUDiscovery = false
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
ipv4RECVPKTINFO = unix.IP_RECVPKTINFO
|
ipv4RECVPKTINFO = unix.IP_RECVPKTINFO
|
|
@ -6,8 +6,7 @@ package quic
|
||||||
import "golang.org/x/sys/unix"
|
import "golang.org/x/sys/unix"
|
||||||
|
|
||||||
const (
|
const (
|
||||||
msgTypeIPTOS = unix.IP_RECVTOS
|
msgTypeIPTOS = unix.IP_RECVTOS
|
||||||
disablePathMTUDiscovery = false
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
|
@ -5,10 +5,7 @@ package quic
|
||||||
|
|
||||||
import "golang.org/x/sys/unix"
|
import "golang.org/x/sys/unix"
|
||||||
|
|
||||||
const (
|
const msgTypeIPTOS = unix.IP_TOS
|
||||||
msgTypeIPTOS = unix.IP_TOS
|
|
||||||
disablePathMTUDiscovery = false
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
ipv4RECVPKTINFO = unix.IP_PKTINFO
|
ipv4RECVPKTINFO = unix.IP_PKTINFO
|
|
@ -5,9 +5,7 @@ package quic
|
||||||
|
|
||||||
import "net"
|
import "net"
|
||||||
|
|
||||||
const disablePathMTUDiscovery = false
|
func newConn(c net.PacketConn) (rawConn, error) {
|
||||||
|
|
||||||
func newConn(c net.PacketConn) (connection, error) {
|
|
||||||
return &basicConn{PacketConn: c}, nil
|
return &basicConn{PacketConn: c}, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,7 +64,7 @@ type oobConn struct {
|
||||||
buffers [batchSize]*packetBuffer
|
buffers [batchSize]*packetBuffer
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ connection = &oobConn{}
|
var _ rawConn = &oobConn{}
|
||||||
|
|
||||||
func newConn(c OOBCapablePacketConn) (*oobConn, error) {
|
func newConn(c OOBCapablePacketConn) (*oobConn, error) {
|
||||||
rawConn, err := c.SyscallConn()
|
rawConn, err := c.SyscallConn()
|
|
@ -12,24 +12,7 @@ import (
|
||||||
"golang.org/x/sys/windows"
|
"golang.org/x/sys/windows"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
func newConn(c OOBCapablePacketConn) (rawConn, error) {
|
||||||
disablePathMTUDiscovery = true
|
|
||||||
IP_DONTFRAGMENT = 14
|
|
||||||
)
|
|
||||||
|
|
||||||
func newConn(c OOBCapablePacketConn) (connection, error) {
|
|
||||||
rawConn, err := c.SyscallConn()
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("couldn't get syscall.RawConn: %w", err)
|
|
||||||
}
|
|
||||||
if err := rawConn.Control(func(fd uintptr) {
|
|
||||||
// This should succeed if the connection is a IPv4 or a dual-stack connection.
|
|
||||||
// It will fail for IPv6 connections.
|
|
||||||
// TODO: properly handle error.
|
|
||||||
_ = windows.SetsockoptInt(windows.Handle(fd), windows.IPPROTO_IP, IP_DONTFRAGMENT, 1)
|
|
||||||
}); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &basicConn{PacketConn: c}, nil
|
return &basicConn{PacketConn: c}, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,13 +20,10 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"runtime"
|
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"golang.org/x/sys/cpu"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -1463,17 +1460,6 @@ func defaultCipherSuitesTLS13() []uint16 {
|
||||||
return varDefaultCipherSuitesTLS13
|
return varDefaultCipherSuitesTLS13
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
|
||||||
hasGCMAsmAMD64 = cpu.X86.HasAES && cpu.X86.HasPCLMULQDQ
|
|
||||||
hasGCMAsmARM64 = cpu.ARM64.HasAES && cpu.ARM64.HasPMULL
|
|
||||||
// Keep in sync with crypto/aes/cipher_s390x.go.
|
|
||||||
hasGCMAsmS390X = cpu.S390X.HasAES && cpu.S390X.HasAESCBC && cpu.S390X.HasAESCTR && (cpu.S390X.HasGHASH || cpu.S390X.HasAESGCM)
|
|
||||||
|
|
||||||
hasAESGCMHardwareSupport = runtime.GOARCH == "amd64" && hasGCMAsmAMD64 ||
|
|
||||||
runtime.GOARCH == "arm64" && hasGCMAsmARM64 ||
|
|
||||||
runtime.GOARCH == "s390x" && hasGCMAsmS390X
|
|
||||||
)
|
|
||||||
|
|
||||||
func initDefaultCipherSuites() {
|
func initDefaultCipherSuites() {
|
||||||
var topCipherSuites []uint16
|
var topCipherSuites []uint16
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
// +build js
|
||||||
|
|
||||||
|
package qtls
|
||||||
|
|
||||||
|
var (
|
||||||
|
hasGCMAsmAMD64 = false
|
||||||
|
hasGCMAsmARM64 = false
|
||||||
|
// Keep in sync with crypto/aes/cipher_s390x.go.
|
||||||
|
hasGCMAsmS390X = false
|
||||||
|
|
||||||
|
hasAESGCMHardwareSupport = false
|
||||||
|
)
|
|
@ -0,0 +1,20 @@
|
||||||
|
// +build !js
|
||||||
|
|
||||||
|
package qtls
|
||||||
|
|
||||||
|
import (
|
||||||
|
"runtime"
|
||||||
|
|
||||||
|
"golang.org/x/sys/cpu"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
hasGCMAsmAMD64 = cpu.X86.HasAES && cpu.X86.HasPCLMULQDQ
|
||||||
|
hasGCMAsmARM64 = cpu.ARM64.HasAES && cpu.ARM64.HasPMULL
|
||||||
|
// Keep in sync with crypto/aes/cipher_s390x.go.
|
||||||
|
hasGCMAsmS390X = cpu.S390X.HasAES && cpu.S390X.HasAESCBC && cpu.S390X.HasAESCTR && (cpu.S390X.HasGHASH || cpu.S390X.HasAESGCM)
|
||||||
|
|
||||||
|
hasAESGCMHardwareSupport = runtime.GOARCH == "amd64" && hasGCMAsmAMD64 ||
|
||||||
|
runtime.GOARCH == "arm64" && hasGCMAsmARM64 ||
|
||||||
|
runtime.GOARCH == "s390x" && hasGCMAsmS390X
|
||||||
|
)
|
|
@ -15,10 +15,8 @@ import (
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"fmt"
|
"fmt"
|
||||||
"hash"
|
"hash"
|
||||||
"runtime"
|
|
||||||
|
|
||||||
"golang.org/x/crypto/chacha20poly1305"
|
"golang.org/x/crypto/chacha20poly1305"
|
||||||
"golang.org/x/sys/cpu"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// CipherSuite is a TLS cipher suite. Note that most functions in this package
|
// CipherSuite is a TLS cipher suite. Note that most functions in this package
|
||||||
|
@ -365,18 +363,6 @@ var defaultCipherSuitesTLS13NoAES = []uint16{
|
||||||
TLS_AES_256_GCM_SHA384,
|
TLS_AES_256_GCM_SHA384,
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
|
||||||
hasGCMAsmAMD64 = cpu.X86.HasAES && cpu.X86.HasPCLMULQDQ
|
|
||||||
hasGCMAsmARM64 = cpu.ARM64.HasAES && cpu.ARM64.HasPMULL
|
|
||||||
// Keep in sync with crypto/aes/cipher_s390x.go.
|
|
||||||
hasGCMAsmS390X = cpu.S390X.HasAES && cpu.S390X.HasAESCBC && cpu.S390X.HasAESCTR &&
|
|
||||||
(cpu.S390X.HasGHASH || cpu.S390X.HasAESGCM)
|
|
||||||
|
|
||||||
hasAESGCMHardwareSupport = runtime.GOARCH == "amd64" && hasGCMAsmAMD64 ||
|
|
||||||
runtime.GOARCH == "arm64" && hasGCMAsmARM64 ||
|
|
||||||
runtime.GOARCH == "s390x" && hasGCMAsmS390X
|
|
||||||
)
|
|
||||||
|
|
||||||
var aesgcmCiphers = map[uint16]bool{
|
var aesgcmCiphers = map[uint16]bool{
|
||||||
// TLS 1.2
|
// TLS 1.2
|
||||||
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: true,
|
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: true,
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
//go:build !js
|
||||||
|
// +build !js
|
||||||
|
|
||||||
|
package qtls
|
||||||
|
|
||||||
|
import (
|
||||||
|
"runtime"
|
||||||
|
|
||||||
|
"golang.org/x/sys/cpu"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
hasGCMAsmAMD64 = cpu.X86.HasAES && cpu.X86.HasPCLMULQDQ
|
||||||
|
hasGCMAsmARM64 = cpu.ARM64.HasAES && cpu.ARM64.HasPMULL
|
||||||
|
// Keep in sync with crypto/aes/cipher_s390x.go.
|
||||||
|
hasGCMAsmS390X = cpu.S390X.HasAES && cpu.S390X.HasAESCBC && cpu.S390X.HasAESCTR &&
|
||||||
|
(cpu.S390X.HasGHASH || cpu.S390X.HasAESGCM)
|
||||||
|
|
||||||
|
hasAESGCMHardwareSupport = runtime.GOARCH == "amd64" && hasGCMAsmAMD64 ||
|
||||||
|
runtime.GOARCH == "arm64" && hasGCMAsmARM64 ||
|
||||||
|
runtime.GOARCH == "s390x" && hasGCMAsmS390X
|
||||||
|
)
|
|
@ -0,0 +1,12 @@
|
||||||
|
//go:build js
|
||||||
|
// +build js
|
||||||
|
|
||||||
|
package qtls
|
||||||
|
|
||||||
|
var (
|
||||||
|
hasGCMAsmAMD64 = false
|
||||||
|
hasGCMAsmARM64 = false
|
||||||
|
hasGCMAsmS390X = false
|
||||||
|
|
||||||
|
hasAESGCMHardwareSupport = false
|
||||||
|
)
|
|
@ -15,10 +15,8 @@ import (
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"fmt"
|
"fmt"
|
||||||
"hash"
|
"hash"
|
||||||
"runtime"
|
|
||||||
|
|
||||||
"golang.org/x/crypto/chacha20poly1305"
|
"golang.org/x/crypto/chacha20poly1305"
|
||||||
"golang.org/x/sys/cpu"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// CipherSuite is a TLS cipher suite. Note that most functions in this package
|
// CipherSuite is a TLS cipher suite. Note that most functions in this package
|
||||||
|
@ -365,18 +363,6 @@ var defaultCipherSuitesTLS13NoAES = []uint16{
|
||||||
TLS_AES_256_GCM_SHA384,
|
TLS_AES_256_GCM_SHA384,
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
|
||||||
hasGCMAsmAMD64 = cpu.X86.HasAES && cpu.X86.HasPCLMULQDQ
|
|
||||||
hasGCMAsmARM64 = cpu.ARM64.HasAES && cpu.ARM64.HasPMULL
|
|
||||||
// Keep in sync with crypto/aes/cipher_s390x.go.
|
|
||||||
hasGCMAsmS390X = cpu.S390X.HasAES && cpu.S390X.HasAESCBC && cpu.S390X.HasAESCTR &&
|
|
||||||
(cpu.S390X.HasGHASH || cpu.S390X.HasAESGCM)
|
|
||||||
|
|
||||||
hasAESGCMHardwareSupport = runtime.GOARCH == "amd64" && hasGCMAsmAMD64 ||
|
|
||||||
runtime.GOARCH == "arm64" && hasGCMAsmARM64 ||
|
|
||||||
runtime.GOARCH == "s390x" && hasGCMAsmS390X
|
|
||||||
)
|
|
||||||
|
|
||||||
var aesgcmCiphers = map[uint16]bool{
|
var aesgcmCiphers = map[uint16]bool{
|
||||||
// TLS 1.2
|
// TLS 1.2
|
||||||
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: true,
|
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: true,
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
//go:build !js
|
||||||
|
// +build !js
|
||||||
|
|
||||||
|
package qtls
|
||||||
|
|
||||||
|
import (
|
||||||
|
"runtime"
|
||||||
|
|
||||||
|
"golang.org/x/sys/cpu"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
hasGCMAsmAMD64 = cpu.X86.HasAES && cpu.X86.HasPCLMULQDQ
|
||||||
|
hasGCMAsmARM64 = cpu.ARM64.HasAES && cpu.ARM64.HasPMULL
|
||||||
|
// Keep in sync with crypto/aes/cipher_s390x.go.
|
||||||
|
hasGCMAsmS390X = cpu.S390X.HasAES && cpu.S390X.HasAESCBC && cpu.S390X.HasAESCTR &&
|
||||||
|
(cpu.S390X.HasGHASH || cpu.S390X.HasAESGCM)
|
||||||
|
|
||||||
|
hasAESGCMHardwareSupport = runtime.GOARCH == "amd64" && hasGCMAsmAMD64 ||
|
||||||
|
runtime.GOARCH == "arm64" && hasGCMAsmARM64 ||
|
||||||
|
runtime.GOARCH == "s390x" && hasGCMAsmS390X
|
||||||
|
)
|
|
@ -0,0 +1,12 @@
|
||||||
|
//go:build js
|
||||||
|
// +build js
|
||||||
|
|
||||||
|
package qtls
|
||||||
|
|
||||||
|
var (
|
||||||
|
hasGCMAsmAMD64 = false
|
||||||
|
hasGCMAsmARM64 = false
|
||||||
|
hasGCMAsmS390X = false
|
||||||
|
|
||||||
|
hasAESGCMHardwareSupport = false
|
||||||
|
)
|
|
@ -201,7 +201,7 @@ github.com/grpc-ecosystem/grpc-opentracing/go/otgrpc
|
||||||
github.com/json-iterator/go
|
github.com/json-iterator/go
|
||||||
# github.com/kylelemons/godebug v1.1.0
|
# github.com/kylelemons/godebug v1.1.0
|
||||||
## explicit; go 1.11
|
## explicit; go 1.11
|
||||||
# github.com/lucas-clemente/quic-go v0.24.0 => github.com/chungthuang/quic-go v0.24.1-0.20220110095058-981dc498cb62
|
# github.com/lucas-clemente/quic-go v0.27.1 => github.com/chungthuang/quic-go v0.27.1-0.20220607112311-13144fbde8da
|
||||||
## explicit; go 1.16
|
## explicit; go 1.16
|
||||||
github.com/lucas-clemente/quic-go
|
github.com/lucas-clemente/quic-go
|
||||||
github.com/lucas-clemente/quic-go/internal/ackhandler
|
github.com/lucas-clemente/quic-go/internal/ackhandler
|
||||||
|
@ -219,13 +219,13 @@ github.com/lucas-clemente/quic-go/quicvarint
|
||||||
# github.com/lucasb-eyer/go-colorful v1.0.3
|
# github.com/lucasb-eyer/go-colorful v1.0.3
|
||||||
## explicit; go 1.12
|
## explicit; go 1.12
|
||||||
github.com/lucasb-eyer/go-colorful
|
github.com/lucasb-eyer/go-colorful
|
||||||
# github.com/marten-seemann/qtls-go1-16 v0.1.4
|
# github.com/marten-seemann/qtls-go1-16 v0.1.5
|
||||||
## explicit; go 1.16
|
## explicit; go 1.16
|
||||||
github.com/marten-seemann/qtls-go1-16
|
github.com/marten-seemann/qtls-go1-16
|
||||||
# github.com/marten-seemann/qtls-go1-17 v0.1.0
|
# github.com/marten-seemann/qtls-go1-17 v0.1.1
|
||||||
## explicit; go 1.17
|
## explicit; go 1.17
|
||||||
github.com/marten-seemann/qtls-go1-17
|
github.com/marten-seemann/qtls-go1-17
|
||||||
# github.com/marten-seemann/qtls-go1-18 v0.1.0-beta.1
|
# github.com/marten-seemann/qtls-go1-18 v0.1.1
|
||||||
## explicit; go 1.18
|
## explicit; go 1.18
|
||||||
github.com/marten-seemann/qtls-go1-18
|
github.com/marten-seemann/qtls-go1-18
|
||||||
# github.com/mattn/go-colorable v0.1.8
|
# github.com/mattn/go-colorable v0.1.8
|
||||||
|
@ -612,5 +612,5 @@ zombiezen.com/go/capnproto2/schemas
|
||||||
zombiezen.com/go/capnproto2/server
|
zombiezen.com/go/capnproto2/server
|
||||||
zombiezen.com/go/capnproto2/std/capnp/rpc
|
zombiezen.com/go/capnproto2/std/capnp/rpc
|
||||||
# github.com/urfave/cli/v2 => github.com/ipostelnik/cli/v2 v2.3.1-0.20210324024421-b6ea8234fe3d
|
# github.com/urfave/cli/v2 => github.com/ipostelnik/cli/v2 v2.3.1-0.20210324024421-b6ea8234fe3d
|
||||||
# github.com/lucas-clemente/quic-go => github.com/chungthuang/quic-go v0.24.1-0.20220110095058-981dc498cb62
|
# github.com/lucas-clemente/quic-go => github.com/chungthuang/quic-go v0.27.1-0.20220607112311-13144fbde8da
|
||||||
# github.com/prometheus/golang_client => github.com/prometheus/golang_client v1.12.1
|
# github.com/prometheus/golang_client => github.com/prometheus/golang_client v1.12.1
|
||||||
|
|
Loading…
Reference in New Issue