diff --git a/conn_go_test.go b/conn_go_test.go new file mode 100644 index 000000000..7333c1d11 --- /dev/null +++ b/conn_go_test.go @@ -0,0 +1,146 @@ +// +build !js + +package dtls + +import ( + "bytes" + "context" + "crypto/tls" + "net" + "testing" + "time" + + "github.com/pion/dtls/v2/pkg/crypto/selfsign" +) + +func TestContextConfig(t *testing.T) { + addrListen, err := net.ResolveUDPAddr("udp", "localhost:0") + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + // Dummy listener + listen, err := net.ListenUDP("udp", addrListen) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + defer func() { + _ = listen.Close() + }() + addr := listen.LocalAddr().(*net.UDPAddr) + + cert, err := selfsign.GenerateSelfSigned() + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + config := &Config{ + ConnectContextMaker: func() (context.Context, func()) { + return context.WithTimeout(context.Background(), 40*time.Millisecond) + }, + Certificates: []tls.Certificate{cert}, + } + + dials := map[string]struct { + f func() (net.Conn, error) + order []byte + }{ + "Dial": { + f: func() (net.Conn, error) { + return Dial("udp", addr, config) + }, + order: []byte{0, 1, 2}, + }, + "DialWithContext": { + f: func() (net.Conn, error) { + ctx, cancel := context.WithTimeout(context.Background(), 60*time.Millisecond) + defer cancel() + return DialWithContext(ctx, "udp", addr, config) + }, + order: []byte{0, 2, 1}, + }, + "Client": { + f: func() (net.Conn, error) { + ca, _ := net.Pipe() + defer func() { + _ = ca.Close() + }() + return Client(ca, config) + }, + order: []byte{0, 1, 2}, + }, + "ClientWithContext": { + f: func() (net.Conn, error) { + ctx, cancel := context.WithTimeout(context.Background(), 60*time.Millisecond) + defer cancel() + ca, _ := net.Pipe() + defer func() { + _ = ca.Close() + }() + return ClientWithContext(ctx, ca, config) + }, + order: []byte{0, 2, 1}, + }, + "Server": { + f: func() (net.Conn, error) { + ca, _ := net.Pipe() + defer func() { + _ = ca.Close() + }() + return Server(ca, config) + }, + order: []byte{0, 1, 2}, + }, + "ServerWithContext": { + f: func() (net.Conn, error) { + ctx, cancel := context.WithTimeout(context.Background(), 60*time.Millisecond) + defer cancel() + ca, _ := net.Pipe() + defer func() { + _ = ca.Close() + }() + return ServerWithContext(ctx, ca, config) + }, + order: []byte{0, 2, 1}, + }, + } + + for name, dial := range dials { + dial := dial + t.Run(name, func(t *testing.T) { + done := make(chan struct{}) + + go func() { + conn, err := dial.f() + if err != errConnectTimeout { + t.Errorf("Expected error: '%v', got: '%v'", errConnectTimeout, err) + close(done) + return + } + done <- struct{}{} + _ = conn.Close() + }() + + var order []byte + early := time.After(30 * time.Millisecond) + late := time.After(50 * time.Millisecond) + func() { + for len(order) < 3 { + select { + case <-early: + order = append(order, 0) + case _, ok := <-done: + if !ok { + return + } + order = append(order, 1) + case <-late: + order = append(order, 2) + } + } + }() + if !bytes.Equal(dial.order, order) { + t.Errorf("Invalid cancel timing, expected: %v, got: %v", dial.order, order) + } + }) + } +} diff --git a/errors.go b/errors.go index 87a8d3e0f..e975e0d79 100644 --- a/errors.go +++ b/errors.go @@ -1,6 +1,11 @@ package dtls -import "errors" +import ( + "context" + "errors" + + "golang.org/x/xerrors" +) // Typed errors var ( @@ -50,11 +55,13 @@ var ( errNoAvailableCipherSuites = errors.New("dtls: Connection can not be created, no CipherSuites satisfy this Config") errInvalidClientKeyExchange = errors.New("dtls: Unable to determine if ClientKeyExchange is a public key or PSK Identity") errNoSupportedEllipticCurves = errors.New("dtls: Client requested zero or more elliptic curves that are not supported by the server") - errConnectTimeout = errors.New("dtls: The connection timed out during the handshake") errRequestedButNoSRTPExtension = errors.New("dtls: SRTP support was requested but server did not respond with use_srtp extension") errClientNoMatchingSRTPProfile = errors.New("dtls: Server responded with SRTP Profile we do not support") errServerNoMatchingSRTPProfile = errors.New("dtls: Client requested SRTP but we have no matching profiles") errServerRequiredButNoClientEMS = errors.New("dtls: Server requires the Extended Master Secret extension, but the client does not support it") errClientRequiredButNoServerEMS = errors.New("dtls: Client required Extended Master Secret extension, but server does not support it") errInvalidCertificate = errors.New("dtls: No certificate provided") + + // Wrapped errors + errConnectTimeout = xerrors.Errorf("dtls: The connection timed out during the handshake: %w", context.DeadlineExceeded) ) diff --git a/go.mod b/go.mod index f140a333e..b3dc82fe0 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ require ( github.com/pion/transport v0.8.10 golang.org/x/crypto v0.0.0-20200128174031-69ecbb4d6d5d golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 + golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 ) go 1.13 diff --git a/go.sum b/go.sum index 1417add23..27cfefd8d 100644 --- a/go.sum +++ b/go.sum @@ -14,5 +14,7 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=