Skip to content

Commit e859984

Browse files
authored
server: with TLS, set TCP user timeout on the underlying raw connection (#5646) (#6321)
1 parent 1634254 commit e859984

File tree

3 files changed

+103
-25
lines changed

3 files changed

+103
-25
lines changed

internal/transport/http2_server.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,7 @@ func NewServerTransport(conn net.Conn, config *ServerConfig) (_ ServerTransport,
238238
kp.Timeout = defaultServerKeepaliveTimeout
239239
}
240240
if kp.Time != infinity {
241-
if err = syscall.SetTCPUserTimeout(conn, kp.Timeout); err != nil {
241+
if err = syscall.SetTCPUserTimeout(rawConn, kp.Timeout); err != nil {
242242
return nil, connectionErrorf(false, err, "transport: failed to set TCP_USER_TIMEOUT: %v", err)
243243
}
244244
}

internal/transport/keepalive_test.go

Lines changed: 98 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -24,18 +24,23 @@ package transport
2424

2525
import (
2626
"context"
27+
"crypto/tls"
28+
"crypto/x509"
2729
"fmt"
2830
"io"
2931
"net"
32+
"os"
3033
"strings"
3134
"testing"
3235
"time"
3336

3437
"golang.org/x/net/http2"
38+
"google.golang.org/grpc/credentials"
3539
"google.golang.org/grpc/internal/channelz"
3640
"google.golang.org/grpc/internal/grpctest"
3741
"google.golang.org/grpc/internal/syscall"
3842
"google.golang.org/grpc/keepalive"
43+
"google.golang.org/grpc/testdata"
3944
)
4045

4146
const defaultTestTimeout = 10 * time.Second
@@ -581,47 +586,82 @@ func (s) TestKeepaliveServerEnforcementWithDormantKeepaliveOnClient(t *testing.T
581586
// the keepalive timeout, as detailed in proposal A18.
582587
func (s) TestTCPUserTimeout(t *testing.T) {
583588
tests := []struct {
589+
tls bool
584590
time time.Duration
585591
timeout time.Duration
586592
clientWantTimeout time.Duration
587593
serverWantTimeout time.Duration
588594
}{
589595
{
596+
false,
590597
10 * time.Second,
591598
10 * time.Second,
592599
10 * 1000 * time.Millisecond,
593600
10 * 1000 * time.Millisecond,
594601
},
595602
{
603+
false,
596604
0,
597605
0,
598606
0,
599607
20 * 1000 * time.Millisecond,
600608
},
601609
{
610+
false,
611+
infinity,
612+
infinity,
613+
0,
614+
0,
615+
},
616+
{
617+
true,
618+
10 * time.Second,
619+
10 * time.Second,
620+
10 * 1000 * time.Millisecond,
621+
10 * 1000 * time.Millisecond,
622+
},
623+
{
624+
true,
625+
0,
626+
0,
627+
0,
628+
20 * 1000 * time.Millisecond,
629+
},
630+
{
631+
true,
602632
infinity,
603633
infinity,
604634
0,
605635
0,
606636
},
607637
}
608638
for _, tt := range tests {
639+
sopts := &ServerConfig{
640+
KeepaliveParams: keepalive.ServerParameters{
641+
Time: tt.time,
642+
Timeout: tt.timeout,
643+
},
644+
}
645+
646+
copts := ConnectOptions{
647+
KeepaliveParams: keepalive.ClientParameters{
648+
Time: tt.time,
649+
Timeout: tt.timeout,
650+
},
651+
}
652+
653+
if tt.tls {
654+
copts.TransportCredentials = makeTLSCreds(t, "x509/client1_cert.pem", "x509/client1_key.pem", "x509/server_ca_cert.pem")
655+
sopts.Credentials = makeTLSCreds(t, "x509/server1_cert.pem", "x509/server1_key.pem", "x509/client_ca_cert.pem")
656+
657+
}
658+
609659
server, client, cancel := setUpWithOptions(
610660
t,
611661
0,
612-
&ServerConfig{
613-
KeepaliveParams: keepalive.ServerParameters{
614-
Time: tt.time,
615-
Timeout: tt.timeout,
616-
},
617-
},
662+
sopts,
618663
normal,
619-
ConnectOptions{
620-
KeepaliveParams: keepalive.ClientParameters{
621-
Time: tt.time,
622-
Timeout: tt.timeout,
623-
},
624-
},
664+
copts,
625665
)
626666
defer func() {
627667
client.Close(fmt.Errorf("closed manually by test"))
@@ -630,6 +670,7 @@ func (s) TestTCPUserTimeout(t *testing.T) {
630670
}()
631671

632672
var sc *http2Server
673+
var srawConn net.Conn
633674
// Wait until the server transport is setup.
634675
for {
635676
server.mu.Lock()
@@ -644,6 +685,7 @@ func (s) TestTCPUserTimeout(t *testing.T) {
644685
if !ok {
645686
t.Fatalf("Failed to convert %v to *http2Server", k)
646687
}
688+
srawConn = server.conns[k]
647689
}
648690
server.mu.Unlock()
649691
break
@@ -657,25 +699,60 @@ func (s) TestTCPUserTimeout(t *testing.T) {
657699
}
658700
client.CloseStream(stream, io.EOF)
659701

660-
cltOpt, err := syscall.GetTCPUserTimeout(client.conn)
661-
if err != nil {
662-
t.Fatalf("syscall.GetTCPUserTimeout() failed: %v", err)
702+
// check client TCP user timeout only when non TLS
703+
// TODO : find a way to get the underlying conn for client when TLS
704+
if !tt.tls {
705+
cltOpt, err := syscall.GetTCPUserTimeout(client.conn)
706+
if err != nil {
707+
t.Fatalf("syscall.GetTCPUserTimeout() failed: %v", err)
708+
}
709+
if cltOpt < 0 {
710+
t.Skipf("skipping test on unsupported environment")
711+
}
712+
if gotTimeout := time.Duration(cltOpt) * time.Millisecond; gotTimeout != tt.clientWantTimeout {
713+
t.Fatalf("syscall.GetTCPUserTimeout() = %d, want %d", gotTimeout, tt.clientWantTimeout)
714+
}
663715
}
664-
if cltOpt < 0 {
665-
t.Skipf("skipping test on unsupported environment")
716+
scConn := sc.conn
717+
if tt.tls {
718+
if _, ok := sc.conn.(*net.TCPConn); ok {
719+
t.Fatalf("sc.conn is should have wrapped conn with TLS")
720+
}
721+
scConn = srawConn
666722
}
667-
if gotTimeout := time.Duration(cltOpt) * time.Millisecond; gotTimeout != tt.clientWantTimeout {
668-
t.Fatalf("syscall.GetTCPUserTimeout() = %d, want %d", gotTimeout, tt.clientWantTimeout)
723+
// verify the type of scConn (on which TCP user timeout will be got)
724+
if _, ok := scConn.(*net.TCPConn); !ok {
725+
t.Fatalf("server underlying conn is of type %T, want net.TCPConn", scConn)
669726
}
670-
671-
srvOpt, err := syscall.GetTCPUserTimeout(sc.conn)
727+
srvOpt, err := syscall.GetTCPUserTimeout(scConn)
672728
if err != nil {
673729
t.Fatalf("syscall.GetTCPUserTimeout() failed: %v", err)
674730
}
675731
if gotTimeout := time.Duration(srvOpt) * time.Millisecond; gotTimeout != tt.serverWantTimeout {
676732
t.Fatalf("syscall.GetTCPUserTimeout() = %d, want %d", gotTimeout, tt.serverWantTimeout)
677733
}
734+
735+
}
736+
}
737+
738+
func makeTLSCreds(t *testing.T, certPath, keyPath, rootsPath string) credentials.TransportCredentials {
739+
cert, err := tls.LoadX509KeyPair(testdata.Path(certPath), testdata.Path(keyPath))
740+
if err != nil {
741+
t.Fatalf("tls.LoadX509KeyPair(%q, %q) failed: %v", certPath, keyPath, err)
742+
}
743+
b, err := os.ReadFile(testdata.Path(rootsPath))
744+
if err != nil {
745+
t.Fatalf("os.ReadFile(%q) failed: %v", rootsPath, err)
746+
}
747+
roots := x509.NewCertPool()
748+
if !roots.AppendCertsFromPEM(b) {
749+
t.Fatal("failed to append certificates")
678750
}
751+
return credentials.NewTLS(&tls.Config{
752+
Certificates: []tls.Certificate{cert},
753+
RootCAs: roots,
754+
InsecureSkipVerify: true,
755+
})
679756
}
680757

681758
// checkForHealthyStream attempts to create a stream and return error if any.

internal/transport/transport_test.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -297,7 +297,7 @@ type server struct {
297297
port string
298298
startedErr chan error // error (or nil) with server start value
299299
mu sync.Mutex
300-
conns map[ServerTransport]bool
300+
conns map[ServerTransport]net.Conn
301301
h *testStreamHandler
302302
ready chan struct{}
303303
channelzID *channelz.Identifier
@@ -329,13 +329,14 @@ func (s *server) start(t *testing.T, port int, serverConfig *ServerConfig, ht hT
329329
return
330330
}
331331
s.port = p
332-
s.conns = make(map[ServerTransport]bool)
332+
s.conns = make(map[ServerTransport]net.Conn)
333333
s.startedErr <- nil
334334
for {
335335
conn, err := s.lis.Accept()
336336
if err != nil {
337337
return
338338
}
339+
rawConn := conn
339340
transport, err := NewServerTransport(conn, serverConfig)
340341
if err != nil {
341342
return
@@ -346,7 +347,7 @@ func (s *server) start(t *testing.T, port int, serverConfig *ServerConfig, ht hT
346347
transport.Close(errors.New("s.conns is nil"))
347348
return
348349
}
349-
s.conns[transport] = true
350+
s.conns[transport] = rawConn
350351
h := &testStreamHandler{t: transport.(*http2Server)}
351352
s.h = h
352353
s.mu.Unlock()

0 commit comments

Comments
 (0)