router_test.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530
  1. // Copyright 2013 Julien Schmidt. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be found
  3. // in the LICENSE file.
  4. package httprouter
  5. import (
  6. "errors"
  7. "fmt"
  8. "net/http"
  9. "net/http/httptest"
  10. "reflect"
  11. "testing"
  12. )
  13. type mockResponseWriter struct{}
  14. func (m *mockResponseWriter) Header() (h http.Header) {
  15. return http.Header{}
  16. }
  17. func (m *mockResponseWriter) Write(p []byte) (n int, err error) {
  18. return len(p), nil
  19. }
  20. func (m *mockResponseWriter) WriteString(s string) (n int, err error) {
  21. return len(s), nil
  22. }
  23. func (m *mockResponseWriter) WriteHeader(int) {}
  24. func TestParams(t *testing.T) {
  25. ps := Params{
  26. Param{"param1", "value1"},
  27. Param{"param2", "value2"},
  28. Param{"param3", "value3"},
  29. }
  30. for i := range ps {
  31. if val := ps.ByName(ps[i].Key); val != ps[i].Value {
  32. t.Errorf("Wrong value for %s: Got %s; Want %s", ps[i].Key, val, ps[i].Value)
  33. }
  34. }
  35. if val := ps.ByName("noKey"); val != "" {
  36. t.Errorf("Expected empty string for not found key; got: %s", val)
  37. }
  38. }
  39. func TestRouter(t *testing.T) {
  40. router := New()
  41. routed := false
  42. router.Handle("GET", "/user/:name", func(w http.ResponseWriter, r *http.Request, ps Params) {
  43. routed = true
  44. want := Params{Param{"name", "gopher"}}
  45. if !reflect.DeepEqual(ps, want) {
  46. t.Fatalf("wrong wildcard values: want %v, got %v", want, ps)
  47. }
  48. })
  49. w := new(mockResponseWriter)
  50. req, _ := http.NewRequest("GET", "/user/gopher", nil)
  51. router.ServeHTTP(w, req)
  52. if !routed {
  53. t.Fatal("routing failed")
  54. }
  55. }
  56. type handlerStruct struct {
  57. handled *bool
  58. }
  59. func (h handlerStruct) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  60. *h.handled = true
  61. }
  62. func TestRouterAPI(t *testing.T) {
  63. var get, head, options, post, put, patch, delete, handler, handlerFunc bool
  64. httpHandler := handlerStruct{&handler}
  65. router := New()
  66. router.GET("/GET", func(w http.ResponseWriter, r *http.Request, _ Params) {
  67. get = true
  68. })
  69. router.HEAD("/GET", func(w http.ResponseWriter, r *http.Request, _ Params) {
  70. head = true
  71. })
  72. router.OPTIONS("/GET", func(w http.ResponseWriter, r *http.Request, _ Params) {
  73. options = true
  74. })
  75. router.POST("/POST", func(w http.ResponseWriter, r *http.Request, _ Params) {
  76. post = true
  77. })
  78. router.PUT("/PUT", func(w http.ResponseWriter, r *http.Request, _ Params) {
  79. put = true
  80. })
  81. router.PATCH("/PATCH", func(w http.ResponseWriter, r *http.Request, _ Params) {
  82. patch = true
  83. })
  84. router.DELETE("/DELETE", func(w http.ResponseWriter, r *http.Request, _ Params) {
  85. delete = true
  86. })
  87. router.Handler("GET", "/Handler", httpHandler)
  88. router.HandlerFunc("GET", "/HandlerFunc", func(w http.ResponseWriter, r *http.Request) {
  89. handlerFunc = true
  90. })
  91. w := new(mockResponseWriter)
  92. r, _ := http.NewRequest("GET", "/GET", nil)
  93. router.ServeHTTP(w, r)
  94. if !get {
  95. t.Error("routing GET failed")
  96. }
  97. r, _ = http.NewRequest("HEAD", "/GET", nil)
  98. router.ServeHTTP(w, r)
  99. if !head {
  100. t.Error("routing HEAD failed")
  101. }
  102. r, _ = http.NewRequest("OPTIONS", "/GET", nil)
  103. router.ServeHTTP(w, r)
  104. if !options {
  105. t.Error("routing OPTIONS failed")
  106. }
  107. r, _ = http.NewRequest("POST", "/POST", nil)
  108. router.ServeHTTP(w, r)
  109. if !post {
  110. t.Error("routing POST failed")
  111. }
  112. r, _ = http.NewRequest("PUT", "/PUT", nil)
  113. router.ServeHTTP(w, r)
  114. if !put {
  115. t.Error("routing PUT failed")
  116. }
  117. r, _ = http.NewRequest("PATCH", "/PATCH", nil)
  118. router.ServeHTTP(w, r)
  119. if !patch {
  120. t.Error("routing PATCH failed")
  121. }
  122. r, _ = http.NewRequest("DELETE", "/DELETE", nil)
  123. router.ServeHTTP(w, r)
  124. if !delete {
  125. t.Error("routing DELETE failed")
  126. }
  127. r, _ = http.NewRequest("GET", "/Handler", nil)
  128. router.ServeHTTP(w, r)
  129. if !handler {
  130. t.Error("routing Handler failed")
  131. }
  132. r, _ = http.NewRequest("GET", "/HandlerFunc", nil)
  133. router.ServeHTTP(w, r)
  134. if !handlerFunc {
  135. t.Error("routing HandlerFunc failed")
  136. }
  137. }
  138. func TestRouterRoot(t *testing.T) {
  139. router := New()
  140. recv := catchPanic(func() {
  141. router.GET("noSlashRoot", nil)
  142. })
  143. if recv == nil {
  144. t.Fatal("registering path not beginning with '/' did not panic")
  145. }
  146. }
  147. func TestRouterChaining(t *testing.T) {
  148. router1 := New()
  149. router2 := New()
  150. router1.NotFound = router2
  151. fooHit := false
  152. router1.POST("/foo", func(w http.ResponseWriter, req *http.Request, _ Params) {
  153. fooHit = true
  154. w.WriteHeader(http.StatusOK)
  155. })
  156. barHit := false
  157. router2.POST("/bar", func(w http.ResponseWriter, req *http.Request, _ Params) {
  158. barHit = true
  159. w.WriteHeader(http.StatusOK)
  160. })
  161. r, _ := http.NewRequest("POST", "/foo", nil)
  162. w := httptest.NewRecorder()
  163. router1.ServeHTTP(w, r)
  164. if !(w.Code == http.StatusOK && fooHit) {
  165. t.Errorf("Regular routing failed with router chaining.")
  166. t.FailNow()
  167. }
  168. r, _ = http.NewRequest("POST", "/bar", nil)
  169. w = httptest.NewRecorder()
  170. router1.ServeHTTP(w, r)
  171. if !(w.Code == http.StatusOK && barHit) {
  172. t.Errorf("Chained routing failed with router chaining.")
  173. t.FailNow()
  174. }
  175. r, _ = http.NewRequest("POST", "/qax", nil)
  176. w = httptest.NewRecorder()
  177. router1.ServeHTTP(w, r)
  178. if !(w.Code == http.StatusNotFound) {
  179. t.Errorf("NotFound behavior failed with router chaining.")
  180. t.FailNow()
  181. }
  182. }
  183. func TestRouterOPTIONS(t *testing.T) {
  184. handlerFunc := func(_ http.ResponseWriter, _ *http.Request, _ Params) {}
  185. router := New()
  186. router.POST("/path", handlerFunc)
  187. // test not allowed
  188. // * (server)
  189. r, _ := http.NewRequest("OPTIONS", "*", nil)
  190. w := httptest.NewRecorder()
  191. router.ServeHTTP(w, r)
  192. if !(w.Code == http.StatusOK) {
  193. t.Errorf("OPTIONS handling failed: Code=%d, Header=%v", w.Code, w.Header())
  194. } else if allow := w.Header().Get("Allow"); allow != "POST, OPTIONS" {
  195. t.Error("unexpected Allow header value: " + allow)
  196. }
  197. // path
  198. r, _ = http.NewRequest("OPTIONS", "/path", nil)
  199. w = httptest.NewRecorder()
  200. router.ServeHTTP(w, r)
  201. if !(w.Code == http.StatusOK) {
  202. t.Errorf("OPTIONS handling failed: Code=%d, Header=%v", w.Code, w.Header())
  203. } else if allow := w.Header().Get("Allow"); allow != "POST, OPTIONS" {
  204. t.Error("unexpected Allow header value: " + allow)
  205. }
  206. r, _ = http.NewRequest("OPTIONS", "/doesnotexist", nil)
  207. w = httptest.NewRecorder()
  208. router.ServeHTTP(w, r)
  209. if !(w.Code == http.StatusNotFound) {
  210. t.Errorf("OPTIONS handling failed: Code=%d, Header=%v", w.Code, w.Header())
  211. }
  212. // add another method
  213. router.GET("/path", handlerFunc)
  214. // test again
  215. // * (server)
  216. r, _ = http.NewRequest("OPTIONS", "*", nil)
  217. w = httptest.NewRecorder()
  218. router.ServeHTTP(w, r)
  219. if !(w.Code == http.StatusOK) {
  220. t.Errorf("OPTIONS handling failed: Code=%d, Header=%v", w.Code, w.Header())
  221. } else if allow := w.Header().Get("Allow"); allow != "POST, GET, OPTIONS" && allow != "GET, POST, OPTIONS" {
  222. t.Error("unexpected Allow header value: " + allow)
  223. }
  224. // path
  225. r, _ = http.NewRequest("OPTIONS", "/path", nil)
  226. w = httptest.NewRecorder()
  227. router.ServeHTTP(w, r)
  228. if !(w.Code == http.StatusOK) {
  229. t.Errorf("OPTIONS handling failed: Code=%d, Header=%v", w.Code, w.Header())
  230. } else if allow := w.Header().Get("Allow"); allow != "POST, GET, OPTIONS" && allow != "GET, POST, OPTIONS" {
  231. t.Error("unexpected Allow header value: " + allow)
  232. }
  233. // custom handler
  234. var custom bool
  235. router.OPTIONS("/path", func(w http.ResponseWriter, r *http.Request, _ Params) {
  236. custom = true
  237. })
  238. // test again
  239. // * (server)
  240. r, _ = http.NewRequest("OPTIONS", "*", nil)
  241. w = httptest.NewRecorder()
  242. router.ServeHTTP(w, r)
  243. if !(w.Code == http.StatusOK) {
  244. t.Errorf("OPTIONS handling failed: Code=%d, Header=%v", w.Code, w.Header())
  245. } else if allow := w.Header().Get("Allow"); allow != "POST, GET, OPTIONS" && allow != "GET, POST, OPTIONS" {
  246. t.Error("unexpected Allow header value: " + allow)
  247. }
  248. if custom {
  249. t.Error("custom handler called on *")
  250. }
  251. // path
  252. r, _ = http.NewRequest("OPTIONS", "/path", nil)
  253. w = httptest.NewRecorder()
  254. router.ServeHTTP(w, r)
  255. if !(w.Code == http.StatusOK) {
  256. t.Errorf("OPTIONS handling failed: Code=%d, Header=%v", w.Code, w.Header())
  257. }
  258. if !custom {
  259. t.Error("custom handler not called")
  260. }
  261. }
  262. func TestRouterNotAllowed(t *testing.T) {
  263. handlerFunc := func(_ http.ResponseWriter, _ *http.Request, _ Params) {}
  264. router := New()
  265. router.POST("/path", handlerFunc)
  266. // test not allowed
  267. r, _ := http.NewRequest("GET", "/path", nil)
  268. w := httptest.NewRecorder()
  269. router.ServeHTTP(w, r)
  270. if !(w.Code == http.StatusMethodNotAllowed) {
  271. t.Errorf("NotAllowed handling failed: Code=%d, Header=%v", w.Code, w.Header())
  272. } else if allow := w.Header().Get("Allow"); allow != "POST, OPTIONS" {
  273. t.Error("unexpected Allow header value: " + allow)
  274. }
  275. // add another method
  276. router.DELETE("/path", handlerFunc)
  277. router.OPTIONS("/path", handlerFunc) // must be ignored
  278. // test again
  279. r, _ = http.NewRequest("GET", "/path", nil)
  280. w = httptest.NewRecorder()
  281. router.ServeHTTP(w, r)
  282. if !(w.Code == http.StatusMethodNotAllowed) {
  283. t.Errorf("NotAllowed handling failed: Code=%d, Header=%v", w.Code, w.Header())
  284. } else if allow := w.Header().Get("Allow"); allow != "POST, DELETE, OPTIONS" && allow != "DELETE, POST, OPTIONS" {
  285. t.Error("unexpected Allow header value: " + allow)
  286. }
  287. // test custom handler
  288. w = httptest.NewRecorder()
  289. responseText := "custom method"
  290. router.MethodNotAllowed = http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
  291. w.WriteHeader(http.StatusTeapot)
  292. w.Write([]byte(responseText))
  293. })
  294. router.ServeHTTP(w, r)
  295. if got := w.Body.String(); !(got == responseText) {
  296. t.Errorf("unexpected response got %q want %q", got, responseText)
  297. }
  298. if w.Code != http.StatusTeapot {
  299. t.Errorf("unexpected response code %d want %d", w.Code, http.StatusTeapot)
  300. }
  301. if allow := w.Header().Get("Allow"); allow != "POST, DELETE, OPTIONS" && allow != "DELETE, POST, OPTIONS" {
  302. t.Error("unexpected Allow header value: " + allow)
  303. }
  304. }
  305. func TestRouterNotFound(t *testing.T) {
  306. handlerFunc := func(_ http.ResponseWriter, _ *http.Request, _ Params) {}
  307. router := New()
  308. router.GET("/path", handlerFunc)
  309. router.GET("/dir/", handlerFunc)
  310. router.GET("/", handlerFunc)
  311. testRoutes := []struct {
  312. route string
  313. code int
  314. header string
  315. }{
  316. {"/path/", 301, "map[Location:[/path]]"}, // TSR -/
  317. {"/dir", 301, "map[Location:[/dir/]]"}, // TSR +/
  318. {"", 301, "map[Location:[/]]"}, // TSR +/
  319. {"/PATH", 301, "map[Location:[/path]]"}, // Fixed Case
  320. {"/DIR/", 301, "map[Location:[/dir/]]"}, // Fixed Case
  321. {"/PATH/", 301, "map[Location:[/path]]"}, // Fixed Case -/
  322. {"/DIR", 301, "map[Location:[/dir/]]"}, // Fixed Case +/
  323. {"/../path", 301, "map[Location:[/path]]"}, // CleanPath
  324. {"/nope", 404, ""}, // NotFound
  325. }
  326. for _, tr := range testRoutes {
  327. r, _ := http.NewRequest("GET", tr.route, nil)
  328. w := httptest.NewRecorder()
  329. router.ServeHTTP(w, r)
  330. if !(w.Code == tr.code && (w.Code == 404 || fmt.Sprint(w.Header()) == tr.header)) {
  331. t.Errorf("NotFound handling route %s failed: Code=%d, Header=%v", tr.route, w.Code, w.Header())
  332. }
  333. }
  334. // Test custom not found handler
  335. var notFound bool
  336. router.NotFound = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
  337. rw.WriteHeader(404)
  338. notFound = true
  339. })
  340. r, _ := http.NewRequest("GET", "/nope", nil)
  341. w := httptest.NewRecorder()
  342. router.ServeHTTP(w, r)
  343. if !(w.Code == 404 && notFound == true) {
  344. t.Errorf("Custom NotFound handler failed: Code=%d, Header=%v", w.Code, w.Header())
  345. }
  346. // Test other method than GET (want 307 instead of 301)
  347. router.PATCH("/path", handlerFunc)
  348. r, _ = http.NewRequest("PATCH", "/path/", nil)
  349. w = httptest.NewRecorder()
  350. router.ServeHTTP(w, r)
  351. if !(w.Code == 307 && fmt.Sprint(w.Header()) == "map[Location:[/path]]") {
  352. t.Errorf("Custom NotFound handler failed: Code=%d, Header=%v", w.Code, w.Header())
  353. }
  354. // Test special case where no node for the prefix "/" exists
  355. router = New()
  356. router.GET("/a", handlerFunc)
  357. r, _ = http.NewRequest("GET", "/", nil)
  358. w = httptest.NewRecorder()
  359. router.ServeHTTP(w, r)
  360. if !(w.Code == 404) {
  361. t.Errorf("NotFound handling route / failed: Code=%d", w.Code)
  362. }
  363. }
  364. func TestRouterPanicHandler(t *testing.T) {
  365. router := New()
  366. panicHandled := false
  367. router.PanicHandler = func(rw http.ResponseWriter, r *http.Request, p interface{}) {
  368. panicHandled = true
  369. }
  370. router.Handle("PUT", "/user/:name", func(_ http.ResponseWriter, _ *http.Request, _ Params) {
  371. panic("oops!")
  372. })
  373. w := new(mockResponseWriter)
  374. req, _ := http.NewRequest("PUT", "/user/gopher", nil)
  375. defer func() {
  376. if rcv := recover(); rcv != nil {
  377. t.Fatal("handling panic failed")
  378. }
  379. }()
  380. router.ServeHTTP(w, req)
  381. if !panicHandled {
  382. t.Fatal("simulating failed")
  383. }
  384. }
  385. func TestRouterLookup(t *testing.T) {
  386. routed := false
  387. wantHandle := func(_ http.ResponseWriter, _ *http.Request, _ Params) {
  388. routed = true
  389. }
  390. wantParams := Params{Param{"name", "gopher"}}
  391. router := New()
  392. // try empty router first
  393. handle, _, tsr := router.Lookup("GET", "/nope")
  394. if handle != nil {
  395. t.Fatalf("Got handle for unregistered pattern: %v", handle)
  396. }
  397. if tsr {
  398. t.Error("Got wrong TSR recommendation!")
  399. }
  400. // insert route and try again
  401. router.GET("/user/:name", wantHandle)
  402. handle, params, tsr := router.Lookup("GET", "/user/gopher")
  403. if handle == nil {
  404. t.Fatal("Got no handle!")
  405. } else {
  406. handle(nil, nil, nil)
  407. if !routed {
  408. t.Fatal("Routing failed!")
  409. }
  410. }
  411. if !reflect.DeepEqual(params, wantParams) {
  412. t.Fatalf("Wrong parameter values: want %v, got %v", wantParams, params)
  413. }
  414. handle, _, tsr = router.Lookup("GET", "/user/gopher/")
  415. if handle != nil {
  416. t.Fatalf("Got handle for unregistered pattern: %v", handle)
  417. }
  418. if !tsr {
  419. t.Error("Got no TSR recommendation!")
  420. }
  421. handle, _, tsr = router.Lookup("GET", "/nope")
  422. if handle != nil {
  423. t.Fatalf("Got handle for unregistered pattern: %v", handle)
  424. }
  425. if tsr {
  426. t.Error("Got wrong TSR recommendation!")
  427. }
  428. }
  429. type mockFileSystem struct {
  430. opened bool
  431. }
  432. func (mfs *mockFileSystem) Open(name string) (http.File, error) {
  433. mfs.opened = true
  434. return nil, errors.New("this is just a mock")
  435. }
  436. func TestRouterServeFiles(t *testing.T) {
  437. router := New()
  438. mfs := &mockFileSystem{}
  439. recv := catchPanic(func() {
  440. router.ServeFiles("/noFilepath", mfs)
  441. })
  442. if recv == nil {
  443. t.Fatal("registering path not ending with '*filepath' did not panic")
  444. }
  445. router.ServeFiles("/*filepath", mfs)
  446. w := new(mockResponseWriter)
  447. r, _ := http.NewRequest("GET", "/favicon.ico", nil)
  448. router.ServeHTTP(w, r)
  449. if !mfs.opened {
  450. t.Error("serving file failed")
  451. }
  452. }