mirror of
https://github.com/apernet/hysteria.git
synced 2025-04-03 04:27:39 +03:00
feat(wip): test reworks
This commit is contained in:
parent
e48bb98024
commit
1c7cb23389
11 changed files with 899 additions and 331 deletions
9
core/client/.mockery.yaml
Normal file
9
core/client/.mockery.yaml
Normal file
|
@ -0,0 +1,9 @@
|
|||
with-expecter: true
|
||||
inpackage: true
|
||||
dir: .
|
||||
packages:
|
||||
github.com/apernet/hysteria/core/client:
|
||||
interfaces:
|
||||
udpIO:
|
||||
config:
|
||||
mockname: mockUDPIO
|
131
core/client/mock_udpIO.go
Normal file
131
core/client/mock_udpIO.go
Normal file
|
@ -0,0 +1,131 @@
|
|||
// Code generated by mockery v2.32.0. DO NOT EDIT.
|
||||
|
||||
package client
|
||||
|
||||
import (
|
||||
protocol "github.com/apernet/hysteria/core/internal/protocol"
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
// mockUDPIO is an autogenerated mock type for the udpIO type
|
||||
type mockUDPIO struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
type mockUDPIO_Expecter struct {
|
||||
mock *mock.Mock
|
||||
}
|
||||
|
||||
func (_m *mockUDPIO) EXPECT() *mockUDPIO_Expecter {
|
||||
return &mockUDPIO_Expecter{mock: &_m.Mock}
|
||||
}
|
||||
|
||||
// ReceiveMessage provides a mock function with given fields:
|
||||
func (_m *mockUDPIO) ReceiveMessage() (*protocol.UDPMessage, error) {
|
||||
ret := _m.Called()
|
||||
|
||||
var r0 *protocol.UDPMessage
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func() (*protocol.UDPMessage, error)); ok {
|
||||
return rf()
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func() *protocol.UDPMessage); ok {
|
||||
r0 = rf()
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*protocol.UDPMessage)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func() error); ok {
|
||||
r1 = rf()
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// mockUDPIO_ReceiveMessage_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ReceiveMessage'
|
||||
type mockUDPIO_ReceiveMessage_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// ReceiveMessage is a helper method to define mock.On call
|
||||
func (_e *mockUDPIO_Expecter) ReceiveMessage() *mockUDPIO_ReceiveMessage_Call {
|
||||
return &mockUDPIO_ReceiveMessage_Call{Call: _e.mock.On("ReceiveMessage")}
|
||||
}
|
||||
|
||||
func (_c *mockUDPIO_ReceiveMessage_Call) Run(run func()) *mockUDPIO_ReceiveMessage_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run()
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *mockUDPIO_ReceiveMessage_Call) Return(_a0 *protocol.UDPMessage, _a1 error) *mockUDPIO_ReceiveMessage_Call {
|
||||
_c.Call.Return(_a0, _a1)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *mockUDPIO_ReceiveMessage_Call) RunAndReturn(run func() (*protocol.UDPMessage, error)) *mockUDPIO_ReceiveMessage_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// SendMessage provides a mock function with given fields: _a0, _a1
|
||||
func (_m *mockUDPIO) SendMessage(_a0 []byte, _a1 *protocol.UDPMessage) error {
|
||||
ret := _m.Called(_a0, _a1)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func([]byte, *protocol.UDPMessage) error); ok {
|
||||
r0 = rf(_a0, _a1)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// mockUDPIO_SendMessage_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SendMessage'
|
||||
type mockUDPIO_SendMessage_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// SendMessage is a helper method to define mock.On call
|
||||
// - _a0 []byte
|
||||
// - _a1 *protocol.UDPMessage
|
||||
func (_e *mockUDPIO_Expecter) SendMessage(_a0 interface{}, _a1 interface{}) *mockUDPIO_SendMessage_Call {
|
||||
return &mockUDPIO_SendMessage_Call{Call: _e.mock.On("SendMessage", _a0, _a1)}
|
||||
}
|
||||
|
||||
func (_c *mockUDPIO_SendMessage_Call) Run(run func(_a0 []byte, _a1 *protocol.UDPMessage)) *mockUDPIO_SendMessage_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run(args[0].([]byte), args[1].(*protocol.UDPMessage))
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *mockUDPIO_SendMessage_Call) Return(_a0 error) *mockUDPIO_SendMessage_Call {
|
||||
_c.Call.Return(_a0)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *mockUDPIO_SendMessage_Call) RunAndReturn(run func([]byte, *protocol.UDPMessage) error) *mockUDPIO_SendMessage_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// newMockUDPIO creates a new instance of mockUDPIO. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
||||
// The first argument is typically a *testing.T value.
|
||||
func newMockUDPIO(t interface {
|
||||
mock.TestingT
|
||||
Cleanup(func())
|
||||
}) *mockUDPIO {
|
||||
mock := &mockUDPIO{}
|
||||
mock.Mock.Test(t)
|
||||
|
||||
t.Cleanup(func() { mock.AssertExpectations(t) })
|
||||
|
||||
return mock
|
||||
}
|
|
@ -2,108 +2,121 @@ package client
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
io2 "io"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/magiconair/properties/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"go.uber.org/goleak"
|
||||
|
||||
coreErrs "github.com/apernet/hysteria/core/errors"
|
||||
"github.com/apernet/hysteria/core/internal/protocol"
|
||||
"go.uber.org/goleak"
|
||||
)
|
||||
|
||||
type udpEchoIO struct {
|
||||
MsgCh chan *protocol.UDPMessage
|
||||
CloseCh chan struct{}
|
||||
}
|
||||
|
||||
func (io *udpEchoIO) ReceiveMessage() (*protocol.UDPMessage, error) {
|
||||
select {
|
||||
case m := <-io.MsgCh:
|
||||
return m, nil
|
||||
case <-io.CloseCh:
|
||||
return nil, errors.New("closed")
|
||||
}
|
||||
}
|
||||
|
||||
func (io *udpEchoIO) SendMessage(buf []byte, msg *protocol.UDPMessage) error {
|
||||
nMsg := *msg
|
||||
nMsg.Data = make([]byte, len(msg.Data))
|
||||
copy(nMsg.Data, msg.Data)
|
||||
io.MsgCh <- &nMsg
|
||||
return nil
|
||||
}
|
||||
|
||||
func (io *udpEchoIO) Close() {
|
||||
close(io.CloseCh)
|
||||
}
|
||||
|
||||
func TestUDPSessionManager(t *testing.T) {
|
||||
io := &udpEchoIO{
|
||||
MsgCh: make(chan *protocol.UDPMessage, 10),
|
||||
CloseCh: make(chan struct{}),
|
||||
}
|
||||
io := newMockUDPIO(t)
|
||||
receiveCh := make(chan *protocol.UDPMessage, 4)
|
||||
io.EXPECT().ReceiveMessage().RunAndReturn(func() (*protocol.UDPMessage, error) {
|
||||
m := <-receiveCh
|
||||
if m == nil {
|
||||
return nil, errors.New("closed")
|
||||
}
|
||||
return m, nil
|
||||
})
|
||||
sm := newUDPSessionManager(io)
|
||||
|
||||
rChan := make(chan error, 5)
|
||||
// Test UDP session IO
|
||||
udpConn1, err := sm.NewUDP()
|
||||
assert.Equal(t, err, nil)
|
||||
udpConn2, err := sm.NewUDP()
|
||||
assert.Equal(t, err, nil)
|
||||
|
||||
for i := 0; i < 5; i++ {
|
||||
go func(id int) {
|
||||
conn, err := sm.NewUDP()
|
||||
if err != nil {
|
||||
rChan <- err
|
||||
return
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
addr := fmt.Sprintf("wow.com:%d", id)
|
||||
for j := 0; j < 2; j++ {
|
||||
s := fmt.Sprintf("hello %d %d", id, j)
|
||||
err = conn.Send([]byte(s), addr)
|
||||
if err != nil {
|
||||
rChan <- err
|
||||
return
|
||||
}
|
||||
bs, addr, err := conn.Receive()
|
||||
if err != nil {
|
||||
rChan <- err
|
||||
return
|
||||
}
|
||||
if string(bs) != s || addr != addr {
|
||||
rChan <- fmt.Errorf("unexpected message: %s %s", bs, addr)
|
||||
return
|
||||
}
|
||||
}
|
||||
rChan <- nil // Success
|
||||
}(i)
|
||||
msg1 := &protocol.UDPMessage{
|
||||
SessionID: 1,
|
||||
PacketID: 0,
|
||||
FragID: 0,
|
||||
FragCount: 1,
|
||||
Addr: "random.site.com:9000",
|
||||
Data: []byte("hello friend"),
|
||||
}
|
||||
io.EXPECT().SendMessage(mock.Anything, msg1).Return(nil).Once()
|
||||
err = udpConn1.Send(msg1.Data, msg1.Addr)
|
||||
assert.Equal(t, err, nil)
|
||||
|
||||
// Check the results
|
||||
for i := 0; i < 5; i++ {
|
||||
err := <-rChan
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
msg2 := &protocol.UDPMessage{
|
||||
SessionID: 2,
|
||||
PacketID: 0,
|
||||
FragID: 0,
|
||||
FragCount: 1,
|
||||
Addr: "another.site.org:8000",
|
||||
Data: []byte("mr robot"),
|
||||
}
|
||||
io.EXPECT().SendMessage(mock.Anything, msg2).Return(nil).Once()
|
||||
err = udpConn2.Send(msg2.Data, msg2.Addr)
|
||||
assert.Equal(t, err, nil)
|
||||
|
||||
respMsg1 := &protocol.UDPMessage{
|
||||
SessionID: 1,
|
||||
PacketID: 0,
|
||||
FragID: 0,
|
||||
FragCount: 1,
|
||||
Addr: msg1.Addr,
|
||||
Data: []byte("goodbye captain price"),
|
||||
}
|
||||
receiveCh <- respMsg1
|
||||
data, addr, err := udpConn1.Receive()
|
||||
assert.Equal(t, err, nil)
|
||||
assert.Equal(t, data, respMsg1.Data)
|
||||
assert.Equal(t, addr, respMsg1.Addr)
|
||||
|
||||
respMsg2 := &protocol.UDPMessage{
|
||||
SessionID: 2,
|
||||
PacketID: 0,
|
||||
FragID: 0,
|
||||
FragCount: 1,
|
||||
Addr: msg2.Addr,
|
||||
Data: []byte("white rose"),
|
||||
}
|
||||
receiveCh <- respMsg2
|
||||
data, addr, err = udpConn2.Receive()
|
||||
assert.Equal(t, err, nil)
|
||||
assert.Equal(t, data, respMsg2.Data)
|
||||
assert.Equal(t, addr, respMsg2.Addr)
|
||||
|
||||
respMsg3 := &protocol.UDPMessage{
|
||||
SessionID: 55, // Bogus session ID that doesn't exist
|
||||
PacketID: 0,
|
||||
FragID: 0,
|
||||
FragCount: 1,
|
||||
Addr: "burgerking.com:27017",
|
||||
Data: []byte("impossible whopper"),
|
||||
}
|
||||
receiveCh <- respMsg3
|
||||
// No test for this, just make sure it doesn't panic
|
||||
|
||||
// Test close UDP connection unblocks Receive()
|
||||
errChan := make(chan error, 1)
|
||||
go func() {
|
||||
_, _, err := udpConn1.Receive()
|
||||
errChan <- err
|
||||
}()
|
||||
assert.Equal(t, udpConn1.Close(), nil)
|
||||
assert.Equal(t, <-errChan, io2.EOF)
|
||||
|
||||
// Test close IO unblocks Receive() and blocks new UDP creation
|
||||
errChan = make(chan error, 1)
|
||||
go func() {
|
||||
_, _, err := udpConn2.Receive()
|
||||
errChan <- err
|
||||
}()
|
||||
close(receiveCh)
|
||||
assert.Equal(t, <-errChan, io2.EOF)
|
||||
_, err = sm.NewUDP()
|
||||
assert.Equal(t, err, coreErrs.ClosedError{})
|
||||
|
||||
// Leak checks
|
||||
// Create another UDP session
|
||||
conn, err := sm.NewUDP()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
io.Close()
|
||||
time.Sleep(1 * time.Second) // Give some time for the goroutines to exit
|
||||
_, _, err = conn.Receive()
|
||||
if err != io2.EOF {
|
||||
t.Fatal("expected EOF after closing io")
|
||||
}
|
||||
_, err = sm.NewUDP()
|
||||
if !errors.Is(err, coreErrs.ClosedError{}) {
|
||||
t.Fatal("expected ClosedError after closing io")
|
||||
}
|
||||
if sm.Count() != 0 {
|
||||
t.Error("session count should be 0")
|
||||
}
|
||||
time.Sleep(1 * time.Second)
|
||||
assert.Equal(t, sm.Count(), 0, "session count should be 0")
|
||||
goleak.VerifyNone(t)
|
||||
}
|
||||
|
|
10
core/go.mod
10
core/go.mod
|
@ -3,20 +3,26 @@ module github.com/apernet/hysteria/core
|
|||
go 1.20
|
||||
|
||||
require (
|
||||
github.com/magiconair/properties v1.8.7
|
||||
github.com/quic-go/quic-go v0.0.0-00010101000000-000000000000
|
||||
github.com/stretchr/testify v1.8.4
|
||||
go.uber.org/goleak v1.2.1
|
||||
golang.org/x/time v0.3.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
|
||||
github.com/golang/mock v1.6.0 // indirect
|
||||
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect
|
||||
github.com/kr/text v0.2.0 // indirect
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
|
||||
github.com/onsi/ginkgo/v2 v2.9.5 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/quic-go/qpack v0.4.0 // indirect
|
||||
github.com/quic-go/qtls-go1-19 v0.3.2 // indirect
|
||||
github.com/quic-go/qtls-go1-20 v0.2.2 // indirect
|
||||
github.com/stretchr/testify v1.8.1 // indirect
|
||||
github.com/stretchr/objx v0.5.0 // indirect
|
||||
golang.org/x/crypto v0.4.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20221205204356-47842c84f3db // indirect
|
||||
golang.org/x/mod v0.10.0 // indirect
|
||||
|
@ -25,6 +31,8 @@ require (
|
|||
golang.org/x/text v0.9.0 // indirect
|
||||
golang.org/x/tools v0.9.1 // indirect
|
||||
google.golang.org/protobuf v1.28.1 // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
replace github.com/quic-go/quic-go => github.com/apernet/quic-go v0.36.1-0.20230627042819-0a89ea8e4c8d
|
||||
|
|
16
core/go.sum
16
core/go.sum
|
@ -3,6 +3,7 @@ github.com/apernet/quic-go v0.36.1-0.20230627042819-0a89ea8e4c8d/go.mod h1:zPetv
|
|||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
|
@ -18,6 +19,14 @@ github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
|||
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE=
|
||||
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
|
||||
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/onsi/ginkgo/v2 v2.9.5 h1:+6Hr4uxzP4XIUyAkg61dWBw8lb/gc4/X5luuxN/EC+Q=
|
||||
github.com/onsi/ginkgo/v2 v2.9.5/go.mod h1:tvAoo1QUJwNEU2ITftXTpR7R1RbCzoZUOs3RonqW57k=
|
||||
github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE=
|
||||
|
@ -31,12 +40,13 @@ github.com/quic-go/qtls-go1-20 v0.2.2 h1:WLOPx6OY/hxtTxKV1Zrq20FtXtDEkeY00CGQm8G
|
|||
github.com/quic-go/qtls-go1-20 v0.2.2/go.mod h1:JKtK6mjbAVcUTN/9jZpvLbGxvdWIKS8uT7EiStoU1SM=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A=
|
||||
go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4=
|
||||
|
@ -84,6 +94,8 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0
|
|||
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
|
||||
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
|
15
core/server/.mockery.yaml
Normal file
15
core/server/.mockery.yaml
Normal file
|
@ -0,0 +1,15 @@
|
|||
with-expecter: true
|
||||
inpackage: true
|
||||
dir: .
|
||||
packages:
|
||||
github.com/apernet/hysteria/core/server:
|
||||
interfaces:
|
||||
udpIO:
|
||||
config:
|
||||
mockname: mockUDPIO
|
||||
udpEventLogger:
|
||||
config:
|
||||
mockname: mockUDPEventLogger
|
||||
UDPConn:
|
||||
config:
|
||||
mockname: mockUDPConn
|
185
core/server/mock_UDPConn.go
Normal file
185
core/server/mock_UDPConn.go
Normal file
|
@ -0,0 +1,185 @@
|
|||
// Code generated by mockery v2.32.0. DO NOT EDIT.
|
||||
|
||||
package server
|
||||
|
||||
import mock "github.com/stretchr/testify/mock"
|
||||
|
||||
// mockUDPConn is an autogenerated mock type for the UDPConn type
|
||||
type mockUDPConn struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
type mockUDPConn_Expecter struct {
|
||||
mock *mock.Mock
|
||||
}
|
||||
|
||||
func (_m *mockUDPConn) EXPECT() *mockUDPConn_Expecter {
|
||||
return &mockUDPConn_Expecter{mock: &_m.Mock}
|
||||
}
|
||||
|
||||
// Close provides a mock function with given fields:
|
||||
func (_m *mockUDPConn) Close() error {
|
||||
ret := _m.Called()
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func() error); ok {
|
||||
r0 = rf()
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// mockUDPConn_Close_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Close'
|
||||
type mockUDPConn_Close_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// Close is a helper method to define mock.On call
|
||||
func (_e *mockUDPConn_Expecter) Close() *mockUDPConn_Close_Call {
|
||||
return &mockUDPConn_Close_Call{Call: _e.mock.On("Close")}
|
||||
}
|
||||
|
||||
func (_c *mockUDPConn_Close_Call) Run(run func()) *mockUDPConn_Close_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run()
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *mockUDPConn_Close_Call) Return(_a0 error) *mockUDPConn_Close_Call {
|
||||
_c.Call.Return(_a0)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *mockUDPConn_Close_Call) RunAndReturn(run func() error) *mockUDPConn_Close_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// ReadFrom provides a mock function with given fields: b
|
||||
func (_m *mockUDPConn) ReadFrom(b []byte) (int, string, error) {
|
||||
ret := _m.Called(b)
|
||||
|
||||
var r0 int
|
||||
var r1 string
|
||||
var r2 error
|
||||
if rf, ok := ret.Get(0).(func([]byte) (int, string, error)); ok {
|
||||
return rf(b)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func([]byte) int); ok {
|
||||
r0 = rf(b)
|
||||
} else {
|
||||
r0 = ret.Get(0).(int)
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func([]byte) string); ok {
|
||||
r1 = rf(b)
|
||||
} else {
|
||||
r1 = ret.Get(1).(string)
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(2).(func([]byte) error); ok {
|
||||
r2 = rf(b)
|
||||
} else {
|
||||
r2 = ret.Error(2)
|
||||
}
|
||||
|
||||
return r0, r1, r2
|
||||
}
|
||||
|
||||
// mockUDPConn_ReadFrom_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ReadFrom'
|
||||
type mockUDPConn_ReadFrom_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// ReadFrom is a helper method to define mock.On call
|
||||
// - b []byte
|
||||
func (_e *mockUDPConn_Expecter) ReadFrom(b interface{}) *mockUDPConn_ReadFrom_Call {
|
||||
return &mockUDPConn_ReadFrom_Call{Call: _e.mock.On("ReadFrom", b)}
|
||||
}
|
||||
|
||||
func (_c *mockUDPConn_ReadFrom_Call) Run(run func(b []byte)) *mockUDPConn_ReadFrom_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run(args[0].([]byte))
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *mockUDPConn_ReadFrom_Call) Return(_a0 int, _a1 string, _a2 error) *mockUDPConn_ReadFrom_Call {
|
||||
_c.Call.Return(_a0, _a1, _a2)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *mockUDPConn_ReadFrom_Call) RunAndReturn(run func([]byte) (int, string, error)) *mockUDPConn_ReadFrom_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// WriteTo provides a mock function with given fields: b, addr
|
||||
func (_m *mockUDPConn) WriteTo(b []byte, addr string) (int, error) {
|
||||
ret := _m.Called(b, addr)
|
||||
|
||||
var r0 int
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func([]byte, string) (int, error)); ok {
|
||||
return rf(b, addr)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func([]byte, string) int); ok {
|
||||
r0 = rf(b, addr)
|
||||
} else {
|
||||
r0 = ret.Get(0).(int)
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func([]byte, string) error); ok {
|
||||
r1 = rf(b, addr)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// mockUDPConn_WriteTo_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'WriteTo'
|
||||
type mockUDPConn_WriteTo_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// WriteTo is a helper method to define mock.On call
|
||||
// - b []byte
|
||||
// - addr string
|
||||
func (_e *mockUDPConn_Expecter) WriteTo(b interface{}, addr interface{}) *mockUDPConn_WriteTo_Call {
|
||||
return &mockUDPConn_WriteTo_Call{Call: _e.mock.On("WriteTo", b, addr)}
|
||||
}
|
||||
|
||||
func (_c *mockUDPConn_WriteTo_Call) Run(run func(b []byte, addr string)) *mockUDPConn_WriteTo_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run(args[0].([]byte), args[1].(string))
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *mockUDPConn_WriteTo_Call) Return(_a0 int, _a1 error) *mockUDPConn_WriteTo_Call {
|
||||
_c.Call.Return(_a0, _a1)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *mockUDPConn_WriteTo_Call) RunAndReturn(run func([]byte, string) (int, error)) *mockUDPConn_WriteTo_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// newMockUDPConn creates a new instance of mockUDPConn. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
||||
// The first argument is typically a *testing.T value.
|
||||
func newMockUDPConn(t interface {
|
||||
mock.TestingT
|
||||
Cleanup(func())
|
||||
}) *mockUDPConn {
|
||||
mock := &mockUDPConn{}
|
||||
mock.Mock.Test(t)
|
||||
|
||||
t.Cleanup(func() { mock.AssertExpectations(t) })
|
||||
|
||||
return mock
|
||||
}
|
100
core/server/mock_udpEventLogger.go
Normal file
100
core/server/mock_udpEventLogger.go
Normal file
|
@ -0,0 +1,100 @@
|
|||
// Code generated by mockery v2.32.0. DO NOT EDIT.
|
||||
|
||||
package server
|
||||
|
||||
import mock "github.com/stretchr/testify/mock"
|
||||
|
||||
// mockUDPEventLogger is an autogenerated mock type for the udpEventLogger type
|
||||
type mockUDPEventLogger struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
type mockUDPEventLogger_Expecter struct {
|
||||
mock *mock.Mock
|
||||
}
|
||||
|
||||
func (_m *mockUDPEventLogger) EXPECT() *mockUDPEventLogger_Expecter {
|
||||
return &mockUDPEventLogger_Expecter{mock: &_m.Mock}
|
||||
}
|
||||
|
||||
// Close provides a mock function with given fields: sessionID, err
|
||||
func (_m *mockUDPEventLogger) Close(sessionID uint32, err error) {
|
||||
_m.Called(sessionID, err)
|
||||
}
|
||||
|
||||
// mockUDPEventLogger_Close_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Close'
|
||||
type mockUDPEventLogger_Close_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// Close is a helper method to define mock.On call
|
||||
// - sessionID uint32
|
||||
// - err error
|
||||
func (_e *mockUDPEventLogger_Expecter) Close(sessionID interface{}, err interface{}) *mockUDPEventLogger_Close_Call {
|
||||
return &mockUDPEventLogger_Close_Call{Call: _e.mock.On("Close", sessionID, err)}
|
||||
}
|
||||
|
||||
func (_c *mockUDPEventLogger_Close_Call) Run(run func(sessionID uint32, err error)) *mockUDPEventLogger_Close_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run(args[0].(uint32), args[1].(error))
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *mockUDPEventLogger_Close_Call) Return() *mockUDPEventLogger_Close_Call {
|
||||
_c.Call.Return()
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *mockUDPEventLogger_Close_Call) RunAndReturn(run func(uint32, error)) *mockUDPEventLogger_Close_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// New provides a mock function with given fields: sessionID, reqAddr
|
||||
func (_m *mockUDPEventLogger) New(sessionID uint32, reqAddr string) {
|
||||
_m.Called(sessionID, reqAddr)
|
||||
}
|
||||
|
||||
// mockUDPEventLogger_New_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'New'
|
||||
type mockUDPEventLogger_New_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// New is a helper method to define mock.On call
|
||||
// - sessionID uint32
|
||||
// - reqAddr string
|
||||
func (_e *mockUDPEventLogger_Expecter) New(sessionID interface{}, reqAddr interface{}) *mockUDPEventLogger_New_Call {
|
||||
return &mockUDPEventLogger_New_Call{Call: _e.mock.On("New", sessionID, reqAddr)}
|
||||
}
|
||||
|
||||
func (_c *mockUDPEventLogger_New_Call) Run(run func(sessionID uint32, reqAddr string)) *mockUDPEventLogger_New_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run(args[0].(uint32), args[1].(string))
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *mockUDPEventLogger_New_Call) Return() *mockUDPEventLogger_New_Call {
|
||||
_c.Call.Return()
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *mockUDPEventLogger_New_Call) RunAndReturn(run func(uint32, string)) *mockUDPEventLogger_New_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// newMockUDPEventLogger creates a new instance of mockUDPEventLogger. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
||||
// The first argument is typically a *testing.T value.
|
||||
func newMockUDPEventLogger(t interface {
|
||||
mock.TestingT
|
||||
Cleanup(func())
|
||||
}) *mockUDPEventLogger {
|
||||
mock := &mockUDPEventLogger{}
|
||||
mock.Mock.Test(t)
|
||||
|
||||
t.Cleanup(func() { mock.AssertExpectations(t) })
|
||||
|
||||
return mock
|
||||
}
|
185
core/server/mock_udpIO.go
Normal file
185
core/server/mock_udpIO.go
Normal file
|
@ -0,0 +1,185 @@
|
|||
// Code generated by mockery v2.32.0. DO NOT EDIT.
|
||||
|
||||
package server
|
||||
|
||||
import (
|
||||
protocol "github.com/apernet/hysteria/core/internal/protocol"
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
// mockUDPIO is an autogenerated mock type for the udpIO type
|
||||
type mockUDPIO struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
type mockUDPIO_Expecter struct {
|
||||
mock *mock.Mock
|
||||
}
|
||||
|
||||
func (_m *mockUDPIO) EXPECT() *mockUDPIO_Expecter {
|
||||
return &mockUDPIO_Expecter{mock: &_m.Mock}
|
||||
}
|
||||
|
||||
// ReceiveMessage provides a mock function with given fields:
|
||||
func (_m *mockUDPIO) ReceiveMessage() (*protocol.UDPMessage, error) {
|
||||
ret := _m.Called()
|
||||
|
||||
var r0 *protocol.UDPMessage
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func() (*protocol.UDPMessage, error)); ok {
|
||||
return rf()
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func() *protocol.UDPMessage); ok {
|
||||
r0 = rf()
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*protocol.UDPMessage)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func() error); ok {
|
||||
r1 = rf()
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// mockUDPIO_ReceiveMessage_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ReceiveMessage'
|
||||
type mockUDPIO_ReceiveMessage_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// ReceiveMessage is a helper method to define mock.On call
|
||||
func (_e *mockUDPIO_Expecter) ReceiveMessage() *mockUDPIO_ReceiveMessage_Call {
|
||||
return &mockUDPIO_ReceiveMessage_Call{Call: _e.mock.On("ReceiveMessage")}
|
||||
}
|
||||
|
||||
func (_c *mockUDPIO_ReceiveMessage_Call) Run(run func()) *mockUDPIO_ReceiveMessage_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run()
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *mockUDPIO_ReceiveMessage_Call) Return(_a0 *protocol.UDPMessage, _a1 error) *mockUDPIO_ReceiveMessage_Call {
|
||||
_c.Call.Return(_a0, _a1)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *mockUDPIO_ReceiveMessage_Call) RunAndReturn(run func() (*protocol.UDPMessage, error)) *mockUDPIO_ReceiveMessage_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// SendMessage provides a mock function with given fields: _a0, _a1
|
||||
func (_m *mockUDPIO) SendMessage(_a0 []byte, _a1 *protocol.UDPMessage) error {
|
||||
ret := _m.Called(_a0, _a1)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func([]byte, *protocol.UDPMessage) error); ok {
|
||||
r0 = rf(_a0, _a1)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// mockUDPIO_SendMessage_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SendMessage'
|
||||
type mockUDPIO_SendMessage_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// SendMessage is a helper method to define mock.On call
|
||||
// - _a0 []byte
|
||||
// - _a1 *protocol.UDPMessage
|
||||
func (_e *mockUDPIO_Expecter) SendMessage(_a0 interface{}, _a1 interface{}) *mockUDPIO_SendMessage_Call {
|
||||
return &mockUDPIO_SendMessage_Call{Call: _e.mock.On("SendMessage", _a0, _a1)}
|
||||
}
|
||||
|
||||
func (_c *mockUDPIO_SendMessage_Call) Run(run func(_a0 []byte, _a1 *protocol.UDPMessage)) *mockUDPIO_SendMessage_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run(args[0].([]byte), args[1].(*protocol.UDPMessage))
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *mockUDPIO_SendMessage_Call) Return(_a0 error) *mockUDPIO_SendMessage_Call {
|
||||
_c.Call.Return(_a0)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *mockUDPIO_SendMessage_Call) RunAndReturn(run func([]byte, *protocol.UDPMessage) error) *mockUDPIO_SendMessage_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// UDP provides a mock function with given fields: reqAddr
|
||||
func (_m *mockUDPIO) UDP(reqAddr string) (UDPConn, error) {
|
||||
ret := _m.Called(reqAddr)
|
||||
|
||||
var r0 UDPConn
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(string) (UDPConn, error)); ok {
|
||||
return rf(reqAddr)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(string) UDPConn); ok {
|
||||
r0 = rf(reqAddr)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(UDPConn)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(string) error); ok {
|
||||
r1 = rf(reqAddr)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// mockUDPIO_UDP_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UDP'
|
||||
type mockUDPIO_UDP_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// UDP is a helper method to define mock.On call
|
||||
// - reqAddr string
|
||||
func (_e *mockUDPIO_Expecter) UDP(reqAddr interface{}) *mockUDPIO_UDP_Call {
|
||||
return &mockUDPIO_UDP_Call{Call: _e.mock.On("UDP", reqAddr)}
|
||||
}
|
||||
|
||||
func (_c *mockUDPIO_UDP_Call) Run(run func(reqAddr string)) *mockUDPIO_UDP_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run(args[0].(string))
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *mockUDPIO_UDP_Call) Return(_a0 UDPConn, _a1 error) *mockUDPIO_UDP_Call {
|
||||
_c.Call.Return(_a0, _a1)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *mockUDPIO_UDP_Call) RunAndReturn(run func(string) (UDPConn, error)) *mockUDPIO_UDP_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// newMockUDPIO creates a new instance of mockUDPIO. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
||||
// The first argument is typically a *testing.T value.
|
||||
func newMockUDPIO(t interface {
|
||||
mock.TestingT
|
||||
Cleanup(func())
|
||||
}) *mockUDPIO {
|
||||
mock := &mockUDPIO{}
|
||||
mock.Mock.Test(t)
|
||||
|
||||
t.Cleanup(func() { mock.AssertExpectations(t) })
|
||||
|
||||
return mock
|
||||
}
|
|
@ -2,260 +2,174 @@ package server
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/apernet/hysteria/core/internal/protocol"
|
||||
"github.com/magiconair/properties/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"go.uber.org/goleak"
|
||||
|
||||
"github.com/apernet/hysteria/core/internal/protocol"
|
||||
)
|
||||
|
||||
var (
|
||||
errUDPBlocked = errors.New("blocked")
|
||||
errUDPClosed = errors.New("closed")
|
||||
)
|
||||
|
||||
type echoUDPConnPkt struct {
|
||||
Data []byte
|
||||
Addr string
|
||||
Close bool
|
||||
}
|
||||
|
||||
type echoUDPConn struct {
|
||||
PktCh chan echoUDPConnPkt
|
||||
}
|
||||
|
||||
func (c *echoUDPConn) ReadFrom(b []byte) (int, string, error) {
|
||||
pkt := <-c.PktCh
|
||||
if pkt.Close {
|
||||
return 0, "", errUDPClosed
|
||||
}
|
||||
n := copy(b, pkt.Data)
|
||||
return n, pkt.Addr, nil
|
||||
}
|
||||
|
||||
func (c *echoUDPConn) WriteTo(b []byte, addr string) (int, error) {
|
||||
nb := make([]byte, len(b))
|
||||
copy(nb, b)
|
||||
c.PktCh <- echoUDPConnPkt{
|
||||
Data: nb,
|
||||
Addr: addr,
|
||||
}
|
||||
return len(b), nil
|
||||
}
|
||||
|
||||
func (c *echoUDPConn) Close() error {
|
||||
c.PktCh <- echoUDPConnPkt{
|
||||
Close: true,
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type udpMockIO struct {
|
||||
ReceiveCh <-chan *protocol.UDPMessage
|
||||
SendCh chan<- *protocol.UDPMessage
|
||||
UDPClose bool // ReadFrom() returns error immediately
|
||||
BlockUDP bool // Block UDP connection creation
|
||||
}
|
||||
|
||||
func (io *udpMockIO) ReceiveMessage() (*protocol.UDPMessage, error) {
|
||||
m := <-io.ReceiveCh
|
||||
if m == nil {
|
||||
return nil, errUDPClosed
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func (io *udpMockIO) SendMessage(buf []byte, msg *protocol.UDPMessage) error {
|
||||
nMsg := *msg
|
||||
nMsg.Data = make([]byte, len(msg.Data))
|
||||
copy(nMsg.Data, msg.Data)
|
||||
io.SendCh <- &nMsg
|
||||
return nil
|
||||
}
|
||||
|
||||
func (io *udpMockIO) UDP(reqAddr string) (UDPConn, error) {
|
||||
if io.BlockUDP {
|
||||
return nil, errUDPBlocked
|
||||
}
|
||||
conn := &echoUDPConn{
|
||||
PktCh: make(chan echoUDPConnPkt, 10),
|
||||
}
|
||||
if io.UDPClose {
|
||||
conn.PktCh <- echoUDPConnPkt{
|
||||
Close: true,
|
||||
}
|
||||
}
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
type udpMockEventNew struct {
|
||||
SessionID uint32
|
||||
ReqAddr string
|
||||
}
|
||||
|
||||
type udpMockEventClose struct {
|
||||
SessionID uint32
|
||||
Err error
|
||||
}
|
||||
|
||||
type udpMockEventLogger struct {
|
||||
NewCh chan<- udpMockEventNew
|
||||
CloseCh chan<- udpMockEventClose
|
||||
}
|
||||
|
||||
func (l *udpMockEventLogger) New(sessionID uint32, reqAddr string) {
|
||||
l.NewCh <- udpMockEventNew{sessionID, reqAddr}
|
||||
}
|
||||
|
||||
func (l *udpMockEventLogger) Close(sessionID uint32, err error) {
|
||||
l.CloseCh <- udpMockEventClose{sessionID, err}
|
||||
}
|
||||
|
||||
func TestUDPSessionManager(t *testing.T) {
|
||||
msgReceiveCh := make(chan *protocol.UDPMessage, 10)
|
||||
msgSendCh := make(chan *protocol.UDPMessage, 10)
|
||||
io := &udpMockIO{
|
||||
ReceiveCh: msgReceiveCh,
|
||||
SendCh: msgSendCh,
|
||||
}
|
||||
eventNewCh := make(chan udpMockEventNew, 10)
|
||||
eventCloseCh := make(chan udpMockEventClose, 10)
|
||||
eventLogger := &udpMockEventLogger{
|
||||
NewCh: eventNewCh,
|
||||
CloseCh: eventCloseCh,
|
||||
}
|
||||
io := newMockUDPIO(t)
|
||||
eventLogger := newMockUDPEventLogger(t)
|
||||
sm := newUDPSessionManager(io, eventLogger, 2*time.Second)
|
||||
|
||||
msgCh := make(chan *protocol.UDPMessage, 4)
|
||||
io.EXPECT().ReceiveMessage().RunAndReturn(func() (*protocol.UDPMessage, error) {
|
||||
m := <-msgCh
|
||||
if m == nil {
|
||||
return nil, errors.New("closed")
|
||||
}
|
||||
return m, nil
|
||||
})
|
||||
|
||||
go sm.Run()
|
||||
|
||||
t.Run("session creation & timeout", func(t *testing.T) {
|
||||
ms := []*protocol.UDPMessage{
|
||||
{
|
||||
SessionID: 1234,
|
||||
PacketID: 0,
|
||||
FragID: 0,
|
||||
FragCount: 1,
|
||||
Addr: "example.com:5353",
|
||||
Data: []byte("hello"),
|
||||
},
|
||||
{
|
||||
SessionID: 5678,
|
||||
PacketID: 0,
|
||||
FragID: 0,
|
||||
FragCount: 1,
|
||||
Addr: "example.com:9999",
|
||||
Data: []byte("goodbye"),
|
||||
},
|
||||
{
|
||||
SessionID: 1234,
|
||||
PacketID: 0,
|
||||
FragID: 0,
|
||||
FragCount: 1,
|
||||
Addr: "example.com:5353",
|
||||
Data: []byte(" world"),
|
||||
},
|
||||
{
|
||||
SessionID: 5678,
|
||||
PacketID: 0,
|
||||
FragID: 0,
|
||||
FragCount: 1,
|
||||
Addr: "example.com:9999",
|
||||
Data: []byte(" girl"),
|
||||
},
|
||||
}
|
||||
for _, m := range ms {
|
||||
msgReceiveCh <- m
|
||||
}
|
||||
// New event order should be consistent with message order
|
||||
for i := 0; i < 2; i++ {
|
||||
newEvent := <-eventNewCh
|
||||
if newEvent.SessionID != ms[i].SessionID || newEvent.ReqAddr != ms[i].Addr {
|
||||
t.Errorf("unexpected new event value: %d:%s", newEvent.SessionID, newEvent.ReqAddr)
|
||||
}
|
||||
}
|
||||
// Message order is not guaranteed so we use a map
|
||||
msgMap := make(map[string]bool)
|
||||
for i := 0; i < 4; i++ {
|
||||
msg := <-msgSendCh
|
||||
msgMap[fmt.Sprintf("%d:%s:%s", msg.SessionID, msg.Addr, string(msg.Data))] = true
|
||||
}
|
||||
for _, m := range ms {
|
||||
key := fmt.Sprintf("%d:%s:%s", m.SessionID, m.Addr, string(m.Data))
|
||||
if !msgMap[key] {
|
||||
t.Errorf("missing message: %s", key)
|
||||
}
|
||||
}
|
||||
// Timeout check
|
||||
startTime := time.Now()
|
||||
closeMap := make(map[uint32]bool)
|
||||
for i := 0; i < 2; i++ {
|
||||
closeEvent := <-eventCloseCh
|
||||
closeMap[closeEvent.SessionID] = true
|
||||
}
|
||||
for i := 0; i < 2; i++ {
|
||||
if !closeMap[ms[i].SessionID] {
|
||||
t.Errorf("missing close event: %d", ms[i].SessionID)
|
||||
}
|
||||
}
|
||||
dur := time.Since(startTime)
|
||||
if dur < 2*time.Second || dur > 4*time.Second {
|
||||
t.Errorf("unexpected timeout duration: %s", dur)
|
||||
udpReadFunc := func(addr string, ch chan []byte, b []byte) (int, string, error) {
|
||||
bs := <-ch
|
||||
if bs == nil {
|
||||
return 0, "", errors.New("closed")
|
||||
}
|
||||
n := copy(b, bs)
|
||||
return n, addr, nil
|
||||
}
|
||||
|
||||
// Test normal session creation & timeout
|
||||
msg1 := &protocol.UDPMessage{
|
||||
SessionID: 1234,
|
||||
PacketID: 0,
|
||||
FragID: 0,
|
||||
FragCount: 1,
|
||||
Addr: "address1.com:9000",
|
||||
Data: []byte("hello"),
|
||||
}
|
||||
eventLogger.EXPECT().New(msg1.SessionID, msg1.Addr).Return().Once()
|
||||
udpConn1 := newMockUDPConn(t)
|
||||
udpConn1Ch := make(chan []byte, 1)
|
||||
io.EXPECT().UDP(msg1.Addr).Return(udpConn1, nil).Once()
|
||||
udpConn1.EXPECT().WriteTo(msg1.Data, msg1.Addr).Return(5, nil).Once()
|
||||
udpConn1.EXPECT().ReadFrom(mock.Anything).RunAndReturn(func(b []byte) (int, string, error) {
|
||||
return udpReadFunc(msg1.Addr, udpConn1Ch, b)
|
||||
})
|
||||
io.EXPECT().SendMessage(mock.Anything, &protocol.UDPMessage{
|
||||
SessionID: msg1.SessionID,
|
||||
PacketID: 0,
|
||||
FragID: 0,
|
||||
FragCount: 1,
|
||||
Addr: msg1.Addr,
|
||||
Data: []byte("hi back"),
|
||||
}).Return(nil).Once()
|
||||
msgCh <- msg1
|
||||
udpConn1Ch <- []byte("hi back")
|
||||
|
||||
t.Run("UDP connection close", func(t *testing.T) {
|
||||
// Close UDP connection immediately after creation
|
||||
io.UDPClose = true
|
||||
|
||||
m := &protocol.UDPMessage{
|
||||
SessionID: 8888,
|
||||
PacketID: 0,
|
||||
FragID: 0,
|
||||
FragCount: 1,
|
||||
Addr: "mygod.org:1514",
|
||||
Data: []byte("goodnight"),
|
||||
}
|
||||
msgReceiveCh <- m
|
||||
// Should have both new and close events immediately
|
||||
newEvent := <-eventNewCh
|
||||
if newEvent.SessionID != m.SessionID || newEvent.ReqAddr != m.Addr {
|
||||
t.Errorf("unexpected new event value: %d:%s", newEvent.SessionID, newEvent.ReqAddr)
|
||||
}
|
||||
closeEvent := <-eventCloseCh
|
||||
if closeEvent.SessionID != m.SessionID || closeEvent.Err != errUDPClosed {
|
||||
t.Errorf("unexpected close event value: %d:%s", closeEvent.SessionID, closeEvent.Err)
|
||||
}
|
||||
msg2 := &protocol.UDPMessage{
|
||||
SessionID: 5678,
|
||||
PacketID: 0,
|
||||
FragID: 0,
|
||||
FragCount: 1,
|
||||
Addr: "address2.net:12450",
|
||||
Data: []byte("how are you"),
|
||||
}
|
||||
eventLogger.EXPECT().New(msg2.SessionID, msg2.Addr).Return().Once()
|
||||
udpConn2 := newMockUDPConn(t)
|
||||
udpConn2Ch := make(chan []byte, 1)
|
||||
io.EXPECT().UDP(msg2.Addr).Return(udpConn2, nil).Once()
|
||||
udpConn2.EXPECT().WriteTo(msg2.Data, msg2.Addr).Return(11, nil).Once()
|
||||
udpConn2.EXPECT().ReadFrom(mock.Anything).RunAndReturn(func(b []byte) (int, string, error) {
|
||||
return udpReadFunc(msg2.Addr, udpConn2Ch, b)
|
||||
})
|
||||
io.EXPECT().SendMessage(mock.Anything, &protocol.UDPMessage{
|
||||
SessionID: msg2.SessionID,
|
||||
PacketID: 0,
|
||||
FragID: 0,
|
||||
FragCount: 1,
|
||||
Addr: msg2.Addr,
|
||||
Data: []byte("im fine"),
|
||||
}).Return(nil).Once()
|
||||
msgCh <- msg2
|
||||
udpConn2Ch <- []byte("im fine")
|
||||
|
||||
t.Run("UDP IO failure", func(t *testing.T) {
|
||||
// Block UDP connection creation
|
||||
io.BlockUDP = true
|
||||
msg3 := &protocol.UDPMessage{
|
||||
SessionID: 1234,
|
||||
PacketID: 0,
|
||||
FragID: 0,
|
||||
FragCount: 1,
|
||||
Addr: "address1.com:9000",
|
||||
Data: []byte("who are you?"),
|
||||
}
|
||||
udpConn1.EXPECT().WriteTo(msg3.Data, msg3.Addr).Return(12, nil).Once()
|
||||
io.EXPECT().SendMessage(mock.Anything, &protocol.UDPMessage{
|
||||
SessionID: msg3.SessionID,
|
||||
PacketID: 0,
|
||||
FragID: 0,
|
||||
FragCount: 1,
|
||||
Addr: msg3.Addr,
|
||||
Data: []byte("im your father"),
|
||||
}).Return(nil).Once()
|
||||
msgCh <- msg3
|
||||
udpConn1Ch <- []byte("im your father")
|
||||
|
||||
m := &protocol.UDPMessage{
|
||||
SessionID: 9999,
|
||||
PacketID: 0,
|
||||
FragID: 0,
|
||||
FragCount: 1,
|
||||
Addr: "xxx.net:12450",
|
||||
Data: []byte("nope"),
|
||||
}
|
||||
msgReceiveCh <- m
|
||||
// Should have both new and close events immediately
|
||||
newEvent := <-eventNewCh
|
||||
if newEvent.SessionID != m.SessionID || newEvent.ReqAddr != m.Addr {
|
||||
t.Errorf("unexpected new event value: %d:%s", newEvent.SessionID, newEvent.ReqAddr)
|
||||
}
|
||||
closeEvent := <-eventCloseCh
|
||||
if closeEvent.SessionID != m.SessionID || closeEvent.Err != errUDPBlocked {
|
||||
t.Errorf("unexpected close event value: %d:%s", closeEvent.SessionID, closeEvent.Err)
|
||||
}
|
||||
})
|
||||
// Make sure timeout works (connections closed & close events emitted)
|
||||
udpConn1.EXPECT().Close().RunAndReturn(func() error {
|
||||
close(udpConn1Ch)
|
||||
return nil
|
||||
}).Once()
|
||||
udpConn2.EXPECT().Close().RunAndReturn(func() error {
|
||||
close(udpConn2Ch)
|
||||
return nil
|
||||
}).Once()
|
||||
eventLogger.EXPECT().Close(msg1.SessionID, nil).Once()
|
||||
eventLogger.EXPECT().Close(msg2.SessionID, nil).Once()
|
||||
|
||||
time.Sleep(3 * time.Second) // Wait for timeout
|
||||
mock.AssertExpectationsForObjects(t, io, eventLogger, udpConn1, udpConn2)
|
||||
|
||||
// Test UDP connection close error propagation
|
||||
errUDPClosed := errors.New("UDP connection closed")
|
||||
msg4 := &protocol.UDPMessage{
|
||||
SessionID: 666,
|
||||
PacketID: 0,
|
||||
FragID: 0,
|
||||
FragCount: 1,
|
||||
Addr: "oh-no.com:27015",
|
||||
Data: []byte("dont say bye"),
|
||||
}
|
||||
eventLogger.EXPECT().New(msg4.SessionID, msg4.Addr).Return().Once()
|
||||
udpConn4 := newMockUDPConn(t)
|
||||
io.EXPECT().UDP(msg4.Addr).Return(udpConn4, nil).Once()
|
||||
udpConn4.EXPECT().WriteTo(msg4.Data, msg4.Addr).Return(12, nil).Once()
|
||||
udpConn4.EXPECT().ReadFrom(mock.Anything).Return(0, "", errUDPClosed).Once()
|
||||
udpConn4.EXPECT().Close().Return(nil).Once()
|
||||
eventLogger.EXPECT().Close(msg4.SessionID, errUDPClosed).Once()
|
||||
msgCh <- msg4
|
||||
|
||||
time.Sleep(1 * time.Second)
|
||||
mock.AssertExpectationsForObjects(t, io, eventLogger, udpConn4)
|
||||
|
||||
// Test UDP connection creation error propagation
|
||||
errUDPIO := errors.New("UDP IO error")
|
||||
msg5 := &protocol.UDPMessage{
|
||||
SessionID: 777,
|
||||
PacketID: 0,
|
||||
FragID: 0,
|
||||
FragCount: 1,
|
||||
Addr: "callmemaybe.com:15353",
|
||||
Data: []byte("babe i miss you"),
|
||||
}
|
||||
eventLogger.EXPECT().New(msg5.SessionID, msg5.Addr).Return().Once()
|
||||
io.EXPECT().UDP(msg5.Addr).Return(nil, errUDPIO).Once()
|
||||
eventLogger.EXPECT().Close(msg5.SessionID, errUDPIO).Once()
|
||||
msgCh <- msg5
|
||||
|
||||
time.Sleep(1 * time.Second)
|
||||
mock.AssertExpectationsForObjects(t, io, eventLogger)
|
||||
|
||||
// Leak checks
|
||||
msgReceiveCh <- nil
|
||||
time.Sleep(1 * time.Second) // Give some time for the goroutines to exit
|
||||
if sm.Count() != 0 {
|
||||
t.Error("session count should be 0")
|
||||
}
|
||||
close(msgCh) // This will return error from ReceiveMessage(), should stop the session manager
|
||||
time.Sleep(1 * time.Second) // Wait one more second just to be sure
|
||||
assert.Equal(t, sm.Count(), 0, "session count should be 0")
|
||||
goleak.VerifyNone(t)
|
||||
}
|
||||
|
|
|
@ -26,11 +26,7 @@ github.com/hashicorp/serf v0.10.1/go.mod h1:yL2t6BqATOLGc5HF7qbFkTfXoPIY0WZdWHfE
|
|||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg=
|
||||
|
@ -49,9 +45,9 @@ golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
|
|||
golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg=
|
||||
golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
|
||||
google.golang.org/api v0.107.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY=
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue