tree.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555
  1. // Copyright 2013 Julien Schmidt. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be found
  3. // in the LICENSE file.
  4. package httprouter
  5. import (
  6. "strings"
  7. "unicode"
  8. )
  9. func min(a, b int) int {
  10. if a <= b {
  11. return a
  12. }
  13. return b
  14. }
  15. func countParams(path string) uint8 {
  16. var n uint
  17. for i := 0; i < len(path); i++ {
  18. if path[i] != ':' && path[i] != '*' {
  19. continue
  20. }
  21. n++
  22. }
  23. if n >= 255 {
  24. return 255
  25. }
  26. return uint8(n)
  27. }
  28. type nodeType uint8
  29. const (
  30. static nodeType = 0
  31. param nodeType = 1
  32. catchAll nodeType = 2
  33. )
  34. type node struct {
  35. path string
  36. wildChild bool
  37. nType nodeType
  38. maxParams uint8
  39. indices string
  40. children []*node
  41. handle Handle
  42. priority uint32
  43. }
  44. // increments priority of the given child and reorders if necessary
  45. func (n *node) incrementChildPrio(pos int) int {
  46. n.children[pos].priority++
  47. prio := n.children[pos].priority
  48. // adjust position (move to front)
  49. newPos := pos
  50. for newPos > 0 && n.children[newPos-1].priority < prio {
  51. // swap node positions
  52. tmpN := n.children[newPos-1]
  53. n.children[newPos-1] = n.children[newPos]
  54. n.children[newPos] = tmpN
  55. newPos--
  56. }
  57. // build new index char string
  58. if newPos != pos {
  59. n.indices = n.indices[:newPos] + // unchanged prefix, might be empty
  60. n.indices[pos:pos+1] + // the index char we move
  61. n.indices[newPos:pos] + n.indices[pos+1:] // rest without char at 'pos'
  62. }
  63. return newPos
  64. }
  65. // addRoute adds a node with the given handle to the path.
  66. // Not concurrency-safe!
  67. func (n *node) addRoute(path string, handle Handle) {
  68. fullPath := path
  69. n.priority++
  70. numParams := countParams(path)
  71. // non-empty tree
  72. if len(n.path) > 0 || len(n.children) > 0 {
  73. walk:
  74. for {
  75. // Update maxParams of the current node
  76. if numParams > n.maxParams {
  77. n.maxParams = numParams
  78. }
  79. // Find the longest common prefix.
  80. // This also implies that the common prefix contains no ':' or '*'
  81. // since the existing key can't contain those chars.
  82. i := 0
  83. max := min(len(path), len(n.path))
  84. for i < max && path[i] == n.path[i] {
  85. i++
  86. }
  87. // Split edge
  88. if i < len(n.path) {
  89. child := node{
  90. path: n.path[i:],
  91. wildChild: n.wildChild,
  92. indices: n.indices,
  93. children: n.children,
  94. handle: n.handle,
  95. priority: n.priority - 1,
  96. }
  97. // Update maxParams (max of all children)
  98. for i := range child.children {
  99. if child.children[i].maxParams > child.maxParams {
  100. child.maxParams = child.children[i].maxParams
  101. }
  102. }
  103. n.children = []*node{&child}
  104. // []byte for proper unicode char conversion, see #65
  105. n.indices = string([]byte{n.path[i]})
  106. n.path = path[:i]
  107. n.handle = nil
  108. n.wildChild = false
  109. }
  110. // Make new node a child of this node
  111. if i < len(path) {
  112. path = path[i:]
  113. if n.wildChild {
  114. n = n.children[0]
  115. n.priority++
  116. // Update maxParams of the child node
  117. if numParams > n.maxParams {
  118. n.maxParams = numParams
  119. }
  120. numParams--
  121. // Check if the wildcard matches
  122. if len(path) >= len(n.path) && n.path == path[:len(n.path)] {
  123. // check for longer wildcard, e.g. :name and :names
  124. if len(n.path) >= len(path) || path[len(n.path)] == '/' {
  125. continue walk
  126. }
  127. }
  128. panic("path segment '" + path +
  129. "' conflicts with existing wildcard '" + n.path +
  130. "' in path '" + fullPath + "'")
  131. }
  132. c := path[0]
  133. // slash after param
  134. if n.nType == param && c == '/' && len(n.children) == 1 {
  135. n = n.children[0]
  136. n.priority++
  137. continue walk
  138. }
  139. // Check if a child with the next path byte exists
  140. for i := 0; i < len(n.indices); i++ {
  141. if c == n.indices[i] {
  142. i = n.incrementChildPrio(i)
  143. n = n.children[i]
  144. continue walk
  145. }
  146. }
  147. // Otherwise insert it
  148. if c != ':' && c != '*' {
  149. // []byte for proper unicode char conversion, see #65
  150. n.indices += string([]byte{c})
  151. child := &node{
  152. maxParams: numParams,
  153. }
  154. n.children = append(n.children, child)
  155. n.incrementChildPrio(len(n.indices) - 1)
  156. n = child
  157. }
  158. n.insertChild(numParams, path, fullPath, handle)
  159. return
  160. } else if i == len(path) { // Make node a (in-path) leaf
  161. if n.handle != nil {
  162. panic("a handle is already registered for path ''" + fullPath + "'")
  163. }
  164. n.handle = handle
  165. }
  166. return
  167. }
  168. } else { // Empty tree
  169. n.insertChild(numParams, path, fullPath, handle)
  170. }
  171. }
  172. func (n *node) insertChild(numParams uint8, path, fullPath string, handle Handle) {
  173. var offset int // already handled bytes of the path
  174. // find prefix until first wildcard (beginning with ':'' or '*'')
  175. for i, max := 0, len(path); numParams > 0; i++ {
  176. c := path[i]
  177. if c != ':' && c != '*' {
  178. continue
  179. }
  180. // find wildcard end (either '/' or path end)
  181. end := i + 1
  182. for end < max && path[end] != '/' {
  183. switch path[end] {
  184. // the wildcard name must not contain ':' and '*'
  185. case ':', '*':
  186. panic("only one wildcard per path segment is allowed, has: '" +
  187. path[i:] + "' in path '" + fullPath + "'")
  188. default:
  189. end++
  190. }
  191. }
  192. // check if this Node existing children which would be
  193. // unreachable if we insert the wildcard here
  194. if len(n.children) > 0 {
  195. panic("wildcard route '" + path[i:end] +
  196. "' conflicts with existing children in path '" + fullPath + "'")
  197. }
  198. // check if the wildcard has a name
  199. if end-i < 2 {
  200. panic("wildcards must be named with a non-empty name in path '" + fullPath + "'")
  201. }
  202. if c == ':' { // param
  203. // split path at the beginning of the wildcard
  204. if i > 0 {
  205. n.path = path[offset:i]
  206. offset = i
  207. }
  208. child := &node{
  209. nType: param,
  210. maxParams: numParams,
  211. }
  212. n.children = []*node{child}
  213. n.wildChild = true
  214. n = child
  215. n.priority++
  216. numParams--
  217. // if the path doesn't end with the wildcard, then there
  218. // will be another non-wildcard subpath starting with '/'
  219. if end < max {
  220. n.path = path[offset:end]
  221. offset = end
  222. child := &node{
  223. maxParams: numParams,
  224. priority: 1,
  225. }
  226. n.children = []*node{child}
  227. n = child
  228. }
  229. } else { // catchAll
  230. if end != max || numParams > 1 {
  231. panic("catch-all routes are only allowed at the end of the path in path '" + fullPath + "'")
  232. }
  233. if len(n.path) > 0 && n.path[len(n.path)-1] == '/' {
  234. panic("catch-all conflicts with existing handle for the path segment root in path '" + fullPath + "'")
  235. }
  236. // currently fixed width 1 for '/'
  237. i--
  238. if path[i] != '/' {
  239. panic("no / before catch-all in path '" + fullPath + "'")
  240. }
  241. n.path = path[offset:i]
  242. // first node: catchAll node with empty path
  243. child := &node{
  244. wildChild: true,
  245. nType: catchAll,
  246. maxParams: 1,
  247. }
  248. n.children = []*node{child}
  249. n.indices = string(path[i])
  250. n = child
  251. n.priority++
  252. // second node: node holding the variable
  253. child = &node{
  254. path: path[i:],
  255. nType: catchAll,
  256. maxParams: 1,
  257. handle: handle,
  258. priority: 1,
  259. }
  260. n.children = []*node{child}
  261. return
  262. }
  263. }
  264. // insert remaining path part and handle to the leaf
  265. n.path = path[offset:]
  266. n.handle = handle
  267. }
  268. // Returns the handle registered with the given path (key). The values of
  269. // wildcards are saved to a map.
  270. // If no handle can be found, a TSR (trailing slash redirect) recommendation is
  271. // made if a handle exists with an extra (without the) trailing slash for the
  272. // given path.
  273. func (n *node) getValue(path string) (handle Handle, p Params, tsr bool) {
  274. walk: // Outer loop for walking the tree
  275. for {
  276. if len(path) > len(n.path) {
  277. if path[:len(n.path)] == n.path {
  278. path = path[len(n.path):]
  279. // If this node does not have a wildcard (param or catchAll)
  280. // child, we can just look up the next child node and continue
  281. // to walk down the tree
  282. if !n.wildChild {
  283. c := path[0]
  284. for i := 0; i < len(n.indices); i++ {
  285. if c == n.indices[i] {
  286. n = n.children[i]
  287. continue walk
  288. }
  289. }
  290. // Nothing found.
  291. // We can recommend to redirect to the same URL without a
  292. // trailing slash if a leaf exists for that path.
  293. tsr = (path == "/" && n.handle != nil)
  294. return
  295. }
  296. // handle wildcard child
  297. n = n.children[0]
  298. switch n.nType {
  299. case param:
  300. // find param end (either '/' or path end)
  301. end := 0
  302. for end < len(path) && path[end] != '/' {
  303. end++
  304. }
  305. // save param value
  306. if p == nil {
  307. // lazy allocation
  308. p = make(Params, 0, n.maxParams)
  309. }
  310. i := len(p)
  311. p = p[:i+1] // expand slice within preallocated capacity
  312. p[i].Key = n.path[1:]
  313. p[i].Value = path[:end]
  314. // we need to go deeper!
  315. if end < len(path) {
  316. if len(n.children) > 0 {
  317. path = path[end:]
  318. n = n.children[0]
  319. continue walk
  320. }
  321. // ... but we can't
  322. tsr = (len(path) == end+1)
  323. return
  324. }
  325. if handle = n.handle; handle != nil {
  326. return
  327. } else if len(n.children) == 1 {
  328. // No handle found. Check if a handle for this path + a
  329. // trailing slash exists for TSR recommendation
  330. n = n.children[0]
  331. tsr = (n.path == "/" && n.handle != nil)
  332. }
  333. return
  334. case catchAll:
  335. // save param value
  336. if p == nil {
  337. // lazy allocation
  338. p = make(Params, 0, n.maxParams)
  339. }
  340. i := len(p)
  341. p = p[:i+1] // expand slice within preallocated capacity
  342. p[i].Key = n.path[2:]
  343. p[i].Value = path
  344. handle = n.handle
  345. return
  346. default:
  347. panic("invalid node type")
  348. }
  349. }
  350. } else if path == n.path {
  351. // We should have reached the node containing the handle.
  352. // Check if this node has a handle registered.
  353. if handle = n.handle; handle != nil {
  354. return
  355. }
  356. // No handle found. Check if a handle for this path + a
  357. // trailing slash exists for trailing slash recommendation
  358. for i := 0; i < len(n.indices); i++ {
  359. if n.indices[i] == '/' {
  360. n = n.children[i]
  361. tsr = (len(n.path) == 1 && n.handle != nil) ||
  362. (n.nType == catchAll && n.children[0].handle != nil)
  363. return
  364. }
  365. }
  366. return
  367. }
  368. // Nothing found. We can recommend to redirect to the same URL with an
  369. // extra trailing slash if a leaf exists for that path
  370. tsr = (path == "/") ||
  371. (len(n.path) == len(path)+1 && n.path[len(path)] == '/' &&
  372. path == n.path[:len(n.path)-1] && n.handle != nil)
  373. return
  374. }
  375. }
  376. // Makes a case-insensitive lookup of the given path and tries to find a handler.
  377. // It can optionally also fix trailing slashes.
  378. // It returns the case-corrected path and a bool indicating whether the lookup
  379. // was successful.
  380. func (n *node) findCaseInsensitivePath(path string, fixTrailingSlash bool) (ciPath []byte, found bool) {
  381. ciPath = make([]byte, 0, len(path)+1) // preallocate enough memory
  382. // Outer loop for walking the tree
  383. for len(path) >= len(n.path) && strings.ToLower(path[:len(n.path)]) == strings.ToLower(n.path) {
  384. path = path[len(n.path):]
  385. ciPath = append(ciPath, n.path...)
  386. if len(path) > 0 {
  387. // If this node does not have a wildcard (param or catchAll) child,
  388. // we can just look up the next child node and continue to walk down
  389. // the tree
  390. if !n.wildChild {
  391. r := unicode.ToLower(rune(path[0]))
  392. for i, index := range n.indices {
  393. // must use recursive approach since both index and
  394. // ToLower(index) could exist. We must check both.
  395. if r == unicode.ToLower(index) {
  396. out, found := n.children[i].findCaseInsensitivePath(path, fixTrailingSlash)
  397. if found {
  398. return append(ciPath, out...), true
  399. }
  400. }
  401. }
  402. // Nothing found. We can recommend to redirect to the same URL
  403. // without a trailing slash if a leaf exists for that path
  404. found = (fixTrailingSlash && path == "/" && n.handle != nil)
  405. return
  406. }
  407. n = n.children[0]
  408. switch n.nType {
  409. case param:
  410. // find param end (either '/' or path end)
  411. k := 0
  412. for k < len(path) && path[k] != '/' {
  413. k++
  414. }
  415. // add param value to case insensitive path
  416. ciPath = append(ciPath, path[:k]...)
  417. // we need to go deeper!
  418. if k < len(path) {
  419. if len(n.children) > 0 {
  420. path = path[k:]
  421. n = n.children[0]
  422. continue
  423. }
  424. // ... but we can't
  425. if fixTrailingSlash && len(path) == k+1 {
  426. return ciPath, true
  427. }
  428. return
  429. }
  430. if n.handle != nil {
  431. return ciPath, true
  432. } else if fixTrailingSlash && len(n.children) == 1 {
  433. // No handle found. Check if a handle for this path + a
  434. // trailing slash exists
  435. n = n.children[0]
  436. if n.path == "/" && n.handle != nil {
  437. return append(ciPath, '/'), true
  438. }
  439. }
  440. return
  441. case catchAll:
  442. return append(ciPath, path...), true
  443. default:
  444. panic("invalid node type")
  445. }
  446. } else {
  447. // We should have reached the node containing the handle.
  448. // Check if this node has a handle registered.
  449. if n.handle != nil {
  450. return ciPath, true
  451. }
  452. // No handle found.
  453. // Try to fix the path by adding a trailing slash
  454. if fixTrailingSlash {
  455. for i := 0; i < len(n.indices); i++ {
  456. if n.indices[i] == '/' {
  457. n = n.children[i]
  458. if (len(n.path) == 1 && n.handle != nil) ||
  459. (n.nType == catchAll && n.children[0].handle != nil) {
  460. return append(ciPath, '/'), true
  461. }
  462. return
  463. }
  464. }
  465. }
  466. return
  467. }
  468. }
  469. // Nothing found.
  470. // Try to fix the path by adding / removing a trailing slash
  471. if fixTrailingSlash {
  472. if path == "/" {
  473. return ciPath, true
  474. }
  475. if len(path)+1 == len(n.path) && n.path[len(path)] == '/' &&
  476. strings.ToLower(path) == strings.ToLower(n.path[:len(path)]) &&
  477. n.handle != nil {
  478. return append(ciPath, n.path...), true
  479. }
  480. }
  481. return
  482. }