debug_test.go 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. // Copyright (c) 2021 Tailscale Inc & AUTHORS All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. package tsweb
  5. import (
  6. "fmt"
  7. "io"
  8. "net/http"
  9. "net/http/httptest"
  10. "runtime"
  11. "strings"
  12. "testing"
  13. )
  14. func TestDebugger(t *testing.T) {
  15. mux := http.NewServeMux()
  16. dbg1 := Debugger(mux)
  17. if dbg1 == nil {
  18. t.Fatal("didn't get a debugger from mux")
  19. }
  20. dbg2 := Debugger(mux)
  21. if dbg2 != dbg1 {
  22. t.Fatal("Debugger returned different debuggers for the same mux")
  23. }
  24. t.Run("cpu_pprof", func(t *testing.T) {
  25. if testing.Short() {
  26. t.Skip("skipping second long test")
  27. }
  28. switch runtime.GOOS {
  29. case "linux", "darwin":
  30. default:
  31. t.Skipf("skipping test on %v", runtime.GOOS)
  32. }
  33. req := httptest.NewRequest("GET", "/debug/pprof/profile?seconds=1", nil)
  34. req.RemoteAddr = "100.101.102.103:1234"
  35. rec := httptest.NewRecorder()
  36. mux.ServeHTTP(rec, req)
  37. res := rec.Result()
  38. if res.StatusCode != 200 {
  39. t.Errorf("unexpected %v", res.Status)
  40. }
  41. })
  42. }
  43. func get(m http.Handler, path, srcIP string) (int, string) {
  44. req := httptest.NewRequest("GET", path, nil)
  45. req.RemoteAddr = srcIP + ":1234"
  46. rec := httptest.NewRecorder()
  47. m.ServeHTTP(rec, req)
  48. return rec.Result().StatusCode, rec.Body.String()
  49. }
  50. const (
  51. tsIP = "100.100.100.100"
  52. pubIP = "8.8.8.8"
  53. )
  54. func TestDebuggerKV(t *testing.T) {
  55. mux := http.NewServeMux()
  56. dbg := Debugger(mux)
  57. dbg.KV("Donuts", 42)
  58. dbg.KV("Secret code", "hunter2")
  59. val := "red"
  60. dbg.KVFunc("Condition", func() interface{} { return val })
  61. code, _ := get(mux, "/debug/", pubIP)
  62. if code != 403 {
  63. t.Fatalf("debug access wasn't denied, got %v", code)
  64. }
  65. code, body := get(mux, "/debug/", tsIP)
  66. if code != 200 {
  67. t.Fatalf("debug access failed, got %v", code)
  68. }
  69. for _, want := range []string{"Donuts", "42", "Secret code", "hunter2", "Condition", "red"} {
  70. if !strings.Contains(body, want) {
  71. t.Errorf("want %q in output, not found", want)
  72. }
  73. }
  74. val = "green"
  75. code, body = get(mux, "/debug/", tsIP)
  76. if code != 200 {
  77. t.Fatalf("debug access failed, got %v", code)
  78. }
  79. for _, want := range []string{"Condition", "green"} {
  80. if !strings.Contains(body, want) {
  81. t.Errorf("want %q in output, not found", want)
  82. }
  83. }
  84. }
  85. func TestDebuggerURL(t *testing.T) {
  86. mux := http.NewServeMux()
  87. dbg := Debugger(mux)
  88. dbg.URL("https://www.tailscale.com", "Homepage")
  89. code, body := get(mux, "/debug/", tsIP)
  90. if code != 200 {
  91. t.Fatalf("debug access failed, got %v", code)
  92. }
  93. for _, want := range []string{"https://www.tailscale.com", "Homepage"} {
  94. if !strings.Contains(body, want) {
  95. t.Errorf("want %q in output, not found", want)
  96. }
  97. }
  98. }
  99. func TestDebuggerSection(t *testing.T) {
  100. mux := http.NewServeMux()
  101. dbg := Debugger(mux)
  102. dbg.Section(func(w io.Writer, r *http.Request) {
  103. fmt.Fprintf(w, "Test output %v", r.RemoteAddr)
  104. })
  105. code, body := get(mux, "/debug/", tsIP)
  106. if code != 200 {
  107. t.Fatalf("debug access failed, got %v", code)
  108. }
  109. want := `Test output 100.100.100.100:1234`
  110. if !strings.Contains(body, want) {
  111. t.Errorf("want %q in output, not found", want)
  112. }
  113. }
  114. func TestDebuggerHandle(t *testing.T) {
  115. mux := http.NewServeMux()
  116. dbg := Debugger(mux)
  117. dbg.Handle("check", "Consistency check", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  118. fmt.Fprintf(w, "Test output %v", r.RemoteAddr)
  119. }))
  120. code, body := get(mux, "/debug/", tsIP)
  121. if code != 200 {
  122. t.Fatalf("debug access failed, got %v", code)
  123. }
  124. for _, want := range []string{"/debug/check", "Consistency check"} {
  125. if !strings.Contains(body, want) {
  126. t.Errorf("want %q in output, not found", want)
  127. }
  128. }
  129. code, _ = get(mux, "/debug/check", pubIP)
  130. if code != 403 {
  131. t.Fatal("/debug/check should be protected, but isn't")
  132. }
  133. code, body = get(mux, "/debug/check", tsIP)
  134. if code != 200 {
  135. t.Fatal("/debug/check denied debug access")
  136. }
  137. want := "Test output " + tsIP
  138. if !strings.Contains(body, want) {
  139. t.Errorf("want %q in output, not found", want)
  140. }
  141. }
  142. func ExampleDebugHandler_Handle() {
  143. mux := http.NewServeMux()
  144. dbg := Debugger(mux)
  145. // Registers /debug/flushcache with the given handler, and adds a
  146. // link to /debug/ with the description "Flush caches".
  147. dbg.Handle("flushcache", "Flush caches", http.HandlerFunc(http.NotFound))
  148. }
  149. func ExampleDebugHandler_KV() {
  150. mux := http.NewServeMux()
  151. dbg := Debugger(mux)
  152. // Adds two list items to /debug/, showing that the condition is
  153. // red and there are 42 donuts.
  154. dbg.KV("Condition", "red")
  155. dbg.KV("Donuts", 42)
  156. }
  157. func ExampleDebugHandler_KVFunc() {
  158. mux := http.NewServeMux()
  159. dbg := Debugger(mux)
  160. // Adds an count of page renders to /debug/. Note this example
  161. // isn't concurrency-safe.
  162. views := 0
  163. dbg.KVFunc("Debug pageviews", func() interface{} {
  164. views = views + 1
  165. return views
  166. })
  167. dbg.KV("Donuts", 42)
  168. }
  169. func ExampleDebugHandler_URL() {
  170. mux := http.NewServeMux()
  171. dbg := Debugger(mux)
  172. // Links to the Tailscale website from /debug/.
  173. dbg.URL("https://www.tailscale.com", "Homepage")
  174. }
  175. func ExampleDebugHandler_Section() {
  176. mux := http.NewServeMux()
  177. dbg := Debugger(mux)
  178. // Adds a section to /debug/ that dumps the HTTP request of the
  179. // visitor.
  180. dbg.Section(func(w io.Writer, r *http.Request) {
  181. io.WriteString(w, "<h3>Dump of your HTTP request</h3>")
  182. fmt.Fprintf(w, "<code>%#v</code>", r)
  183. })
  184. }