This repository has been archived by the owner on Jul 12, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
conn.go
155 lines (135 loc) · 3.99 KB
/
conn.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
package transportc
import (
"errors"
"io"
"net"
"os"
"time"
"github.com/pion/datachannel"
"golang.org/x/net/context"
)
// Conn is a net.Conn implementation for WebRTC DataChannels.
type Conn struct {
dataChannel datachannel.ReadWriteCloser
mtu int // Max Transmission Unit for both recv and send
readDeadline time.Time
readBuf chan []byte
writeDeadline time.Time
}
// Read implements the net.Conn Read method.
func (c *Conn) Read(p []byte) (int, error) {
if c.readDeadline.Before(time.Now()) && !c.readDeadline.IsZero() {
return 0, os.ErrDeadlineExceeded
}
var ctx context.Context = context.Background()
var cancel context.CancelFunc = func() {}
if !c.readDeadline.IsZero() {
ctx, cancel = context.WithDeadline(ctx, c.readDeadline)
}
defer cancel()
select {
case <-ctx.Done():
if errors.Is(ctx.Err(), context.DeadlineExceeded) {
return 0, os.ErrDeadlineExceeded
}
return 0, ctx.Err()
case b := <-c.readBuf:
var err error = nil
if len(b) > len(p) {
err = io.ErrShortBuffer
}
return copy(p, b), err
}
}
// Write implements the net.Conn Write method.
// If the size of the buffer is greater than the MTU,
// the data could still be sent but will be fragmented (not recommended).
func (c *Conn) Write(p []byte) (int, error) {
if c.writeDeadline.Before(time.Now()) && !c.writeDeadline.IsZero() {
return 0, os.ErrDeadlineExceeded
}
var written int
if len(p) > c.mtu {
// split into multiple packets then write
for len(p) > c.mtu {
var err error
segment_written, err := c.dataChannel.Write(p[:c.mtu])
written += segment_written
if err != nil {
return written, err
}
p = p[c.mtu:]
}
}
// write the remaining bytes
segment_written, err := c.dataChannel.Write(p)
written += segment_written
return written, err
}
// Close implements the net.Conn Close method.
func (c *Conn) Close() error {
return c.dataChannel.Close()
}
// LocalAddr implements the net.Conn LocalAddr method.
//
// It is hardcoded to return nil since WebRTC DataChannels are P2P
// and Local addresses are therefore trivial.
func (*Conn) LocalAddr() net.Addr {
return nil
}
// RemoteAddr implements the net.Conn RemoteAddr method.
//
// It is hardcoded to return nil since WebRTC DataChannels are P2P
// and Remote addresses are therefore trivial.
func (*Conn) RemoteAddr() net.Addr {
return nil
}
// SetDeadline implements the net.Conn SetDeadline method.
// It sets both read and write deadlines in a single call.
//
// See SetReadDeadline and SetWriteDeadline for the behavior of the deadlines.
func (c *Conn) SetDeadline(deadline time.Time) error {
if deadline.Before(time.Now()) && !deadline.IsZero() {
return errors.New("deadline is in the past")
}
c.readDeadline = deadline
c.writeDeadline = deadline
return nil
}
// SetReadDeadline sets the deadline for future Read calls.
//
// A Read call will fail and return os.ErrDeadlineExceeded
// before attempting to read from the buffer if the deadline has passed.
// And a Read call will block till no later than the set read deadline.
func (c *Conn) SetReadDeadline(deadline time.Time) error {
if deadline.Before(time.Now()) && !deadline.IsZero() {
return errors.New("deadline is in the past")
}
c.readDeadline = deadline
return nil
}
// SetWriteDeadline sets the deadline for future Write calls.
//
// A Write call will fail and return os.ErrDeadlineExceeded
// before attempting to write to the buffer if the deadline has passed.
// Otherwise the set write deadline will not affect the WriteTo call.
func (c *Conn) SetWriteDeadline(deadline time.Time) error {
if deadline.Before(time.Now()) && !deadline.IsZero() {
return errors.New("deadline is in the past")
}
c.writeDeadline = deadline
return nil
}
// readLoop reads from the underlying DataChannel and writes to the readBuf channel.
//
// Start running in a goroutine once the datachannel is opened.
func (c *Conn) readLoop() {
for {
b := make([]byte, c.mtu)
n, err := c.dataChannel.Read(b)
if err != nil {
break // Conn failed or closed
}
c.readBuf <- b[:n]
}
}