mirror of
https://github.com/cmz0228/hysteria-dev.git
synced 2025-07-20 02:18:02 +00:00
feat(wip): test reworks
This commit is contained in:
@@ -1,10 +1,9 @@
|
||||
package integration_tests
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"io"
|
||||
"net"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
@@ -15,17 +14,18 @@ import (
|
||||
)
|
||||
|
||||
// TestClientServerTCPClose tests whether the client/server propagates the close of a connection correctly.
|
||||
// In other words, closing one of the client/remote connections should cause the other to close as well.
|
||||
// Closing one side of the connection should close the other side as well.
|
||||
func TestClientServerTCPClose(t *testing.T) {
|
||||
// Create server
|
||||
udpAddr := &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 14514}
|
||||
udpConn, err := net.ListenUDP("udp", udpAddr)
|
||||
udpConn, udpAddr, err := serverConn()
|
||||
assert.NoError(t, err)
|
||||
serverOb := mocks.NewMockOutbound(t)
|
||||
auth := mocks.NewMockAuthenticator(t)
|
||||
auth.EXPECT().Authenticate(mock.Anything, mock.Anything, mock.Anything).Return(true, "nobody")
|
||||
s, err := server.NewServer(&server.Config{
|
||||
TLSConfig: serverTLSConfig(),
|
||||
Conn: udpConn,
|
||||
Outbound: serverOb,
|
||||
Authenticator: auth,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
@@ -40,123 +40,137 @@ func TestClientServerTCPClose(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
defer c.Close()
|
||||
|
||||
t.Run("Close local", func(t *testing.T) {
|
||||
// TCP sink server
|
||||
sinkAddr := &net.TCPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 33344}
|
||||
sinkListener, err := net.ListenTCP("tcp", sinkAddr)
|
||||
assert.NoError(t, err)
|
||||
sinkCh := make(chan sinkEvent, 1)
|
||||
sinkServer := &tcpSinkServer{
|
||||
Listener: sinkListener,
|
||||
Ch: sinkCh,
|
||||
}
|
||||
defer sinkServer.Close()
|
||||
go sinkServer.Serve()
|
||||
addr := "hi-and-goodbye:2333"
|
||||
|
||||
// Generate some random data
|
||||
sData := make([]byte, 1024000)
|
||||
_, err = rand.Read(sData)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Dial and send data to TCP sink server
|
||||
conn, err := c.DialTCP(sinkAddr.String())
|
||||
assert.NoError(t, err)
|
||||
_, err = conn.Write(sData)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Close the connection
|
||||
// This should cause the sink server to send an event to the channel
|
||||
_ = conn.Close()
|
||||
event := <-sinkCh
|
||||
assert.NoError(t, event.Err)
|
||||
assert.Equal(t, sData, event.Data)
|
||||
// Test close from client side:
|
||||
// Client creates a connection, writes something, then closes it.
|
||||
// Server outbound connection should write the same thing, then close.
|
||||
sobConn := mocks.NewMockConn(t)
|
||||
sobConnCh := make(chan struct{}) // For close signal only
|
||||
sobConn.EXPECT().Read(mock.Anything).RunAndReturn(func(bs []byte) (int, error) {
|
||||
<-sobConnCh
|
||||
return 0, io.EOF
|
||||
})
|
||||
|
||||
t.Run("Close remote", func(t *testing.T) {
|
||||
// Generate some random data
|
||||
sData := make([]byte, 1024000)
|
||||
_, err = rand.Read(sData)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// TCP sender server
|
||||
senderAddr := &net.TCPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 33345}
|
||||
senderListener, err := net.ListenTCP("tcp", senderAddr)
|
||||
assert.NoError(t, err)
|
||||
|
||||
senderServer := &tcpSenderServer{
|
||||
Listener: senderListener,
|
||||
Data: sData,
|
||||
}
|
||||
defer senderServer.Close()
|
||||
go senderServer.Serve()
|
||||
|
||||
// Dial and read data from TCP sender server
|
||||
conn, err := c.DialTCP(senderAddr.String())
|
||||
assert.NoError(t, err)
|
||||
defer conn.Close()
|
||||
rData, err := io.ReadAll(conn)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, sData, rData)
|
||||
sobConn.EXPECT().Write([]byte("happy")).Return(5, nil)
|
||||
sobConn.EXPECT().Close().RunAndReturn(func() error {
|
||||
close(sobConnCh)
|
||||
return nil
|
||||
})
|
||||
serverOb.EXPECT().TCP(addr).Return(sobConn, nil).Once()
|
||||
conn, err := c.TCP(addr)
|
||||
assert.NoError(t, err)
|
||||
_, err = conn.Write([]byte("happy"))
|
||||
assert.NoError(t, err)
|
||||
err = conn.Close()
|
||||
assert.NoError(t, err)
|
||||
time.Sleep(1 * time.Second)
|
||||
mock.AssertExpectationsForObjects(t, sobConn, serverOb)
|
||||
|
||||
// Test close from server side:
|
||||
// Client creates a connection.
|
||||
// Server outbound connection reads something, then closes.
|
||||
// Client connection should read the same thing, then close.
|
||||
sobConn = mocks.NewMockConn(t)
|
||||
sobConnCh2 := make(chan []byte, 1)
|
||||
sobConn.EXPECT().Read(mock.Anything).RunAndReturn(func(bs []byte) (int, error) {
|
||||
d := <-sobConnCh2
|
||||
if d == nil {
|
||||
return 0, io.EOF
|
||||
} else {
|
||||
return copy(bs, d), nil
|
||||
}
|
||||
})
|
||||
sobConn.EXPECT().Close().Return(nil)
|
||||
serverOb.EXPECT().TCP(addr).Return(sobConn, nil).Once()
|
||||
conn, err = c.TCP(addr)
|
||||
assert.NoError(t, err)
|
||||
sobConnCh2 <- []byte("happy")
|
||||
close(sobConnCh2)
|
||||
bs, err := io.ReadAll(conn)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "happy", string(bs))
|
||||
_ = conn.Close()
|
||||
}
|
||||
|
||||
// TestClientServerUDPClose is the same as TestClientServerTCPClose, but for UDP.
|
||||
// Checking for UDP close is a bit tricky, so we will rely on the server event for now.
|
||||
func TestClientServerUDPClose(t *testing.T) {
|
||||
urCh := make(chan udpRequestEvent, 1)
|
||||
ueCh := make(chan udpErrorEvent, 1)
|
||||
|
||||
// TestServerUDPIdleTimeout tests whether the server's UDP idle timeout works correctly.
|
||||
func TestServerUDPIdleTimeout(t *testing.T) {
|
||||
// Create server
|
||||
udpAddr := &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 14514}
|
||||
udpConn, err := net.ListenUDP("udp", udpAddr)
|
||||
if err != nil {
|
||||
t.Fatal("error creating server:", err)
|
||||
}
|
||||
udpConn, udpAddr, err := serverConn()
|
||||
assert.NoError(t, err)
|
||||
serverOb := mocks.NewMockOutbound(t)
|
||||
auth := mocks.NewMockAuthenticator(t)
|
||||
auth.EXPECT().Authenticate(mock.Anything, mock.Anything, mock.Anything).Return(true, "nobody")
|
||||
eventLogger := mocks.NewMockEventLogger(t)
|
||||
eventLogger.EXPECT().Connect(mock.Anything, "nobody", mock.Anything).Once()
|
||||
eventLogger.EXPECT().Disconnect(mock.Anything, "nobody", mock.Anything).Maybe() // Depends on the timing, don't care
|
||||
s, err := server.NewServer(&server.Config{
|
||||
TLSConfig: serverTLSConfig(),
|
||||
Conn: udpConn,
|
||||
Authenticator: &pwAuthenticator{
|
||||
Password: "password",
|
||||
ID: "nobody",
|
||||
},
|
||||
EventLogger: &channelEventLogger{
|
||||
UDPRequestEventCh: urCh,
|
||||
UDPErrorEventCh: ueCh,
|
||||
},
|
||||
TLSConfig: serverTLSConfig(),
|
||||
Conn: udpConn,
|
||||
Outbound: serverOb,
|
||||
UDPIdleTimeout: 2 * time.Second,
|
||||
Authenticator: auth,
|
||||
EventLogger: eventLogger,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal("error creating server:", err)
|
||||
}
|
||||
assert.NoError(t, err)
|
||||
defer s.Close()
|
||||
go s.Serve()
|
||||
|
||||
// Create client
|
||||
c, err := client.NewClient(&client.Config{
|
||||
ServerAddr: udpAddr,
|
||||
Auth: "password",
|
||||
TLSConfig: client.TLSConfig{InsecureSkipVerify: true},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal("error creating client:", err)
|
||||
}
|
||||
assert.NoError(t, err)
|
||||
defer c.Close()
|
||||
|
||||
// Listen UDP and close it, then check the server events
|
||||
conn, err := c.ListenUDP()
|
||||
if err != nil {
|
||||
t.Fatal("error listening UDP:", err)
|
||||
}
|
||||
_ = conn.Close()
|
||||
addr := "spy.x.family:2023"
|
||||
|
||||
reqEvent := <-urCh
|
||||
if reqEvent.ID != "nobody" {
|
||||
t.Fatal("incorrect ID in request event")
|
||||
// On the client side, create a UDP session and send a packet every 1 second,
|
||||
// 4 packets in total. The server should have one UDP session and receive all
|
||||
// 4 packets. Then the UDP connection on the server side will receive a packet
|
||||
// every 1 second, 4 packets in total. The client session should receive all
|
||||
// 4 packets. Then the session will be idle for 3 seconds - should be enough
|
||||
// to trigger the server's UDP idle timeout.
|
||||
sobConn := mocks.NewMockUDPConn(t)
|
||||
sobConnCh := make(chan []byte, 1)
|
||||
sobConn.EXPECT().ReadFrom(mock.Anything).RunAndReturn(func(bs []byte) (int, string, error) {
|
||||
d := <-sobConnCh
|
||||
if d == nil {
|
||||
return 0, "", io.EOF
|
||||
} else {
|
||||
return copy(bs, d), addr, nil
|
||||
}
|
||||
})
|
||||
sobConn.EXPECT().WriteTo([]byte("happy"), addr).Return(5, nil).Times(4)
|
||||
serverOb.EXPECT().UDP(addr).Return(sobConn, nil).Once()
|
||||
eventLogger.EXPECT().UDPRequest(mock.Anything, mock.Anything, uint32(1), addr).Once()
|
||||
cu, err := c.UDP()
|
||||
assert.NoError(t, err)
|
||||
// Client sends 4 packets
|
||||
for i := 0; i < 4; i++ {
|
||||
err = cu.Send([]byte("happy"), addr)
|
||||
assert.NoError(t, err)
|
||||
time.Sleep(1 * time.Second)
|
||||
}
|
||||
errEvent := <-ueCh
|
||||
if errEvent.ID != "nobody" {
|
||||
t.Fatal("incorrect ID in error event")
|
||||
}
|
||||
if errEvent.Err != nil {
|
||||
t.Fatal("non-nil error received from server:", errEvent.Err)
|
||||
// Client receives 4 packets
|
||||
go func() {
|
||||
for i := 0; i < 4; i++ {
|
||||
sobConnCh <- []byte("sad")
|
||||
time.Sleep(1 * time.Second)
|
||||
}
|
||||
}()
|
||||
for i := 0; i < 4; i++ {
|
||||
bs, rAddr, err := cu.Receive()
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "sad", string(bs))
|
||||
assert.Equal(t, addr, rAddr)
|
||||
}
|
||||
// Now we wait for 3 seconds, the server should close the UDP session.
|
||||
sobConn.EXPECT().Close().RunAndReturn(func() error {
|
||||
close(sobConnCh)
|
||||
return nil
|
||||
})
|
||||
eventLogger.EXPECT().UDPError(mock.Anything, mock.Anything, uint32(1), nil).Once()
|
||||
time.Sleep(3 * time.Second)
|
||||
mock.AssertExpectationsForObjects(t, sobConn, serverOb, eventLogger)
|
||||
}
|
||||
|
Reference in New Issue
Block a user