manager_test.go 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. package reverseproxy
  2. import (
  3. "fmt"
  4. "io"
  5. "net/http"
  6. "net/http/httptest"
  7. "os"
  8. "path/filepath"
  9. "testing"
  10. )
  11. func TestReverseProxyManagerEndToEnd(t *testing.T) {
  12. // 1. Setup mock backend server that echoes received request path and headers
  13. backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  14. w.Header().Set("X-Echo-Path", r.URL.Path)
  15. w.Header().Set("X-Echo-Query", r.URL.RawQuery)
  16. w.Header().Set("X-Echo-Host", r.Host)
  17. w.WriteHeader(http.StatusOK)
  18. _, _ = fmt.Fprintf(w, "OK: %s", r.URL.Path)
  19. }))
  20. defer backend.Close()
  21. // 2. Setup manager with temporary config file
  22. tmpDir := t.TempDir()
  23. cfgPath := filepath.Join(tmpDir, "reverse_proxy.json")
  24. mgr := NewManager(cfgPath, nil)
  25. defer mgr.Stop()
  26. // 3. Add Rule for /app1 pointing to backend/app1
  27. rule1, err := mgr.SaveRule(&Rule{
  28. Name: "App1 Rule",
  29. Enabled: true,
  30. Domains: []string{"example.com"},
  31. Path: "/app1",
  32. TargetURL: backend.URL + "/app1",
  33. StripPath: false,
  34. })
  35. if err != nil {
  36. t.Fatalf("failed to save rule: %v", err)
  37. }
  38. // 4. Test request to /app1/module1/attribute1
  39. req := httptest.NewRequest(http.MethodGet, "http://example.com/app1/module1/attribute1?page=2", nil)
  40. rec := httptest.NewRecorder()
  41. handled := mgr.HandleHTTP(rec, req)
  42. if !handled {
  43. t.Fatalf("expected request to be handled by reverse proxy")
  44. }
  45. res := rec.Result()
  46. if res.StatusCode != http.StatusOK {
  47. t.Fatalf("unexpected status code: %d", res.StatusCode)
  48. }
  49. if got := res.Header.Get("X-Echo-Path"); got != "/app1/module1/attribute1" {
  50. t.Errorf("X-Echo-Path = %q; want %q", got, "/app1/module1/attribute1")
  51. }
  52. if got := res.Header.Get("X-Echo-Query"); got != "page=2" {
  53. t.Errorf("X-Echo-Query = %q; want %q", got, "page=2")
  54. }
  55. body, _ := io.ReadAll(res.Body)
  56. if string(body) != "OK: /app1/module1/attribute1" {
  57. t.Errorf("body = %q; want %q", string(body), "OK: /app1/module1/attribute1")
  58. }
  59. // 5. Test Rule Stats
  60. updatedRule, err := mgr.GetRule(rule1.ID)
  61. if err != nil {
  62. t.Fatalf("failed to get rule: %v", err)
  63. }
  64. if updatedRule.Stats.Requests != 1 {
  65. t.Errorf("expected 1 request in stats, got %d", updatedRule.Stats.Requests)
  66. }
  67. // 6. Test Request to unmapped path /unmapped - should NOT be handled
  68. req2 := httptest.NewRequest(http.MethodGet, "http://example.com/unmapped", nil)
  69. rec2 := httptest.NewRecorder()
  70. if mgr.HandleHTTP(rec2, req2) {
  71. t.Errorf("expected unmapped request to NOT be handled by proxy")
  72. }
  73. // 7. Test StripPath option
  74. rule2, err := mgr.SaveRule(&Rule{
  75. Name: "Strip Path Rule",
  76. Enabled: true,
  77. Domains: []string{"strip.example.com"},
  78. Path: "/service",
  79. TargetURL: backend.URL,
  80. StripPath: true,
  81. })
  82. if err != nil {
  83. t.Fatalf("failed to save rule2: %v", err)
  84. }
  85. req3 := httptest.NewRequest(http.MethodGet, "http://strip.example.com/service/sub/item", nil)
  86. rec3 := httptest.NewRecorder()
  87. if !mgr.HandleHTTP(rec3, req3) {
  88. t.Fatalf("expected strip path request to be handled")
  89. }
  90. res3 := rec3.Result()
  91. if got := res3.Header.Get("X-Echo-Path"); got != "/sub/item" {
  92. t.Errorf("X-Echo-Path with stripPath = %q; want %q", got, "/sub/item")
  93. }
  94. // 8. Test HTTP to HTTPS redirect
  95. _, err = mgr.SaveRule(&Rule{
  96. Name: "HTTPS Only Rule",
  97. Enabled: true,
  98. Domains: []string{"secure.example.com"},
  99. Path: "/",
  100. TargetURL: backend.URL,
  101. Protocol: ProtocolHTTPS,
  102. HTTPToHTTPS: true,
  103. })
  104. if err != nil {
  105. t.Fatalf("failed to save https rule: %v", err)
  106. }
  107. req4 := httptest.NewRequest(http.MethodGet, "http://secure.example.com/login", nil)
  108. rec4 := httptest.NewRecorder()
  109. if !mgr.HandleHTTP(rec4, req4) {
  110. t.Fatalf("expected HTTP to HTTPS redirect to be handled")
  111. }
  112. res4 := rec4.Result()
  113. if res4.StatusCode != http.StatusMovedPermanently {
  114. t.Errorf("expected status 301, got %d", res4.StatusCode)
  115. }
  116. if loc := res4.Header.Get("Location"); loc != "https://secure.example.com/login" {
  117. t.Errorf("redirect Location = %q; want %q", loc, "https://secure.example.com/login")
  118. }
  119. // Clean up temp file
  120. _ = os.Remove(cfgPath)
  121. _ = rule2
  122. }