mux_test.go 28 KB

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