github.com/eagleql/xray-core@v1.4.4/common/buf/copy.go (about) 1 package buf 2 3 import ( 4 "io" 5 "time" 6 7 "github.com/eagleql/xray-core/common/errors" 8 "github.com/eagleql/xray-core/common/signal" 9 ) 10 11 type dataHandler func(MultiBuffer) 12 13 type copyHandler struct { 14 onData []dataHandler 15 } 16 17 // SizeCounter is for counting bytes copied by Copy(). 18 type SizeCounter struct { 19 Size int64 20 } 21 22 // CopyOption is an option for copying data. 23 type CopyOption func(*copyHandler) 24 25 // UpdateActivity is a CopyOption to update activity on each data copy operation. 26 func UpdateActivity(timer signal.ActivityUpdater) CopyOption { 27 return func(handler *copyHandler) { 28 handler.onData = append(handler.onData, func(MultiBuffer) { 29 timer.Update() 30 }) 31 } 32 } 33 34 // CountSize is a CopyOption that sums the total size of data copied into the given SizeCounter. 35 func CountSize(sc *SizeCounter) CopyOption { 36 return func(handler *copyHandler) { 37 handler.onData = append(handler.onData, func(b MultiBuffer) { 38 sc.Size += int64(b.Len()) 39 }) 40 } 41 } 42 43 type readError struct { 44 error 45 } 46 47 func (e readError) Error() string { 48 return e.error.Error() 49 } 50 51 func (e readError) Inner() error { 52 return e.error 53 } 54 55 // IsReadError returns true if the error in Copy() comes from reading. 56 func IsReadError(err error) bool { 57 _, ok := err.(readError) 58 return ok 59 } 60 61 type writeError struct { 62 error 63 } 64 65 func (e writeError) Error() string { 66 return e.error.Error() 67 } 68 69 func (e writeError) Inner() error { 70 return e.error 71 } 72 73 // IsWriteError returns true if the error in Copy() comes from writing. 74 func IsWriteError(err error) bool { 75 _, ok := err.(writeError) 76 return ok 77 } 78 79 func copyInternal(reader Reader, writer Writer, handler *copyHandler) error { 80 for { 81 buffer, err := reader.ReadMultiBuffer() 82 if !buffer.IsEmpty() { 83 for _, handler := range handler.onData { 84 handler(buffer) 85 } 86 87 if werr := writer.WriteMultiBuffer(buffer); werr != nil { 88 return writeError{werr} 89 } 90 } 91 92 if err != nil { 93 return readError{err} 94 } 95 } 96 } 97 98 // Copy dumps all payload from reader to writer or stops when an error occurs. It returns nil when EOF. 99 func Copy(reader Reader, writer Writer, options ...CopyOption) error { 100 var handler copyHandler 101 for _, option := range options { 102 option(&handler) 103 } 104 err := copyInternal(reader, writer, &handler) 105 if err != nil && errors.Cause(err) != io.EOF { 106 return err 107 } 108 return nil 109 } 110 111 var ErrNotTimeoutReader = newError("not a TimeoutReader") 112 113 func CopyOnceTimeout(reader Reader, writer Writer, timeout time.Duration) error { 114 timeoutReader, ok := reader.(TimeoutReader) 115 if !ok { 116 return ErrNotTimeoutReader 117 } 118 mb, err := timeoutReader.ReadMultiBufferTimeout(timeout) 119 if err != nil { 120 return err 121 } 122 return writer.WriteMultiBuffer(mb) 123 }