4
0

mux_test.go 74 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347
  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. "bufio"
  7. "bytes"
  8. "errors"
  9. "fmt"
  10. "net/http"
  11. "net/url"
  12. "reflect"
  13. "strings"
  14. "testing"
  15. )
  16. func (r *Route) GoString() string {
  17. matchers := make([]string, len(r.matchers))
  18. for i, m := range r.matchers {
  19. matchers[i] = fmt.Sprintf("%#v", m)
  20. }
  21. return fmt.Sprintf("&Route{matchers:[]matcher{%s}}", strings.Join(matchers, ", "))
  22. }
  23. func (r *routeRegexp) GoString() string {
  24. return fmt.Sprintf("&routeRegexp{template: %q, regexpType: %v, options: %v, regexp: regexp.MustCompile(%q), reverse: %q, varsN: %v, varsR: %v", r.template, r.regexpType, r.options, r.regexp.String(), r.reverse, r.varsN, r.varsR)
  25. }
  26. type routeTest struct {
  27. title string // title of the test
  28. route *Route // the route being tested
  29. request *http.Request // a request to test the route
  30. vars map[string]string // the expected vars of the match
  31. scheme string // the expected scheme of the built URL
  32. host string // the expected host of the built URL
  33. path string // the expected path of the built URL
  34. query string // the expected query string of the built URL
  35. pathTemplate string // the expected path template of the route
  36. hostTemplate string // the expected host template of the route
  37. queriesTemplate string // the expected query template of the route
  38. methods []string // the expected route methods
  39. pathRegexp string // the expected path regexp
  40. queriesRegexp string // the expected query regexp
  41. shouldMatch bool // whether the request is expected to match the route at all
  42. shouldRedirect bool // whether the request should result in a redirect
  43. }
  44. func TestHost(t *testing.T) {
  45. // newRequestHost a new request with a method, url, and host header
  46. newRequestHost := func(method, url, host string) *http.Request {
  47. req, err := http.NewRequest(method, url, nil)
  48. if err != nil {
  49. panic(err)
  50. }
  51. req.Host = host
  52. return req
  53. }
  54. tests := []routeTest{
  55. {
  56. title: "Host route match",
  57. route: new(Route).Host("aaa.bbb.ccc"),
  58. request: newRequest("GET", "http://aaa.bbb.ccc/111/222/333"),
  59. vars: map[string]string{},
  60. host: "aaa.bbb.ccc",
  61. path: "",
  62. shouldMatch: true,
  63. },
  64. {
  65. title: "Host route, wrong host in request URL",
  66. route: new(Route).Host("aaa.bbb.ccc"),
  67. request: newRequest("GET", "http://aaa.222.ccc/111/222/333"),
  68. vars: map[string]string{},
  69. host: "aaa.bbb.ccc",
  70. path: "",
  71. shouldMatch: false,
  72. },
  73. {
  74. title: "Host route with port, match",
  75. route: new(Route).Host("aaa.bbb.ccc:1234"),
  76. request: newRequest("GET", "http://aaa.bbb.ccc:1234/111/222/333"),
  77. vars: map[string]string{},
  78. host: "aaa.bbb.ccc:1234",
  79. path: "",
  80. shouldMatch: true,
  81. },
  82. {
  83. title: "Host route with port, wrong port in request URL",
  84. route: new(Route).Host("aaa.bbb.ccc:1234"),
  85. request: newRequest("GET", "http://aaa.bbb.ccc:9999/111/222/333"),
  86. vars: map[string]string{},
  87. host: "aaa.bbb.ccc:1234",
  88. path: "",
  89. shouldMatch: false,
  90. },
  91. {
  92. title: "Host route, match with host in request header",
  93. route: new(Route).Host("aaa.bbb.ccc"),
  94. request: newRequestHost("GET", "/111/222/333", "aaa.bbb.ccc"),
  95. vars: map[string]string{},
  96. host: "aaa.bbb.ccc",
  97. path: "",
  98. shouldMatch: true,
  99. },
  100. {
  101. title: "Host route, wrong host in request header",
  102. route: new(Route).Host("aaa.bbb.ccc"),
  103. request: newRequestHost("GET", "/111/222/333", "aaa.222.ccc"),
  104. vars: map[string]string{},
  105. host: "aaa.bbb.ccc",
  106. path: "",
  107. shouldMatch: false,
  108. },
  109. // 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},
  110. {
  111. title: "Host route with port, wrong host in request header",
  112. route: new(Route).Host("aaa.bbb.ccc:1234"),
  113. request: newRequestHost("GET", "/111/222/333", "aaa.bbb.ccc:9999"),
  114. vars: map[string]string{},
  115. host: "aaa.bbb.ccc:1234",
  116. path: "",
  117. shouldMatch: false,
  118. },
  119. {
  120. title: "Host route with pattern, match",
  121. route: new(Route).Host("aaa.{v1:[a-z]{3}}.ccc"),
  122. request: newRequest("GET", "http://aaa.bbb.ccc/111/222/333"),
  123. vars: map[string]string{"v1": "bbb"},
  124. host: "aaa.bbb.ccc",
  125. path: "",
  126. hostTemplate: `aaa.{v1:[a-z]{3}}.ccc`,
  127. shouldMatch: true,
  128. },
  129. {
  130. title: "Host route with pattern, additional capturing group, match",
  131. route: new(Route).Host("aaa.{v1:[a-z]{2}(?:b|c)}.ccc"),
  132. request: newRequest("GET", "http://aaa.bbb.ccc/111/222/333"),
  133. vars: map[string]string{"v1": "bbb"},
  134. host: "aaa.bbb.ccc",
  135. path: "",
  136. hostTemplate: `aaa.{v1:[a-z]{2}(?:b|c)}.ccc`,
  137. shouldMatch: true,
  138. },
  139. {
  140. title: "Host route with pattern, wrong host in request URL",
  141. route: new(Route).Host("aaa.{v1:[a-z]{3}}.ccc"),
  142. request: newRequest("GET", "http://aaa.222.ccc/111/222/333"),
  143. vars: map[string]string{"v1": "bbb"},
  144. host: "aaa.bbb.ccc",
  145. path: "",
  146. hostTemplate: `aaa.{v1:[a-z]{3}}.ccc`,
  147. shouldMatch: false,
  148. },
  149. {
  150. title: "Host route with multiple patterns, match",
  151. route: new(Route).Host("{v1:[a-z]{3}}.{v2:[a-z]{3}}.{v3:[a-z]{3}}"),
  152. request: newRequest("GET", "http://aaa.bbb.ccc/111/222/333"),
  153. vars: map[string]string{"v1": "aaa", "v2": "bbb", "v3": "ccc"},
  154. host: "aaa.bbb.ccc",
  155. path: "",
  156. hostTemplate: `{v1:[a-z]{3}}.{v2:[a-z]{3}}.{v3:[a-z]{3}}`,
  157. shouldMatch: true,
  158. },
  159. {
  160. title: "Host route with multiple patterns, wrong host in request URL",
  161. route: new(Route).Host("{v1:[a-z]{3}}.{v2:[a-z]{3}}.{v3:[a-z]{3}}"),
  162. request: newRequest("GET", "http://aaa.222.ccc/111/222/333"),
  163. vars: map[string]string{"v1": "aaa", "v2": "bbb", "v3": "ccc"},
  164. host: "aaa.bbb.ccc",
  165. path: "",
  166. hostTemplate: `{v1:[a-z]{3}}.{v2:[a-z]{3}}.{v3:[a-z]{3}}`,
  167. shouldMatch: false,
  168. },
  169. {
  170. title: "Host route with hyphenated name and pattern, match",
  171. route: new(Route).Host("aaa.{v-1:[a-z]{3}}.ccc"),
  172. request: newRequest("GET", "http://aaa.bbb.ccc/111/222/333"),
  173. vars: map[string]string{"v-1": "bbb"},
  174. host: "aaa.bbb.ccc",
  175. path: "",
  176. hostTemplate: `aaa.{v-1:[a-z]{3}}.ccc`,
  177. shouldMatch: true,
  178. },
  179. {
  180. title: "Host route with hyphenated name and pattern, additional capturing group, match",
  181. route: new(Route).Host("aaa.{v-1:[a-z]{2}(?:b|c)}.ccc"),
  182. request: newRequest("GET", "http://aaa.bbb.ccc/111/222/333"),
  183. vars: map[string]string{"v-1": "bbb"},
  184. host: "aaa.bbb.ccc",
  185. path: "",
  186. hostTemplate: `aaa.{v-1:[a-z]{2}(?:b|c)}.ccc`,
  187. shouldMatch: true,
  188. },
  189. {
  190. title: "Host route with multiple hyphenated names and patterns, match",
  191. route: new(Route).Host("{v-1:[a-z]{3}}.{v-2:[a-z]{3}}.{v-3:[a-z]{3}}"),
  192. request: newRequest("GET", "http://aaa.bbb.ccc/111/222/333"),
  193. vars: map[string]string{"v-1": "aaa", "v-2": "bbb", "v-3": "ccc"},
  194. host: "aaa.bbb.ccc",
  195. path: "",
  196. hostTemplate: `{v-1:[a-z]{3}}.{v-2:[a-z]{3}}.{v-3:[a-z]{3}}`,
  197. shouldMatch: true,
  198. },
  199. }
  200. for _, test := range tests {
  201. testRoute(t, test)
  202. testTemplate(t, test)
  203. }
  204. }
  205. func TestPath(t *testing.T) {
  206. tests := []routeTest{
  207. {
  208. title: "Path route, match",
  209. route: new(Route).Path("/111/222/333"),
  210. request: newRequest("GET", "http://localhost/111/222/333"),
  211. vars: map[string]string{},
  212. host: "",
  213. path: "/111/222/333",
  214. shouldMatch: true,
  215. },
  216. {
  217. title: "Path route, match with trailing slash in request and path",
  218. route: new(Route).Path("/111/"),
  219. request: newRequest("GET", "http://localhost/111/"),
  220. vars: map[string]string{},
  221. host: "",
  222. path: "/111/",
  223. shouldMatch: true,
  224. },
  225. {
  226. title: "Path route, do not match with trailing slash in path",
  227. route: new(Route).Path("/111/"),
  228. request: newRequest("GET", "http://localhost/111"),
  229. vars: map[string]string{},
  230. host: "",
  231. path: "/111",
  232. pathTemplate: `/111/`,
  233. pathRegexp: `^/111/$`,
  234. shouldMatch: false,
  235. },
  236. {
  237. title: "Path route, do not match with trailing slash in request",
  238. route: new(Route).Path("/111"),
  239. request: newRequest("GET", "http://localhost/111/"),
  240. vars: map[string]string{},
  241. host: "",
  242. path: "/111/",
  243. pathTemplate: `/111`,
  244. shouldMatch: false,
  245. },
  246. {
  247. title: "Path route, match root with no host",
  248. route: new(Route).Path("/"),
  249. request: newRequest("GET", "/"),
  250. vars: map[string]string{},
  251. host: "",
  252. path: "/",
  253. pathTemplate: `/`,
  254. pathRegexp: `^/$`,
  255. shouldMatch: true,
  256. },
  257. {
  258. title: "Path route, match root with no host, App Engine format",
  259. route: new(Route).Path("/"),
  260. request: func() *http.Request {
  261. r := newRequest("GET", "http://localhost/")
  262. r.RequestURI = "/"
  263. return r
  264. }(),
  265. vars: map[string]string{},
  266. host: "",
  267. path: "/",
  268. pathTemplate: `/`,
  269. shouldMatch: true,
  270. },
  271. {
  272. title: "Path route, wrong path in request in request URL",
  273. route: new(Route).Path("/111/222/333"),
  274. request: newRequest("GET", "http://localhost/1/2/3"),
  275. vars: map[string]string{},
  276. host: "",
  277. path: "/111/222/333",
  278. shouldMatch: false,
  279. },
  280. {
  281. title: "Path route with pattern, match",
  282. route: new(Route).Path("/111/{v1:[0-9]{3}}/333"),
  283. request: newRequest("GET", "http://localhost/111/222/333"),
  284. vars: map[string]string{"v1": "222"},
  285. host: "",
  286. path: "/111/222/333",
  287. pathTemplate: `/111/{v1:[0-9]{3}}/333`,
  288. shouldMatch: true,
  289. },
  290. {
  291. title: "Path route with pattern, URL in request does not match",
  292. route: new(Route).Path("/111/{v1:[0-9]{3}}/333"),
  293. request: newRequest("GET", "http://localhost/111/aaa/333"),
  294. vars: map[string]string{"v1": "222"},
  295. host: "",
  296. path: "/111/222/333",
  297. pathTemplate: `/111/{v1:[0-9]{3}}/333`,
  298. pathRegexp: `^/111/(?P<v0>[0-9]{3})/333$`,
  299. shouldMatch: false,
  300. },
  301. {
  302. title: "Path route with multiple patterns, match",
  303. route: new(Route).Path("/{v1:[0-9]{3}}/{v2:[0-9]{3}}/{v3:[0-9]{3}}"),
  304. request: newRequest("GET", "http://localhost/111/222/333"),
  305. vars: map[string]string{"v1": "111", "v2": "222", "v3": "333"},
  306. host: "",
  307. path: "/111/222/333",
  308. pathTemplate: `/{v1:[0-9]{3}}/{v2:[0-9]{3}}/{v3:[0-9]{3}}`,
  309. pathRegexp: `^/(?P<v0>[0-9]{3})/(?P<v1>[0-9]{3})/(?P<v2>[0-9]{3})$`,
  310. shouldMatch: true,
  311. },
  312. {
  313. title: "Path route with multiple patterns, URL in request does not match",
  314. route: new(Route).Path("/{v1:[0-9]{3}}/{v2:[0-9]{3}}/{v3:[0-9]{3}}"),
  315. request: newRequest("GET", "http://localhost/111/aaa/333"),
  316. vars: map[string]string{"v1": "111", "v2": "222", "v3": "333"},
  317. host: "",
  318. path: "/111/222/333",
  319. pathTemplate: `/{v1:[0-9]{3}}/{v2:[0-9]{3}}/{v3:[0-9]{3}}`,
  320. pathRegexp: `^/(?P<v0>[0-9]{3})/(?P<v1>[0-9]{3})/(?P<v2>[0-9]{3})$`,
  321. shouldMatch: false,
  322. },
  323. {
  324. title: "Path route with multiple patterns with pipe, match",
  325. route: new(Route).Path("/{category:a|(?:b/c)}/{product}/{id:[0-9]+}"),
  326. request: newRequest("GET", "http://localhost/a/product_name/1"),
  327. vars: map[string]string{"category": "a", "product": "product_name", "id": "1"},
  328. host: "",
  329. path: "/a/product_name/1",
  330. pathTemplate: `/{category:a|(?:b/c)}/{product}/{id:[0-9]+}`,
  331. pathRegexp: `^/(?P<v0>a|(?:b/c))/(?P<v1>[^/]+)/(?P<v2>[0-9]+)$`,
  332. shouldMatch: true,
  333. },
  334. {
  335. title: "Path route with hyphenated name and pattern, match",
  336. route: new(Route).Path("/111/{v-1:[0-9]{3}}/333"),
  337. request: newRequest("GET", "http://localhost/111/222/333"),
  338. vars: map[string]string{"v-1": "222"},
  339. host: "",
  340. path: "/111/222/333",
  341. pathTemplate: `/111/{v-1:[0-9]{3}}/333`,
  342. pathRegexp: `^/111/(?P<v0>[0-9]{3})/333$`,
  343. shouldMatch: true,
  344. },
  345. {
  346. title: "Path route with multiple hyphenated names and patterns, match",
  347. route: new(Route).Path("/{v-1:[0-9]{3}}/{v-2:[0-9]{3}}/{v-3:[0-9]{3}}"),
  348. request: newRequest("GET", "http://localhost/111/222/333"),
  349. vars: map[string]string{"v-1": "111", "v-2": "222", "v-3": "333"},
  350. host: "",
  351. path: "/111/222/333",
  352. pathTemplate: `/{v-1:[0-9]{3}}/{v-2:[0-9]{3}}/{v-3:[0-9]{3}}`,
  353. pathRegexp: `^/(?P<v0>[0-9]{3})/(?P<v1>[0-9]{3})/(?P<v2>[0-9]{3})$`,
  354. shouldMatch: true,
  355. },
  356. {
  357. title: "Path route with multiple hyphenated names and patterns with pipe, match",
  358. route: new(Route).Path("/{product-category:a|(?:b/c)}/{product-name}/{product-id:[0-9]+}"),
  359. request: newRequest("GET", "http://localhost/a/product_name/1"),
  360. vars: map[string]string{"product-category": "a", "product-name": "product_name", "product-id": "1"},
  361. host: "",
  362. path: "/a/product_name/1",
  363. pathTemplate: `/{product-category:a|(?:b/c)}/{product-name}/{product-id:[0-9]+}`,
  364. pathRegexp: `^/(?P<v0>a|(?:b/c))/(?P<v1>[^/]+)/(?P<v2>[0-9]+)$`,
  365. shouldMatch: true,
  366. },
  367. {
  368. title: "Path route with multiple hyphenated names and patterns with pipe and case insensitive, match",
  369. route: new(Route).Path("/{type:(?i:daily|mini|variety)}-{date:\\d{4,4}-\\d{2,2}-\\d{2,2}}"),
  370. request: newRequest("GET", "http://localhost/daily-2016-01-01"),
  371. vars: map[string]string{"type": "daily", "date": "2016-01-01"},
  372. host: "",
  373. path: "/daily-2016-01-01",
  374. pathTemplate: `/{type:(?i:daily|mini|variety)}-{date:\d{4,4}-\d{2,2}-\d{2,2}}`,
  375. pathRegexp: `^/(?P<v0>(?i:daily|mini|variety))-(?P<v1>\d{4,4}-\d{2,2}-\d{2,2})$`,
  376. shouldMatch: true,
  377. },
  378. {
  379. title: "Path route with empty match right after other match",
  380. route: new(Route).Path(`/{v1:[0-9]*}{v2:[a-z]*}/{v3:[0-9]*}`),
  381. request: newRequest("GET", "http://localhost/111/222"),
  382. vars: map[string]string{"v1": "111", "v2": "", "v3": "222"},
  383. host: "",
  384. path: "/111/222",
  385. pathTemplate: `/{v1:[0-9]*}{v2:[a-z]*}/{v3:[0-9]*}`,
  386. pathRegexp: `^/(?P<v0>[0-9]*)(?P<v1>[a-z]*)/(?P<v2>[0-9]*)$`,
  387. shouldMatch: true,
  388. },
  389. {
  390. title: "Path route with single pattern with pipe, match",
  391. route: new(Route).Path("/{category:a|b/c}"),
  392. request: newRequest("GET", "http://localhost/a"),
  393. vars: map[string]string{"category": "a"},
  394. host: "",
  395. path: "/a",
  396. pathTemplate: `/{category:a|b/c}`,
  397. shouldMatch: true,
  398. },
  399. {
  400. title: "Path route with single pattern with pipe, match",
  401. route: new(Route).Path("/{category:a|b/c}"),
  402. request: newRequest("GET", "http://localhost/b/c"),
  403. vars: map[string]string{"category": "b/c"},
  404. host: "",
  405. path: "/b/c",
  406. pathTemplate: `/{category:a|b/c}`,
  407. shouldMatch: true,
  408. },
  409. {
  410. title: "Path route with multiple patterns with pipe, match",
  411. route: new(Route).Path("/{category:a|b/c}/{product}/{id:[0-9]+}"),
  412. request: newRequest("GET", "http://localhost/a/product_name/1"),
  413. vars: map[string]string{"category": "a", "product": "product_name", "id": "1"},
  414. host: "",
  415. path: "/a/product_name/1",
  416. pathTemplate: `/{category:a|b/c}/{product}/{id:[0-9]+}`,
  417. shouldMatch: true,
  418. },
  419. {
  420. title: "Path route with multiple patterns with pipe, match",
  421. route: new(Route).Path("/{category:a|b/c}/{product}/{id:[0-9]+}"),
  422. request: newRequest("GET", "http://localhost/b/c/product_name/1"),
  423. vars: map[string]string{"category": "b/c", "product": "product_name", "id": "1"},
  424. host: "",
  425. path: "/b/c/product_name/1",
  426. pathTemplate: `/{category:a|b/c}/{product}/{id:[0-9]+}`,
  427. shouldMatch: true,
  428. },
  429. }
  430. for _, test := range tests {
  431. testRoute(t, test)
  432. testTemplate(t, test)
  433. testUseEscapedRoute(t, test)
  434. testRegexp(t, test)
  435. }
  436. }
  437. func TestPathPrefix(t *testing.T) {
  438. tests := []routeTest{
  439. {
  440. title: "PathPrefix route, match",
  441. route: new(Route).PathPrefix("/111"),
  442. request: newRequest("GET", "http://localhost/111/222/333"),
  443. vars: map[string]string{},
  444. host: "",
  445. path: "/111",
  446. shouldMatch: true,
  447. },
  448. {
  449. title: "PathPrefix route, match substring",
  450. route: new(Route).PathPrefix("/1"),
  451. request: newRequest("GET", "http://localhost/111/222/333"),
  452. vars: map[string]string{},
  453. host: "",
  454. path: "/1",
  455. shouldMatch: true,
  456. },
  457. {
  458. title: "PathPrefix route, URL prefix in request does not match",
  459. route: new(Route).PathPrefix("/111"),
  460. request: newRequest("GET", "http://localhost/1/2/3"),
  461. vars: map[string]string{},
  462. host: "",
  463. path: "/111",
  464. shouldMatch: false,
  465. },
  466. {
  467. title: "PathPrefix route with pattern, match",
  468. route: new(Route).PathPrefix("/111/{v1:[0-9]{3}}"),
  469. request: newRequest("GET", "http://localhost/111/222/333"),
  470. vars: map[string]string{"v1": "222"},
  471. host: "",
  472. path: "/111/222",
  473. pathTemplate: `/111/{v1:[0-9]{3}}`,
  474. shouldMatch: true,
  475. },
  476. {
  477. title: "PathPrefix route with pattern, URL prefix in request does not match",
  478. route: new(Route).PathPrefix("/111/{v1:[0-9]{3}}"),
  479. request: newRequest("GET", "http://localhost/111/aaa/333"),
  480. vars: map[string]string{"v1": "222"},
  481. host: "",
  482. path: "/111/222",
  483. pathTemplate: `/111/{v1:[0-9]{3}}`,
  484. shouldMatch: false,
  485. },
  486. {
  487. title: "PathPrefix route with multiple patterns, match",
  488. route: new(Route).PathPrefix("/{v1:[0-9]{3}}/{v2:[0-9]{3}}"),
  489. request: newRequest("GET", "http://localhost/111/222/333"),
  490. vars: map[string]string{"v1": "111", "v2": "222"},
  491. host: "",
  492. path: "/111/222",
  493. pathTemplate: `/{v1:[0-9]{3}}/{v2:[0-9]{3}}`,
  494. shouldMatch: true,
  495. },
  496. {
  497. title: "PathPrefix route with multiple patterns, URL prefix in request does not match",
  498. route: new(Route).PathPrefix("/{v1:[0-9]{3}}/{v2:[0-9]{3}}"),
  499. request: newRequest("GET", "http://localhost/111/aaa/333"),
  500. vars: map[string]string{"v1": "111", "v2": "222"},
  501. host: "",
  502. path: "/111/222",
  503. pathTemplate: `/{v1:[0-9]{3}}/{v2:[0-9]{3}}`,
  504. shouldMatch: false,
  505. },
  506. }
  507. for _, test := range tests {
  508. testRoute(t, test)
  509. testTemplate(t, test)
  510. testUseEscapedRoute(t, test)
  511. }
  512. }
  513. func TestSchemeHostPath(t *testing.T) {
  514. tests := []routeTest{
  515. {
  516. title: "Host and Path route, match",
  517. route: new(Route).Host("aaa.bbb.ccc").Path("/111/222/333"),
  518. request: newRequest("GET", "http://aaa.bbb.ccc/111/222/333"),
  519. vars: map[string]string{},
  520. scheme: "http",
  521. host: "aaa.bbb.ccc",
  522. path: "/111/222/333",
  523. pathTemplate: `/111/222/333`,
  524. hostTemplate: `aaa.bbb.ccc`,
  525. shouldMatch: true,
  526. },
  527. {
  528. title: "Scheme, Host, and Path route, match",
  529. route: new(Route).Schemes("https").Host("aaa.bbb.ccc").Path("/111/222/333"),
  530. request: newRequest("GET", "https://aaa.bbb.ccc/111/222/333"),
  531. vars: map[string]string{},
  532. scheme: "https",
  533. host: "aaa.bbb.ccc",
  534. path: "/111/222/333",
  535. pathTemplate: `/111/222/333`,
  536. hostTemplate: `aaa.bbb.ccc`,
  537. shouldMatch: true,
  538. },
  539. {
  540. title: "Host and Path route, wrong host in request URL",
  541. route: new(Route).Host("aaa.bbb.ccc").Path("/111/222/333"),
  542. request: newRequest("GET", "http://aaa.222.ccc/111/222/333"),
  543. vars: map[string]string{},
  544. scheme: "http",
  545. host: "aaa.bbb.ccc",
  546. path: "/111/222/333",
  547. pathTemplate: `/111/222/333`,
  548. hostTemplate: `aaa.bbb.ccc`,
  549. shouldMatch: false,
  550. },
  551. {
  552. title: "Host and Path route with pattern, match",
  553. route: new(Route).Host("aaa.{v1:[a-z]{3}}.ccc").Path("/111/{v2:[0-9]{3}}/333"),
  554. request: newRequest("GET", "http://aaa.bbb.ccc/111/222/333"),
  555. vars: map[string]string{"v1": "bbb", "v2": "222"},
  556. scheme: "http",
  557. host: "aaa.bbb.ccc",
  558. path: "/111/222/333",
  559. pathTemplate: `/111/{v2:[0-9]{3}}/333`,
  560. hostTemplate: `aaa.{v1:[a-z]{3}}.ccc`,
  561. shouldMatch: true,
  562. },
  563. {
  564. title: "Scheme, Host, and Path route with host and path patterns, match",
  565. route: new(Route).Schemes("ftp", "ssss").Host("aaa.{v1:[a-z]{3}}.ccc").Path("/111/{v2:[0-9]{3}}/333"),
  566. request: newRequest("GET", "ssss://aaa.bbb.ccc/111/222/333"),
  567. vars: map[string]string{"v1": "bbb", "v2": "222"},
  568. scheme: "ftp",
  569. host: "aaa.bbb.ccc",
  570. path: "/111/222/333",
  571. pathTemplate: `/111/{v2:[0-9]{3}}/333`,
  572. hostTemplate: `aaa.{v1:[a-z]{3}}.ccc`,
  573. shouldMatch: true,
  574. },
  575. {
  576. title: "Host and Path route with pattern, URL in request does not match",
  577. route: new(Route).Host("aaa.{v1:[a-z]{3}}.ccc").Path("/111/{v2:[0-9]{3}}/333"),
  578. request: newRequest("GET", "http://aaa.222.ccc/111/222/333"),
  579. vars: map[string]string{"v1": "bbb", "v2": "222"},
  580. scheme: "http",
  581. host: "aaa.bbb.ccc",
  582. path: "/111/222/333",
  583. pathTemplate: `/111/{v2:[0-9]{3}}/333`,
  584. hostTemplate: `aaa.{v1:[a-z]{3}}.ccc`,
  585. shouldMatch: false,
  586. },
  587. {
  588. title: "Host and Path route with multiple patterns, match",
  589. 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}}"),
  590. request: newRequest("GET", "http://aaa.bbb.ccc/111/222/333"),
  591. vars: map[string]string{"v1": "aaa", "v2": "bbb", "v3": "ccc", "v4": "111", "v5": "222", "v6": "333"},
  592. scheme: "http",
  593. host: "aaa.bbb.ccc",
  594. path: "/111/222/333",
  595. pathTemplate: `/{v4:[0-9]{3}}/{v5:[0-9]{3}}/{v6:[0-9]{3}}`,
  596. hostTemplate: `{v1:[a-z]{3}}.{v2:[a-z]{3}}.{v3:[a-z]{3}}`,
  597. shouldMatch: true,
  598. },
  599. {
  600. title: "Host and Path route with multiple patterns, URL in request does not match",
  601. 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}}"),
  602. request: newRequest("GET", "http://aaa.222.ccc/111/222/333"),
  603. vars: map[string]string{"v1": "aaa", "v2": "bbb", "v3": "ccc", "v4": "111", "v5": "222", "v6": "333"},
  604. scheme: "http",
  605. host: "aaa.bbb.ccc",
  606. path: "/111/222/333",
  607. pathTemplate: `/{v4:[0-9]{3}}/{v5:[0-9]{3}}/{v6:[0-9]{3}}`,
  608. hostTemplate: `{v1:[a-z]{3}}.{v2:[a-z]{3}}.{v3:[a-z]{3}}`,
  609. shouldMatch: false,
  610. },
  611. }
  612. for _, test := range tests {
  613. testRoute(t, test)
  614. testTemplate(t, test)
  615. testUseEscapedRoute(t, test)
  616. }
  617. }
  618. func TestHeaders(t *testing.T) {
  619. // newRequestHeaders creates a new request with a method, url, and headers
  620. newRequestHeaders := func(method, url string, headers map[string]string) *http.Request {
  621. req, err := http.NewRequest(method, url, nil)
  622. if err != nil {
  623. panic(err)
  624. }
  625. for k, v := range headers {
  626. req.Header.Add(k, v)
  627. }
  628. return req
  629. }
  630. tests := []routeTest{
  631. {
  632. title: "Headers route, match",
  633. route: new(Route).Headers("foo", "bar", "baz", "ding"),
  634. request: newRequestHeaders("GET", "http://localhost", map[string]string{"foo": "bar", "baz": "ding"}),
  635. vars: map[string]string{},
  636. host: "",
  637. path: "",
  638. shouldMatch: true,
  639. },
  640. {
  641. title: "Headers route, bad header values",
  642. route: new(Route).Headers("foo", "bar", "baz", "ding"),
  643. request: newRequestHeaders("GET", "http://localhost", map[string]string{"foo": "bar", "baz": "dong"}),
  644. vars: map[string]string{},
  645. host: "",
  646. path: "",
  647. shouldMatch: false,
  648. },
  649. {
  650. title: "Headers route, regex header values to match",
  651. route: new(Route).Headers("foo", "ba[zr]"),
  652. request: newRequestHeaders("GET", "http://localhost", map[string]string{"foo": "bar"}),
  653. vars: map[string]string{},
  654. host: "",
  655. path: "",
  656. shouldMatch: false,
  657. },
  658. {
  659. title: "Headers route, regex header values to match",
  660. route: new(Route).HeadersRegexp("foo", "ba[zr]"),
  661. request: newRequestHeaders("GET", "http://localhost", map[string]string{"foo": "baz"}),
  662. vars: map[string]string{},
  663. host: "",
  664. path: "",
  665. shouldMatch: true,
  666. },
  667. }
  668. for _, test := range tests {
  669. testRoute(t, test)
  670. testTemplate(t, test)
  671. }
  672. }
  673. func TestMethods(t *testing.T) {
  674. tests := []routeTest{
  675. {
  676. title: "Methods route, match GET",
  677. route: new(Route).Methods("GET", "POST"),
  678. request: newRequest("GET", "http://localhost"),
  679. vars: map[string]string{},
  680. host: "",
  681. path: "",
  682. methods: []string{"GET", "POST"},
  683. shouldMatch: true,
  684. },
  685. {
  686. title: "Methods route, match POST",
  687. route: new(Route).Methods("GET", "POST"),
  688. request: newRequest("POST", "http://localhost"),
  689. vars: map[string]string{},
  690. host: "",
  691. path: "",
  692. methods: []string{"GET", "POST"},
  693. shouldMatch: true,
  694. },
  695. {
  696. title: "Methods route, bad method",
  697. route: new(Route).Methods("GET", "POST"),
  698. request: newRequest("PUT", "http://localhost"),
  699. vars: map[string]string{},
  700. host: "",
  701. path: "",
  702. methods: []string{"GET", "POST"},
  703. shouldMatch: false,
  704. },
  705. {
  706. title: "Route without methods",
  707. route: new(Route),
  708. request: newRequest("PUT", "http://localhost"),
  709. vars: map[string]string{},
  710. host: "",
  711. path: "",
  712. methods: []string{},
  713. shouldMatch: true,
  714. },
  715. }
  716. for _, test := range tests {
  717. testRoute(t, test)
  718. testTemplate(t, test)
  719. testMethods(t, test)
  720. }
  721. }
  722. func TestQueries(t *testing.T) {
  723. tests := []routeTest{
  724. {
  725. title: "Queries route, match",
  726. route: new(Route).Queries("foo", "bar", "baz", "ding"),
  727. request: newRequest("GET", "http://localhost?foo=bar&baz=ding"),
  728. vars: map[string]string{},
  729. host: "",
  730. path: "",
  731. query: "foo=bar&baz=ding",
  732. queriesTemplate: "foo=bar,baz=ding",
  733. queriesRegexp: "^foo=bar$,^baz=ding$",
  734. shouldMatch: true,
  735. },
  736. {
  737. title: "Queries route, match with a query string",
  738. route: new(Route).Host("www.example.com").Path("/api").Queries("foo", "bar", "baz", "ding"),
  739. request: newRequest("GET", "http://www.example.com/api?foo=bar&baz=ding"),
  740. vars: map[string]string{},
  741. host: "",
  742. path: "",
  743. query: "foo=bar&baz=ding",
  744. pathTemplate: `/api`,
  745. hostTemplate: `www.example.com`,
  746. queriesTemplate: "foo=bar,baz=ding",
  747. queriesRegexp: "^foo=bar$,^baz=ding$",
  748. shouldMatch: true,
  749. },
  750. {
  751. title: "Queries route, match with a query string out of order",
  752. route: new(Route).Host("www.example.com").Path("/api").Queries("foo", "bar", "baz", "ding"),
  753. request: newRequest("GET", "http://www.example.com/api?baz=ding&foo=bar"),
  754. vars: map[string]string{},
  755. host: "",
  756. path: "",
  757. query: "foo=bar&baz=ding",
  758. pathTemplate: `/api`,
  759. hostTemplate: `www.example.com`,
  760. queriesTemplate: "foo=bar,baz=ding",
  761. queriesRegexp: "^foo=bar$,^baz=ding$",
  762. shouldMatch: true,
  763. },
  764. {
  765. title: "Queries route, bad query",
  766. route: new(Route).Queries("foo", "bar", "baz", "ding"),
  767. request: newRequest("GET", "http://localhost?foo=bar&baz=dong"),
  768. vars: map[string]string{},
  769. host: "",
  770. path: "",
  771. queriesTemplate: "foo=bar,baz=ding",
  772. queriesRegexp: "^foo=bar$,^baz=ding$",
  773. shouldMatch: false,
  774. },
  775. {
  776. title: "Queries route with pattern, match",
  777. route: new(Route).Queries("foo", "{v1}"),
  778. request: newRequest("GET", "http://localhost?foo=bar"),
  779. vars: map[string]string{"v1": "bar"},
  780. host: "",
  781. path: "",
  782. query: "foo=bar",
  783. queriesTemplate: "foo={v1}",
  784. queriesRegexp: "^foo=(?P<v0>.*)$",
  785. shouldMatch: true,
  786. },
  787. {
  788. title: "Queries route with multiple patterns, match",
  789. route: new(Route).Queries("foo", "{v1}", "baz", "{v2}"),
  790. request: newRequest("GET", "http://localhost?foo=bar&baz=ding"),
  791. vars: map[string]string{"v1": "bar", "v2": "ding"},
  792. host: "",
  793. path: "",
  794. query: "foo=bar&baz=ding",
  795. queriesTemplate: "foo={v1},baz={v2}",
  796. queriesRegexp: "^foo=(?P<v0>.*)$,^baz=(?P<v0>.*)$",
  797. shouldMatch: true,
  798. },
  799. {
  800. title: "Queries route with regexp pattern, match",
  801. route: new(Route).Queries("foo", "{v1:[0-9]+}"),
  802. request: newRequest("GET", "http://localhost?foo=10"),
  803. vars: map[string]string{"v1": "10"},
  804. host: "",
  805. path: "",
  806. query: "foo=10",
  807. queriesTemplate: "foo={v1:[0-9]+}",
  808. queriesRegexp: "^foo=(?P<v0>[0-9]+)$",
  809. shouldMatch: true,
  810. },
  811. {
  812. title: "Queries route with regexp pattern, regexp does not match",
  813. route: new(Route).Queries("foo", "{v1:[0-9]+}"),
  814. request: newRequest("GET", "http://localhost?foo=a"),
  815. vars: map[string]string{},
  816. host: "",
  817. path: "",
  818. queriesTemplate: "foo={v1:[0-9]+}",
  819. queriesRegexp: "^foo=(?P<v0>[0-9]+)$",
  820. shouldMatch: false,
  821. },
  822. {
  823. title: "Queries route with regexp pattern with quantifier, match",
  824. route: new(Route).Queries("foo", "{v1:[0-9]{1}}"),
  825. request: newRequest("GET", "http://localhost?foo=1"),
  826. vars: map[string]string{"v1": "1"},
  827. host: "",
  828. path: "",
  829. query: "foo=1",
  830. queriesTemplate: "foo={v1:[0-9]{1}}",
  831. queriesRegexp: "^foo=(?P<v0>[0-9]{1})$",
  832. shouldMatch: true,
  833. },
  834. {
  835. title: "Queries route with regexp pattern with quantifier, additional variable in query string, match",
  836. route: new(Route).Queries("foo", "{v1:[0-9]{1}}"),
  837. request: newRequest("GET", "http://localhost?bar=2&foo=1"),
  838. vars: map[string]string{"v1": "1"},
  839. host: "",
  840. path: "",
  841. query: "foo=1",
  842. queriesTemplate: "foo={v1:[0-9]{1}}",
  843. queriesRegexp: "^foo=(?P<v0>[0-9]{1})$",
  844. shouldMatch: true,
  845. },
  846. {
  847. title: "Queries route with regexp pattern with quantifier, regexp does not match",
  848. route: new(Route).Queries("foo", "{v1:[0-9]{1}}"),
  849. request: newRequest("GET", "http://localhost?foo=12"),
  850. vars: map[string]string{},
  851. host: "",
  852. path: "",
  853. queriesTemplate: "foo={v1:[0-9]{1}}",
  854. queriesRegexp: "^foo=(?P<v0>[0-9]{1})$",
  855. shouldMatch: false,
  856. },
  857. {
  858. title: "Queries route with regexp pattern with quantifier, additional capturing group",
  859. route: new(Route).Queries("foo", "{v1:[0-9]{1}(?:a|b)}"),
  860. request: newRequest("GET", "http://localhost?foo=1a"),
  861. vars: map[string]string{"v1": "1a"},
  862. host: "",
  863. path: "",
  864. query: "foo=1a",
  865. queriesTemplate: "foo={v1:[0-9]{1}(?:a|b)}",
  866. queriesRegexp: "^foo=(?P<v0>[0-9]{1}(?:a|b))$",
  867. shouldMatch: true,
  868. },
  869. {
  870. title: "Queries route with regexp pattern with quantifier, additional variable in query string, regexp does not match",
  871. route: new(Route).Queries("foo", "{v1:[0-9]{1}}"),
  872. request: newRequest("GET", "http://localhost?foo=12"),
  873. vars: map[string]string{},
  874. host: "",
  875. path: "",
  876. queriesTemplate: "foo={v1:[0-9]{1}}",
  877. queriesRegexp: "^foo=(?P<v0>[0-9]{1})$",
  878. shouldMatch: false,
  879. },
  880. {
  881. title: "Queries route with hyphenated name, match",
  882. route: new(Route).Queries("foo", "{v-1}"),
  883. request: newRequest("GET", "http://localhost?foo=bar"),
  884. vars: map[string]string{"v-1": "bar"},
  885. host: "",
  886. path: "",
  887. query: "foo=bar",
  888. queriesTemplate: "foo={v-1}",
  889. queriesRegexp: "^foo=(?P<v0>.*)$",
  890. shouldMatch: true,
  891. },
  892. {
  893. title: "Queries route with multiple hyphenated names, match",
  894. route: new(Route).Queries("foo", "{v-1}", "baz", "{v-2}"),
  895. request: newRequest("GET", "http://localhost?foo=bar&baz=ding"),
  896. vars: map[string]string{"v-1": "bar", "v-2": "ding"},
  897. host: "",
  898. path: "",
  899. query: "foo=bar&baz=ding",
  900. queriesTemplate: "foo={v-1},baz={v-2}",
  901. queriesRegexp: "^foo=(?P<v0>.*)$,^baz=(?P<v0>.*)$",
  902. shouldMatch: true,
  903. },
  904. {
  905. title: "Queries route with hyphenate name and pattern, match",
  906. route: new(Route).Queries("foo", "{v-1:[0-9]+}"),
  907. request: newRequest("GET", "http://localhost?foo=10"),
  908. vars: map[string]string{"v-1": "10"},
  909. host: "",
  910. path: "",
  911. query: "foo=10",
  912. queriesTemplate: "foo={v-1:[0-9]+}",
  913. queriesRegexp: "^foo=(?P<v0>[0-9]+)$",
  914. shouldMatch: true,
  915. },
  916. {
  917. title: "Queries route with hyphenated name and pattern with quantifier, additional capturing group",
  918. route: new(Route).Queries("foo", "{v-1:[0-9]{1}(?:a|b)}"),
  919. request: newRequest("GET", "http://localhost?foo=1a"),
  920. vars: map[string]string{"v-1": "1a"},
  921. host: "",
  922. path: "",
  923. query: "foo=1a",
  924. queriesTemplate: "foo={v-1:[0-9]{1}(?:a|b)}",
  925. queriesRegexp: "^foo=(?P<v0>[0-9]{1}(?:a|b))$",
  926. shouldMatch: true,
  927. },
  928. {
  929. title: "Queries route with empty value, should match",
  930. route: new(Route).Queries("foo", ""),
  931. request: newRequest("GET", "http://localhost?foo=bar"),
  932. vars: map[string]string{},
  933. host: "",
  934. path: "",
  935. query: "foo=",
  936. queriesTemplate: "foo=",
  937. queriesRegexp: "^foo=.*$",
  938. shouldMatch: true,
  939. },
  940. {
  941. title: "Queries route with empty value and no parameter in request, should not match",
  942. route: new(Route).Queries("foo", ""),
  943. request: newRequest("GET", "http://localhost"),
  944. vars: map[string]string{},
  945. host: "",
  946. path: "",
  947. queriesTemplate: "foo=",
  948. queriesRegexp: "^foo=.*$",
  949. shouldMatch: false,
  950. },
  951. {
  952. title: "Queries route with empty value and empty parameter in request, should match",
  953. route: new(Route).Queries("foo", ""),
  954. request: newRequest("GET", "http://localhost?foo="),
  955. vars: map[string]string{},
  956. host: "",
  957. path: "",
  958. query: "foo=",
  959. queriesTemplate: "foo=",
  960. queriesRegexp: "^foo=.*$",
  961. shouldMatch: true,
  962. },
  963. {
  964. title: "Queries route with overlapping value, should not match",
  965. route: new(Route).Queries("foo", "bar"),
  966. request: newRequest("GET", "http://localhost?foo=barfoo"),
  967. vars: map[string]string{},
  968. host: "",
  969. path: "",
  970. queriesTemplate: "foo=bar",
  971. queriesRegexp: "^foo=bar$",
  972. shouldMatch: false,
  973. },
  974. {
  975. title: "Queries route with no parameter in request, should not match",
  976. route: new(Route).Queries("foo", "{bar}"),
  977. request: newRequest("GET", "http://localhost"),
  978. vars: map[string]string{},
  979. host: "",
  980. path: "",
  981. queriesTemplate: "foo={bar}",
  982. queriesRegexp: "^foo=(?P<v0>.*)$",
  983. shouldMatch: false,
  984. },
  985. {
  986. title: "Queries route with empty parameter in request, should match",
  987. route: new(Route).Queries("foo", "{bar}"),
  988. request: newRequest("GET", "http://localhost?foo="),
  989. vars: map[string]string{"foo": ""},
  990. host: "",
  991. path: "",
  992. query: "foo=",
  993. queriesTemplate: "foo={bar}",
  994. queriesRegexp: "^foo=(?P<v0>.*)$",
  995. shouldMatch: true,
  996. },
  997. {
  998. title: "Queries route, bad submatch",
  999. route: new(Route).Queries("foo", "bar", "baz", "ding"),
  1000. request: newRequest("GET", "http://localhost?fffoo=bar&baz=dingggg"),
  1001. vars: map[string]string{},
  1002. host: "",
  1003. path: "",
  1004. queriesTemplate: "foo=bar,baz=ding",
  1005. queriesRegexp: "^foo=bar$,^baz=ding$",
  1006. shouldMatch: false,
  1007. },
  1008. {
  1009. title: "Queries route with pattern, match, escaped value",
  1010. route: new(Route).Queries("foo", "{v1}"),
  1011. request: newRequest("GET", "http://localhost?foo=%25bar%26%20%2F%3D%3F"),
  1012. vars: map[string]string{"v1": "%bar& /=?"},
  1013. host: "",
  1014. path: "",
  1015. query: "foo=%25bar%26+%2F%3D%3F",
  1016. queriesTemplate: "foo={v1}",
  1017. queriesRegexp: "^foo=(?P<v0>.*)$",
  1018. shouldMatch: true,
  1019. },
  1020. }
  1021. for _, test := range tests {
  1022. testRoute(t, test)
  1023. testTemplate(t, test)
  1024. testQueriesTemplates(t, test)
  1025. testUseEscapedRoute(t, test)
  1026. testQueriesRegexp(t, test)
  1027. }
  1028. }
  1029. func TestSchemes(t *testing.T) {
  1030. tests := []routeTest{
  1031. // Schemes
  1032. {
  1033. title: "Schemes route, default scheme, match http, build http",
  1034. route: new(Route).Host("localhost"),
  1035. request: newRequest("GET", "http://localhost"),
  1036. scheme: "http",
  1037. host: "localhost",
  1038. shouldMatch: true,
  1039. },
  1040. {
  1041. title: "Schemes route, match https, build https",
  1042. route: new(Route).Schemes("https", "ftp").Host("localhost"),
  1043. request: newRequest("GET", "https://localhost"),
  1044. scheme: "https",
  1045. host: "localhost",
  1046. shouldMatch: true,
  1047. },
  1048. {
  1049. title: "Schemes route, match ftp, build https",
  1050. route: new(Route).Schemes("https", "ftp").Host("localhost"),
  1051. request: newRequest("GET", "ftp://localhost"),
  1052. scheme: "https",
  1053. host: "localhost",
  1054. shouldMatch: true,
  1055. },
  1056. {
  1057. title: "Schemes route, match ftp, build ftp",
  1058. route: new(Route).Schemes("ftp", "https").Host("localhost"),
  1059. request: newRequest("GET", "ftp://localhost"),
  1060. scheme: "ftp",
  1061. host: "localhost",
  1062. shouldMatch: true,
  1063. },
  1064. {
  1065. title: "Schemes route, bad scheme",
  1066. route: new(Route).Schemes("https", "ftp").Host("localhost"),
  1067. request: newRequest("GET", "http://localhost"),
  1068. scheme: "https",
  1069. host: "localhost",
  1070. shouldMatch: false,
  1071. },
  1072. }
  1073. for _, test := range tests {
  1074. testRoute(t, test)
  1075. testTemplate(t, test)
  1076. }
  1077. }
  1078. func TestMatcherFunc(t *testing.T) {
  1079. m := func(r *http.Request, m *RouteMatch) bool {
  1080. if r.URL.Host == "aaa.bbb.ccc" {
  1081. return true
  1082. }
  1083. return false
  1084. }
  1085. tests := []routeTest{
  1086. {
  1087. title: "MatchFunc route, match",
  1088. route: new(Route).MatcherFunc(m),
  1089. request: newRequest("GET", "http://aaa.bbb.ccc"),
  1090. vars: map[string]string{},
  1091. host: "",
  1092. path: "",
  1093. shouldMatch: true,
  1094. },
  1095. {
  1096. title: "MatchFunc route, non-match",
  1097. route: new(Route).MatcherFunc(m),
  1098. request: newRequest("GET", "http://aaa.222.ccc"),
  1099. vars: map[string]string{},
  1100. host: "",
  1101. path: "",
  1102. shouldMatch: false,
  1103. },
  1104. }
  1105. for _, test := range tests {
  1106. testRoute(t, test)
  1107. testTemplate(t, test)
  1108. }
  1109. }
  1110. func TestBuildVarsFunc(t *testing.T) {
  1111. tests := []routeTest{
  1112. {
  1113. title: "BuildVarsFunc set on route",
  1114. route: new(Route).Path(`/111/{v1:\d}{v2:.*}`).BuildVarsFunc(func(vars map[string]string) map[string]string {
  1115. vars["v1"] = "3"
  1116. vars["v2"] = "a"
  1117. return vars
  1118. }),
  1119. request: newRequest("GET", "http://localhost/111/2"),
  1120. path: "/111/3a",
  1121. pathTemplate: `/111/{v1:\d}{v2:.*}`,
  1122. shouldMatch: true,
  1123. },
  1124. {
  1125. title: "BuildVarsFunc set on route and parent route",
  1126. route: new(Route).PathPrefix(`/{v1:\d}`).BuildVarsFunc(func(vars map[string]string) map[string]string {
  1127. vars["v1"] = "2"
  1128. return vars
  1129. }).Subrouter().Path(`/{v2:\w}`).BuildVarsFunc(func(vars map[string]string) map[string]string {
  1130. vars["v2"] = "b"
  1131. return vars
  1132. }),
  1133. request: newRequest("GET", "http://localhost/1/a"),
  1134. path: "/2/b",
  1135. pathTemplate: `/{v1:\d}/{v2:\w}`,
  1136. shouldMatch: true,
  1137. },
  1138. }
  1139. for _, test := range tests {
  1140. testRoute(t, test)
  1141. testTemplate(t, test)
  1142. }
  1143. }
  1144. func TestSubRouter(t *testing.T) {
  1145. subrouter1 := new(Route).Host("{v1:[a-z]+}.google.com").Subrouter()
  1146. subrouter2 := new(Route).PathPrefix("/foo/{v1}").Subrouter()
  1147. subrouter3 := new(Route).PathPrefix("/foo").Subrouter()
  1148. subrouter4 := new(Route).PathPrefix("/foo/bar").Subrouter()
  1149. subrouter5 := new(Route).PathPrefix("/{category}").Subrouter()
  1150. tests := []routeTest{
  1151. {
  1152. route: subrouter1.Path("/{v2:[a-z]+}"),
  1153. request: newRequest("GET", "http://aaa.google.com/bbb"),
  1154. vars: map[string]string{"v1": "aaa", "v2": "bbb"},
  1155. host: "aaa.google.com",
  1156. path: "/bbb",
  1157. pathTemplate: `/{v2:[a-z]+}`,
  1158. hostTemplate: `{v1:[a-z]+}.google.com`,
  1159. shouldMatch: true,
  1160. },
  1161. {
  1162. route: subrouter1.Path("/{v2:[a-z]+}"),
  1163. request: newRequest("GET", "http://111.google.com/111"),
  1164. vars: map[string]string{"v1": "aaa", "v2": "bbb"},
  1165. host: "aaa.google.com",
  1166. path: "/bbb",
  1167. pathTemplate: `/{v2:[a-z]+}`,
  1168. hostTemplate: `{v1:[a-z]+}.google.com`,
  1169. shouldMatch: false,
  1170. },
  1171. {
  1172. route: subrouter2.Path("/baz/{v2}"),
  1173. request: newRequest("GET", "http://localhost/foo/bar/baz/ding"),
  1174. vars: map[string]string{"v1": "bar", "v2": "ding"},
  1175. host: "",
  1176. path: "/foo/bar/baz/ding",
  1177. pathTemplate: `/foo/{v1}/baz/{v2}`,
  1178. shouldMatch: true,
  1179. },
  1180. {
  1181. route: subrouter2.Path("/baz/{v2}"),
  1182. request: newRequest("GET", "http://localhost/foo/bar"),
  1183. vars: map[string]string{"v1": "bar", "v2": "ding"},
  1184. host: "",
  1185. path: "/foo/bar/baz/ding",
  1186. pathTemplate: `/foo/{v1}/baz/{v2}`,
  1187. shouldMatch: false,
  1188. },
  1189. {
  1190. route: subrouter3.Path("/"),
  1191. request: newRequest("GET", "http://localhost/foo/"),
  1192. vars: map[string]string{},
  1193. host: "",
  1194. path: "/foo/",
  1195. pathTemplate: `/foo/`,
  1196. shouldMatch: true,
  1197. },
  1198. {
  1199. route: subrouter3.Path(""),
  1200. request: newRequest("GET", "http://localhost/foo"),
  1201. vars: map[string]string{},
  1202. host: "",
  1203. path: "/foo",
  1204. pathTemplate: `/foo`,
  1205. shouldMatch: true,
  1206. },
  1207. {
  1208. route: subrouter4.Path("/"),
  1209. request: newRequest("GET", "http://localhost/foo/bar/"),
  1210. vars: map[string]string{},
  1211. host: "",
  1212. path: "/foo/bar/",
  1213. pathTemplate: `/foo/bar/`,
  1214. shouldMatch: true,
  1215. },
  1216. {
  1217. route: subrouter4.Path(""),
  1218. request: newRequest("GET", "http://localhost/foo/bar"),
  1219. vars: map[string]string{},
  1220. host: "",
  1221. path: "/foo/bar",
  1222. pathTemplate: `/foo/bar`,
  1223. shouldMatch: true,
  1224. },
  1225. {
  1226. route: subrouter5.Path("/"),
  1227. request: newRequest("GET", "http://localhost/baz/"),
  1228. vars: map[string]string{"category": "baz"},
  1229. host: "",
  1230. path: "/baz/",
  1231. pathTemplate: `/{category}/`,
  1232. shouldMatch: true,
  1233. },
  1234. {
  1235. route: subrouter5.Path(""),
  1236. request: newRequest("GET", "http://localhost/baz"),
  1237. vars: map[string]string{"category": "baz"},
  1238. host: "",
  1239. path: "/baz",
  1240. pathTemplate: `/{category}`,
  1241. shouldMatch: true,
  1242. },
  1243. {
  1244. title: "Build with scheme on parent router",
  1245. route: new(Route).Schemes("ftp").Host("google.com").Subrouter().Path("/"),
  1246. request: newRequest("GET", "ftp://google.com/"),
  1247. scheme: "ftp",
  1248. host: "google.com",
  1249. path: "/",
  1250. pathTemplate: `/`,
  1251. hostTemplate: `google.com`,
  1252. shouldMatch: true,
  1253. },
  1254. {
  1255. title: "Prefer scheme on child route when building URLs",
  1256. route: new(Route).Schemes("https", "ftp").Host("google.com").Subrouter().Schemes("ftp").Path("/"),
  1257. request: newRequest("GET", "ftp://google.com/"),
  1258. scheme: "ftp",
  1259. host: "google.com",
  1260. path: "/",
  1261. pathTemplate: `/`,
  1262. hostTemplate: `google.com`,
  1263. shouldMatch: true,
  1264. },
  1265. }
  1266. for _, test := range tests {
  1267. testRoute(t, test)
  1268. testTemplate(t, test)
  1269. testUseEscapedRoute(t, test)
  1270. }
  1271. }
  1272. func TestNamedRoutes(t *testing.T) {
  1273. r1 := NewRouter()
  1274. r1.NewRoute().Name("a")
  1275. r1.NewRoute().Name("b")
  1276. r1.NewRoute().Name("c")
  1277. r2 := r1.NewRoute().Subrouter()
  1278. r2.NewRoute().Name("d")
  1279. r2.NewRoute().Name("e")
  1280. r2.NewRoute().Name("f")
  1281. r3 := r2.NewRoute().Subrouter()
  1282. r3.NewRoute().Name("g")
  1283. r3.NewRoute().Name("h")
  1284. r3.NewRoute().Name("i")
  1285. if r1.namedRoutes == nil || len(r1.namedRoutes) != 9 {
  1286. t.Errorf("Expected 9 named routes, got %v", r1.namedRoutes)
  1287. } else if r1.Get("i") == nil {
  1288. t.Errorf("Subroute name not registered")
  1289. }
  1290. }
  1291. func TestStrictSlash(t *testing.T) {
  1292. r := NewRouter()
  1293. r.StrictSlash(true)
  1294. tests := []routeTest{
  1295. {
  1296. title: "Redirect path without slash",
  1297. route: r.NewRoute().Path("/111/"),
  1298. request: newRequest("GET", "http://localhost/111"),
  1299. vars: map[string]string{},
  1300. host: "",
  1301. path: "/111/",
  1302. shouldMatch: true,
  1303. shouldRedirect: true,
  1304. },
  1305. {
  1306. title: "Do not redirect path with slash",
  1307. route: r.NewRoute().Path("/111/"),
  1308. request: newRequest("GET", "http://localhost/111/"),
  1309. vars: map[string]string{},
  1310. host: "",
  1311. path: "/111/",
  1312. shouldMatch: true,
  1313. shouldRedirect: false,
  1314. },
  1315. {
  1316. title: "Redirect path with slash",
  1317. route: r.NewRoute().Path("/111"),
  1318. request: newRequest("GET", "http://localhost/111/"),
  1319. vars: map[string]string{},
  1320. host: "",
  1321. path: "/111",
  1322. shouldMatch: true,
  1323. shouldRedirect: true,
  1324. },
  1325. {
  1326. title: "Do not redirect path without slash",
  1327. route: r.NewRoute().Path("/111"),
  1328. request: newRequest("GET", "http://localhost/111"),
  1329. vars: map[string]string{},
  1330. host: "",
  1331. path: "/111",
  1332. shouldMatch: true,
  1333. shouldRedirect: false,
  1334. },
  1335. {
  1336. title: "Propagate StrictSlash to subrouters",
  1337. route: r.NewRoute().PathPrefix("/static/").Subrouter().Path("/images/"),
  1338. request: newRequest("GET", "http://localhost/static/images"),
  1339. vars: map[string]string{},
  1340. host: "",
  1341. path: "/static/images/",
  1342. shouldMatch: true,
  1343. shouldRedirect: true,
  1344. },
  1345. {
  1346. title: "Ignore StrictSlash for path prefix",
  1347. route: r.NewRoute().PathPrefix("/static/"),
  1348. request: newRequest("GET", "http://localhost/static/logo.png"),
  1349. vars: map[string]string{},
  1350. host: "",
  1351. path: "/static/",
  1352. shouldMatch: true,
  1353. shouldRedirect: false,
  1354. },
  1355. }
  1356. for _, test := range tests {
  1357. testRoute(t, test)
  1358. testTemplate(t, test)
  1359. testUseEscapedRoute(t, test)
  1360. }
  1361. }
  1362. func TestUseEncodedPath(t *testing.T) {
  1363. r := NewRouter()
  1364. r.UseEncodedPath()
  1365. tests := []routeTest{
  1366. {
  1367. title: "Router with useEncodedPath, URL with encoded slash does match",
  1368. route: r.NewRoute().Path("/v1/{v1}/v2"),
  1369. request: newRequest("GET", "http://localhost/v1/1%2F2/v2"),
  1370. vars: map[string]string{"v1": "1%2F2"},
  1371. host: "",
  1372. path: "/v1/1%2F2/v2",
  1373. pathTemplate: `/v1/{v1}/v2`,
  1374. shouldMatch: true,
  1375. },
  1376. {
  1377. title: "Router with useEncodedPath, URL with encoded slash doesn't match",
  1378. route: r.NewRoute().Path("/v1/1/2/v2"),
  1379. request: newRequest("GET", "http://localhost/v1/1%2F2/v2"),
  1380. vars: map[string]string{"v1": "1%2F2"},
  1381. host: "",
  1382. path: "/v1/1%2F2/v2",
  1383. pathTemplate: `/v1/1/2/v2`,
  1384. shouldMatch: false,
  1385. },
  1386. }
  1387. for _, test := range tests {
  1388. testRoute(t, test)
  1389. testTemplate(t, test)
  1390. }
  1391. }
  1392. func TestWalkSingleDepth(t *testing.T) {
  1393. r0 := NewRouter()
  1394. r1 := NewRouter()
  1395. r2 := NewRouter()
  1396. r0.Path("/g")
  1397. r0.Path("/o")
  1398. r0.Path("/d").Handler(r1)
  1399. r0.Path("/r").Handler(r2)
  1400. r0.Path("/a")
  1401. r1.Path("/z")
  1402. r1.Path("/i")
  1403. r1.Path("/l")
  1404. r1.Path("/l")
  1405. r2.Path("/i")
  1406. r2.Path("/l")
  1407. r2.Path("/l")
  1408. paths := []string{"g", "o", "r", "i", "l", "l", "a"}
  1409. depths := []int{0, 0, 0, 1, 1, 1, 0}
  1410. i := 0
  1411. err := r0.Walk(func(route *Route, router *Router, ancestors []*Route) error {
  1412. matcher := route.matchers[0].(*routeRegexp)
  1413. if matcher.template == "/d" {
  1414. return SkipRouter
  1415. }
  1416. if len(ancestors) != depths[i] {
  1417. t.Errorf(`Expected depth of %d at i = %d; got "%d"`, depths[i], i, len(ancestors))
  1418. }
  1419. if matcher.template != "/"+paths[i] {
  1420. t.Errorf(`Expected "/%s" at i = %d; got "%s"`, paths[i], i, matcher.template)
  1421. }
  1422. i++
  1423. return nil
  1424. })
  1425. if err != nil {
  1426. panic(err)
  1427. }
  1428. if i != len(paths) {
  1429. t.Errorf("Expected %d routes, found %d", len(paths), i)
  1430. }
  1431. }
  1432. func TestWalkNested(t *testing.T) {
  1433. router := NewRouter()
  1434. g := router.Path("/g").Subrouter()
  1435. o := g.PathPrefix("/o").Subrouter()
  1436. r := o.PathPrefix("/r").Subrouter()
  1437. i := r.PathPrefix("/i").Subrouter()
  1438. l1 := i.PathPrefix("/l").Subrouter()
  1439. l2 := l1.PathPrefix("/l").Subrouter()
  1440. l2.Path("/a")
  1441. testCases := []struct {
  1442. path string
  1443. ancestors []*Route
  1444. }{
  1445. {"/g", []*Route{}},
  1446. {"/g/o", []*Route{g.parent.(*Route)}},
  1447. {"/g/o/r", []*Route{g.parent.(*Route), o.parent.(*Route)}},
  1448. {"/g/o/r/i", []*Route{g.parent.(*Route), o.parent.(*Route), r.parent.(*Route)}},
  1449. {"/g/o/r/i/l", []*Route{g.parent.(*Route), o.parent.(*Route), r.parent.(*Route), i.parent.(*Route)}},
  1450. {"/g/o/r/i/l/l", []*Route{g.parent.(*Route), o.parent.(*Route), r.parent.(*Route), i.parent.(*Route), l1.parent.(*Route)}},
  1451. {"/g/o/r/i/l/l/a", []*Route{g.parent.(*Route), o.parent.(*Route), r.parent.(*Route), i.parent.(*Route), l1.parent.(*Route), l2.parent.(*Route)}},
  1452. }
  1453. idx := 0
  1454. err := router.Walk(func(route *Route, router *Router, ancestors []*Route) error {
  1455. path := testCases[idx].path
  1456. tpl := route.regexp.path.template
  1457. if tpl != path {
  1458. t.Errorf(`Expected %s got %s`, path, tpl)
  1459. }
  1460. currWantAncestors := testCases[idx].ancestors
  1461. if !reflect.DeepEqual(currWantAncestors, ancestors) {
  1462. t.Errorf(`Expected %+v got %+v`, currWantAncestors, ancestors)
  1463. }
  1464. idx++
  1465. return nil
  1466. })
  1467. if err != nil {
  1468. panic(err)
  1469. }
  1470. if idx != len(testCases) {
  1471. t.Errorf("Expected %d routes, found %d", len(testCases), idx)
  1472. }
  1473. }
  1474. func TestWalkSubrouters(t *testing.T) {
  1475. router := NewRouter()
  1476. g := router.Path("/g").Subrouter()
  1477. o := g.PathPrefix("/o").Subrouter()
  1478. o.Methods("GET")
  1479. o.Methods("PUT")
  1480. // all 4 routes should be matched, but final 2 routes do not have path templates
  1481. paths := []string{"/g", "/g/o", "", ""}
  1482. idx := 0
  1483. err := router.Walk(func(route *Route, router *Router, ancestors []*Route) error {
  1484. path := paths[idx]
  1485. tpl, _ := route.GetPathTemplate()
  1486. if tpl != path {
  1487. t.Errorf(`Expected %s got %s`, path, tpl)
  1488. }
  1489. idx++
  1490. return nil
  1491. })
  1492. if err != nil {
  1493. panic(err)
  1494. }
  1495. if idx != len(paths) {
  1496. t.Errorf("Expected %d routes, found %d", len(paths), idx)
  1497. }
  1498. }
  1499. func TestWalkErrorRoute(t *testing.T) {
  1500. router := NewRouter()
  1501. router.Path("/g")
  1502. expectedError := errors.New("error")
  1503. err := router.Walk(func(route *Route, router *Router, ancestors []*Route) error {
  1504. return expectedError
  1505. })
  1506. if err != expectedError {
  1507. t.Errorf("Expected %v routes, found %v", expectedError, err)
  1508. }
  1509. }
  1510. func TestWalkErrorMatcher(t *testing.T) {
  1511. router := NewRouter()
  1512. expectedError := router.Path("/g").Subrouter().Path("").GetError()
  1513. err := router.Walk(func(route *Route, router *Router, ancestors []*Route) error {
  1514. return route.GetError()
  1515. })
  1516. if err != expectedError {
  1517. t.Errorf("Expected %v routes, found %v", expectedError, err)
  1518. }
  1519. }
  1520. func TestWalkErrorHandler(t *testing.T) {
  1521. handler := NewRouter()
  1522. expectedError := handler.Path("/path").Subrouter().Path("").GetError()
  1523. router := NewRouter()
  1524. router.Path("/g").Handler(handler)
  1525. err := router.Walk(func(route *Route, router *Router, ancestors []*Route) error {
  1526. return route.GetError()
  1527. })
  1528. if err != expectedError {
  1529. t.Errorf("Expected %v routes, found %v", expectedError, err)
  1530. }
  1531. }
  1532. func TestSubrouterErrorHandling(t *testing.T) {
  1533. superRouterCalled := false
  1534. subRouterCalled := false
  1535. router := NewRouter()
  1536. router.NotFoundHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  1537. superRouterCalled = true
  1538. })
  1539. subRouter := router.PathPrefix("/bign8").Subrouter()
  1540. subRouter.NotFoundHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  1541. subRouterCalled = true
  1542. })
  1543. req, _ := http.NewRequest("GET", "http://localhost/bign8/was/here", nil)
  1544. router.ServeHTTP(NewRecorder(), req)
  1545. if superRouterCalled {
  1546. t.Error("Super router 404 handler called when sub-router 404 handler is available.")
  1547. }
  1548. if !subRouterCalled {
  1549. t.Error("Sub-router 404 handler was not called.")
  1550. }
  1551. }
  1552. // See: https://github.com/gorilla/mux/issues/200
  1553. func TestPanicOnCapturingGroups(t *testing.T) {
  1554. defer func() {
  1555. if recover() == nil {
  1556. t.Errorf("(Test that capturing groups now fail fast) Expected panic, however test completed successfully.\n")
  1557. }
  1558. }()
  1559. NewRouter().NewRoute().Path("/{type:(promo|special)}/{promoId}.json")
  1560. }
  1561. // ----------------------------------------------------------------------------
  1562. // Helpers
  1563. // ----------------------------------------------------------------------------
  1564. func getRouteTemplate(route *Route) string {
  1565. host, err := route.GetHostTemplate()
  1566. if err != nil {
  1567. host = "none"
  1568. }
  1569. path, err := route.GetPathTemplate()
  1570. if err != nil {
  1571. path = "none"
  1572. }
  1573. return fmt.Sprintf("Host: %v, Path: %v", host, path)
  1574. }
  1575. func testRoute(t *testing.T, test routeTest) {
  1576. request := test.request
  1577. route := test.route
  1578. vars := test.vars
  1579. shouldMatch := test.shouldMatch
  1580. query := test.query
  1581. shouldRedirect := test.shouldRedirect
  1582. uri := url.URL{
  1583. Scheme: test.scheme,
  1584. Host: test.host,
  1585. Path: test.path,
  1586. }
  1587. if uri.Scheme == "" {
  1588. uri.Scheme = "http"
  1589. }
  1590. var match RouteMatch
  1591. ok := route.Match(request, &match)
  1592. if ok != shouldMatch {
  1593. msg := "Should match"
  1594. if !shouldMatch {
  1595. msg = "Should not match"
  1596. }
  1597. t.Errorf("(%v) %v:\nRoute: %#v\nRequest: %#v\nVars: %v\n", test.title, msg, route, request, vars)
  1598. return
  1599. }
  1600. if shouldMatch {
  1601. if vars != nil && !stringMapEqual(vars, match.Vars) {
  1602. t.Errorf("(%v) Vars not equal: expected %v, got %v", test.title, vars, match.Vars)
  1603. return
  1604. }
  1605. if test.scheme != "" {
  1606. u, err := route.URL(mapToPairs(match.Vars)...)
  1607. if err != nil {
  1608. t.Fatalf("(%v) URL error: %v -- %v", test.title, err, getRouteTemplate(route))
  1609. }
  1610. if uri.Scheme != u.Scheme {
  1611. t.Errorf("(%v) URLScheme not equal: expected %v, got %v", test.title, uri.Scheme, u.Scheme)
  1612. return
  1613. }
  1614. }
  1615. if test.host != "" {
  1616. u, err := test.route.URLHost(mapToPairs(match.Vars)...)
  1617. if err != nil {
  1618. t.Fatalf("(%v) URLHost error: %v -- %v", test.title, err, getRouteTemplate(route))
  1619. }
  1620. if uri.Scheme != u.Scheme {
  1621. t.Errorf("(%v) URLHost scheme not equal: expected %v, got %v -- %v", test.title, uri.Scheme, u.Scheme, getRouteTemplate(route))
  1622. return
  1623. }
  1624. if uri.Host != u.Host {
  1625. t.Errorf("(%v) URLHost host not equal: expected %v, got %v -- %v", test.title, uri.Host, u.Host, getRouteTemplate(route))
  1626. return
  1627. }
  1628. }
  1629. if test.path != "" {
  1630. u, err := route.URLPath(mapToPairs(match.Vars)...)
  1631. if err != nil {
  1632. t.Fatalf("(%v) URLPath error: %v -- %v", test.title, err, getRouteTemplate(route))
  1633. }
  1634. if uri.Path != u.Path {
  1635. t.Errorf("(%v) URLPath not equal: expected %v, got %v -- %v", test.title, uri.Path, u.Path, getRouteTemplate(route))
  1636. return
  1637. }
  1638. }
  1639. if test.host != "" && test.path != "" {
  1640. u, err := route.URL(mapToPairs(match.Vars)...)
  1641. if err != nil {
  1642. t.Fatalf("(%v) URL error: %v -- %v", test.title, err, getRouteTemplate(route))
  1643. }
  1644. if expected, got := uri.String(), u.String(); expected != got {
  1645. t.Errorf("(%v) URL not equal: expected %v, got %v -- %v", test.title, expected, got, getRouteTemplate(route))
  1646. return
  1647. }
  1648. }
  1649. if query != "" {
  1650. u, _ := route.URL(mapToPairs(match.Vars)...)
  1651. if query != u.RawQuery {
  1652. t.Errorf("(%v) URL query not equal: expected %v, got %v", test.title, query, u.RawQuery)
  1653. return
  1654. }
  1655. }
  1656. if shouldRedirect && match.Handler == nil {
  1657. t.Errorf("(%v) Did not redirect", test.title)
  1658. return
  1659. }
  1660. if !shouldRedirect && match.Handler != nil {
  1661. t.Errorf("(%v) Unexpected redirect", test.title)
  1662. return
  1663. }
  1664. }
  1665. }
  1666. func testUseEscapedRoute(t *testing.T, test routeTest) {
  1667. test.route.useEncodedPath = true
  1668. testRoute(t, test)
  1669. }
  1670. func testTemplate(t *testing.T, test routeTest) {
  1671. route := test.route
  1672. pathTemplate := test.pathTemplate
  1673. if len(pathTemplate) == 0 {
  1674. pathTemplate = test.path
  1675. }
  1676. hostTemplate := test.hostTemplate
  1677. if len(hostTemplate) == 0 {
  1678. hostTemplate = test.host
  1679. }
  1680. routePathTemplate, pathErr := route.GetPathTemplate()
  1681. if pathErr == nil && routePathTemplate != pathTemplate {
  1682. t.Errorf("(%v) GetPathTemplate not equal: expected %v, got %v", test.title, pathTemplate, routePathTemplate)
  1683. }
  1684. routeHostTemplate, hostErr := route.GetHostTemplate()
  1685. if hostErr == nil && routeHostTemplate != hostTemplate {
  1686. t.Errorf("(%v) GetHostTemplate not equal: expected %v, got %v", test.title, hostTemplate, routeHostTemplate)
  1687. }
  1688. }
  1689. func testMethods(t *testing.T, test routeTest) {
  1690. route := test.route
  1691. methods, _ := route.GetMethods()
  1692. if strings.Join(methods, ",") != strings.Join(test.methods, ",") {
  1693. t.Errorf("(%v) GetMethods not equal: expected %v, got %v", test.title, test.methods, methods)
  1694. }
  1695. }
  1696. func testRegexp(t *testing.T, test routeTest) {
  1697. route := test.route
  1698. routePathRegexp, regexpErr := route.GetPathRegexp()
  1699. if test.pathRegexp != "" && regexpErr == nil && routePathRegexp != test.pathRegexp {
  1700. t.Errorf("(%v) GetPathRegexp not equal: expected %v, got %v", test.title, test.pathRegexp, routePathRegexp)
  1701. }
  1702. }
  1703. func testQueriesRegexp(t *testing.T, test routeTest) {
  1704. route := test.route
  1705. queries, queriesErr := route.GetQueriesRegexp()
  1706. gotQueries := strings.Join(queries, ",")
  1707. if test.queriesRegexp != "" && queriesErr == nil && gotQueries != test.queriesRegexp {
  1708. t.Errorf("(%v) GetQueriesRegexp not equal: expected %v, got %v", test.title, test.queriesRegexp, gotQueries)
  1709. }
  1710. }
  1711. func testQueriesTemplates(t *testing.T, test routeTest) {
  1712. route := test.route
  1713. queries, queriesErr := route.GetQueriesTemplates()
  1714. gotQueries := strings.Join(queries, ",")
  1715. if test.queriesTemplate != "" && queriesErr == nil && gotQueries != test.queriesTemplate {
  1716. t.Errorf("(%v) GetQueriesTemplates not equal: expected %v, got %v", test.title, test.queriesTemplate, gotQueries)
  1717. }
  1718. }
  1719. type TestA301ResponseWriter struct {
  1720. hh http.Header
  1721. status int
  1722. }
  1723. func (ho *TestA301ResponseWriter) Header() http.Header {
  1724. return http.Header(ho.hh)
  1725. }
  1726. func (ho *TestA301ResponseWriter) Write(b []byte) (int, error) {
  1727. return 0, nil
  1728. }
  1729. func (ho *TestA301ResponseWriter) WriteHeader(code int) {
  1730. ho.status = code
  1731. }
  1732. func Test301Redirect(t *testing.T) {
  1733. m := make(http.Header)
  1734. func1 := func(w http.ResponseWriter, r *http.Request) {}
  1735. func2 := func(w http.ResponseWriter, r *http.Request) {}
  1736. r := NewRouter()
  1737. r.HandleFunc("/api/", func2).Name("func2")
  1738. r.HandleFunc("/", func1).Name("func1")
  1739. req, _ := http.NewRequest("GET", "http://localhost//api/?abc=def", nil)
  1740. res := TestA301ResponseWriter{
  1741. hh: m,
  1742. status: 0,
  1743. }
  1744. r.ServeHTTP(&res, req)
  1745. if "http://localhost/api/?abc=def" != res.hh["Location"][0] {
  1746. t.Errorf("Should have complete URL with query string")
  1747. }
  1748. }
  1749. func TestSkipClean(t *testing.T) {
  1750. func1 := func(w http.ResponseWriter, r *http.Request) {}
  1751. func2 := func(w http.ResponseWriter, r *http.Request) {}
  1752. r := NewRouter()
  1753. r.SkipClean(true)
  1754. r.HandleFunc("/api/", func2).Name("func2")
  1755. r.HandleFunc("/", func1).Name("func1")
  1756. req, _ := http.NewRequest("GET", "http://localhost//api/?abc=def", nil)
  1757. res := NewRecorder()
  1758. r.ServeHTTP(res, req)
  1759. if len(res.HeaderMap["Location"]) != 0 {
  1760. t.Errorf("Shouldn't redirect since skip clean is disabled")
  1761. }
  1762. }
  1763. // https://plus.google.com/101022900381697718949/posts/eWy6DjFJ6uW
  1764. func TestSubrouterHeader(t *testing.T) {
  1765. expected := "func1 response"
  1766. func1 := func(w http.ResponseWriter, r *http.Request) {
  1767. fmt.Fprint(w, expected)
  1768. }
  1769. func2 := func(http.ResponseWriter, *http.Request) {}
  1770. r := NewRouter()
  1771. s := r.Headers("SomeSpecialHeader", "").Subrouter()
  1772. s.HandleFunc("/", func1).Name("func1")
  1773. r.HandleFunc("/", func2).Name("func2")
  1774. req, _ := http.NewRequest("GET", "http://localhost/", nil)
  1775. req.Header.Add("SomeSpecialHeader", "foo")
  1776. match := new(RouteMatch)
  1777. matched := r.Match(req, match)
  1778. if !matched {
  1779. t.Errorf("Should match request")
  1780. }
  1781. if match.Route.GetName() != "func1" {
  1782. t.Errorf("Expecting func1 handler, got %s", match.Route.GetName())
  1783. }
  1784. resp := NewRecorder()
  1785. match.Handler.ServeHTTP(resp, req)
  1786. if resp.Body.String() != expected {
  1787. t.Errorf("Expecting %q", expected)
  1788. }
  1789. }
  1790. func TestNoMatchMethodErrorHandler(t *testing.T) {
  1791. func1 := func(w http.ResponseWriter, r *http.Request) {}
  1792. r := NewRouter()
  1793. r.HandleFunc("/", func1).Methods("GET", "POST")
  1794. req, _ := http.NewRequest("PUT", "http://localhost/", nil)
  1795. match := new(RouteMatch)
  1796. matched := r.Match(req, match)
  1797. if matched {
  1798. t.Error("Should not have matched route for methods")
  1799. }
  1800. if match.MatchErr != ErrMethodMismatch {
  1801. t.Error("Should get ErrMethodMismatch error")
  1802. }
  1803. resp := NewRecorder()
  1804. r.ServeHTTP(resp, req)
  1805. if resp.Code != 405 {
  1806. t.Errorf("Expecting code %v", 405)
  1807. }
  1808. // Add matching route
  1809. r.HandleFunc("/", func1).Methods("PUT")
  1810. match = new(RouteMatch)
  1811. matched = r.Match(req, match)
  1812. if !matched {
  1813. t.Error("Should have matched route for methods")
  1814. }
  1815. if match.MatchErr != nil {
  1816. t.Error("Should not have any matching error. Found:", match.MatchErr)
  1817. }
  1818. }
  1819. func TestErrMatchNotFound(t *testing.T) {
  1820. emptyHandler := func(w http.ResponseWriter, r *http.Request) {}
  1821. r := NewRouter()
  1822. r.HandleFunc("/", emptyHandler)
  1823. s := r.PathPrefix("/sub/").Subrouter()
  1824. s.HandleFunc("/", emptyHandler)
  1825. // Regular 404 not found
  1826. req, _ := http.NewRequest("GET", "/sub/whatever", nil)
  1827. match := new(RouteMatch)
  1828. matched := r.Match(req, match)
  1829. if matched {
  1830. t.Errorf("Subrouter should not have matched that, got %v", match.Route)
  1831. }
  1832. // Even without a custom handler, MatchErr is set to ErrNotFound
  1833. if match.MatchErr != ErrNotFound {
  1834. t.Errorf("Expected ErrNotFound MatchErr, but was %v", match.MatchErr)
  1835. }
  1836. // Now lets add a 404 handler to subrouter
  1837. s.NotFoundHandler = http.NotFoundHandler()
  1838. req, _ = http.NewRequest("GET", "/sub/whatever", nil)
  1839. // Test the subrouter first
  1840. match = new(RouteMatch)
  1841. matched = s.Match(req, match)
  1842. // Now we should get a match
  1843. if !matched {
  1844. t.Errorf("Subrouter should have matched %s", req.RequestURI)
  1845. }
  1846. // But MatchErr should be set to ErrNotFound anyway
  1847. if match.MatchErr != ErrNotFound {
  1848. t.Errorf("Expected ErrNotFound MatchErr, but was %v", match.MatchErr)
  1849. }
  1850. // Now test the parent (MatchErr should propagate)
  1851. match = new(RouteMatch)
  1852. matched = r.Match(req, match)
  1853. // Now we should get a match
  1854. if !matched {
  1855. t.Errorf("Router should have matched %s via subrouter", req.RequestURI)
  1856. }
  1857. // But MatchErr should be set to ErrNotFound anyway
  1858. if match.MatchErr != ErrNotFound {
  1859. t.Errorf("Expected ErrNotFound MatchErr, but was %v", match.MatchErr)
  1860. }
  1861. }
  1862. // methodsSubrouterTest models the data necessary for testing handler
  1863. // matching for subrouters created after HTTP methods matcher registration.
  1864. type methodsSubrouterTest struct {
  1865. title string
  1866. wantCode int
  1867. router *Router
  1868. // method is the input into the request and expected response
  1869. method string
  1870. // input request path
  1871. path string
  1872. // redirectTo is the expected location path for strict-slash matches
  1873. redirectTo string
  1874. }
  1875. // methodHandler writes the method string in response.
  1876. func methodHandler(method string) http.HandlerFunc {
  1877. return func(w http.ResponseWriter, r *http.Request) {
  1878. w.Write([]byte(method))
  1879. }
  1880. }
  1881. // TestMethodsSubrouterCatchall matches handlers for subrouters where a
  1882. // catchall handler is set for a mis-matching method.
  1883. func TestMethodsSubrouterCatchall(t *testing.T) {
  1884. t.Parallel()
  1885. router := NewRouter()
  1886. router.Methods("PATCH").Subrouter().PathPrefix("/").HandlerFunc(methodHandler("PUT"))
  1887. router.Methods("GET").Subrouter().HandleFunc("/foo", methodHandler("GET"))
  1888. router.Methods("POST").Subrouter().HandleFunc("/foo", methodHandler("POST"))
  1889. router.Methods("DELETE").Subrouter().HandleFunc("/foo", methodHandler("DELETE"))
  1890. tests := []methodsSubrouterTest{
  1891. {
  1892. title: "match GET handler",
  1893. router: router,
  1894. path: "http://localhost/foo",
  1895. method: "GET",
  1896. wantCode: http.StatusOK,
  1897. },
  1898. {
  1899. title: "match POST handler",
  1900. router: router,
  1901. method: "POST",
  1902. path: "http://localhost/foo",
  1903. wantCode: http.StatusOK,
  1904. },
  1905. {
  1906. title: "match DELETE handler",
  1907. router: router,
  1908. method: "DELETE",
  1909. path: "http://localhost/foo",
  1910. wantCode: http.StatusOK,
  1911. },
  1912. {
  1913. title: "disallow PUT method",
  1914. router: router,
  1915. method: "PUT",
  1916. path: "http://localhost/foo",
  1917. wantCode: http.StatusMethodNotAllowed,
  1918. },
  1919. }
  1920. for _, test := range tests {
  1921. testMethodsSubrouter(t, test)
  1922. }
  1923. }
  1924. // TestMethodsSubrouterStrictSlash matches handlers on subrouters with
  1925. // strict-slash matchers.
  1926. func TestMethodsSubrouterStrictSlash(t *testing.T) {
  1927. t.Parallel()
  1928. router := NewRouter()
  1929. sub := router.PathPrefix("/").Subrouter()
  1930. sub.StrictSlash(true).Path("/foo").Methods("GET").Subrouter().HandleFunc("", methodHandler("GET"))
  1931. sub.StrictSlash(true).Path("/foo/").Methods("PUT").Subrouter().HandleFunc("/", methodHandler("PUT"))
  1932. sub.StrictSlash(true).Path("/foo/").Methods("POST").Subrouter().HandleFunc("/", methodHandler("POST"))
  1933. tests := []methodsSubrouterTest{
  1934. {
  1935. title: "match POST handler",
  1936. router: router,
  1937. method: "POST",
  1938. path: "http://localhost/foo/",
  1939. wantCode: http.StatusOK,
  1940. },
  1941. {
  1942. title: "match GET handler",
  1943. router: router,
  1944. method: "GET",
  1945. path: "http://localhost/foo",
  1946. wantCode: http.StatusOK,
  1947. },
  1948. {
  1949. title: "match POST handler, redirect strict-slash",
  1950. router: router,
  1951. method: "POST",
  1952. path: "http://localhost/foo",
  1953. redirectTo: "http://localhost/foo/",
  1954. wantCode: http.StatusMovedPermanently,
  1955. },
  1956. {
  1957. title: "match GET handler, redirect strict-slash",
  1958. router: router,
  1959. method: "GET",
  1960. path: "http://localhost/foo/",
  1961. redirectTo: "http://localhost/foo",
  1962. wantCode: http.StatusMovedPermanently,
  1963. },
  1964. {
  1965. title: "disallow DELETE method",
  1966. router: router,
  1967. method: "DELETE",
  1968. path: "http://localhost/foo",
  1969. wantCode: http.StatusMethodNotAllowed,
  1970. },
  1971. }
  1972. for _, test := range tests {
  1973. testMethodsSubrouter(t, test)
  1974. }
  1975. }
  1976. // TestMethodsSubrouterPathPrefix matches handlers on subrouters created
  1977. // on a router with a path prefix matcher and method matcher.
  1978. func TestMethodsSubrouterPathPrefix(t *testing.T) {
  1979. t.Parallel()
  1980. router := NewRouter()
  1981. router.PathPrefix("/1").Methods("POST").Subrouter().HandleFunc("/2", methodHandler("POST"))
  1982. router.PathPrefix("/1").Methods("DELETE").Subrouter().HandleFunc("/2", methodHandler("DELETE"))
  1983. router.PathPrefix("/1").Methods("PUT").Subrouter().HandleFunc("/2", methodHandler("PUT"))
  1984. router.PathPrefix("/1").Methods("POST").Subrouter().HandleFunc("/2", methodHandler("POST2"))
  1985. tests := []methodsSubrouterTest{
  1986. {
  1987. title: "match first POST handler",
  1988. router: router,
  1989. method: "POST",
  1990. path: "http://localhost/1/2",
  1991. wantCode: http.StatusOK,
  1992. },
  1993. {
  1994. title: "match DELETE handler",
  1995. router: router,
  1996. method: "DELETE",
  1997. path: "http://localhost/1/2",
  1998. wantCode: http.StatusOK,
  1999. },
  2000. {
  2001. title: "match PUT handler",
  2002. router: router,
  2003. method: "PUT",
  2004. path: "http://localhost/1/2",
  2005. wantCode: http.StatusOK,
  2006. },
  2007. {
  2008. title: "disallow PATCH method",
  2009. router: router,
  2010. method: "PATCH",
  2011. path: "http://localhost/1/2",
  2012. wantCode: http.StatusMethodNotAllowed,
  2013. },
  2014. }
  2015. for _, test := range tests {
  2016. testMethodsSubrouter(t, test)
  2017. }
  2018. }
  2019. // TestMethodsSubrouterSubrouter matches handlers on subrouters produced
  2020. // from method matchers registered on a root subrouter.
  2021. func TestMethodsSubrouterSubrouter(t *testing.T) {
  2022. t.Parallel()
  2023. router := NewRouter()
  2024. sub := router.PathPrefix("/1").Subrouter()
  2025. sub.Methods("POST").Subrouter().HandleFunc("/2", methodHandler("POST"))
  2026. sub.Methods("GET").Subrouter().HandleFunc("/2", methodHandler("GET"))
  2027. sub.Methods("PATCH").Subrouter().HandleFunc("/2", methodHandler("PATCH"))
  2028. sub.HandleFunc("/2", methodHandler("PUT")).Subrouter().Methods("PUT")
  2029. sub.HandleFunc("/2", methodHandler("POST2")).Subrouter().Methods("POST")
  2030. tests := []methodsSubrouterTest{
  2031. {
  2032. title: "match first POST handler",
  2033. router: router,
  2034. method: "POST",
  2035. path: "http://localhost/1/2",
  2036. wantCode: http.StatusOK,
  2037. },
  2038. {
  2039. title: "match GET handler",
  2040. router: router,
  2041. method: "GET",
  2042. path: "http://localhost/1/2",
  2043. wantCode: http.StatusOK,
  2044. },
  2045. {
  2046. title: "match PATCH handler",
  2047. router: router,
  2048. method: "PATCH",
  2049. path: "http://localhost/1/2",
  2050. wantCode: http.StatusOK,
  2051. },
  2052. {
  2053. title: "match PUT handler",
  2054. router: router,
  2055. method: "PUT",
  2056. path: "http://localhost/1/2",
  2057. wantCode: http.StatusOK,
  2058. },
  2059. {
  2060. title: "disallow DELETE method",
  2061. router: router,
  2062. method: "DELETE",
  2063. path: "http://localhost/1/2",
  2064. wantCode: http.StatusMethodNotAllowed,
  2065. },
  2066. }
  2067. for _, test := range tests {
  2068. testMethodsSubrouter(t, test)
  2069. }
  2070. }
  2071. // TestMethodsSubrouterPathVariable matches handlers on matching paths
  2072. // with path variables in them.
  2073. func TestMethodsSubrouterPathVariable(t *testing.T) {
  2074. t.Parallel()
  2075. router := NewRouter()
  2076. router.Methods("GET").Subrouter().HandleFunc("/foo", methodHandler("GET"))
  2077. router.Methods("POST").Subrouter().HandleFunc("/{any}", methodHandler("POST"))
  2078. router.Methods("DELETE").Subrouter().HandleFunc("/1/{any}", methodHandler("DELETE"))
  2079. router.Methods("PUT").Subrouter().HandleFunc("/1/{any}", methodHandler("PUT"))
  2080. tests := []methodsSubrouterTest{
  2081. {
  2082. title: "match GET handler",
  2083. router: router,
  2084. method: "GET",
  2085. path: "http://localhost/foo",
  2086. wantCode: http.StatusOK,
  2087. },
  2088. {
  2089. title: "match POST handler",
  2090. router: router,
  2091. method: "POST",
  2092. path: "http://localhost/foo",
  2093. wantCode: http.StatusOK,
  2094. },
  2095. {
  2096. title: "match DELETE handler",
  2097. router: router,
  2098. method: "DELETE",
  2099. path: "http://localhost/1/foo",
  2100. wantCode: http.StatusOK,
  2101. },
  2102. {
  2103. title: "match PUT handler",
  2104. router: router,
  2105. method: "PUT",
  2106. path: "http://localhost/1/foo",
  2107. wantCode: http.StatusOK,
  2108. },
  2109. {
  2110. title: "disallow PATCH method",
  2111. router: router,
  2112. method: "PATCH",
  2113. path: "http://localhost/1/foo",
  2114. wantCode: http.StatusMethodNotAllowed,
  2115. },
  2116. }
  2117. for _, test := range tests {
  2118. testMethodsSubrouter(t, test)
  2119. }
  2120. }
  2121. // testMethodsSubrouter runs an individual methodsSubrouterTest.
  2122. func testMethodsSubrouter(t *testing.T, test methodsSubrouterTest) {
  2123. // Execute request
  2124. req, _ := http.NewRequest(test.method, test.path, nil)
  2125. resp := NewRecorder()
  2126. test.router.ServeHTTP(resp, req)
  2127. switch test.wantCode {
  2128. case http.StatusMethodNotAllowed:
  2129. if resp.Code != http.StatusMethodNotAllowed {
  2130. t.Errorf(`(%s) Expected "405 Method Not Allowed", but got %d code`, test.title, resp.Code)
  2131. } else if matchedMethod := resp.Body.String(); matchedMethod != "" {
  2132. t.Errorf(`(%s) Expected "405 Method Not Allowed", but %q handler was called`, test.title, matchedMethod)
  2133. }
  2134. case http.StatusMovedPermanently:
  2135. if gotLocation := resp.HeaderMap.Get("Location"); gotLocation != test.redirectTo {
  2136. t.Errorf("(%s) Expected %q route-match to redirect to %q, but got %q", test.title, test.method, test.redirectTo, gotLocation)
  2137. }
  2138. case http.StatusOK:
  2139. if matchedMethod := resp.Body.String(); matchedMethod != test.method {
  2140. t.Errorf("(%s) Expected %q handler to be called, but %q handler was called", test.title, test.method, matchedMethod)
  2141. }
  2142. default:
  2143. expectedCodes := []int{http.StatusMethodNotAllowed, http.StatusMovedPermanently, http.StatusOK}
  2144. t.Errorf("(%s) Expected wantCode to be one of: %v, but got %d", test.title, expectedCodes, test.wantCode)
  2145. }
  2146. }
  2147. // mapToPairs converts a string map to a slice of string pairs
  2148. func mapToPairs(m map[string]string) []string {
  2149. var i int
  2150. p := make([]string, len(m)*2)
  2151. for k, v := range m {
  2152. p[i] = k
  2153. p[i+1] = v
  2154. i += 2
  2155. }
  2156. return p
  2157. }
  2158. // stringMapEqual checks the equality of two string maps
  2159. func stringMapEqual(m1, m2 map[string]string) bool {
  2160. nil1 := m1 == nil
  2161. nil2 := m2 == nil
  2162. if nil1 != nil2 || len(m1) != len(m2) {
  2163. return false
  2164. }
  2165. for k, v := range m1 {
  2166. if v != m2[k] {
  2167. return false
  2168. }
  2169. }
  2170. return true
  2171. }
  2172. // newRequest is a helper function to create a new request with a method and url.
  2173. // The request returned is a 'server' request as opposed to a 'client' one through
  2174. // simulated write onto the wire and read off of the wire.
  2175. // The differences between requests are detailed in the net/http package.
  2176. func newRequest(method, url string) *http.Request {
  2177. req, err := http.NewRequest(method, url, nil)
  2178. if err != nil {
  2179. panic(err)
  2180. }
  2181. // extract the escaped original host+path from url
  2182. // http://localhost/path/here?v=1#frag -> //localhost/path/here
  2183. opaque := ""
  2184. if i := len(req.URL.Scheme); i > 0 {
  2185. opaque = url[i+1:]
  2186. }
  2187. if i := strings.LastIndex(opaque, "?"); i > -1 {
  2188. opaque = opaque[:i]
  2189. }
  2190. if i := strings.LastIndex(opaque, "#"); i > -1 {
  2191. opaque = opaque[:i]
  2192. }
  2193. // Escaped host+path workaround as detailed in https://golang.org/pkg/net/url/#URL
  2194. // for < 1.5 client side workaround
  2195. req.URL.Opaque = opaque
  2196. // Simulate writing to wire
  2197. var buff bytes.Buffer
  2198. req.Write(&buff)
  2199. ioreader := bufio.NewReader(&buff)
  2200. // Parse request off of 'wire'
  2201. req, err = http.ReadRequest(ioreader)
  2202. if err != nil {
  2203. panic(err)
  2204. }
  2205. return req
  2206. }