connection_test.go 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. package oscar
  2. import (
  3. "bytes"
  4. "context"
  5. "io"
  6. "log/slog"
  7. "sync"
  8. "testing"
  9. "time"
  10. "github.com/mk6i/retro-aim-server/state"
  11. "github.com/mk6i/retro-aim-server/wire"
  12. "github.com/stretchr/testify/assert"
  13. "github.com/stretchr/testify/mock"
  14. )
  15. func TestHandleChatConnection_MessageRelay(t *testing.T) {
  16. sessionManager := state.NewInMemorySessionManager(slog.Default())
  17. // add a user to session that will receive relayed messages
  18. sess, _ := sessionManager.AddSession(nil, "bob")
  19. // start the server connection handler in the background
  20. serverReader, _ := io.Pipe()
  21. clientReader, serverWriter := io.Pipe()
  22. go func() {
  23. flapc := wire.NewFlapClient(0, nil, serverWriter)
  24. rateLimitUpdater := newMockRateLimitUpdater(t)
  25. err := dispatchIncomingMessages(context.Background(), sess, flapc, serverReader, slog.Default(), nil, rateLimitUpdater, wire.DefaultSNACRateLimits())
  26. assert.NoError(t, err)
  27. }()
  28. inboundMsgs := []wire.SNACMessage{
  29. {
  30. Frame: wire.SNACFrame{
  31. FoodGroup: wire.Chat,
  32. SubGroup: wire.ChatUsersJoined,
  33. },
  34. Body: wire.SNAC_0x0E_0x03_ChatUsersJoined{
  35. Users: []wire.TLVUserInfo{
  36. {
  37. ScreenName: "screenname1",
  38. },
  39. },
  40. },
  41. },
  42. {
  43. Frame: wire.SNACFrame{
  44. FoodGroup: wire.Chat,
  45. SubGroup: wire.ChatUsersLeft,
  46. },
  47. Body: wire.SNAC_0x0E_0x03_ChatUsersJoined{
  48. Users: []wire.TLVUserInfo{
  49. {
  50. ScreenName: "screenname2",
  51. },
  52. },
  53. },
  54. },
  55. }
  56. // relay messages to user session
  57. for _, msg := range inboundMsgs {
  58. sessionManager.RelayToScreenName(context.Background(), state.NewIdentScreenName("bob"), msg)
  59. }
  60. // consume and verify the relayed messages
  61. for i := 0; i < len(inboundMsgs); i++ {
  62. flap := wire.FLAPFrame{}
  63. assert.NoError(t, wire.UnmarshalBE(&flap, clientReader))
  64. frame := wire.SNACFrame{}
  65. buf := bytes.NewBuffer(flap.Payload)
  66. assert.NoError(t, wire.UnmarshalBE(&frame, buf))
  67. assert.Equal(t, inboundMsgs[i].Frame, frame)
  68. body := wire.SNAC_0x0E_0x03_ChatUsersJoined{}
  69. assert.NoError(t, wire.UnmarshalBE(&body, buf))
  70. assert.Equal(t, inboundMsgs[i].Body, body)
  71. }
  72. // stop the session, which terminates the connection handler goroutine
  73. sess.Close()
  74. <-sess.Closed()
  75. // verify the connection handler sends client disconnection message before
  76. // terminating
  77. flap := wire.FLAPFrame{}
  78. assert.NoError(t, wire.UnmarshalBE(&flap, clientReader))
  79. assert.Equal(t, wire.FLAPFrameSignoff, flap.FrameType)
  80. }
  81. func TestHandleChatConnection_ClientRequest(t *testing.T) {
  82. sessionManager := state.NewInMemorySessionManager(slog.Default())
  83. // add session so that the function can terminate upon closure
  84. sess, _ := sessionManager.AddSession(nil, "bob")
  85. sess.SetRateClasses(time.Now(), defaultRateLimitClasses())
  86. inboundMsgs := []wire.SNACMessage{
  87. {
  88. Frame: wire.SNACFrame{
  89. FoodGroup: wire.Chat,
  90. SubGroup: wire.ChatUsersJoined,
  91. },
  92. Body: wire.SNAC_0x0E_0x03_ChatUsersJoined{
  93. Users: []wire.TLVUserInfo{
  94. {
  95. ScreenName: "screenname1",
  96. },
  97. },
  98. },
  99. },
  100. {
  101. Frame: wire.SNACFrame{
  102. FoodGroup: wire.Chat,
  103. SubGroup: wire.ChatUsersLeft,
  104. },
  105. Body: wire.SNAC_0x0E_0x03_ChatUsersJoined{
  106. Users: []wire.TLVUserInfo{
  107. {
  108. ScreenName: "screenname2",
  109. },
  110. },
  111. },
  112. },
  113. }
  114. wg := &sync.WaitGroup{}
  115. wg.Add(len(inboundMsgs))
  116. // set up mock handlers to receive messages and verify their contents
  117. router := newMockHandler(t)
  118. for _, msg := range inboundMsgs {
  119. msg := msg
  120. router.EXPECT().
  121. Handle(mock.Anything, sess, msg.Frame, mock.Anything, mock.Anything).
  122. Run(func(ctx context.Context, sess *state.Session, inFrame wire.SNACFrame, r io.Reader, rw ResponseWriter) {
  123. defer wg.Done()
  124. body := wire.SNAC_0x0E_0x03_ChatUsersJoined{}
  125. assert.NoError(t, wire.UnmarshalBE(&body, r))
  126. assert.Equal(t, msg.Body, body)
  127. }).
  128. Return(nil)
  129. }
  130. // start the server connection handler in the background
  131. serverReader, clientWriter := io.Pipe()
  132. clientReader, serverWriter := io.Pipe()
  133. go func() {
  134. flapc := wire.NewFlapClient(0, nil, serverWriter)
  135. rateLimitUpdater := newMockRateLimitUpdater(t)
  136. assert.NoError(t, dispatchIncomingMessages(context.Background(), sess, flapc, serverReader, slog.Default(), router, rateLimitUpdater, wire.DefaultSNACRateLimits()))
  137. }()
  138. // send client messages
  139. flapc := wire.NewFlapClient(0, nil, clientWriter)
  140. for _, msg := range inboundMsgs {
  141. err := flapc.SendSNAC(msg.Frame, msg.Body)
  142. assert.NoError(t, err)
  143. }
  144. wg.Wait()
  145. // stop the session, which terminates the connection handler goroutine
  146. sess.Close()
  147. <-sess.Closed()
  148. // verify the connection handler sends client disconnection message before
  149. // terminating
  150. flap := wire.FLAPFrame{}
  151. assert.NoError(t, wire.UnmarshalBE(&flap, clientReader))
  152. assert.Equal(t, wire.FLAPFrameSignoff, flap.FrameType)
  153. }
  154. func defaultRateLimitClasses() wire.RateLimitClasses {
  155. return wire.NewRateLimitClasses(
  156. [5]wire.RateClass{
  157. {
  158. ID: 1,
  159. WindowSize: 80,
  160. ClearLevel: 2500,
  161. AlertLevel: 2000,
  162. LimitLevel: 1500,
  163. DisconnectLevel: 800,
  164. MaxLevel: 6000,
  165. },
  166. {
  167. ID: 2,
  168. WindowSize: 80,
  169. ClearLevel: 3000,
  170. AlertLevel: 2000,
  171. LimitLevel: 1500,
  172. DisconnectLevel: 1000,
  173. MaxLevel: 6000,
  174. },
  175. {
  176. ID: 3,
  177. WindowSize: 20,
  178. ClearLevel: 5100,
  179. AlertLevel: 5000,
  180. LimitLevel: 4000,
  181. DisconnectLevel: 3000,
  182. MaxLevel: 6000,
  183. },
  184. {
  185. ID: 4,
  186. WindowSize: 20,
  187. ClearLevel: 5500,
  188. AlertLevel: 5300,
  189. LimitLevel: 4200,
  190. DisconnectLevel: 3000,
  191. MaxLevel: 8000,
  192. },
  193. {
  194. ID: 5,
  195. WindowSize: 10,
  196. ClearLevel: 5500,
  197. AlertLevel: 5300,
  198. LimitLevel: 4200,
  199. DisconnectLevel: 3000,
  200. MaxLevel: 8000,
  201. },
  202. },
  203. )
  204. }