forked from getlantern/marionette
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathconn.go
77 lines (66 loc) · 1.77 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
package marionette
import (
"io"
"net"
"strings"
)
type BufferedConn struct {
net.Conn
buf []byte
}
func NewBufferedConn(conn net.Conn, bufferSize int) *BufferedConn {
return &BufferedConn{
Conn: conn,
buf: make([]byte, 0, bufferSize),
}
}
// Read is unavailable for BufferedConn.
func (conn *BufferedConn) Read(p []byte) (int, error) {
panic("BufferedConn.Read(): unavailable, use Peek/Seek")
}
// Peek returns the first n bytes of the read buffer.
// If n is -1 then returns available buffer once any bytes are available.
func (conn *BufferedConn) Peek(n int) ([]byte, error) {
for {
if n >= 0 && len(conn.buf) >= n {
return conn.buf[:n], nil
} else if n == -1 && len(conn.buf) > 0 {
return conn.buf, nil
}
capacity := cap(conn.buf)
if n >= 0 {
capacity = n - len(conn.buf)
}
nn, err := conn.Conn.Read(conn.buf[len(conn.buf) : len(conn.buf)+capacity])
if isTimeoutError(err) {
continue
} else if isEOFError(err) {
return conn.buf, io.EOF
} else if err != nil {
return conn.buf, err
}
conn.buf = conn.buf[:len(conn.buf)+nn]
}
}
// Seek moves the buffer forward a given number of bytes.
// This implementation only supports io.SeekCurrent.
func (conn *BufferedConn) Seek(offset int64, whence int) (int64, error) {
assert(whence == io.SeekCurrent)
assert(offset <= int64(len(conn.buf)))
b := conn.buf[offset:]
conn.buf = conn.buf[:len(b)]
copy(conn.buf, b)
return 0, nil
}
// isTimeoutError returns true if the error is a timeout error.
func isTimeoutError(err error) bool {
if err == nil {
return false
} else if err, ok := err.(interface{ Timeout() bool }); ok && err.Timeout() {
return true
}
return false
}
func isEOFError(err error) bool {
return err != nil && strings.Contains(err.Error(), "connection reset by peer")
}