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
Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

conn_test.go 6.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293
  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/9seconds/mtg/v2/internal/testlib"
  12. "github.com/9seconds/mtg/v2/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) TestStopOnUnderlyingWriteError() {
  111. suite.connMock.
  112. On("Write", mock.AnythingOfType("[]uint8")).
  113. Return(0, errors.New("connection reset")).
  114. Maybe()
  115. c := suite.makeConn()
  116. _, _ = c.Write([]byte("data"))
  117. suite.Eventually(func() bool {
  118. _, err := c.Write([]byte{1})
  119. return err != nil
  120. }, 2*time.Second, time.Millisecond)
  121. }
  122. func (suite *ConnTestSuite) TestSyncWriteDataSent() {
  123. suite.connMock.
  124. On("Write", mock.AnythingOfType("[]uint8")).
  125. Return(0, nil).
  126. Maybe()
  127. c := suite.makeConn()
  128. defer c.Stop()
  129. payload := []byte("sync hello")
  130. n, err := c.SyncWrite(payload)
  131. suite.NoError(err)
  132. suite.Equal(len(payload), n)
  133. // SyncWrite returns only after data is flushed to the wire.
  134. assembled := &bytes.Buffer{}
  135. reader := bytes.NewReader(suite.connMock.Written())
  136. for {
  137. header := make([]byte, tls.SizeHeader)
  138. if _, err := io.ReadFull(reader, header); err != nil {
  139. break
  140. }
  141. suite.Equal(byte(tls.TypeApplicationData), header[0])
  142. length := binary.BigEndian.Uint16(header[tls.SizeRecordType+tls.SizeVersion:])
  143. rec := make([]byte, length)
  144. _, err := io.ReadFull(reader, rec)
  145. suite.NoError(err)
  146. assembled.Write(rec)
  147. }
  148. suite.Equal(payload, assembled.Bytes())
  149. }
  150. func (suite *ConnTestSuite) TestSyncWriteDrainsBufferFirst() {
  151. suite.connMock.
  152. On("Write", mock.AnythingOfType("[]uint8")).
  153. Return(0, nil).
  154. Maybe()
  155. c := suite.makeConn()
  156. defer c.Stop()
  157. // Buffer some data via async Write.
  158. _, err := c.Write([]byte("first"))
  159. suite.NoError(err)
  160. // SyncWrite must drain "first" before sending "second".
  161. n, err := c.SyncWrite([]byte("second"))
  162. suite.NoError(err)
  163. suite.Equal(6, n)
  164. // All data should be on the wire now.
  165. assembled := &bytes.Buffer{}
  166. reader := bytes.NewReader(suite.connMock.Written())
  167. for {
  168. header := make([]byte, tls.SizeHeader)
  169. if _, err := io.ReadFull(reader, header); err != nil {
  170. break
  171. }
  172. length := binary.BigEndian.Uint16(header[tls.SizeRecordType+tls.SizeVersion:])
  173. rec := make([]byte, length)
  174. _, err := io.ReadFull(reader, rec)
  175. suite.NoError(err)
  176. assembled.Write(rec)
  177. }
  178. suite.Equal([]byte("firstsecond"), assembled.Bytes())
  179. }
  180. func (suite *ConnTestSuite) TestSyncWriteBlocksAsyncWrite() {
  181. suite.connMock.
  182. On("Write", mock.AnythingOfType("[]uint8")).
  183. Return(0, nil).
  184. Maybe()
  185. c := suite.makeConn()
  186. defer c.Stop()
  187. // Start SyncWrite — it holds exclusive lock.
  188. syncDone := make(chan struct{})
  189. go func() {
  190. defer close(syncDone)
  191. c.SyncWrite([]byte("exclusive")) //nolint: errcheck
  192. }()
  193. // Give SyncWrite time to acquire the lock.
  194. time.Sleep(10 * time.Millisecond)
  195. // Async Write should block until SyncWrite completes.
  196. writeDone := make(chan struct{})
  197. go func() {
  198. defer close(writeDone)
  199. c.Write([]byte("blocked")) //nolint: errcheck
  200. }()
  201. // SyncWrite should finish first.
  202. <-syncDone
  203. select {
  204. case <-writeDone:
  205. // Write completed after SyncWrite — correct.
  206. case <-time.After(2 * time.Second):
  207. suite.Fail("async Write did not unblock after SyncWrite completed")
  208. }
  209. }
  210. func (suite *ConnTestSuite) TestSyncWriteReturnsErrorAfterStop() {
  211. suite.connMock.
  212. On("Write", mock.AnythingOfType("[]uint8")).
  213. Return(0, nil).
  214. Maybe()
  215. c := suite.makeConn()
  216. c.Stop()
  217. time.Sleep(10 * time.Millisecond)
  218. _, err := c.SyncWrite([]byte("too late"))
  219. suite.Error(err)
  220. }
  221. func TestConn(t *testing.T) {
  222. t.Parallel()
  223. suite.Run(t, &ConnTestSuite{})
  224. }