-
Notifications
You must be signed in to change notification settings - Fork 1.2k
/
loopback.go
207 lines (180 loc) · 5.25 KB
/
loopback.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
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
// Copyright 2015 Keybase, Inc. All rights reserved. Use of
// this source code is governed by the included BSD license.
package libkb
import (
"bytes"
"io"
"net"
"sync"
"syscall"
"time"
)
// LoopbackAddr is an address class that implement the net.Addr interface for loopback
// devices
type LoopbackAddr struct{}
// LoopbackListener is a listener that creates new loopback connections.
// It is goroutine safe.
type LoopbackListener struct {
logCtx LogContext
// Protects closing of ch so that we can close ch
// and set isClosed atomically.
mutex sync.Mutex
ch chan *LoopbackConn
isClosed bool
}
// LoopbackConn implments the net.Conn interface but is used to loopback
// from a process to itself. It is goroutine safe.
type LoopbackConn struct {
// wMutex protects isClosed and ch to protect against
// double-closes, and writes after close. It protects the
// writer, hence the 'w'.
wMutex sync.Mutex
isClosed bool
ch chan<- []byte
// rMutex Protects partnerCh and buf, to ensure that only
// one Go routine is reading at a time. It protects the
// reader hence the 'r'
rMutex sync.Mutex
partnerCh <-chan []byte
buf bytes.Buffer
}
// NewLoopbackListener creates a new Loopback listener
func NewLoopbackListener(ctx LogContext) *LoopbackListener {
return &LoopbackListener{
logCtx: ctx,
ch: make(chan *LoopbackConn),
isClosed: false,
}
}
// NewLoopbackConnPair makes a new loopback connection pair
func NewLoopbackConnPair() (*LoopbackConn, *LoopbackConn) {
aCh := make(chan []byte)
bCh := make(chan []byte)
a := &LoopbackConn{ch: aCh}
b := &LoopbackConn{ch: bCh}
a.partnerCh = bCh
b.partnerCh = aCh
return a, b
}
// LoopbackDial dials the given LoopbackListener and yields an new net.Conn
// that's a connection to it.
func (ll *LoopbackListener) Dial() (net.Conn, error) {
ll.logCtx.GetLog().Debug("+ LoopbackListener.Dial")
ll.mutex.Lock()
defer ll.mutex.Unlock()
if ll.isClosed {
return nil, syscall.EINVAL
}
a, b := NewLoopbackConnPair()
ll.ch <- a
return b, nil
}
// Accept waits for and returns the next connection to the listener.
func (ll *LoopbackListener) Accept() (ret net.Conn, err error) {
ll.logCtx.GetLog().Debug("+ LoopbackListener.Accept")
var ok bool
// We can't hold the lock (even if we had to) since that would
// deadlock the process (to have the Accepter and Dialer contending
// the same lock).
if ret, ok = <-ll.ch; !ok {
err = syscall.EINVAL
}
ll.logCtx.GetLog().Debug("- LoopbackListener.Accept -> %v", err)
return ret, err
}
// Close closes the listener.
// Any blocked Accept operations will be unblocked and return errors
func (ll *LoopbackListener) Close() (err error) {
ll.mutex.Lock()
defer ll.mutex.Unlock()
if ll.isClosed {
return syscall.EINVAL
}
ll.isClosed = true
close(ll.ch)
return
}
// Addr returns the listener's network address.
func (ll *LoopbackListener) Addr() (addr net.Addr) {
return LoopbackAddr{}
}
// Read reads data from the connection.
func (lc *LoopbackConn) Read(b []byte) (n int, err error) {
lc.rMutex.Lock()
defer lc.rMutex.Unlock()
if lc.buf.Len() > 0 {
return lc.buf.Read(b)
}
msg, ok := <-lc.partnerCh
if !ok {
return 0, io.EOF
}
lc.buf.Write(msg)
return lc.buf.Read(b)
}
// Write writes data to the connection.
func (lc *LoopbackConn) Write(b []byte) (n int, err error) {
lc.wMutex.Lock()
defer lc.wMutex.Unlock()
if lc.isClosed {
return 0, syscall.EINVAL
}
lc.ch <- b
return len(b), nil
}
// Close closes the connection.
// Any blocked Read or Write operations will be unblocked and return errors.
func (lc *LoopbackConn) Close() (err error) {
lc.wMutex.Lock()
defer lc.wMutex.Unlock()
if lc.isClosed {
return syscall.EINVAL
}
lc.isClosed = true
close(lc.ch)
return nil
}
// LocalAddr returns the local network address.
func (lc *LoopbackConn) LocalAddr() (addr net.Addr) {
return
}
// RemoteAddr returns the remote network address.
func (lc *LoopbackConn) RemoteAddr() (addr net.Addr) {
return
}
// SetDeadline sets the read and write deadlines associated
// with the connection. It is equivalent to calling both
// SetReadDeadline and SetWriteDeadline.
//
// A deadline is an absolute time after which I/O operations
// fail with a timeout (see type Error) instead of
// blocking. The deadline applies to all future I/O, not just
// the immediately following call to Read or Write.
//
// An idle timeout can be implemented by repeatedly extending
// the deadline after successful Read or Write calls.
//
// A zero value for t means I/O operations will not time out.
func (lc *LoopbackConn) SetDeadline(t time.Time) (err error) {
return
}
// SetReadDeadline sets the deadline for future Read calls.
// A zero value for t means Read will not time out.
func (lc *LoopbackConn) SetReadDeadline(t time.Time) (err error) {
return
}
// SetWriteDeadline sets the deadline for future Write calls.
// Even if write times out, it may return n > 0, indicating that
// some of the data was successfully written.
// A zero value for t means Write will not time out.
func (lc *LoopbackConn) SetWriteDeadline(t time.Time) (err error) {
return
}
// Network returns the name of the network
func (la LoopbackAddr) Network() (s string) {
return "loopback"
}
// String returns the string form of address
func (la LoopbackAddr) String() (s string) {
return "0"
}