diff --git a/mplexer/conn.go b/mplexer/conn.go index c9beee1..12051d4 100644 --- a/mplexer/conn.go +++ b/mplexer/conn.go @@ -1,6 +1,7 @@ package telebit import ( + "bufio" "net" "time" ) @@ -16,6 +17,7 @@ var encryptedSchemes = map[string]struct{}{ type Conn struct { relaySourceAddr Addr relayTargetAddr Addr + peeker *bufio.Reader relay net.Conn local net.Conn //terminated bool @@ -37,9 +39,19 @@ func NewConn(conn *Conn) *Conn { // Read can be made to time out and return an Error with Timeout() == true // after a fixed time limit; see SetDeadline and SetReadDeadline. func (c *Conn) Read(b []byte) (n int, err error) { + if nil != c.peeker { + return c.peeker.Read(b) + } return c.relay.Read(b) } +func (c *Conn) Peek(n int) (b []byte, err error) { + if nil == c.peeker { + c.peeker = bufio.NewReaderSize(c, defaultPeekerSize) + } + return c.peeker.Peek(n) +} + // Write writes data to the connection. // Write can be made to time out and return an Error with Timeout() == true // after a fixed time limit; see SetDeadline and SetWriteDeadline. diff --git a/mplexer/connwrap.go b/mplexer/connwrap.go index ef0890b..e0d698f 100644 --- a/mplexer/connwrap.go +++ b/mplexer/connwrap.go @@ -1,17 +1,46 @@ package telebit import ( + "bufio" "net" "time" ) // ConnWrap is just a cheap way to DRY up some switch conn.(type) statements to handle special features of Conn type ConnWrap struct { - Conn net.Conn - Plain net.Conn + //Conn net.Conn + peeker *bufio.Reader + Conn net.Conn + Plain net.Conn +} + +type Peeker interface { + Peek(n int) ([]byte, error) +} + +func (c *ConnWrap) Peek(n int) ([]byte, error) { + if nil != c.peeker { + return c.peeker.Peek(n) + } + + switch conn := c.Conn.(type) { + case *ConnWrap: + return conn.Peek(n) + case *Conn: + return conn.Peek(n) + default: + // *net.UDPConn,*net.TCPConn,*net.IPConn,*net.UnixConn + if nil == c.peeker { + c.peeker = bufio.NewReaderSize(c, defaultPeekerSize) + } + return c.peeker.Peek(n) + } } func (c *ConnWrap) Read(b []byte) (n int, err error) { + if nil != c.peeker { + return c.peeker.Read(b) + } return c.Conn.Read(b) } @@ -48,7 +77,7 @@ func (c *ConnWrap) Scheme() string { func (c *ConnWrap) Servername() string { if nil != c.Plain { tlsConn := &ConnWrap{Conn: c.Plain} - return tlsConn.Scheme() + return tlsConn.Servername() } switch conn := c.Conn.(type) { @@ -63,6 +92,7 @@ func (c *ConnWrap) Servername() string { // isTerminated returns true if net.Conn is either a ConnWrap{ tls.Conn }, // or a telebit.Conn with a non-encrypted `scheme` such as "tcp" or "http". func (c *ConnWrap) isTerminated() bool { + // TODO look at SNI, may need context for Peek() timeout if nil != c.Plain { return true } diff --git a/mplexer/telebit.go b/mplexer/telebit.go index 18028a1..f7085af 100644 --- a/mplexer/telebit.go +++ b/mplexer/telebit.go @@ -22,6 +22,7 @@ import ( // Nerds posting benchmarks on SO show that 8k seems about right, // but even 1024b could work well. var defaultBufferSize = 8192 +var defaultPeekerSize = 1024 // ErrBadGateway means that the target did not accept the connection var ErrBadGateway = errors.New("EBADGATEWAY")