HttpRouter.swift 5.0 KB

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