tree.go 16 KB

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