@@ -14,10 +14,12 @@ import (
14
14
"testing"
15
15
"time"
16
16
17
+ "github.com/pion/logging"
17
18
"github.com/pion/randutil"
18
19
"github.com/pion/rtcp"
19
20
"github.com/pion/rtp"
20
21
"github.com/pion/transport/test"
22
+ "github.com/pion/transport/vnet"
21
23
"github.com/pion/webrtc/v3/pkg/media"
22
24
"github.com/stretchr/testify/assert"
23
25
"github.com/stretchr/testify/require"
@@ -1052,3 +1054,186 @@ func TestPeerConnection_RaceReplaceTrack(t *testing.T) {
1052
1054
1053
1055
assert .NoError (t , pc .Close ())
1054
1056
}
1057
+
1058
+ // Issue #1636
1059
+ func TestPeerConnection_DTLS_Restart (t * testing.T ) {
1060
+ lim := test .TimeOut (time .Second * 30 )
1061
+ defer lim .Stop ()
1062
+
1063
+ // First prepare network configuration
1064
+
1065
+ router , err := vnet .NewRouter (& vnet.RouterConfig {
1066
+ CIDR : "0.0.0.0/0" ,
1067
+ LoggerFactory : logging .NewDefaultLoggerFactory (),
1068
+ })
1069
+ assert .NoError (t , err )
1070
+
1071
+ networkA1 := vnet .NewNet (& vnet.NetConfig {
1072
+ NetworkConditioner : vnet .NewNetworkConditioner (vnet .NetworkConditionerPresetNone ),
1073
+ })
1074
+
1075
+ networkA2 := vnet .NewNet (& vnet.NetConfig {
1076
+ NetworkConditioner : vnet .NewNetworkConditioner (vnet .NetworkConditionerPresetNone ),
1077
+ })
1078
+
1079
+ networkB := vnet .NewNet (& vnet.NetConfig {
1080
+ NetworkConditioner : vnet .NewNetworkConditioner (vnet .NetworkConditionerPresetNone ),
1081
+ })
1082
+
1083
+ assert .NoError (t , router .AddNet (networkA1 ))
1084
+ assert .NoError (t , router .AddNet (networkA2 ))
1085
+ assert .NoError (t , router .AddNet (networkB ))
1086
+
1087
+ assert .NoError (t , router .Start ())
1088
+ defer func () { _ = router .Stop () }()
1089
+
1090
+ // ... then the clients
1091
+
1092
+ makeClient := func (network * vnet.Net ) (* PeerConnection , * TrackLocalStaticSample ) {
1093
+ m := & MediaEngine {}
1094
+ assert .NoError (t , m .RegisterDefaultCodecs ())
1095
+
1096
+ s := SettingEngine {}
1097
+ s .SetVNet (network )
1098
+ s .SetICETimeouts (2 * time .Second , 5 * time .Second , 1 * time .Second )
1099
+
1100
+ api := NewAPI (WithSettingEngine (s ), WithMediaEngine (m ))
1101
+ pc , cliErr := api .NewPeerConnection (Configuration {})
1102
+ assert .NoError (t , cliErr )
1103
+
1104
+ track , cliErr := NewTrackLocalStaticSample (RTPCodecCapability {MimeType : MimeTypeOpus }, "audio" , "test-client" )
1105
+ assert .NoError (t , cliErr )
1106
+
1107
+ _ , cliErr = pc .AddTrack (track )
1108
+ assert .NoError (t , cliErr )
1109
+
1110
+ return pc , track
1111
+ }
1112
+
1113
+ clientA1 , _ := makeClient (networkA1 )
1114
+ defer func () { _ = clientA1 .Close () }()
1115
+
1116
+ clientB , localClientBTrack := makeClient (networkB )
1117
+ defer func () { _ = clientB .Close () }()
1118
+
1119
+ // ... clientB starts publishing media
1120
+ publishClientBCtx , publishCancel := context .WithCancel (context .Background ())
1121
+ go func () {
1122
+ ticker := time .NewTicker (20 * time .Millisecond )
1123
+ defer ticker .Stop ()
1124
+
1125
+ for {
1126
+ select {
1127
+ case <- publishClientBCtx .Done ():
1128
+ return
1129
+ case <- ticker .C :
1130
+ _ = localClientBTrack .WriteSample (media.Sample {
1131
+ Data : []byte {0xbb },
1132
+ Timestamp : time .Now (),
1133
+ Duration : 20 * time .Millisecond ,
1134
+ })
1135
+ }
1136
+ }
1137
+ }()
1138
+ defer publishCancel ()
1139
+
1140
+ clientA1Tracks := make (chan * TrackRemote , 1 )
1141
+ clientA1 .OnTrack (func (remote * TrackRemote , receiver * RTPReceiver ) {
1142
+ clientA1Tracks <- remote
1143
+ })
1144
+
1145
+ // ClientA1 connects to ClientB
1146
+
1147
+ gatherCompletePromiseA1 := GatheringCompletePromise (clientA1 )
1148
+ offerA1 , err := clientA1 .CreateOffer (nil )
1149
+ assert .NoError (t , err )
1150
+ assert .NoError (t , clientA1 .SetLocalDescription (offerA1 ))
1151
+ <- gatherCompletePromiseA1
1152
+
1153
+ assert .NoError (t , clientB .SetRemoteDescription (* clientA1 .LocalDescription ()))
1154
+
1155
+ gatherCompletePromiseB := GatheringCompletePromise (clientB )
1156
+ answerB , err := clientB .CreateAnswer (nil )
1157
+ assert .NoError (t , err )
1158
+ assert .NoError (t , clientB .SetLocalDescription (answerB ))
1159
+ <- gatherCompletePromiseB
1160
+
1161
+ clientA1Connected := make (chan struct {}, 1 )
1162
+ clientA1Disconnected := make (chan struct {}, 1 )
1163
+ clientA1 .OnICEConnectionStateChange (func (s ICEConnectionState ) {
1164
+ if s == ICEConnectionStateConnected {
1165
+ clientA1Connected <- struct {}{}
1166
+ } else if s == ICEConnectionStateDisconnected {
1167
+ clientA1Disconnected <- struct {}{}
1168
+ }
1169
+ })
1170
+
1171
+ assert .NoError (t , clientA1 .SetRemoteDescription (answerB ))
1172
+
1173
+ // Wait for connection
1174
+ <- clientA1Connected
1175
+
1176
+ // At this point, clientA1 should have received a track, and some media
1177
+ clientA1RemoteTrack := <- clientA1Tracks
1178
+ pkt , _ , err := clientA1RemoteTrack .ReadRTP ()
1179
+ assert .NotNil (t , pkt )
1180
+ assert .NoError (t , err )
1181
+
1182
+ networkA1 .SetNetworkConditioner (vnet .NewNetworkConditioner (vnet .NetworkConditionerPresetFullLoss ))
1183
+
1184
+ <- clientA1Disconnected
1185
+
1186
+ // ClientA1 has been disconnected – in a mobile app context, this could be a switch to the background
1187
+ // or a killed app.
1188
+ //
1189
+ // In these scenarios, the client will reconnect with a different PeerConnection – here ClientA2.
1190
+
1191
+ clientA2 , _ := makeClient (networkA2 )
1192
+ defer func () { _ = clientA2 .Close () }()
1193
+
1194
+ clientA2Connected := make (chan struct {}, 1 )
1195
+ clientA2 .OnICEConnectionStateChange (func (s ICEConnectionState ) {
1196
+ if s == ICEConnectionStateConnected {
1197
+ clientA2Connected <- struct {}{}
1198
+ } else if s == ICEConnectionStateFailed {
1199
+ assert .FailNow (t , "should not fail" )
1200
+ }
1201
+ })
1202
+
1203
+ clientA2Tracks := make (chan * TrackRemote , 1 )
1204
+ clientA2 .OnTrack (func (remote * TrackRemote , receiver * RTPReceiver ) {
1205
+ clientA2Tracks <- remote
1206
+ })
1207
+
1208
+ // ClientA2 connects to ClientB
1209
+
1210
+ gatherCompletePromiseA2 := GatheringCompletePromise (clientA2 )
1211
+ // We can't do an ICE Restart here, since it's a different PeerConnection
1212
+ offerA2 , err := clientA2 .CreateOffer (nil )
1213
+ assert .NoError (t , err )
1214
+ assert .NoError (t , clientA2 .SetLocalDescription (offerA2 ))
1215
+ <- gatherCompletePromiseA2
1216
+
1217
+ assert .NoError (t , clientB .SetRemoteDescription (* clientA2 .LocalDescription ()))
1218
+
1219
+ gatherCompletePromiseB = GatheringCompletePromise (clientB )
1220
+ answerB , err = clientB .CreateAnswer (nil )
1221
+ assert .NoError (t , err )
1222
+ assert .NoError (t , clientB .SetLocalDescription (answerB ))
1223
+ <- gatherCompletePromiseB
1224
+
1225
+ assert .NoError (t , clientA2 .SetRemoteDescription (answerB ))
1226
+
1227
+ // Wait for connection
1228
+ <- clientA2Connected
1229
+
1230
+ // At this point, clientA2 should have received a track, and some media
1231
+ clientA2RemoteTrack := <- clientA2Tracks
1232
+
1233
+ // Read a bunch of RTPs
1234
+ for ndx := 0 ; ndx < 10 ; ndx ++ {
1235
+ pkt , _ , err = clientA2RemoteTrack .ReadRTP ()
1236
+ assert .NotNil (t , pkt )
1237
+ assert .NoError (t , err )
1238
+ }
1239
+ }
0 commit comments