mux_test.go 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804
  1. // Copyright 2012 The Gorilla 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 mux
  5. import (
  6. "fmt"
  7. "net/http"
  8. "testing"
  9. "github.com/coreos/etcd/third_party/github.com/gorilla/context"
  10. )
  11. type routeTest struct {
  12. title string // title of the test
  13. route *Route // the route being tested
  14. request *http.Request // a request to test the route
  15. vars map[string]string // the expected vars of the match
  16. host string // the expected host of the match
  17. path string // the expected path of the match
  18. shouldMatch bool // whether the request is expected to match the route at all
  19. }
  20. func TestHost(t *testing.T) {
  21. // newRequestHost a new request with a method, url, and host header
  22. newRequestHost := func(method, url, host string) *http.Request {
  23. req, err := http.NewRequest(method, url, nil)
  24. if err != nil {
  25. panic(err)
  26. }
  27. req.Host = host
  28. return req
  29. }
  30. tests := []routeTest{
  31. {
  32. title: "Host route match",
  33. route: new(Route).Host("aaa.bbb.ccc"),
  34. request: newRequest("GET", "http://aaa.bbb.ccc/111/222/333"),
  35. vars: map[string]string{},
  36. host: "aaa.bbb.ccc",
  37. path: "",
  38. shouldMatch: true,
  39. },
  40. {
  41. title: "Host route, wrong host in request URL",
  42. route: new(Route).Host("aaa.bbb.ccc"),
  43. request: newRequest("GET", "http://aaa.222.ccc/111/222/333"),
  44. vars: map[string]string{},
  45. host: "aaa.bbb.ccc",
  46. path: "",
  47. shouldMatch: false,
  48. },
  49. {
  50. title: "Host route with port, match",
  51. route: new(Route).Host("aaa.bbb.ccc:1234"),
  52. request: newRequest("GET", "http://aaa.bbb.ccc:1234/111/222/333"),
  53. vars: map[string]string{},
  54. host: "aaa.bbb.ccc:1234",
  55. path: "",
  56. shouldMatch: true,
  57. },
  58. {
  59. title: "Host route with port, wrong port in request URL",
  60. route: new(Route).Host("aaa.bbb.ccc:1234"),
  61. request: newRequest("GET", "http://aaa.bbb.ccc:9999/111/222/333"),
  62. vars: map[string]string{},
  63. host: "aaa.bbb.ccc:1234",
  64. path: "",
  65. shouldMatch: false,
  66. },
  67. {
  68. title: "Host route, match with host in request header",
  69. route: new(Route).Host("aaa.bbb.ccc"),
  70. request: newRequestHost("GET", "/111/222/333", "aaa.bbb.ccc"),
  71. vars: map[string]string{},
  72. host: "aaa.bbb.ccc",
  73. path: "",
  74. shouldMatch: true,
  75. },
  76. {
  77. title: "Host route, wrong host in request header",
  78. route: new(Route).Host("aaa.bbb.ccc"),
  79. request: newRequestHost("GET", "/111/222/333", "aaa.222.ccc"),
  80. vars: map[string]string{},
  81. host: "aaa.bbb.ccc",
  82. path: "",
  83. shouldMatch: false,
  84. },
  85. // BUG {new(Route).Host("aaa.bbb.ccc:1234"), newRequestHost("GET", "/111/222/333", "aaa.bbb.ccc:1234"), map[string]string{}, "aaa.bbb.ccc:1234", "", true},
  86. {
  87. title: "Host route with port, wrong host in request header",
  88. route: new(Route).Host("aaa.bbb.ccc:1234"),
  89. request: newRequestHost("GET", "/111/222/333", "aaa.bbb.ccc:9999"),
  90. vars: map[string]string{},
  91. host: "aaa.bbb.ccc:1234",
  92. path: "",
  93. shouldMatch: false,
  94. },
  95. {
  96. title: "Host route with pattern, match",
  97. route: new(Route).Host("aaa.{v1:[a-z]{3}}.ccc"),
  98. request: newRequest("GET", "http://aaa.bbb.ccc/111/222/333"),
  99. vars: map[string]string{"v1": "bbb"},
  100. host: "aaa.bbb.ccc",
  101. path: "",
  102. shouldMatch: true,
  103. },
  104. {
  105. title: "Host route with pattern, wrong host in request URL",
  106. route: new(Route).Host("aaa.{v1:[a-z]{3}}.ccc"),
  107. request: newRequest("GET", "http://aaa.222.ccc/111/222/333"),
  108. vars: map[string]string{"v1": "bbb"},
  109. host: "aaa.bbb.ccc",
  110. path: "",
  111. shouldMatch: false,
  112. },
  113. {
  114. title: "Host route with multiple patterns, match",
  115. route: new(Route).Host("{v1:[a-z]{3}}.{v2:[a-z]{3}}.{v3:[a-z]{3}}"),
  116. request: newRequest("GET", "http://aaa.bbb.ccc/111/222/333"),
  117. vars: map[string]string{"v1": "aaa", "v2": "bbb", "v3": "ccc"},
  118. host: "aaa.bbb.ccc",
  119. path: "",
  120. shouldMatch: true,
  121. },
  122. {
  123. title: "Host route with multiple patterns, wrong host in request URL",
  124. route: new(Route).Host("{v1:[a-z]{3}}.{v2:[a-z]{3}}.{v3:[a-z]{3}}"),
  125. request: newRequest("GET", "http://aaa.222.ccc/111/222/333"),
  126. vars: map[string]string{"v1": "aaa", "v2": "bbb", "v3": "ccc"},
  127. host: "aaa.bbb.ccc",
  128. path: "",
  129. shouldMatch: false,
  130. },
  131. }
  132. for _, test := range tests {
  133. testRoute(t, test)
  134. }
  135. }
  136. func TestPath(t *testing.T) {
  137. tests := []routeTest{
  138. {
  139. title: "Path route, match",
  140. route: new(Route).Path("/111/222/333"),
  141. request: newRequest("GET", "http://localhost/111/222/333"),
  142. vars: map[string]string{},
  143. host: "",
  144. path: "/111/222/333",
  145. shouldMatch: true,
  146. },
  147. {
  148. title: "Path route, wrong path in request in request URL",
  149. route: new(Route).Path("/111/222/333"),
  150. request: newRequest("GET", "http://localhost/1/2/3"),
  151. vars: map[string]string{},
  152. host: "",
  153. path: "/111/222/333",
  154. shouldMatch: false,
  155. },
  156. {
  157. title: "Path route with pattern, match",
  158. route: new(Route).Path("/111/{v1:[0-9]{3}}/333"),
  159. request: newRequest("GET", "http://localhost/111/222/333"),
  160. vars: map[string]string{"v1": "222"},
  161. host: "",
  162. path: "/111/222/333",
  163. shouldMatch: true,
  164. },
  165. {
  166. title: "Path route with pattern, URL in request does not match",
  167. route: new(Route).Path("/111/{v1:[0-9]{3}}/333"),
  168. request: newRequest("GET", "http://localhost/111/aaa/333"),
  169. vars: map[string]string{"v1": "222"},
  170. host: "",
  171. path: "/111/222/333",
  172. shouldMatch: false,
  173. },
  174. {
  175. title: "Path route with multiple patterns, match",
  176. route: new(Route).Path("/{v1:[0-9]{3}}/{v2:[0-9]{3}}/{v3:[0-9]{3}}"),
  177. request: newRequest("GET", "http://localhost/111/222/333"),
  178. vars: map[string]string{"v1": "111", "v2": "222", "v3": "333"},
  179. host: "",
  180. path: "/111/222/333",
  181. shouldMatch: true,
  182. },
  183. {
  184. title: "Path route with multiple patterns, URL in request does not match",
  185. route: new(Route).Path("/{v1:[0-9]{3}}/{v2:[0-9]{3}}/{v3:[0-9]{3}}"),
  186. request: newRequest("GET", "http://localhost/111/aaa/333"),
  187. vars: map[string]string{"v1": "111", "v2": "222", "v3": "333"},
  188. host: "",
  189. path: "/111/222/333",
  190. shouldMatch: false,
  191. },
  192. }
  193. for _, test := range tests {
  194. testRoute(t, test)
  195. }
  196. }
  197. func TestPathPrefix(t *testing.T) {
  198. tests := []routeTest{
  199. {
  200. title: "PathPrefix route, match",
  201. route: new(Route).PathPrefix("/111"),
  202. request: newRequest("GET", "http://localhost/111/222/333"),
  203. vars: map[string]string{},
  204. host: "",
  205. path: "/111",
  206. shouldMatch: true,
  207. },
  208. {
  209. title: "PathPrefix route, URL prefix in request does not match",
  210. route: new(Route).PathPrefix("/111"),
  211. request: newRequest("GET", "http://localhost/1/2/3"),
  212. vars: map[string]string{},
  213. host: "",
  214. path: "/111",
  215. shouldMatch: false,
  216. },
  217. {
  218. title: "PathPrefix route with pattern, match",
  219. route: new(Route).PathPrefix("/111/{v1:[0-9]{3}}"),
  220. request: newRequest("GET", "http://localhost/111/222/333"),
  221. vars: map[string]string{"v1": "222"},
  222. host: "",
  223. path: "/111/222",
  224. shouldMatch: true,
  225. },
  226. {
  227. title: "PathPrefix route with pattern, URL prefix in request does not match",
  228. route: new(Route).PathPrefix("/111/{v1:[0-9]{3}}"),
  229. request: newRequest("GET", "http://localhost/111/aaa/333"),
  230. vars: map[string]string{"v1": "222"},
  231. host: "",
  232. path: "/111/222",
  233. shouldMatch: false,
  234. },
  235. {
  236. title: "PathPrefix route with multiple patterns, match",
  237. route: new(Route).PathPrefix("/{v1:[0-9]{3}}/{v2:[0-9]{3}}"),
  238. request: newRequest("GET", "http://localhost/111/222/333"),
  239. vars: map[string]string{"v1": "111", "v2": "222"},
  240. host: "",
  241. path: "/111/222",
  242. shouldMatch: true,
  243. },
  244. {
  245. title: "PathPrefix route with multiple patterns, URL prefix in request does not match",
  246. route: new(Route).PathPrefix("/{v1:[0-9]{3}}/{v2:[0-9]{3}}"),
  247. request: newRequest("GET", "http://localhost/111/aaa/333"),
  248. vars: map[string]string{"v1": "111", "v2": "222"},
  249. host: "",
  250. path: "/111/222",
  251. shouldMatch: false,
  252. },
  253. }
  254. for _, test := range tests {
  255. testRoute(t, test)
  256. }
  257. }
  258. func TestHostPath(t *testing.T) {
  259. tests := []routeTest{
  260. {
  261. title: "Host and Path route, match",
  262. route: new(Route).Host("aaa.bbb.ccc").Path("/111/222/333"),
  263. request: newRequest("GET", "http://aaa.bbb.ccc/111/222/333"),
  264. vars: map[string]string{},
  265. host: "",
  266. path: "",
  267. shouldMatch: true,
  268. },
  269. {
  270. title: "Host and Path route, wrong host in request URL",
  271. route: new(Route).Host("aaa.bbb.ccc").Path("/111/222/333"),
  272. request: newRequest("GET", "http://aaa.222.ccc/111/222/333"),
  273. vars: map[string]string{},
  274. host: "",
  275. path: "",
  276. shouldMatch: false,
  277. },
  278. {
  279. title: "Host and Path route with pattern, match",
  280. route: new(Route).Host("aaa.{v1:[a-z]{3}}.ccc").Path("/111/{v2:[0-9]{3}}/333"),
  281. request: newRequest("GET", "http://aaa.bbb.ccc/111/222/333"),
  282. vars: map[string]string{"v1": "bbb", "v2": "222"},
  283. host: "aaa.bbb.ccc",
  284. path: "/111/222/333",
  285. shouldMatch: true,
  286. },
  287. {
  288. title: "Host and Path route with pattern, URL in request does not match",
  289. route: new(Route).Host("aaa.{v1:[a-z]{3}}.ccc").Path("/111/{v2:[0-9]{3}}/333"),
  290. request: newRequest("GET", "http://aaa.222.ccc/111/222/333"),
  291. vars: map[string]string{"v1": "bbb", "v2": "222"},
  292. host: "aaa.bbb.ccc",
  293. path: "/111/222/333",
  294. shouldMatch: false,
  295. },
  296. {
  297. title: "Host and Path route with multiple patterns, match",
  298. route: new(Route).Host("{v1:[a-z]{3}}.{v2:[a-z]{3}}.{v3:[a-z]{3}}").Path("/{v4:[0-9]{3}}/{v5:[0-9]{3}}/{v6:[0-9]{3}}"),
  299. request: newRequest("GET", "http://aaa.bbb.ccc/111/222/333"),
  300. vars: map[string]string{"v1": "aaa", "v2": "bbb", "v3": "ccc", "v4": "111", "v5": "222", "v6": "333"},
  301. host: "aaa.bbb.ccc",
  302. path: "/111/222/333",
  303. shouldMatch: true,
  304. },
  305. {
  306. title: "Host and Path route with multiple patterns, URL in request does not match",
  307. route: new(Route).Host("{v1:[a-z]{3}}.{v2:[a-z]{3}}.{v3:[a-z]{3}}").Path("/{v4:[0-9]{3}}/{v5:[0-9]{3}}/{v6:[0-9]{3}}"),
  308. request: newRequest("GET", "http://aaa.222.ccc/111/222/333"),
  309. vars: map[string]string{"v1": "aaa", "v2": "bbb", "v3": "ccc", "v4": "111", "v5": "222", "v6": "333"},
  310. host: "aaa.bbb.ccc",
  311. path: "/111/222/333",
  312. shouldMatch: false,
  313. },
  314. }
  315. for _, test := range tests {
  316. testRoute(t, test)
  317. }
  318. }
  319. func TestHeaders(t *testing.T) {
  320. // newRequestHeaders creates a new request with a method, url, and headers
  321. newRequestHeaders := func(method, url string, headers map[string]string) *http.Request {
  322. req, err := http.NewRequest(method, url, nil)
  323. if err != nil {
  324. panic(err)
  325. }
  326. for k, v := range headers {
  327. req.Header.Add(k, v)
  328. }
  329. return req
  330. }
  331. tests := []routeTest{
  332. {
  333. title: "Headers route, match",
  334. route: new(Route).Headers("foo", "bar", "baz", "ding"),
  335. request: newRequestHeaders("GET", "http://localhost", map[string]string{"foo": "bar", "baz": "ding"}),
  336. vars: map[string]string{},
  337. host: "",
  338. path: "",
  339. shouldMatch: true,
  340. },
  341. {
  342. title: "Headers route, bad header values",
  343. route: new(Route).Headers("foo", "bar", "baz", "ding"),
  344. request: newRequestHeaders("GET", "http://localhost", map[string]string{"foo": "bar", "baz": "dong"}),
  345. vars: map[string]string{},
  346. host: "",
  347. path: "",
  348. shouldMatch: false,
  349. },
  350. }
  351. for _, test := range tests {
  352. testRoute(t, test)
  353. }
  354. }
  355. func TestMethods(t *testing.T) {
  356. tests := []routeTest{
  357. {
  358. title: "Methods route, match GET",
  359. route: new(Route).Methods("GET", "POST"),
  360. request: newRequest("GET", "http://localhost"),
  361. vars: map[string]string{},
  362. host: "",
  363. path: "",
  364. shouldMatch: true,
  365. },
  366. {
  367. title: "Methods route, match POST",
  368. route: new(Route).Methods("GET", "POST"),
  369. request: newRequest("POST", "http://localhost"),
  370. vars: map[string]string{},
  371. host: "",
  372. path: "",
  373. shouldMatch: true,
  374. },
  375. {
  376. title: "Methods route, bad method",
  377. route: new(Route).Methods("GET", "POST"),
  378. request: newRequest("PUT", "http://localhost"),
  379. vars: map[string]string{},
  380. host: "",
  381. path: "",
  382. shouldMatch: false,
  383. },
  384. }
  385. for _, test := range tests {
  386. testRoute(t, test)
  387. }
  388. }
  389. func TestQueries(t *testing.T) {
  390. tests := []routeTest{
  391. {
  392. title: "Queries route, match",
  393. route: new(Route).Queries("foo", "bar", "baz", "ding"),
  394. request: newRequest("GET", "http://localhost?foo=bar&baz=ding"),
  395. vars: map[string]string{},
  396. host: "",
  397. path: "",
  398. shouldMatch: true,
  399. },
  400. {
  401. title: "Queries route, match with a query string",
  402. route: new(Route).Host("www.example.com").Path("/api").Queries("foo", "bar", "baz", "ding"),
  403. request: newRequest("GET", "http://www.example.com/api?foo=bar&baz=ding"),
  404. vars: map[string]string{},
  405. host: "",
  406. path: "",
  407. shouldMatch: true,
  408. },
  409. {
  410. title: "Queries route, bad query",
  411. route: new(Route).Queries("foo", "bar", "baz", "ding"),
  412. request: newRequest("GET", "http://localhost?foo=bar&baz=dong"),
  413. vars: map[string]string{},
  414. host: "",
  415. path: "",
  416. shouldMatch: false,
  417. },
  418. }
  419. for _, test := range tests {
  420. testRoute(t, test)
  421. }
  422. }
  423. func TestSchemes(t *testing.T) {
  424. tests := []routeTest{
  425. // Schemes
  426. {
  427. title: "Schemes route, match https",
  428. route: new(Route).Schemes("https", "ftp"),
  429. request: newRequest("GET", "https://localhost"),
  430. vars: map[string]string{},
  431. host: "",
  432. path: "",
  433. shouldMatch: true,
  434. },
  435. {
  436. title: "Schemes route, match ftp",
  437. route: new(Route).Schemes("https", "ftp"),
  438. request: newRequest("GET", "ftp://localhost"),
  439. vars: map[string]string{},
  440. host: "",
  441. path: "",
  442. shouldMatch: true,
  443. },
  444. {
  445. title: "Schemes route, bad scheme",
  446. route: new(Route).Schemes("https", "ftp"),
  447. request: newRequest("GET", "http://localhost"),
  448. vars: map[string]string{},
  449. host: "",
  450. path: "",
  451. shouldMatch: false,
  452. },
  453. }
  454. for _, test := range tests {
  455. testRoute(t, test)
  456. }
  457. }
  458. func TestMatcherFunc(t *testing.T) {
  459. m := func(r *http.Request, m *RouteMatch) bool {
  460. if r.URL.Host == "aaa.bbb.ccc" {
  461. return true
  462. }
  463. return false
  464. }
  465. tests := []routeTest{
  466. {
  467. title: "MatchFunc route, match",
  468. route: new(Route).MatcherFunc(m),
  469. request: newRequest("GET", "http://aaa.bbb.ccc"),
  470. vars: map[string]string{},
  471. host: "",
  472. path: "",
  473. shouldMatch: true,
  474. },
  475. {
  476. title: "MatchFunc route, non-match",
  477. route: new(Route).MatcherFunc(m),
  478. request: newRequest("GET", "http://aaa.222.ccc"),
  479. vars: map[string]string{},
  480. host: "",
  481. path: "",
  482. shouldMatch: false,
  483. },
  484. }
  485. for _, test := range tests {
  486. testRoute(t, test)
  487. }
  488. }
  489. func TestSubRouter(t *testing.T) {
  490. subrouter1 := new(Route).Host("{v1:[a-z]+}.google.com").Subrouter()
  491. subrouter2 := new(Route).PathPrefix("/foo/{v1}").Subrouter()
  492. tests := []routeTest{
  493. {
  494. route: subrouter1.Path("/{v2:[a-z]+}"),
  495. request: newRequest("GET", "http://aaa.google.com/bbb"),
  496. vars: map[string]string{"v1": "aaa", "v2": "bbb"},
  497. host: "aaa.google.com",
  498. path: "/bbb",
  499. shouldMatch: true,
  500. },
  501. {
  502. route: subrouter1.Path("/{v2:[a-z]+}"),
  503. request: newRequest("GET", "http://111.google.com/111"),
  504. vars: map[string]string{"v1": "aaa", "v2": "bbb"},
  505. host: "aaa.google.com",
  506. path: "/bbb",
  507. shouldMatch: false,
  508. },
  509. {
  510. route: subrouter2.Path("/baz/{v2}"),
  511. request: newRequest("GET", "http://localhost/foo/bar/baz/ding"),
  512. vars: map[string]string{"v1": "bar", "v2": "ding"},
  513. host: "",
  514. path: "/foo/bar/baz/ding",
  515. shouldMatch: true,
  516. },
  517. {
  518. route: subrouter2.Path("/baz/{v2}"),
  519. request: newRequest("GET", "http://localhost/foo/bar"),
  520. vars: map[string]string{"v1": "bar", "v2": "ding"},
  521. host: "",
  522. path: "/foo/bar/baz/ding",
  523. shouldMatch: false,
  524. },
  525. }
  526. for _, test := range tests {
  527. testRoute(t, test)
  528. }
  529. }
  530. func TestNamedRoutes(t *testing.T) {
  531. r1 := NewRouter()
  532. r1.NewRoute().Name("a")
  533. r1.NewRoute().Name("b")
  534. r1.NewRoute().Name("c")
  535. r2 := r1.NewRoute().Subrouter()
  536. r2.NewRoute().Name("d")
  537. r2.NewRoute().Name("e")
  538. r2.NewRoute().Name("f")
  539. r3 := r2.NewRoute().Subrouter()
  540. r3.NewRoute().Name("g")
  541. r3.NewRoute().Name("h")
  542. r3.NewRoute().Name("i")
  543. if r1.namedRoutes == nil || len(r1.namedRoutes) != 9 {
  544. t.Errorf("Expected 9 named routes, got %v", r1.namedRoutes)
  545. } else if r1.Get("i") == nil {
  546. t.Errorf("Subroute name not registered")
  547. }
  548. }
  549. func TestStrictSlash(t *testing.T) {
  550. var r *Router
  551. var req *http.Request
  552. var route *Route
  553. var match *RouteMatch
  554. var matched bool
  555. // StrictSlash should be ignored for path prefix.
  556. // So we register a route ending in slash but it doesn't attempt to add
  557. // the slash for a path not ending in slash.
  558. r = NewRouter()
  559. r.StrictSlash(true)
  560. route = r.NewRoute().PathPrefix("/static/")
  561. req, _ = http.NewRequest("GET", "http://localhost/static/logo.png", nil)
  562. match = new(RouteMatch)
  563. matched = r.Match(req, match)
  564. if !matched {
  565. t.Errorf("Should match request %q -- %v", req.URL.Path, getRouteTemplate(route))
  566. }
  567. if match.Handler != nil {
  568. t.Errorf("Should not redirect")
  569. }
  570. }
  571. // ----------------------------------------------------------------------------
  572. // Helpers
  573. // ----------------------------------------------------------------------------
  574. func getRouteTemplate(route *Route) string {
  575. host, path := "none", "none"
  576. if route.regexp != nil {
  577. if route.regexp.host != nil {
  578. host = route.regexp.host.template
  579. }
  580. if route.regexp.path != nil {
  581. path = route.regexp.path.template
  582. }
  583. }
  584. return fmt.Sprintf("Host: %v, Path: %v", host, path)
  585. }
  586. func testRoute(t *testing.T, test routeTest) {
  587. request := test.request
  588. route := test.route
  589. vars := test.vars
  590. shouldMatch := test.shouldMatch
  591. host := test.host
  592. path := test.path
  593. url := test.host + test.path
  594. var match RouteMatch
  595. ok := route.Match(request, &match)
  596. if ok != shouldMatch {
  597. msg := "Should match"
  598. if !shouldMatch {
  599. msg = "Should not match"
  600. }
  601. t.Errorf("(%v) %v:\nRoute: %#v\nRequest: %#v\nVars: %v\n", test.title, msg, route, request, vars)
  602. return
  603. }
  604. if shouldMatch {
  605. if test.vars != nil && !stringMapEqual(test.vars, match.Vars) {
  606. t.Errorf("(%v) Vars not equal: expected %v, got %v", test.title, vars, match.Vars)
  607. return
  608. }
  609. if host != "" {
  610. u, _ := test.route.URLHost(mapToPairs(match.Vars)...)
  611. if host != u.Host {
  612. t.Errorf("(%v) URLHost not equal: expected %v, got %v -- %v", test.title, host, u.Host, getRouteTemplate(route))
  613. return
  614. }
  615. }
  616. if path != "" {
  617. u, _ := route.URLPath(mapToPairs(match.Vars)...)
  618. if path != u.Path {
  619. t.Errorf("(%v) URLPath not equal: expected %v, got %v -- %v", test.title, path, u.Path, getRouteTemplate(route))
  620. return
  621. }
  622. }
  623. if url != "" {
  624. u, _ := route.URL(mapToPairs(match.Vars)...)
  625. if url != u.Host+u.Path {
  626. t.Errorf("(%v) URL not equal: expected %v, got %v -- %v", test.title, url, u.Host+u.Path, getRouteTemplate(route))
  627. return
  628. }
  629. }
  630. }
  631. }
  632. // Tests that the context is cleared or not cleared properly depending on
  633. // the configuration of the router
  634. func TestKeepContext(t *testing.T) {
  635. func1 := func(w http.ResponseWriter, r *http.Request) {}
  636. r := NewRouter()
  637. r.HandleFunc("/", func1).Name("func1")
  638. req, _ := http.NewRequest("GET", "http://localhost/", nil)
  639. context.Set(req, "t", 1)
  640. res := new(http.ResponseWriter)
  641. r.ServeHTTP(*res, req)
  642. if _, ok := context.GetOk(req, "t"); ok {
  643. t.Error("Context should have been cleared at end of request")
  644. }
  645. r.KeepContext = true
  646. req, _ = http.NewRequest("GET", "http://localhost/", nil)
  647. context.Set(req, "t", 1)
  648. r.ServeHTTP(*res, req)
  649. if _, ok := context.GetOk(req, "t"); !ok {
  650. t.Error("Context should NOT have been cleared at end of request")
  651. }
  652. }
  653. type TestA301ResponseWriter struct {
  654. hh http.Header
  655. status int
  656. }
  657. func (ho TestA301ResponseWriter) Header() http.Header {
  658. return http.Header(ho.hh)
  659. }
  660. func (ho TestA301ResponseWriter) Write(b []byte) (int, error) {
  661. return 0, nil
  662. }
  663. func (ho TestA301ResponseWriter) WriteHeader(code int) {
  664. ho.status = code
  665. }
  666. func Test301Redirect(t *testing.T) {
  667. m := make(http.Header)
  668. func1 := func(w http.ResponseWriter, r *http.Request) {}
  669. func2 := func(w http.ResponseWriter, r *http.Request) {}
  670. r := NewRouter()
  671. r.HandleFunc("/api/", func2).Name("func2")
  672. r.HandleFunc("/", func1).Name("func1")
  673. req, _ := http.NewRequest("GET", "http://localhost//api/?abc=def", nil)
  674. res := TestA301ResponseWriter{
  675. hh: m,
  676. status: 0,
  677. }
  678. r.ServeHTTP(&res, req)
  679. if "http://localhost/api/?abc=def" != res.hh["Location"][0] {
  680. t.Errorf("Should have complete URL with query string")
  681. }
  682. }
  683. // https://plus.google.com/101022900381697718949/posts/eWy6DjFJ6uW
  684. func TestSubrouterHeader(t *testing.T) {
  685. expected := "func1 response"
  686. func1 := func(w http.ResponseWriter, r *http.Request) {
  687. fmt.Fprint(w, expected)
  688. }
  689. func2 := func(http.ResponseWriter, *http.Request) {}
  690. r := NewRouter()
  691. s := r.Headers("SomeSpecialHeader", "").Subrouter()
  692. s.HandleFunc("/", func1).Name("func1")
  693. r.HandleFunc("/", func2).Name("func2")
  694. req, _ := http.NewRequest("GET", "http://localhost/", nil)
  695. req.Header.Add("SomeSpecialHeader", "foo")
  696. match := new(RouteMatch)
  697. matched := r.Match(req, match)
  698. if !matched {
  699. t.Errorf("Should match request")
  700. }
  701. if match.Route.GetName() != "func1" {
  702. t.Errorf("Expecting func1 handler, got %s", match.Route.GetName())
  703. }
  704. resp := NewRecorder()
  705. match.Handler.ServeHTTP(resp, req)
  706. if resp.Body.String() != expected {
  707. t.Errorf("Expecting %q", expected)
  708. }
  709. }
  710. // mapToPairs converts a string map to a slice of string pairs
  711. func mapToPairs(m map[string]string) []string {
  712. var i int
  713. p := make([]string, len(m)*2)
  714. for k, v := range m {
  715. p[i] = k
  716. p[i+1] = v
  717. i += 2
  718. }
  719. return p
  720. }
  721. // stringMapEqual checks the equality of two string maps
  722. func stringMapEqual(m1, m2 map[string]string) bool {
  723. nil1 := m1 == nil
  724. nil2 := m2 == nil
  725. if nil1 != nil2 || len(m1) != len(m2) {
  726. return false
  727. }
  728. for k, v := range m1 {
  729. if v != m2[k] {
  730. return false
  731. }
  732. }
  733. return true
  734. }
  735. // newRequest is a helper function to create a new request with a method and url
  736. func newRequest(method, url string) *http.Request {
  737. req, err := http.NewRequest(method, url, nil)
  738. if err != nil {
  739. panic(err)
  740. }
  741. return req
  742. }