1- package masque
1+ package masque_test
22
33import (
44 "bytes"
55 "context"
6- "errors"
6+ "crypto/tls"
7+ "fmt"
78 "io"
89 "log"
910 "math/rand/v2"
11+ "net"
12+ "net/http"
1013 "os"
1114 "testing"
1215 "time"
1316
17+ "github.com/quic-go/masque-go"
18+
1419 "github.com/quic-go/quic-go/http3"
20+ "github.com/yosida95/uritemplate/v3"
21+
1522 "github.com/stretchr/testify/require"
16- "go.uber.org/mock/gomock"
1723)
1824
25+ func setupProxiedConn (t * testing.T ) (http3.Stream , net.PacketConn ) {
26+ t .Helper ()
27+
28+ targetConn := newUDPConnLocalhost (t )
29+
30+ strChan := make (chan http3.Stream , 1 )
31+ mux := http .NewServeMux ()
32+ mux .HandleFunc ("/masque" , func (w http.ResponseWriter , r * http.Request ) {
33+ strChan <- w .(http3.HTTPStreamer ).HTTPStream ()
34+ })
35+ server := http3.Server {
36+ TLSConfig : tlsConf ,
37+ Handler : mux ,
38+ EnableDatagrams : true ,
39+ }
40+ t .Cleanup (func () { server .Close () })
41+ serverConn := newUDPConnLocalhost (t )
42+ go server .Serve (serverConn )
43+
44+ cl := masque.Client {
45+ TLSClientConfig : & tls.Config {
46+ ClientCAs : certPool ,
47+ NextProtos : []string {http3 .NextProtoH3 },
48+ InsecureSkipVerify : true ,
49+ },
50+ }
51+ t .Cleanup (func () { cl .Close () })
52+
53+ ctx , cancel := context .WithTimeout (context .Background (), time .Second )
54+ defer cancel ()
55+ conn , rsp , err := cl .Dial (
56+ ctx ,
57+ uritemplate .MustNew (fmt .Sprintf ("https://localhost:%d/masque?h={target_host}&p={target_port}" , serverConn .LocalAddr ().(* net.UDPAddr ).Port )),
58+ targetConn .LocalAddr ().(* net.UDPAddr ),
59+ )
60+ require .NoError (t , err )
61+ require .Equal (t , http .StatusOK , rsp .StatusCode )
62+ t .Cleanup (func () { conn .Close () })
63+
64+ var str http3.Stream
65+ select {
66+ case str = <- strChan :
67+ case <- time .After (time .Second ):
68+ t .Fatal ("timeout" )
69+ }
70+ return str , conn
71+ }
72+
1973func TestCapsuleSkipping (t * testing.T ) {
2074 log .SetOutput (io .Discard )
2175 defer log .SetOutput (os .Stderr )
2276
77+ str , conn := setupProxiedConn (t )
78+
2379 var buf bytes.Buffer
2480 require .NoError (t , http3 .WriteCapsule (& buf , 1337 , []byte ("foo" )))
2581 require .NoError (t , http3 .WriteCapsule (& buf , 42 , []byte ("bar" )))
26- require .ErrorIs (t , skipCapsules (& buf ), io .EOF )
82+ _ , err := str .Write (buf .Bytes ())
83+ require .NoError (t , err )
84+ require .NoError (t , str .Close ())
85+
86+ _ , _ , err = conn .ReadFrom (make ([]byte , 100 ))
87+ require .ErrorIs (t , err , io .EOF )
2788}
2889
2990func TestReadDeadline (t * testing.T ) {
30- setupStreamAndConn := func () (* MockStream , * proxiedConn ) {
31- str := NewMockStream (gomock .NewController (t ))
32- done := make (chan struct {})
33- t .Cleanup (func () {
34- str .EXPECT ().Close ().MaxTimes (1 )
35- close (done )
36- })
37- str .EXPECT ().Read (gomock .Any ()).DoAndReturn (func ([]byte ) (int , error ) {
38- <- done
39- return 0 , errors .New ("test done" )
40- }).MaxTimes (1 )
41- return str , newProxiedConn (str , nil )
42- }
43-
4491 t .Run ("read after deadline" , func (t * testing.T ) {
45- str , conn := setupStreamAndConn ()
46- str .EXPECT ().ReceiveDatagram (gomock .Any ()).DoAndReturn (func (ctx context.Context ) ([]byte , error ) {
47- <- ctx .Done ()
48- return nil , ctx .Err ()
49- })
92+ _ , conn := setupProxiedConn (t )
5093
5194 require .NoError (t , conn .SetReadDeadline (time .Now ().Add (- time .Second )))
5295 _ , _ , err := conn .ReadFrom (make ([]byte , 100 ))
5396 require .ErrorIs (t , err , os .ErrDeadlineExceeded )
5497 })
5598
5699 t .Run ("unblocking read" , func (t * testing.T ) {
57- str , conn := setupStreamAndConn ()
58- str .EXPECT ().ReceiveDatagram (gomock .Any ()).DoAndReturn (func (ctx context.Context ) ([]byte , error ) {
59- <- ctx .Done ()
60- return nil , ctx .Err ()
61- }).Times (2 )
100+ _ , conn := setupProxiedConn (t )
101+
62102 errChan := make (chan error , 1 )
63103 go func () {
64104 _ , _ , err := conn .ReadFrom (make ([]byte , 100 ))
@@ -81,11 +121,7 @@ func TestReadDeadline(t *testing.T) {
81121 })
82122
83123 t .Run ("extending the deadline" , func (t * testing.T ) {
84- str , conn := setupStreamAndConn ()
85- str .EXPECT ().ReceiveDatagram (gomock .Any ()).DoAndReturn (func (ctx context.Context ) ([]byte , error ) {
86- <- ctx .Done ()
87- return nil , ctx .Err ()
88- }).MaxTimes (2 ) // might be called a 2nd time depending on when the cancellation Go routine does its job
124+ _ , conn := setupProxiedConn (t )
89125
90126 start := time .Now ()
91127 d := scaleDuration (75 * time .Millisecond )
@@ -108,11 +144,7 @@ func TestReadDeadline(t *testing.T) {
108144 })
109145
110146 t .Run ("cancelling the deadline" , func (t * testing.T ) {
111- str , conn := setupStreamAndConn ()
112- str .EXPECT ().ReceiveDatagram (gomock .Any ()).DoAndReturn (func (ctx context.Context ) ([]byte , error ) {
113- <- ctx .Done ()
114- return nil , ctx .Err ()
115- })
147+ _ , conn := setupProxiedConn (t )
116148
117149 start := time .Now ()
118150 d := scaleDuration (75 * time .Millisecond )
@@ -140,13 +172,10 @@ func TestReadDeadline(t *testing.T) {
140172 })
141173
142174 t .Run ("multiple deadlines" , func (t * testing.T ) {
143- str , conn := setupStreamAndConn ()
175+ _ , conn := setupProxiedConn (t )
176+
144177 const num = 10
145178 const maxDeadline = 5 * time .Millisecond
146- str .EXPECT ().ReceiveDatagram (gomock .Any ()).DoAndReturn (func (ctx context.Context ) ([]byte , error ) {
147- <- ctx .Done ()
148- return nil , ctx .Err ()
149- }).MinTimes (num )
150179
151180 for range num {
152181 // random duration between -5ms and 5ms
0 commit comments