tree.go 16 KB

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