xray_traffic_inform_test.go 2.7 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  1. package job
  2. import (
  3. "errors"
  4. "io"
  5. "net"
  6. "testing"
  7. "time"
  8. "github.com/valyala/fasthttp"
  9. )
  10. // stallingListener accepts connections, reads whatever is sent and then never
  11. // answers and never closes — the receiver shape that used to hold the traffic
  12. // job open indefinitely (#6115).
  13. func stallingListener(t *testing.T) (addr string, release func()) {
  14. t.Helper()
  15. ln, err := net.Listen("tcp", "127.0.0.1:0")
  16. if err != nil {
  17. t.Fatalf("listen: %v", err)
  18. }
  19. done := make(chan struct{})
  20. go func() {
  21. defer close(done)
  22. for {
  23. conn, err := ln.Accept()
  24. if err != nil {
  25. return
  26. }
  27. go func(c net.Conn) {
  28. <-done
  29. _ = c.Close()
  30. }(conn)
  31. go func(c net.Conn) { _, _ = io.Copy(io.Discard, c) }(conn)
  32. }
  33. }()
  34. return ln.Addr().String(), func() {
  35. _ = ln.Close()
  36. <-done
  37. }
  38. }
  39. func TestExternalInformClient_StalledReceiverTimesOut(t *testing.T) {
  40. addr, release := stallingListener(t)
  41. defer release()
  42. request := fasthttp.AcquireRequest()
  43. defer fasthttp.ReleaseRequest(request)
  44. request.Header.SetMethod("POST")
  45. request.Header.SetContentType("application/json; charset=UTF-8")
  46. request.SetBody([]byte(`{"clientTraffics":[],"inboundTraffics":[]}`))
  47. request.SetRequestURI("http://" + addr + "/inform")
  48. request.Header.SetConnectionClose()
  49. response := fasthttp.AcquireResponse()
  50. defer fasthttp.ReleaseResponse(response)
  51. start := time.Now()
  52. err := externalInformClient.DoTimeout(request, response, externalInformTimeout)
  53. elapsed := time.Since(start)
  54. if err == nil {
  55. t.Fatal("a receiver that never answers must produce an error, not a completed request")
  56. }
  57. if !errors.Is(err, fasthttp.ErrTimeout) {
  58. t.Errorf("want a timeout error, got %v", err)
  59. }
  60. // The whole point is that the call cannot outlast the 5s poll cadence.
  61. if elapsed > externalInformTimeout+2*time.Second {
  62. t.Errorf("call took %v, must be bounded by %v", elapsed, externalInformTimeout)
  63. }
  64. }
  65. func TestExternalInformClient_HasDeadlines(t *testing.T) {
  66. if externalInformClient.ReadTimeout <= 0 || externalInformClient.WriteTimeout <= 0 {
  67. t.Fatal("the traffic-notify client must carry read and write deadlines")
  68. }
  69. if externalInformTimeout >= 5*time.Second {
  70. t.Errorf("the call budget (%v) must stay under the 5s traffic poll cadence", externalInformTimeout)
  71. }
  72. }
  73. // An idle panel posts nothing, which keeps a misbehaving receiver out of the
  74. // job's path entirely for most ticks.
  75. func TestInformSkippedWhenNothingToReport(t *testing.T) {
  76. j := &XrayTrafficJob{}
  77. done := make(chan struct{})
  78. go func() {
  79. defer close(done)
  80. j.informTrafficToExternalAPI(nil, nil)
  81. }()
  82. select {
  83. case <-done:
  84. case <-time.After(2 * time.Second):
  85. t.Fatal("an empty payload must return before touching settings or the network")
  86. }
  87. }