mirror of https://github.com/cbeuw/Cloak
Better and more explicit handling of first packet, reduces exposure of active probing
This commit is contained in:
parent
2bb102a5b6
commit
253ea94d2a
2
go.mod
2
go.mod
|
|
@ -9,8 +9,10 @@ require (
|
|||
github.com/juju/ratelimit v1.0.1
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect
|
||||
github.com/kr/pretty v0.1.0 // indirect
|
||||
github.com/mitchellh/gox v1.0.1 // indirect
|
||||
github.com/refraction-networking/utls v0.0.0-20190909200633-43c36d3c1f57
|
||||
github.com/sirupsen/logrus v1.5.0
|
||||
github.com/stretchr/testify v1.2.2
|
||||
go.etcd.io/bbolt v1.3.4
|
||||
golang.org/x/crypto v0.0.0-20200414173820-0848c9571904
|
||||
golang.org/x/sys v0.0.0-20200413165638-669c56c373c4 // indirect
|
||||
|
|
|
|||
6
go.sum
6
go.sum
|
|
@ -6,6 +6,8 @@ github.com/gorilla/mux v1.7.3 h1:gnP5JzjVOuiZD07fKKToCAOjS0yOpj/qPETTXCCS6hw=
|
|||
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||
github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM=
|
||||
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/hashicorp/go-version v1.0.0 h1:21MVWPKDphxa7ineQQTrCU5brh7OuVVAzGOCnnCPtE8=
|
||||
github.com/hashicorp/go-version v1.0.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||
github.com/juju/ratelimit v1.0.1 h1:+7AIFJVQ0EQgq/K9+0Krm7m530Du7tIz0METWzN0RgY=
|
||||
github.com/juju/ratelimit v1.0.1/go.mod h1:qapgC/Gy+xNh9UxzV13HGGl/6UXNN+ct+vwSgWNm/qk=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
|
||||
|
|
@ -17,6 +19,10 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN
|
|||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/mitchellh/gox v1.0.1 h1:x0jD3dcHk9a9xPSDN6YEL4xL6Qz0dvNYm8yZqui5chI=
|
||||
github.com/mitchellh/gox v1.0.1/go.mod h1:ED6BioOGXMswlXa2zxfh/xdd5QhwYliBFn9V18Ap4z4=
|
||||
github.com/mitchellh/iochan v1.0.0 h1:C+X3KsSTLFVBr/tK1eYN/vs4rJcvsiLU338UhYPJWeY=
|
||||
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/refraction-networking/utls v0.0.0-20190909200633-43c36d3c1f57 h1:SL1K0QAuC1b54KoY1pjPWe6kSlsFHwK9/oC960fKrTY=
|
||||
|
|
|
|||
|
|
@ -74,6 +74,7 @@ func (tls *TLSConn) Read(buffer []byte) (n int, err error) {
|
|||
err = io.ErrShortBuffer
|
||||
return
|
||||
}
|
||||
// we overwrite the record layer here
|
||||
return io.ReadFull(tls.Conn, buffer[:dataLength])
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -66,18 +66,7 @@ var ErrBadProxyMethod = errors.New("invalid proxy method")
|
|||
// if it is from a Cloak client, it returns the ClientInfo with the decrypted fields. It doesn't check if the user
|
||||
// is authorised. It also returns a finisher callback function to be called when the caller wishes to proceed with
|
||||
// the handshake
|
||||
func AuthFirstPacket(firstPacket []byte, sta *State) (info ClientInfo, finisher Responder, err error) {
|
||||
var transport Transport
|
||||
switch firstPacket[0] {
|
||||
case 0x47:
|
||||
transport = &WebSocket{}
|
||||
case 0x16:
|
||||
transport = &TLS{}
|
||||
default:
|
||||
err = ErrUnrecognisedProtocol
|
||||
return
|
||||
}
|
||||
|
||||
func AuthFirstPacket(firstPacket []byte, transport Transport, sta *State) (info ClientInfo, finisher Responder, err error) {
|
||||
fragments, finisher, err := transport.processFirstPacket(firstPacket, sta.StaticPv)
|
||||
if err != nil {
|
||||
return
|
||||
|
|
|
|||
|
|
@ -138,7 +138,7 @@ func TestAuthFirstPacket(t *testing.T) {
|
|||
t.Run("TLS correct", func(t *testing.T) {
|
||||
sta := getNewState()
|
||||
chBytes, _ := hex.DecodeString("1603010200010001fc0303ac530b5778469dbbc3f9a83c6ac35b63aa6a70c2014026ade30f2faf0266f0242068424f320bcad49b4315a761f9f6dec32b0a403c2d8c0ab337608a694c6e411c0024130113031302c02bc02fcca9cca8c02cc030c00ac009c013c01400330039002f0035000a0100018f00000011000f00000c7777772e62696e672e636f6d00170000ff01000100000a000e000c001d00170018001901000101000b00020100002300000010000e000c02683208687474702f312e310005000501000000000033006b0069001d00204655c2c83aaed1db2e89ed17d671fcdc76dc96e36bde8840022f1bda2f31019600170041543af1f8d28b37d984073f40e8361613da502f16e4039f00656f427de0f66480b2e77e3e552e126bb0cc097168f6e5454c7f9501126a2377fb40151f6cfc007e0e002b0009080304030303020301000d0018001604030503060308040805080604010501060102030201002d00020101001c00024001001500920000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")
|
||||
info, _, err := AuthFirstPacket(chBytes, sta)
|
||||
info, _, err := AuthFirstPacket(chBytes, TLS{}, sta)
|
||||
if err != nil {
|
||||
t.Errorf("failed to get client info: %v", err)
|
||||
return
|
||||
|
|
@ -155,12 +155,12 @@ func TestAuthFirstPacket(t *testing.T) {
|
|||
t.Run("TLS correct but replay", func(t *testing.T) {
|
||||
sta := getNewState()
|
||||
chBytes, _ := hex.DecodeString("1603010200010001fc0303ac530b5778469dbbc3f9a83c6ac35b63aa6a70c2014026ade30f2faf0266f0242068424f320bcad49b4315a761f9f6dec32b0a403c2d8c0ab337608a694c6e411c0024130113031302c02bc02fcca9cca8c02cc030c00ac009c013c01400330039002f0035000a0100018f00000011000f00000c7777772e62696e672e636f6d00170000ff01000100000a000e000c001d00170018001901000101000b00020100002300000010000e000c02683208687474702f312e310005000501000000000033006b0069001d00204655c2c83aaed1db2e89ed17d671fcdc76dc96e36bde8840022f1bda2f31019600170041543af1f8d28b37d984073f40e8361613da502f16e4039f00656f427de0f66480b2e77e3e552e126bb0cc097168f6e5454c7f9501126a2377fb40151f6cfc007e0e002b0009080304030303020301000d0018001604030503060308040805080604010501060102030201002d00020101001c00024001001500920000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")
|
||||
_, _, err := AuthFirstPacket(chBytes, sta)
|
||||
_, _, err := AuthFirstPacket(chBytes, TLS{}, sta)
|
||||
if err != nil {
|
||||
t.Error("failed to prepare for the first time")
|
||||
return
|
||||
}
|
||||
_, _, err = AuthFirstPacket(chBytes, sta)
|
||||
_, _, err = AuthFirstPacket(chBytes, TLS{}, sta)
|
||||
if err != ErrReplay {
|
||||
t.Errorf("failed to return ErrReplay, got %v instead", err)
|
||||
return
|
||||
|
|
@ -181,7 +181,7 @@ Sec-WebSocket-Version: 13
|
|||
Upgrade: websocket
|
||||
|
||||
`
|
||||
info, _, err := AuthFirstPacket([]byte(req), sta)
|
||||
info, _, err := AuthFirstPacket([]byte(req), WebSocket{}, sta)
|
||||
if err != nil {
|
||||
t.Errorf("failed to get client info: %v", err)
|
||||
return
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@ package server
|
|||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"github.com/cbeuw/Cloak/internal/common"
|
||||
"github.com/cbeuw/Cloak/internal/server/usermanager"
|
||||
"io"
|
||||
|
|
@ -37,21 +39,91 @@ func Serve(l net.Listener, sta *State) {
|
|||
}
|
||||
}
|
||||
|
||||
func connReadLine(conn net.Conn, buf []byte) (int, error) {
|
||||
i := 0
|
||||
for ; i < len(buf); i++ {
|
||||
_, err := io.ReadFull(conn, buf[i:i+1])
|
||||
if err != nil {
|
||||
return i, err
|
||||
}
|
||||
if buf[i] == '\n' {
|
||||
return i + 1, nil
|
||||
}
|
||||
}
|
||||
return i, io.ErrShortBuffer
|
||||
}
|
||||
|
||||
func readFirstPacket(conn net.Conn, buf []byte, timeout time.Duration) (int, Transport, bool, error) {
|
||||
conn.SetReadDeadline(time.Now().Add(timeout))
|
||||
defer conn.SetReadDeadline(time.Time{})
|
||||
|
||||
_, err := io.ReadFull(conn, buf[:1])
|
||||
if err != nil {
|
||||
err = fmt.Errorf("read error after connection is established: %v", err)
|
||||
conn.Close()
|
||||
return 0, nil, false, err
|
||||
}
|
||||
|
||||
// TODO: give the option to match the protocol with port
|
||||
bufOffset := 1
|
||||
var transport Transport
|
||||
switch buf[0] {
|
||||
case 0x16:
|
||||
transport = TLS{}
|
||||
recordLayerLength := 5
|
||||
|
||||
i, err := io.ReadFull(conn, buf[bufOffset:recordLayerLength])
|
||||
bufOffset += i
|
||||
if err != nil {
|
||||
err = fmt.Errorf("read error after connection is established: %v", err)
|
||||
conn.Close()
|
||||
return bufOffset, transport, false, err
|
||||
}
|
||||
dataLength := int(binary.BigEndian.Uint16(buf[3:5]))
|
||||
if dataLength+recordLayerLength > len(buf) {
|
||||
return bufOffset, transport, true, io.ErrShortBuffer
|
||||
}
|
||||
|
||||
i, err = io.ReadFull(conn, buf[recordLayerLength:dataLength+recordLayerLength])
|
||||
bufOffset += i
|
||||
if err != nil {
|
||||
err = fmt.Errorf("read error after connection is established: %v", err)
|
||||
conn.Close()
|
||||
return bufOffset, transport, false, err
|
||||
}
|
||||
case 0x47:
|
||||
transport = WebSocket{}
|
||||
|
||||
for {
|
||||
i, err := connReadLine(conn, buf[bufOffset:])
|
||||
line := buf[bufOffset : bufOffset+i]
|
||||
bufOffset += i
|
||||
if err != nil {
|
||||
if err == io.ErrShortBuffer {
|
||||
return bufOffset, transport, true, err
|
||||
} else {
|
||||
err = fmt.Errorf("error reading first packet: %v", err)
|
||||
conn.Close()
|
||||
return bufOffset, transport, false, err
|
||||
}
|
||||
}
|
||||
|
||||
if bytes.Equal(line, []byte("\r\n")) {
|
||||
break
|
||||
}
|
||||
}
|
||||
default:
|
||||
err = fmt.Errorf("unrecognised protocol signature")
|
||||
return bufOffset, transport, true, ErrUnrecognisedProtocol
|
||||
}
|
||||
return bufOffset, transport, true, nil
|
||||
}
|
||||
|
||||
func dispatchConnection(conn net.Conn, sta *State) {
|
||||
remoteAddr := conn.RemoteAddr()
|
||||
var err error
|
||||
buf := make([]byte, 1500)
|
||||
|
||||
// TODO: potential fingerprint for active probers here
|
||||
conn.SetReadDeadline(time.Now().Add(3 * time.Second))
|
||||
i, err := io.ReadAtLeast(conn, buf, 1)
|
||||
if err != nil {
|
||||
log.WithField("remoteAddr", remoteAddr).
|
||||
Infof("failed to read anything after connection is established: %v", err)
|
||||
conn.Close()
|
||||
return
|
||||
}
|
||||
conn.SetReadDeadline(time.Time{})
|
||||
i, transport, redirOnErr, err := readFirstPacket(conn, buf, 15*time.Second)
|
||||
data := buf[:i]
|
||||
|
||||
goWeb := func() {
|
||||
|
|
@ -73,10 +145,21 @@ func dispatchConnection(conn net.Conn, sta *State) {
|
|||
go io.Copy(conn, webConn)
|
||||
}
|
||||
|
||||
ci, finishHandshake, err := AuthFirstPacket(data, sta)
|
||||
if err != nil {
|
||||
log.WithField("remoteAddr", conn.RemoteAddr()).
|
||||
Warnf("error reading first packet: %v", err)
|
||||
if redirOnErr {
|
||||
goWeb()
|
||||
} else {
|
||||
conn.Close()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
ci, finishHandshake, err := AuthFirstPacket(data, transport, sta)
|
||||
if err != nil {
|
||||
log.WithFields(log.Fields{
|
||||
"remoteAddr": remoteAddr,
|
||||
"remoteAddr": conn.RemoteAddr(),
|
||||
"UID": b64(ci.UID),
|
||||
"sessionId": ci.SessionId,
|
||||
"proxyMethod": ci.ProxyMethod,
|
||||
|
|
@ -132,7 +215,7 @@ func dispatchConnection(conn net.Conn, sta *State) {
|
|||
if err != nil {
|
||||
log.WithFields(log.Fields{
|
||||
"UID": b64(ci.UID),
|
||||
"remoteAddr": remoteAddr,
|
||||
"remoteAddr": conn.RemoteAddr(),
|
||||
"error": err,
|
||||
}).Warn("+1 unauthorised UID")
|
||||
goWeb()
|
||||
|
|
|
|||
|
|
@ -0,0 +1,192 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"github.com/cbeuw/connutil"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"io"
|
||||
"net"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
type rfpReturnValue struct {
|
||||
n int
|
||||
transport Transport
|
||||
redirOnErr bool
|
||||
err error
|
||||
}
|
||||
|
||||
const timeout = 500 * time.Millisecond
|
||||
|
||||
func TestReadFirstPacket(t *testing.T) {
|
||||
rfp := func(conn net.Conn, buf []byte, retChan chan<- rfpReturnValue) {
|
||||
ret := rfpReturnValue{}
|
||||
ret.n, ret.transport, ret.redirOnErr, ret.err = readFirstPacket(conn, buf, timeout)
|
||||
retChan <- ret
|
||||
}
|
||||
|
||||
t.Run("Good TLS", func(t *testing.T) {
|
||||
local, remote := connutil.AsyncPipe()
|
||||
buf := make([]byte, 1500)
|
||||
retChan := make(chan rfpReturnValue)
|
||||
go rfp(remote, buf, retChan)
|
||||
|
||||
first, _ := hex.DecodeString("1603010200010001fc0303ac530b5778469dbbc3f9a83c6ac35b63aa6a70c2014026ade30f2faf0266f0242068424f320bcad49b4315a761f9f6dec32b0a403c2d8c0ab337608a694c6e411c0024130113031302c02bc02fcca9cca8c02cc030c00ac009c013c01400330039002f0035000a0100018f00000011000f00000c7777772e62696e672e636f6d00170000ff01000100000a000e000c001d00170018001901000101000b00020100002300000010000e000c02683208687474702f312e310005000501000000000033006b0069001d00204655c2c83aaed1db2e89ed17d671fcdc76dc96e36bde8840022f1bda2f31019600170041543af1f8d28b37d984073f40e8361613da502f16e4039f00656f427de0f66480b2e77e3e552e126bb0cc097168f6e5454c7f9501126a2377fb40151f6cfc007e0e002b0009080304030303020301000d0018001604030503060308040805080604010501060102030201002d00020101001c00024001001500920000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")
|
||||
local.Write(first)
|
||||
|
||||
ret := <-retChan
|
||||
|
||||
assert.Equal(t, len(first), ret.n)
|
||||
assert.Equal(t, first, buf[:ret.n])
|
||||
assert.IsType(t, TLS{}, ret.transport)
|
||||
assert.NoError(t, ret.err)
|
||||
})
|
||||
|
||||
t.Run("Good TLS but buf too small", func(t *testing.T) {
|
||||
local, remote := connutil.AsyncPipe()
|
||||
buf := make([]byte, 10)
|
||||
retChan := make(chan rfpReturnValue)
|
||||
go rfp(remote, buf, retChan)
|
||||
|
||||
first, _ := hex.DecodeString("1603010200010001fc0303ac530b5778469dbbc3f9a83c6ac35b63aa6a70c2014026ade30f2faf0266f0242068424f320bcad49b4315a761f9f6dec32b0a403c2d8c0ab337608a694c6e411c0024130113031302c02bc02fcca9cca8c02cc030c00ac009c013c01400330039002f0035000a0100018f00000011000f00000c7777772e62696e672e636f6d00170000ff01000100000a000e000c001d00170018001901000101000b00020100002300000010000e000c02683208687474702f312e310005000501000000000033006b0069001d00204655c2c83aaed1db2e89ed17d671fcdc76dc96e36bde8840022f1bda2f31019600170041543af1f8d28b37d984073f40e8361613da502f16e4039f00656f427de0f66480b2e77e3e552e126bb0cc097168f6e5454c7f9501126a2377fb40151f6cfc007e0e002b0009080304030303020301000d0018001604030503060308040805080604010501060102030201002d00020101001c00024001001500920000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")
|
||||
local.Write(first)
|
||||
|
||||
ret := <-retChan
|
||||
|
||||
assert.Equal(t, io.ErrShortBuffer, ret.err)
|
||||
assert.True(t, ret.redirOnErr)
|
||||
assert.Equal(t, first[:ret.n], buf[:ret.n])
|
||||
|
||||
})
|
||||
|
||||
t.Run("Incomplete timeout", func(t *testing.T) {
|
||||
local, remote := connutil.AsyncPipe()
|
||||
buf := make([]byte, 1500)
|
||||
retChan := make(chan rfpReturnValue)
|
||||
go rfp(remote, buf, retChan)
|
||||
|
||||
first, _ := hex.DecodeString("160301")
|
||||
local.Write(first)
|
||||
select {
|
||||
case ret := <-retChan:
|
||||
assert.Equal(t, len(first), ret.n)
|
||||
assert.False(t, ret.redirOnErr)
|
||||
assert.Error(t, ret.err)
|
||||
case <-time.After(2 * timeout):
|
||||
assert.Fail(t, "readFirstPacket should have timed out")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Incomplete payload timeout", func(t *testing.T) {
|
||||
local, remote := connutil.AsyncPipe()
|
||||
buf := make([]byte, 1500)
|
||||
retChan := make(chan rfpReturnValue)
|
||||
go rfp(remote, buf, retChan)
|
||||
|
||||
first, _ := hex.DecodeString("16030101010000")
|
||||
local.Write(first)
|
||||
select {
|
||||
case ret := <-retChan:
|
||||
assert.Equal(t, len(first), ret.n)
|
||||
assert.False(t, ret.redirOnErr)
|
||||
assert.Error(t, ret.err)
|
||||
case <-time.After(2 * timeout):
|
||||
assert.Fail(t, "readFirstPacket should have timed out")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Good TLS staggered", func(t *testing.T) {
|
||||
local, remote := connutil.AsyncPipe()
|
||||
buf := make([]byte, 1500)
|
||||
retChan := make(chan rfpReturnValue)
|
||||
go rfp(remote, buf, retChan)
|
||||
|
||||
first, _ := hex.DecodeString("1603010200010001fc0303ac530b5778469dbbc3f9a83c6ac35b63aa6a70c2014026ade30f2faf0266f0242068424f320bcad49b4315a761f9f6dec32b0a403c2d8c0ab337608a694c6e411c0024130113031302c02bc02fcca9cca8c02cc030c00ac009c013c01400330039002f0035000a0100018f00000011000f00000c7777772e62696e672e636f6d00170000ff01000100000a000e000c001d00170018001901000101000b00020100002300000010000e000c02683208687474702f312e310005000501000000000033006b0069001d00204655c2c83aaed1db2e89ed17d671fcdc76dc96e36bde8840022f1bda2f31019600170041543af1f8d28b37d984073f40e8361613da502f16e4039f00656f427de0f66480b2e77e3e552e126bb0cc097168f6e5454c7f9501126a2377fb40151f6cfc007e0e002b0009080304030303020301000d0018001604030503060308040805080604010501060102030201002d00020101001c00024001001500920000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")
|
||||
local.Write(first[:100])
|
||||
time.Sleep(timeout / 2)
|
||||
local.Write(first[100:])
|
||||
|
||||
ret := <-retChan
|
||||
|
||||
assert.Equal(t, len(first), ret.n)
|
||||
assert.Equal(t, first, buf[:ret.n])
|
||||
assert.IsType(t, TLS{}, ret.transport)
|
||||
assert.NoError(t, ret.err)
|
||||
})
|
||||
|
||||
t.Run("Good WebSocket", func(t *testing.T) {
|
||||
local, remote := connutil.AsyncPipe()
|
||||
buf := make([]byte, 1500)
|
||||
retChan := make(chan rfpReturnValue)
|
||||
go rfp(remote, buf, retChan)
|
||||
|
||||
reqStr := "GET / HTTP/1.1\r\nHost: d2jkinvisak5y9.cloudfront.net:443\r\nUser-Agent: Go-http-client/1.1\r\nConnection: Upgrade\r\nHidden: oJxeEwfDWg5k5Jbl8ttZD1sc0fHp8VjEtXHsqEoSrnaLRe/M+KGXkOzpc/2fRRg9Vk+wIWRsfv8IpoBPLbqO+ZfGsPXTjUJGiI9BqxrcJfkxncXA7FAHGpTc84tzBtZZ\r\nSec-WebSocket-Key: lJYh7X8DRXW1U0h9WKwVMA==\r\nSec-WebSocket-Version: 13\r\nUpgrade: websocket\r\n\r\n"
|
||||
req := []byte(reqStr)
|
||||
local.Write(req)
|
||||
|
||||
ret := <-retChan
|
||||
|
||||
assert.Equal(t, len(req), ret.n)
|
||||
assert.Equal(t, req, buf[:ret.n])
|
||||
assert.IsType(t, WebSocket{}, ret.transport)
|
||||
assert.NoError(t, ret.err)
|
||||
})
|
||||
|
||||
t.Run("Good WebSocket but buf too small", func(t *testing.T) {
|
||||
local, remote := connutil.AsyncPipe()
|
||||
buf := make([]byte, 10)
|
||||
retChan := make(chan rfpReturnValue)
|
||||
go rfp(remote, buf, retChan)
|
||||
|
||||
reqStr := "GET / HTTP/1.1\r\nHost: d2jkinvisak5y9.cloudfront.net:443\r\nUser-Agent: Go-http-client/1.1\r\nConnection: Upgrade\r\nHidden: oJxeEwfDWg5k5Jbl8ttZD1sc0fHp8VjEtXHsqEoSrnaLRe/M+KGXkOzpc/2fRRg9Vk+wIWRsfv8IpoBPLbqO+ZfGsPXTjUJGiI9BqxrcJfkxncXA7FAHGpTc84tzBtZZ\r\nSec-WebSocket-Key: lJYh7X8DRXW1U0h9WKwVMA==\r\nSec-WebSocket-Version: 13\r\nUpgrade: websocket\r\n\r\n"
|
||||
req := []byte(reqStr)
|
||||
local.Write(req)
|
||||
|
||||
ret := <-retChan
|
||||
|
||||
assert.Equal(t, io.ErrShortBuffer, ret.err)
|
||||
assert.True(t, ret.redirOnErr)
|
||||
assert.Equal(t, req[:ret.n], buf[:ret.n])
|
||||
})
|
||||
|
||||
t.Run("Incomplete WebSocket timeout", func(t *testing.T) {
|
||||
local, remote := connutil.AsyncPipe()
|
||||
buf := make([]byte, 1500)
|
||||
retChan := make(chan rfpReturnValue)
|
||||
go rfp(remote, buf, retChan)
|
||||
|
||||
reqStr := "GET /"
|
||||
req := []byte(reqStr)
|
||||
local.Write(req)
|
||||
|
||||
select {
|
||||
case ret := <-retChan:
|
||||
assert.Equal(t, len(req), ret.n)
|
||||
assert.False(t, ret.redirOnErr)
|
||||
assert.Error(t, ret.err)
|
||||
case <-time.After(2 * timeout):
|
||||
assert.Fail(t, "readFirstPacket should have timed out")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Staggered WebSocket", func(t *testing.T) {
|
||||
local, remote := connutil.AsyncPipe()
|
||||
buf := make([]byte, 1500)
|
||||
retChan := make(chan rfpReturnValue)
|
||||
go rfp(remote, buf, retChan)
|
||||
|
||||
reqStr := "GET / HTTP/1.1\r\nHost: d2jkinvisak5y9.cloudfront.net:443\r\nUser-Agent: Go-http-client/1.1\r\nConnection: Upgrade\r\nHidden: oJxeEwfDWg5k5Jbl8ttZD1sc0fHp8VjEtXHsqEoSrnaLRe/M+KGXkOzpc/2fRRg9Vk+wIWRsfv8IpoBPLbqO+ZfGsPXTjUJGiI9BqxrcJfkxncXA7FAHGpTc84tzBtZZ\r\nSec-WebSocket-Key: lJYh7X8DRXW1U0h9WKwVMA==\r\nSec-WebSocket-Version: 13\r\nUpgrade: websocket\r\n\r\n"
|
||||
req := []byte(reqStr)
|
||||
local.Write(req[:100])
|
||||
time.Sleep(timeout / 2)
|
||||
local.Write(req[100:])
|
||||
|
||||
ret := <-retChan
|
||||
|
||||
assert.Equal(t, len(req), ret.n)
|
||||
assert.Equal(t, req, buf[:ret.n])
|
||||
assert.IsType(t, WebSocket{}, ret.transport)
|
||||
assert.NoError(t, ret.err)
|
||||
})
|
||||
}
|
||||
Loading…
Reference in New Issue