mirror of https://github.com/cbeuw/Cloak
Implement stream SetReadDeadline
This commit is contained in:
parent
e41394c83c
commit
86214a1df0
|
|
@ -69,14 +69,10 @@ func Copy(dst net.Conn, src net.Conn, srcReadTimeout time.Duration) (written int
|
||||||
//}
|
//}
|
||||||
for {
|
for {
|
||||||
if srcReadTimeout != 0 {
|
if srcReadTimeout != 0 {
|
||||||
src.SetReadDeadline(time.Now().Add(srcReadTimeout))
|
err = src.SetReadDeadline(time.Now().Add(srcReadTimeout))
|
||||||
/*
|
if err != nil {
|
||||||
err =
|
break
|
||||||
if err != nil {
|
}
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
nr, er := src.Read(buf)
|
nr, er := src.Read(buf)
|
||||||
if nr > 0 {
|
if nr > 0 {
|
||||||
|
|
|
||||||
|
|
@ -4,18 +4,23 @@ package multiplex
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
const BUF_SIZE_LIMIT = 1 << 20 * 500
|
const BUF_SIZE_LIMIT = 1 << 20 * 500
|
||||||
|
|
||||||
|
var ErrTimeout = errors.New("deadline exceeded")
|
||||||
|
|
||||||
// The point of a bufferedPipe is that Read() will block until data is available
|
// The point of a bufferedPipe is that Read() will block until data is available
|
||||||
type bufferedPipe struct {
|
type bufferedPipe struct {
|
||||||
buf *bytes.Buffer
|
buf *bytes.Buffer
|
||||||
closed uint32
|
closed uint32
|
||||||
rwCond *sync.Cond
|
rwCond *sync.Cond
|
||||||
|
rDeadline time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewBufferedPipe() *bufferedPipe {
|
func NewBufferedPipe() *bufferedPipe {
|
||||||
|
|
@ -33,7 +38,13 @@ func (p *bufferedPipe) Read(target []byte) (int, error) {
|
||||||
if atomic.LoadUint32(&p.closed) == 1 && p.buf.Len() == 0 {
|
if atomic.LoadUint32(&p.closed) == 1 && p.buf.Len() == 0 {
|
||||||
return 0, io.EOF
|
return 0, io.EOF
|
||||||
}
|
}
|
||||||
|
if !p.rDeadline.IsZero() {
|
||||||
|
d := time.Until(p.rDeadline)
|
||||||
|
if d <= 0 {
|
||||||
|
return 0, ErrTimeout
|
||||||
|
}
|
||||||
|
time.AfterFunc(d, p.rwCond.Broadcast)
|
||||||
|
}
|
||||||
if p.buf.Len() > 0 {
|
if p.buf.Len() > 0 {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
@ -75,3 +86,11 @@ func (p *bufferedPipe) Len() int {
|
||||||
defer p.rwCond.L.Unlock()
|
defer p.rwCond.L.Unlock()
|
||||||
return p.buf.Len()
|
return p.buf.Len()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *bufferedPipe) SetReadDeadline(t time.Time) {
|
||||||
|
p.rwCond.L.Lock()
|
||||||
|
defer p.rwCond.L.Unlock()
|
||||||
|
|
||||||
|
p.rDeadline = t
|
||||||
|
p.rwCond.Broadcast()
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"io"
|
"io"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
const DATAGRAM_NUMBER_LIMIT = 1024
|
const DATAGRAM_NUMBER_LIMIT = 1024
|
||||||
|
|
@ -14,9 +15,10 @@ const DATAGRAM_NUMBER_LIMIT = 1024
|
||||||
// instead of byte-oriented. The integrity of datagrams written into this buffer is preserved.
|
// instead of byte-oriented. The integrity of datagrams written into this buffer is preserved.
|
||||||
// it won't get chopped up into individual bytes
|
// it won't get chopped up into individual bytes
|
||||||
type datagramBuffer struct {
|
type datagramBuffer struct {
|
||||||
buf [][]byte
|
buf [][]byte
|
||||||
closed uint32
|
closed uint32
|
||||||
rwCond *sync.Cond
|
rwCond *sync.Cond
|
||||||
|
rDeadline time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDatagramBuffer() *datagramBuffer {
|
func NewDatagramBuffer() *datagramBuffer {
|
||||||
|
|
@ -35,6 +37,14 @@ func (d *datagramBuffer) Read(target []byte) (int, error) {
|
||||||
return 0, io.EOF
|
return 0, io.EOF
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !d.rDeadline.IsZero() {
|
||||||
|
delta := time.Until(d.rDeadline)
|
||||||
|
if delta <= 0 {
|
||||||
|
return 0, ErrTimeout
|
||||||
|
}
|
||||||
|
time.AfterFunc(delta, d.rwCond.Broadcast)
|
||||||
|
}
|
||||||
|
|
||||||
if len(d.buf) > 0 {
|
if len(d.buf) > 0 {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
@ -84,3 +94,11 @@ func (d *datagramBuffer) Close() error {
|
||||||
d.rwCond.Broadcast()
|
d.rwCond.Broadcast()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *datagramBuffer) SetReadDeadline(t time.Time) {
|
||||||
|
d.rwCond.L.Lock()
|
||||||
|
defer d.rwCond.L.Unlock()
|
||||||
|
|
||||||
|
d.rDeadline = t
|
||||||
|
d.rwCond.Broadcast()
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,13 @@
|
||||||
package multiplex
|
package multiplex
|
||||||
|
|
||||||
import "io"
|
import (
|
||||||
|
"io"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
type recvBuffer interface {
|
type recvBuffer interface {
|
||||||
// Read calls' err must be nil | io.EOF | io.ErrShortBuffer
|
// Read calls' err must be nil | io.EOF | io.ErrShortBuffer
|
||||||
io.ReadCloser
|
io.ReadCloser
|
||||||
Write(Frame) (toBeClosed bool, err error)
|
Write(Frame) (toBeClosed bool, err error)
|
||||||
|
SetReadDeadline(time time.Time)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,12 @@ package multiplex
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"github.com/cbeuw/connutil"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"strconv"
|
"strconv"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
var seshConfigOrdered = SessionConfig{
|
var seshConfigOrdered = SessionConfig{
|
||||||
|
|
@ -398,6 +400,50 @@ func TestParallel(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestStream_SetReadDeadline(t *testing.T) {
|
||||||
|
var sessionKey [32]byte
|
||||||
|
rand.Read(sessionKey[:])
|
||||||
|
obfuscator, _ := MakeObfuscator(E_METHOD_PLAIN, sessionKey)
|
||||||
|
seshConfigOrdered.Obfuscator = obfuscator
|
||||||
|
|
||||||
|
testReadDeadline := func(sesh *Session) {
|
||||||
|
t.Run("read after deadline set", func(t *testing.T) {
|
||||||
|
stream, _ := sesh.OpenStream()
|
||||||
|
_ = stream.SetReadDeadline(time.Now().Add(-1 * time.Second))
|
||||||
|
_, err := stream.Read(make([]byte, 1))
|
||||||
|
if err != ErrTimeout {
|
||||||
|
t.Errorf("expecting error %v, got %v", ErrTimeout, err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("unblock when deadline passed", func(t *testing.T) {
|
||||||
|
stream, _ := sesh.OpenStream()
|
||||||
|
|
||||||
|
done := make(chan struct{})
|
||||||
|
go func() {
|
||||||
|
_, _ = stream.Read(make([]byte, 1))
|
||||||
|
done <- struct{}{}
|
||||||
|
}()
|
||||||
|
|
||||||
|
_ = stream.SetReadDeadline(time.Now().Add(100 * time.Millisecond))
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-done:
|
||||||
|
return
|
||||||
|
case <-time.After(500 * time.Millisecond):
|
||||||
|
t.Error("Read did not unblock after deadline has passed")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
sesh := MakeSession(0, seshConfigOrdered)
|
||||||
|
sesh.AddConnection(connutil.Discard())
|
||||||
|
testReadDeadline(sesh)
|
||||||
|
sesh = MakeSession(0, seshConfigUnordered)
|
||||||
|
sesh.AddConnection(connutil.Discard())
|
||||||
|
testReadDeadline(sesh)
|
||||||
|
}
|
||||||
|
|
||||||
func BenchmarkRecvDataFromRemote_Ordered(b *testing.B) {
|
func BenchmarkRecvDataFromRemote_Ordered(b *testing.B) {
|
||||||
testPayloadLen := 1024
|
testPayloadLen := 1024
|
||||||
testPayload := make([]byte, testPayloadLen)
|
testPayload := make([]byte, testPayloadLen)
|
||||||
|
|
|
||||||
|
|
@ -146,5 +146,5 @@ func (s *Stream) RemoteAddr() net.Addr { return s.session.addrs.Load().([]net.Ad
|
||||||
|
|
||||||
// TODO: implement the following
|
// TODO: implement the following
|
||||||
func (s *Stream) SetDeadline(t time.Time) error { return errNotImplemented }
|
func (s *Stream) SetDeadline(t time.Time) error { return errNotImplemented }
|
||||||
func (s *Stream) SetReadDeadline(t time.Time) error { return errNotImplemented }
|
func (s *Stream) SetReadDeadline(t time.Time) error { s.recvBuf.SetReadDeadline(t); return nil }
|
||||||
func (s *Stream) SetWriteDeadline(t time.Time) error { return errNotImplemented }
|
func (s *Stream) SetWriteDeadline(t time.Time) error { return errNotImplemented }
|
||||||
|
|
|
||||||
|
|
@ -7,13 +7,14 @@ package multiplex
|
||||||
// remote side before packet0. Cloak have to therefore sequence the packets so that they
|
// remote side before packet0. Cloak have to therefore sequence the packets so that they
|
||||||
// arrive in order as they were sent by the proxy software
|
// arrive in order as they were sent by the proxy software
|
||||||
//
|
//
|
||||||
// Cloak packets will have a 32-bit sequence number on them, so we know in which order
|
// Cloak packets will have a 64-bit sequence number on them, so we know in which order
|
||||||
// they should be sent to the proxy software. The code in this file provides buffering and sorting.
|
// they should be sent to the proxy software. The code in this file provides buffering and sorting.
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"container/heap"
|
"container/heap"
|
||||||
"fmt"
|
"fmt"
|
||||||
"sync"
|
"sync"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type sorterHeap []*Frame
|
type sorterHeap []*Frame
|
||||||
|
|
@ -57,8 +58,6 @@ func NewStreamBuffer() *streamBuffer {
|
||||||
return sb
|
return sb
|
||||||
}
|
}
|
||||||
|
|
||||||
// recvNewFrame is a forever running loop which receives frames unordered,
|
|
||||||
// cache and order them and send them into sortedBufCh
|
|
||||||
func (sb *streamBuffer) Write(f Frame) (toBeClosed bool, err error) {
|
func (sb *streamBuffer) Write(f Frame) (toBeClosed bool, err error) {
|
||||||
sb.recvM.Lock()
|
sb.recvM.Lock()
|
||||||
defer sb.recvM.Unlock()
|
defer sb.recvM.Unlock()
|
||||||
|
|
@ -100,3 +99,5 @@ func (sb *streamBuffer) Read(buf []byte) (int, error) {
|
||||||
func (sb *streamBuffer) Close() error {
|
func (sb *streamBuffer) Close() error {
|
||||||
return sb.buf.Close()
|
return sb.buf.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (sb *streamBuffer) SetReadDeadline(t time.Time) { sb.buf.SetReadDeadline(t) }
|
||||||
|
|
|
||||||
|
|
@ -173,7 +173,6 @@ func DispatchConnection(conn net.Conn, sta *State) {
|
||||||
}
|
}
|
||||||
log.Tracef("%v endpoint has been successfully connected", ci.ProxyMethod)
|
log.Tracef("%v endpoint has been successfully connected", ci.ProxyMethod)
|
||||||
|
|
||||||
//TODO: stream timeout
|
|
||||||
go func() {
|
go func() {
|
||||||
if _, err := common.Copy(localConn, newStream, sta.Timeout); err != nil {
|
if _, err := common.Copy(localConn, newStream, sta.Timeout); err != nil {
|
||||||
log.Debugf("copying stream to proxy client: %v", err)
|
log.Debugf("copying stream to proxy client: %v", err)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue