Highly-opinionated (ex-bullshit-free) MTPROTO proxy for Telegram. If you use v1.0 or upgrade broke you proxy, please read the chapter Version 2
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

conn_test.go 3.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. package doppel
  2. import (
  3. "bytes"
  4. "context"
  5. "encoding/binary"
  6. "errors"
  7. "io"
  8. "sync"
  9. "testing"
  10. "time"
  11. "github.com/dolonet/mtg-multi/internal/testlib"
  12. "github.com/dolonet/mtg-multi/mtglib/internal/tls"
  13. "github.com/stretchr/testify/mock"
  14. "github.com/stretchr/testify/suite"
  15. )
  16. type ConnMock struct {
  17. testlib.EssentialsConnMock
  18. mu sync.Mutex
  19. writeBuffer bytes.Buffer
  20. }
  21. func (m *ConnMock) Write(p []byte) (int, error) {
  22. args := m.Called(p)
  23. if err := args.Error(1); err != nil {
  24. return args.Int(0), err
  25. }
  26. m.mu.Lock()
  27. defer m.mu.Unlock()
  28. return m.writeBuffer.Write(p)
  29. }
  30. func (m *ConnMock) Written() []byte {
  31. m.mu.Lock()
  32. defer m.mu.Unlock()
  33. return bytes.Clone(m.writeBuffer.Bytes())
  34. }
  35. type ConnTestSuite struct {
  36. suite.Suite
  37. connMock *ConnMock
  38. ctx context.Context
  39. ctxCancel context.CancelFunc
  40. }
  41. func (suite *ConnTestSuite) SetupTest() {
  42. ctx, cancel := context.WithCancel(context.Background())
  43. suite.ctx = ctx
  44. suite.ctxCancel = cancel
  45. suite.connMock = &ConnMock{}
  46. }
  47. func (suite *ConnTestSuite) TearDownTest() {
  48. suite.ctxCancel()
  49. suite.connMock.AssertExpectations(suite.T())
  50. }
  51. func (suite *ConnTestSuite) makeConn() Conn {
  52. return NewConn(suite.ctx, suite.connMock, Stats{
  53. k: 2.0,
  54. lambda: 0.01,
  55. })
  56. }
  57. func (suite *ConnTestSuite) TestWriteBuffersData() {
  58. suite.connMock.
  59. On("Write", mock.AnythingOfType("[]uint8")).
  60. Return(0, nil).
  61. Maybe()
  62. c := suite.makeConn()
  63. defer c.Stop()
  64. n, err := c.Write([]byte{1, 2, 3})
  65. suite.NoError(err)
  66. suite.Equal(3, n)
  67. }
  68. func (suite *ConnTestSuite) TestWriteOutputsTLSRecords() {
  69. suite.connMock.
  70. On("Write", mock.AnythingOfType("[]uint8")).
  71. Return(0, nil).
  72. Maybe()
  73. c := suite.makeConn()
  74. payload := []byte("hello doppelganger")
  75. _, err := c.Write(payload)
  76. suite.NoError(err)
  77. suite.Eventually(func() bool {
  78. return len(suite.connMock.Written()) > 0
  79. }, 2*time.Second, time.Millisecond)
  80. c.Stop()
  81. assembled := &bytes.Buffer{}
  82. reader := bytes.NewReader(suite.connMock.Written())
  83. for {
  84. header := make([]byte, tls.SizeHeader)
  85. if _, err := io.ReadFull(reader, header); err != nil {
  86. break
  87. }
  88. suite.Equal(byte(tls.TypeApplicationData), header[0])
  89. suite.Equal(tls.TLSVersion[:], header[tls.SizeRecordType:tls.SizeRecordType+tls.SizeVersion])
  90. length := binary.BigEndian.Uint16(header[tls.SizeRecordType+tls.SizeVersion:])
  91. suite.Greater(length, uint16(0))
  92. rec := make([]byte, length)
  93. _, err := io.ReadFull(reader, rec)
  94. suite.NoError(err)
  95. assembled.Write(rec)
  96. }
  97. suite.Equal(payload, assembled.Bytes())
  98. }
  99. func (suite *ConnTestSuite) TestWriteReturnsErrorAfterStop() {
  100. suite.connMock.
  101. On("Write", mock.AnythingOfType("[]uint8")).
  102. Return(0, nil).
  103. Maybe()
  104. c := suite.makeConn()
  105. c.Stop()
  106. time.Sleep(10 * time.Millisecond)
  107. _, err := c.Write([]byte{1})
  108. suite.Error(err)
  109. }
  110. func (suite *ConnTestSuite) TestStopDoesNotDeadlockWhenStartIsWaiting() {
  111. suite.connMock.
  112. On("Write", mock.AnythingOfType("[]uint8")).
  113. Return(0, nil).
  114. Maybe()
  115. for range 100 {
  116. func() {
  117. ctx, cancel := context.WithCancel(suite.ctx)
  118. defer cancel()
  119. c := NewConn(ctx, suite.connMock, Stats{
  120. k: 2.0,
  121. lambda: 0.01,
  122. })
  123. done := make(chan struct{})
  124. go func() {
  125. defer close(done)
  126. c.Stop()
  127. }()
  128. select {
  129. case <-done:
  130. case <-time.After(2 * time.Second):
  131. suite.Fail("Stop() deadlocked: start() likely stuck in writtenCond.Wait()")
  132. }
  133. }()
  134. }
  135. }
  136. func (suite *ConnTestSuite) TestStopOnUnderlyingWriteError() {
  137. suite.connMock.
  138. On("Write", mock.AnythingOfType("[]uint8")).
  139. Return(0, errors.New("connection reset")).
  140. Maybe()
  141. c := suite.makeConn()
  142. _, _ = c.Write([]byte("data"))
  143. suite.Eventually(func() bool {
  144. _, err := c.Write([]byte{1})
  145. return err != nil
  146. }, 2*time.Second, time.Millisecond)
  147. }
  148. func TestConn(t *testing.T) {
  149. t.Parallel()
  150. suite.Run(t, &ConnTestSuite{})
  151. }