-
Notifications
You must be signed in to change notification settings - Fork 0
/
copy.go
100 lines (85 loc) · 2.03 KB
/
copy.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
package buf
import (
"io"
"v2ray.com/core/common/errors"
"v2ray.com/core/common/signal"
)
type errorHandler func(error) error
type dataHandler func(MultiBuffer)
type copyHandler struct {
onReadError []errorHandler
onData []dataHandler
onWriteError []errorHandler
}
func (h *copyHandler) readFrom(reader Reader) (MultiBuffer, error) {
mb, err := reader.Read()
if err != nil {
for _, handler := range h.onReadError {
err = handler(err)
}
}
return mb, err
}
func (h *copyHandler) writeTo(writer Writer, mb MultiBuffer) error {
err := writer.Write(mb)
if err != nil {
for _, handler := range h.onWriteError {
err = handler(err)
}
}
return err
}
type CopyOption func(*copyHandler)
func IgnoreReaderError() CopyOption {
return func(handler *copyHandler) {
handler.onReadError = append(handler.onReadError, func(err error) error {
return nil
})
}
}
func IgnoreWriterError() CopyOption {
return func(handler *copyHandler) {
handler.onWriteError = append(handler.onWriteError, func(err error) error {
return nil
})
}
}
func UpdateActivity(timer signal.ActivityUpdater) CopyOption {
return func(handler *copyHandler) {
handler.onData = append(handler.onData, func(MultiBuffer) {
timer.Update()
})
}
}
func copyInternal(reader Reader, writer Writer, handler *copyHandler) error {
for {
buffer, err := handler.readFrom(reader)
if err != nil {
return err
}
if buffer.IsEmpty() {
buffer.Release()
continue
}
for _, handler := range handler.onData {
handler(buffer)
}
if err := handler.writeTo(writer, buffer); err != nil {
buffer.Release()
return err
}
}
}
// Copy dumps all payload from reader to writer or stops when an error occurs.
// ActivityTimer gets updated as soon as there is a payload.
func Copy(reader Reader, writer Writer, options ...CopyOption) error {
handler := new(copyHandler)
for _, option := range options {
option(handler)
}
err := copyInternal(reader, writer, handler)
if err != nil && errors.Cause(err) != io.EOF {
return err
}
return nil
}