mirror of https://github.com/cbeuw/Cloak
Export fields for testing
This commit is contained in:
parent
d53b80208f
commit
93cf6dec6e
|
|
@ -63,7 +63,7 @@ type DirectTLS struct {
|
||||||
|
|
||||||
// NewClientTransport handles the TLS handshake for a given conn and returns the sessionKey
|
// NewClientTransport handles the TLS handshake for a given conn and returns the sessionKey
|
||||||
// if the server proceed with Cloak authentication
|
// if the server proceed with Cloak authentication
|
||||||
func (tls *DirectTLS) Handshake(rawConn net.Conn, authInfo authInfo) (sessionKey [32]byte, err error) {
|
func (tls *DirectTLS) Handshake(rawConn net.Conn, authInfo AuthInfo) (sessionKey [32]byte, err error) {
|
||||||
payload, sharedSecret := makeAuthenticationPayload(authInfo)
|
payload, sharedSecret := makeAuthenticationPayload(authInfo)
|
||||||
chOnly := tls.browser.composeClientHello(genStegClientHello(payload, authInfo.MockDomain))
|
chOnly := tls.browser.composeClientHello(genStegClientHello(payload, authInfo.MockDomain))
|
||||||
chWithRecordLayer := common.AddRecordLayer(chOnly, common.Handshake, common.VersionTLS11)
|
chWithRecordLayer := common.AddRecordLayer(chOnly, common.Handshake, common.VersionTLS11)
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ type authenticationPayload struct {
|
||||||
|
|
||||||
// makeAuthenticationPayload generates the ephemeral key pair, calculates the shared secret, and then compose and
|
// makeAuthenticationPayload generates the ephemeral key pair, calculates the shared secret, and then compose and
|
||||||
// encrypt the authenticationPayload
|
// encrypt the authenticationPayload
|
||||||
func makeAuthenticationPayload(authInfo authInfo) (ret authenticationPayload, sharedSecret [32]byte) {
|
func makeAuthenticationPayload(authInfo AuthInfo) (ret authenticationPayload, sharedSecret [32]byte) {
|
||||||
/*
|
/*
|
||||||
Authentication data:
|
Authentication data:
|
||||||
+----------+----------------+---------------------+-------------+--------------+--------+------------+
|
+----------+----------------+---------------------+-------------+--------------+--------+------------+
|
||||||
|
|
|
||||||
|
|
@ -10,12 +10,12 @@ import (
|
||||||
|
|
||||||
func TestMakeAuthenticationPayload(t *testing.T) {
|
func TestMakeAuthenticationPayload(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
authInfo authInfo
|
authInfo AuthInfo
|
||||||
expPayload authenticationPayload
|
expPayload authenticationPayload
|
||||||
expSecret [32]byte
|
expSecret [32]byte
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
authInfo{
|
AuthInfo{
|
||||||
Unordered: false,
|
Unordered: false,
|
||||||
SessionId: 3421516597,
|
SessionId: 3421516597,
|
||||||
UID: []byte{
|
UID: []byte{
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ import (
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
func MakeSession(connConfig remoteConnConfig, authInfo authInfo, dialer common.Dialer, isAdmin bool) *mux.Session {
|
func MakeSession(connConfig RemoteConnConfig, authInfo AuthInfo, dialer common.Dialer, isAdmin bool) *mux.Session {
|
||||||
log.Info("Attempting to start a new session")
|
log.Info("Attempting to start a new session")
|
||||||
//TODO: let caller set this
|
//TODO: let caller set this
|
||||||
if !isAdmin {
|
if !isAdmin {
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ import (
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
func RouteUDP(localConfig localConnConfig, newSeshFunc func() *mux.Session) {
|
func RouteUDP(localConfig LocalConnConfig, newSeshFunc func() *mux.Session) {
|
||||||
var sesh *mux.Session
|
var sesh *mux.Session
|
||||||
localUDPAddr, err := net.ResolveUDPAddr("udp", localConfig.LocalAddr)
|
localUDPAddr, err := net.ResolveUDPAddr("udp", localConfig.LocalAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -38,19 +38,19 @@ type RawConfig struct {
|
||||||
KeepAlive int // nullable
|
KeepAlive int // nullable
|
||||||
}
|
}
|
||||||
|
|
||||||
type remoteConnConfig struct {
|
type RemoteConnConfig struct {
|
||||||
NumConn int
|
NumConn int
|
||||||
KeepAlive time.Duration
|
KeepAlive time.Duration
|
||||||
RemoteAddr string
|
RemoteAddr string
|
||||||
TransportMaker func() Transport
|
TransportMaker func() Transport
|
||||||
}
|
}
|
||||||
|
|
||||||
type localConnConfig struct {
|
type LocalConnConfig struct {
|
||||||
LocalAddr string
|
LocalAddr string
|
||||||
Timeout time.Duration
|
Timeout time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
type authInfo struct {
|
type AuthInfo struct {
|
||||||
UID []byte
|
UID []byte
|
||||||
SessionId uint32
|
SessionId uint32
|
||||||
ProxyMethod string
|
ProxyMethod string
|
||||||
|
|
@ -120,8 +120,8 @@ func ParseConfig(conf string) (raw *RawConfig, err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (raw *RawConfig) SplitConfigs(worldState common.WorldState) (local localConnConfig, remote remoteConnConfig, auth authInfo, err error) {
|
func (raw *RawConfig) SplitConfigs(worldState common.WorldState) (local LocalConnConfig, remote RemoteConnConfig, auth AuthInfo, err error) {
|
||||||
nullErr := func(field string) (local localConnConfig, remote remoteConnConfig, auth authInfo, err error) {
|
nullErr := func(field string) (local LocalConnConfig, remote RemoteConnConfig, auth AuthInfo, err error) {
|
||||||
err = fmt.Errorf("%v cannot be empty", field)
|
err = fmt.Errorf("%v cannot be empty", field)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,6 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type Transport interface {
|
type Transport interface {
|
||||||
Handshake(rawConn net.Conn, authInfo authInfo) (sessionKey [32]byte, err error)
|
Handshake(rawConn net.Conn, authInfo AuthInfo) (sessionKey [32]byte, err error)
|
||||||
net.Conn
|
net.Conn
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ type WSOverTLS struct {
|
||||||
cdnDomainPort string
|
cdnDomainPort string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ws *WSOverTLS) Handshake(rawConn net.Conn, authInfo authInfo) (sessionKey [32]byte, err error) {
|
func (ws *WSOverTLS) Handshake(rawConn net.Conn, authInfo AuthInfo) (sessionKey [32]byte, err error) {
|
||||||
utlsConfig := &utls.Config{
|
utlsConfig := &utls.Config{
|
||||||
ServerName: authInfo.MockDomain,
|
ServerName: authInfo.MockDomain,
|
||||||
InsecureSkipVerify: true,
|
InsecureSkipVerify: true,
|
||||||
|
|
|
||||||
|
|
@ -1,36 +0,0 @@
|
||||||
package integration_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/base64"
|
|
||||||
"github.com/cbeuw/Cloak/internal/client"
|
|
||||||
"github.com/cbeuw/Cloak/internal/server"
|
|
||||||
)
|
|
||||||
|
|
||||||
var bypassUID = []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}
|
|
||||||
var publicKey, _ = base64.StdEncoding.DecodeString("7f7TuKrs264VNSgMno8PkDlyhGhVuOSR8JHLE6H4Ljc=")
|
|
||||||
var privateKey, _ = base64.StdEncoding.DecodeString("SMWeC6VuZF8S/id65VuFQFlfa7hTEJBpL6wWhqPP100=")
|
|
||||||
|
|
||||||
var clientConfig = client.RawConfig{
|
|
||||||
ServerName: "www.example.com",
|
|
||||||
ProxyMethod: "test",
|
|
||||||
EncryptionMethod: "plain",
|
|
||||||
UID: bypassUID,
|
|
||||||
PublicKey: publicKey,
|
|
||||||
NumConn: 3,
|
|
||||||
UDP: false,
|
|
||||||
BrowserSig: "chrome",
|
|
||||||
Transport: "direct",
|
|
||||||
}
|
|
||||||
|
|
||||||
var serverState = server.State{
|
|
||||||
ProxyBook: nil,
|
|
||||||
ProxyDialer: nil,
|
|
||||||
AdminUID: nil,
|
|
||||||
Timeout: 0,
|
|
||||||
BypassUID: nil,
|
|
||||||
RedirHost: nil,
|
|
||||||
RedirPort: "",
|
|
||||||
RedirDialer: nil,
|
|
||||||
Panel: nil,
|
|
||||||
LocalAPIRouter: nil,
|
|
||||||
}
|
|
||||||
|
|
@ -78,7 +78,7 @@ func AuthFirstPacket(firstPacket []byte, sta *State) (info ClientInfo, finisher
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
fragments, finisher, err := transport.processFirstPacket(firstPacket, sta.staticPv)
|
fragments, finisher, err := transport.processFirstPacket(firstPacket, sta.StaticPv)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -130,7 +130,7 @@ func TestAuthFirstPacket(t *testing.T) {
|
||||||
|
|
||||||
getNewState := func() *State {
|
getNewState := func() *State {
|
||||||
sta, _ := InitState(RawConfig{}, common.WorldOfTime(time.Unix(1565998966, 0)))
|
sta, _ := InitState(RawConfig{}, common.WorldOfTime(time.Unix(1565998966, 0)))
|
||||||
sta.staticPv = p.(crypto.PrivateKey)
|
sta.StaticPv = p.(crypto.PrivateKey)
|
||||||
sta.ProxyBook["shadowsocks"] = nil
|
sta.ProxyBook["shadowsocks"] = nil
|
||||||
return sta
|
return sta
|
||||||
}
|
}
|
||||||
|
|
@ -168,7 +168,7 @@ func TestAuthFirstPacket(t *testing.T) {
|
||||||
})
|
})
|
||||||
t.Run("Websocket correct", func(t *testing.T) {
|
t.Run("Websocket correct", func(t *testing.T) {
|
||||||
sta, _ := InitState(RawConfig{}, common.WorldOfTime(time.Unix(1584358419, 0)))
|
sta, _ := InitState(RawConfig{}, common.WorldOfTime(time.Unix(1584358419, 0)))
|
||||||
sta.staticPv = p.(crypto.PrivateKey)
|
sta.StaticPv = p.(crypto.PrivateKey)
|
||||||
sta.ProxyBook["shadowsocks"] = nil
|
sta.ProxyBook["shadowsocks"] = nil
|
||||||
|
|
||||||
req := `GET / HTTP/1.1
|
req := `GET / HTTP/1.1
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,7 @@ type State struct {
|
||||||
//KeepAlive time.Duration
|
//KeepAlive time.Duration
|
||||||
|
|
||||||
BypassUID map[[16]byte]struct{}
|
BypassUID map[[16]byte]struct{}
|
||||||
staticPv crypto.PrivateKey
|
StaticPv crypto.PrivateKey
|
||||||
|
|
||||||
// TODO: this doesn't have to be a net.Addr; resolution is done in Dial automatically
|
// TODO: this doesn't have to be a net.Addr; resolution is done in Dial automatically
|
||||||
RedirHost net.Addr
|
RedirHost net.Addr
|
||||||
|
|
@ -48,7 +48,7 @@ type State struct {
|
||||||
RedirDialer common.Dialer
|
RedirDialer common.Dialer
|
||||||
|
|
||||||
usedRandomM sync.RWMutex
|
usedRandomM sync.RWMutex
|
||||||
usedRandom map[[32]byte]int64
|
UsedRandom map[[32]byte]int64
|
||||||
|
|
||||||
Panel *userPanel
|
Panel *userPanel
|
||||||
LocalAPIRouter *gmux.Router
|
LocalAPIRouter *gmux.Router
|
||||||
|
|
@ -148,7 +148,7 @@ func InitState(preParse RawConfig, worldState common.WorldState) (sta *State, er
|
||||||
sta = &State{
|
sta = &State{
|
||||||
BypassUID: make(map[[16]byte]struct{}),
|
BypassUID: make(map[[16]byte]struct{}),
|
||||||
ProxyBook: map[string]net.Addr{},
|
ProxyBook: map[string]net.Addr{},
|
||||||
usedRandom: map[[32]byte]int64{},
|
UsedRandom: map[[32]byte]int64{},
|
||||||
RedirDialer: &net.Dialer{},
|
RedirDialer: &net.Dialer{},
|
||||||
WorldState: worldState,
|
WorldState: worldState,
|
||||||
}
|
}
|
||||||
|
|
@ -188,7 +188,7 @@ func InitState(preParse RawConfig, worldState common.WorldState) (sta *State, er
|
||||||
|
|
||||||
var pv [32]byte
|
var pv [32]byte
|
||||||
copy(pv[:], preParse.PrivateKey)
|
copy(pv[:], preParse.PrivateKey)
|
||||||
sta.staticPv = &pv
|
sta.StaticPv = &pv
|
||||||
|
|
||||||
sta.AdminUID = preParse.AdminUID
|
sta.AdminUID = preParse.AdminUID
|
||||||
|
|
||||||
|
|
@ -221,9 +221,9 @@ func (sta *State) UsedRandomCleaner() {
|
||||||
for {
|
for {
|
||||||
time.Sleep(CACHE_CLEAN_INTERVAL)
|
time.Sleep(CACHE_CLEAN_INTERVAL)
|
||||||
sta.usedRandomM.Lock()
|
sta.usedRandomM.Lock()
|
||||||
for key, t := range sta.usedRandom {
|
for key, t := range sta.UsedRandom {
|
||||||
if time.Unix(t, 0).Before(sta.WorldState.Now().Add(TIMESTAMP_TOLERANCE)) {
|
if time.Unix(t, 0).Before(sta.WorldState.Now().Add(TIMESTAMP_TOLERANCE)) {
|
||||||
delete(sta.usedRandom, key)
|
delete(sta.UsedRandom, key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sta.usedRandomM.Unlock()
|
sta.usedRandomM.Unlock()
|
||||||
|
|
@ -232,8 +232,8 @@ func (sta *State) UsedRandomCleaner() {
|
||||||
|
|
||||||
func (sta *State) registerRandom(r [32]byte) bool {
|
func (sta *State) registerRandom(r [32]byte) bool {
|
||||||
sta.usedRandomM.Lock()
|
sta.usedRandomM.Lock()
|
||||||
_, used := sta.usedRandom[r]
|
_, used := sta.UsedRandom[r]
|
||||||
sta.usedRandom[r] = sta.WorldState.Now().Unix()
|
sta.UsedRandom[r] = sta.WorldState.Now().Unix()
|
||||||
sta.usedRandomM.Unlock()
|
sta.usedRandomM.Unlock()
|
||||||
return used
|
return used
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,174 @@
|
||||||
|
package test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/base64"
|
||||||
|
"github.com/cbeuw/Cloak/internal/client"
|
||||||
|
"github.com/cbeuw/Cloak/internal/common"
|
||||||
|
mux "github.com/cbeuw/Cloak/internal/multiplex"
|
||||||
|
"github.com/cbeuw/Cloak/internal/server"
|
||||||
|
"github.com/cbeuw/Cloak/internal/server/usermanager"
|
||||||
|
"github.com/cbeuw/connutil"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"math/rand"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"sync"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
func serveEcho(l net.Listener) {
|
||||||
|
for {
|
||||||
|
conn, err := l.Accept()
|
||||||
|
if err != nil {
|
||||||
|
// TODO: pass the error back
|
||||||
|
return
|
||||||
|
}
|
||||||
|
go func() {
|
||||||
|
_, err := io.Copy(conn, conn)
|
||||||
|
if err != nil {
|
||||||
|
// TODO: pass the error back
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var bypassUID = [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}
|
||||||
|
var publicKey, _ = base64.StdEncoding.DecodeString("7f7TuKrs264VNSgMno8PkDlyhGhVuOSR8JHLE6H4Ljc=")
|
||||||
|
var privateKey, _ = base64.StdEncoding.DecodeString("SMWeC6VuZF8S/id65VuFQFlfa7hTEJBpL6wWhqPP100=")
|
||||||
|
|
||||||
|
func basicClientConfigs(state common.WorldState) (client.LocalConnConfig, client.RemoteConnConfig, client.AuthInfo) {
|
||||||
|
var clientConfig = client.RawConfig{
|
||||||
|
ServerName: "www.example.com",
|
||||||
|
ProxyMethod: "test",
|
||||||
|
EncryptionMethod: "plain",
|
||||||
|
UID: bypassUID[:],
|
||||||
|
PublicKey: publicKey,
|
||||||
|
NumConn: 4,
|
||||||
|
UDP: false,
|
||||||
|
Transport: "direct",
|
||||||
|
RemoteHost: "fake.com",
|
||||||
|
RemotePort: "9999",
|
||||||
|
LocalHost: "127.0.0.1",
|
||||||
|
LocalPort: "9999",
|
||||||
|
}
|
||||||
|
lcl, rmt, auth, _ := clientConfig.SplitConfigs(state)
|
||||||
|
return lcl, rmt, auth
|
||||||
|
}
|
||||||
|
|
||||||
|
func basicServerState(ws common.WorldState, db *os.File) *server.State {
|
||||||
|
manager, _ := usermanager.MakeLocalManager(db.Name())
|
||||||
|
var pv [32]byte
|
||||||
|
copy(pv[:], privateKey)
|
||||||
|
serverState := &server.State{
|
||||||
|
ProxyBook: map[string]net.Addr{"test": &net.TCPAddr{}},
|
||||||
|
UsedRandom: map[[32]byte]int64{},
|
||||||
|
Timeout: 0,
|
||||||
|
BypassUID: map[[16]byte]struct{}{bypassUID: {}},
|
||||||
|
RedirHost: &net.TCPAddr{},
|
||||||
|
RedirPort: "9999",
|
||||||
|
Panel: server.MakeUserPanel(manager),
|
||||||
|
LocalAPIRouter: nil,
|
||||||
|
StaticPv: &pv,
|
||||||
|
WorldState: ws,
|
||||||
|
}
|
||||||
|
return serverState
|
||||||
|
}
|
||||||
|
|
||||||
|
func establishSession(lcc client.LocalConnConfig, rcc client.RemoteConnConfig, ai client.AuthInfo, serverState *server.State) (common.Dialer, net.Listener, common.Dialer, net.Listener, error) {
|
||||||
|
// transport
|
||||||
|
ckClientDialer, ckServerListener := connutil.DialerListener(128)
|
||||||
|
|
||||||
|
clientSeshMaker := func() *mux.Session {
|
||||||
|
return client.MakeSession(rcc, ai, ckClientDialer, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
proxyToCkClientD, proxyToCkClientL := connutil.DialerListener(128)
|
||||||
|
go client.RouteTCP(proxyToCkClientL, lcc.Timeout, clientSeshMaker)
|
||||||
|
|
||||||
|
// set up server
|
||||||
|
ckServerToProxyD, ckServerToProxyL := connutil.DialerListener(128)
|
||||||
|
ckServerToWebD, ckServerToWebL := connutil.DialerListener(128)
|
||||||
|
serverState.ProxyDialer = ckServerToProxyD
|
||||||
|
serverState.RedirDialer = ckServerToWebD
|
||||||
|
|
||||||
|
go server.Serve(ckServerListener, serverState)
|
||||||
|
|
||||||
|
return proxyToCkClientD, ckServerToProxyL, ckClientDialer, ckServerToWebL, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func runEchoTest(t *testing.T, conns []net.Conn) {
|
||||||
|
const testDataLen = 16384
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
for _, conn := range conns {
|
||||||
|
wg.Add(1)
|
||||||
|
go func(conn net.Conn) {
|
||||||
|
testData := make([]byte, testDataLen)
|
||||||
|
rand.Read(testData)
|
||||||
|
|
||||||
|
n, err := conn.Write(testData)
|
||||||
|
if n != testDataLen {
|
||||||
|
t.Fatalf("written only %v, err %v", n, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
recvBuf := make([]byte, testDataLen)
|
||||||
|
_, err = io.ReadFull(conn, recvBuf)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to read back: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !bytes.Equal(testData, recvBuf) {
|
||||||
|
t.Fatalf("echoed data not correct")
|
||||||
|
}
|
||||||
|
wg.Done()
|
||||||
|
}(conn)
|
||||||
|
}
|
||||||
|
wg.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTCP(t *testing.T) {
|
||||||
|
var tmpDB, _ = ioutil.TempFile("", "ck_user_info")
|
||||||
|
defer os.Remove(tmpDB.Name())
|
||||||
|
log.SetOutput(ioutil.Discard)
|
||||||
|
|
||||||
|
worldState := common.WorldOfTime(time.Unix(10, 0))
|
||||||
|
lcc, rcc, ai := basicClientConfigs(worldState)
|
||||||
|
sta := basicServerState(worldState, tmpDB)
|
||||||
|
|
||||||
|
pxyClientD, pxyServerL, dialerToCkServer, rdirServerL, err := establishSession(lcc, rcc, ai, sta)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Run("user echo", func(t *testing.T) {
|
||||||
|
go serveEcho(pxyServerL)
|
||||||
|
const numConns = 2000 // -race option limits the number of goroutines to 8192
|
||||||
|
var conns [numConns]net.Conn
|
||||||
|
for i := 0; i < numConns; i++ {
|
||||||
|
conns[i], err = pxyClientD.Dial("", "")
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
runEchoTest(t, conns[:])
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("redir echo", func(t *testing.T) {
|
||||||
|
go serveEcho(rdirServerL)
|
||||||
|
const numConns = 2000 // -race option limits the number of goroutines to 8192
|
||||||
|
var conns [numConns]net.Conn
|
||||||
|
for i := 0; i < numConns; i++ {
|
||||||
|
conns[i], err = dialerToCkServer.Dial("", "")
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
runEchoTest(t, conns[:])
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
package test
|
||||||
|
|
||||||
|
func blah() {}
|
||||||
Loading…
Reference in New Issue