HttpRouter.swift 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. //
  2. // HttpRouter.swift
  3. // Swifter
  4. //
  5. // Copyright (c) 2014-2016 Damian Kołakowski. All rights reserved.
  6. //
  7. import Foundation
  8. public class HttpRouter {
  9. private class Node {
  10. var nodes = [String: Node]()
  11. var handler: ((HttpRequest) -> HttpResponse)? = nil
  12. }
  13. private var rootNode = Node()
  14. public init() { }
  15. public func routes() -> [String] {
  16. var routes = [String]()
  17. for (_, child) in rootNode.nodes {
  18. routes.append(contentsOf: routesForNode(child));
  19. }
  20. return routes
  21. }
  22. private func routesForNode(_ node: Node, prefix: String = "") -> [String] {
  23. var result = [String]()
  24. if let _ = node.handler {
  25. result.append(prefix)
  26. }
  27. for (key, child) in node.nodes {
  28. result.append(contentsOf: routesForNode(child, prefix: prefix + "/" + key));
  29. }
  30. return result
  31. }
  32. public func register(_ method: String?, path: String, handler: ((HttpRequest) -> HttpResponse)?) {
  33. var pathSegments = stripQuery(path).split("/")
  34. if let method = method {
  35. pathSegments.insert(method, at: 0)
  36. } else {
  37. pathSegments.insert("*", at: 0)
  38. }
  39. var pathSegmentsGenerator = pathSegments.makeIterator()
  40. inflate(&rootNode, generator: &pathSegmentsGenerator).handler = handler
  41. }
  42. public func route(_ method: String?, path: String) -> ([String: String], (HttpRequest) -> HttpResponse)? {
  43. if let method = method {
  44. let pathSegments = (method + "/" + stripQuery(path)).split("/")
  45. var pathSegmentsGenerator = pathSegments.makeIterator()
  46. var params = [String:String]()
  47. if let handler = findHandler(&rootNode, params: &params, generator: &pathSegmentsGenerator) {
  48. return (params, handler)
  49. }
  50. }
  51. let pathSegments = ("*/" + stripQuery(path)).split("/")
  52. var pathSegmentsGenerator = pathSegments.makeIterator()
  53. var params = [String:String]()
  54. if let handler = findHandler(&rootNode, params: &params, generator: &pathSegmentsGenerator) {
  55. return (params, handler)
  56. }
  57. return nil
  58. }
  59. private func inflate(_ node: inout Node, generator: inout IndexingIterator<[String]>) -> Node {
  60. if let pathSegment = generator.next() {
  61. if let _ = node.nodes[pathSegment] {
  62. return inflate(&node.nodes[pathSegment]!, generator: &generator)
  63. }
  64. var nextNode = Node()
  65. node.nodes[pathSegment] = nextNode
  66. return inflate(&nextNode, generator: &generator)
  67. }
  68. return node
  69. }
  70. private func findHandler(_ node: inout Node, params: inout [String: String], generator: inout IndexingIterator<[String]>) -> ((HttpRequest) -> HttpResponse)? {
  71. guard let pathToken = generator.next() else {
  72. return node.handler
  73. }
  74. let variableNodes = node.nodes.filter { $0.0.characters.first == ":" }
  75. if let variableNode = variableNodes.first {
  76. if variableNode.1.nodes.count == 0 {
  77. // if it's the last element of the pattern and it's a variable, stop the search and
  78. // append a tail as a value for the variable.
  79. let tail = generator.joined(separator: "/")
  80. if tail.utf8.count > 0 {
  81. params[variableNode.0] = pathToken + "/" + tail
  82. } else {
  83. params[variableNode.0] = pathToken
  84. }
  85. return variableNode.1.handler
  86. }
  87. params[variableNode.0] = pathToken
  88. return findHandler(&node.nodes[variableNode.0]!, params: &params, generator: &generator)
  89. }
  90. if let _ = node.nodes[pathToken] {
  91. return findHandler(&node.nodes[pathToken]!, params: &params, generator: &generator)
  92. }
  93. if let _ = node.nodes["*"] {
  94. return findHandler(&node.nodes["*"]!, params: &params, generator: &generator)
  95. }
  96. if let startStarNode = node.nodes["**"] {
  97. let startStarNodeKeys = startStarNode.nodes.keys
  98. while let pathToken = generator.next() {
  99. if startStarNodeKeys.contains(pathToken) {
  100. return findHandler(&startStarNode.nodes[pathToken]!, params: &params, generator: &generator)
  101. }
  102. }
  103. }
  104. return nil
  105. }
  106. private func stripQuery(_ path: String) -> String {
  107. if let path = path.split("?").first {
  108. return path
  109. }
  110. return path
  111. }
  112. }