Skip to content
This repository was archived by the owner on Aug 30, 2024. It is now read-only.

perf: Reduce RTC connection times #341

Merged
merged 2 commits into from
May 11, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 19 additions & 7 deletions wsnet/dial.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,17 +113,16 @@ func (d *Dialer) negotiate() (err error) {

go func() {
defer close(errCh)
err := waitForDataChannelOpen(context.Background(), d.ctrl)
err := waitForConnectionOpen(context.Background(), d.rtc)
if err != nil {
_ = d.conn.Close()
errCh <- err
return
}
d.ctrlrw, err = d.ctrl.Detach()
if err != nil {
errCh <- err
}
_ = d.conn.Close()
go func() {
// Closing this connection took 30ms+.
_ = d.conn.Close()
}()
}()

for {
Expand Down Expand Up @@ -179,7 +178,20 @@ func (d *Dialer) Close() error {

// Ping sends a ping through the control channel.
func (d *Dialer) Ping(ctx context.Context) error {
_, err := d.ctrlrw.Write([]byte{'a'})
// Since we control the client and server we could open this
// data channel with `Negotiated` true to reduce traffic being
// sent when the RTC connection is opened.
err := waitForDataChannelOpen(context.Background(), d.ctrl)
if err != nil {
return err
}
if d.ctrlrw == nil {
d.ctrlrw, err = d.ctrl.Detach()
if err != nil {
return err
}
}
_, err = d.ctrlrw.Write([]byte{'a'})
if err != nil {
return fmt.Errorf("write: %w", err)
}
Expand Down
10 changes: 8 additions & 2 deletions wsnet/listen.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ func (l *listener) dial(ctx context.Context) (<-chan error, error) {
// Negotiates the handshake protocol over the connection provided.
// This functions control-flow is important to readability,
// so the cognitive overload linter has been disabled.
// nolint:gocognit
// nolint:gocognit,nestif
func (l *listener) negotiate(conn net.Conn) {
var (
err error
Expand Down Expand Up @@ -172,11 +172,17 @@ func (l *listener) negotiate(conn net.Conn) {
closeError(err)
return
}
rtc.OnConnectionStateChange(func(pcs webrtc.PeerConnectionState) {
if pcs == webrtc.PeerConnectionStateConnecting {
return
}
_ = conn.Close()
})
flushCandidates := proxyICECandidates(rtc, conn)
l.connClosersMut.Lock()
l.connClosers = append(l.connClosers, rtc)
l.connClosersMut.Unlock()
rtc.OnDataChannel(l.handle)
flushCandidates := proxyICECandidates(rtc, conn)
err = rtc.SetRemoteDescription(*msg.Offer)
if err != nil {
closeError(fmt.Errorf("apply offer: %w", err))
Expand Down
22 changes: 22 additions & 0 deletions wsnet/rtc.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,8 @@ func dialICEURL(server webrtc.ICEServer, rawURL string, options *DialICEOptions)
// Generalizes creating a new peer connection with consistent options.
func newPeerConnection(servers []webrtc.ICEServer) (*webrtc.PeerConnection, error) {
se := webrtc.SettingEngine{}
se.SetNetworkTypes([]webrtc.NetworkType{webrtc.NetworkTypeUDP4})
se.SetSrflxAcceptanceMinWait(0)
se.DetachDataChannels()
se.SetICETimeouts(time.Second*5, time.Second*5, time.Second*2)

Expand All @@ -165,6 +167,7 @@ func newPeerConnection(servers []webrtc.ICEServer) (*webrtc.PeerConnection, erro
if server.Credential != nil && len(server.URLs) == 1 {
url, err := ice.ParseURL(server.URLs[0])
if err == nil && url.Proto == ice.ProtoTypeTCP {
se.SetNetworkTypes([]webrtc.NetworkType{webrtc.NetworkTypeTCP4, webrtc.NetworkTypeTCP6})
se.SetRelayAcceptanceMinWait(0)
}
}
Expand Down Expand Up @@ -213,6 +216,25 @@ func proxyICECandidates(conn *webrtc.PeerConnection, w io.Writer) func() {
}
}

// Waits for a PeerConnection to hit the open state.
func waitForConnectionOpen(ctx context.Context, conn *webrtc.PeerConnection) error {
if conn.ConnectionState() == webrtc.PeerConnectionStateConnected {
return nil
}
ctx, cancelFunc := context.WithTimeout(ctx, time.Second*15)
defer cancelFunc()
conn.OnConnectionStateChange(func(pcs webrtc.PeerConnectionState) {
if pcs == webrtc.PeerConnectionStateConnected {
cancelFunc()
}
})
<-ctx.Done()
if ctx.Err() == context.DeadlineExceeded {
return ctx.Err()
}
return nil
}

// Waits for a DataChannel to hit the open state.
func waitForDataChannelOpen(ctx context.Context, channel *webrtc.DataChannel) error {
if channel.ReadyState() == webrtc.DataChannelStateOpen {
Expand Down