HttpRouter.swift 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  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. public class HttpRouter {
  13. private class Node {
  14. var nodes = [String: Node]()
  15. var handler: (HttpRequest -> HttpResponse)? = nil
  16. }
  17. private var rootNode = Node()
  18. public func routes() -> [String] {
  19. var routes = [String]()
  20. for (_, child) in rootNode.nodes {
  21. routes.appendContentsOf(routesForNode(child));
  22. }
  23. return routes
  24. }
  25. private func routesForNode(node: Node, prefix: String = "") -> [String] {
  26. var result = [String]()
  27. if let _ = node.handler {
  28. result.append(prefix)
  29. }
  30. for (key, child) in node.nodes {
  31. result.appendContentsOf(routesForNode(child, prefix: prefix + "/" + key));
  32. }
  33. return result
  34. }
  35. public func register(method: String?, path: String, handler: (HttpRequest -> HttpResponse)?) {
  36. var pathSegments = stripQuery(path).split("/")
  37. if let method = method {
  38. pathSegments.insert(method, atIndex: 0)
  39. } else {
  40. pathSegments.insert("*", atIndex: 0)
  41. }
  42. var pathSegmentsGenerator = pathSegments.generate()
  43. inflate(&rootNode, generator: &pathSegmentsGenerator).handler = handler
  44. }
  45. public func route(method: String?, path: String) -> ([String: String], HttpRequest -> HttpResponse)? {
  46. if let method = method {
  47. let pathSegments = (method + "/" + stripQuery(path)).split("/")
  48. var pathSegmentsGenerator = pathSegments.generate()
  49. var params = [String:String]()
  50. if let handler = findHandler(&rootNode, params: &params, generator: &pathSegmentsGenerator) {
  51. return (params, handler)
  52. }
  53. }
  54. let pathSegments = ("*/" + stripQuery(path)).split("/")
  55. var pathSegmentsGenerator = pathSegments.generate()
  56. var params = [String:String]()
  57. if let handler = findHandler(&rootNode, params: &params, generator: &pathSegmentsGenerator) {
  58. return (params, handler)
  59. }
  60. return nil
  61. }
  62. private func inflate(inout node: Node, inout generator: IndexingGenerator<[String]>) -> Node {
  63. if let pathSegment = generator.next() {
  64. if let _ = node.nodes[pathSegment] {
  65. return inflate(&node.nodes[pathSegment]!, generator: &generator)
  66. }
  67. var nextNode = Node()
  68. node.nodes[pathSegment] = nextNode
  69. return inflate(&nextNode, generator: &generator)
  70. }
  71. return node
  72. }
  73. private func findHandler(inout node: Node, inout params: [String: String], inout generator: IndexingGenerator<[String]>) -> (HttpRequest -> HttpResponse)? {
  74. guard let pathToken = generator.next() else {
  75. return node.handler
  76. }
  77. let variableNodes = node.nodes.filter { $0.0.characters.first == ":" }
  78. if let variableNode = variableNodes.first {
  79. if variableNode.1.nodes.count == 0 {
  80. // if it's the last element of the pattern and it's a variable, stop the search and
  81. // append a tail as a value for the variable.
  82. let tail = generator.joinWithSeparator("/")
  83. if tail.utf8.count > 0 {
  84. params[variableNode.0] = pathToken + "/" + tail
  85. } else {
  86. params[variableNode.0] = pathToken
  87. }
  88. return variableNode.1.handler
  89. }
  90. params[variableNode.0] = pathToken
  91. return findHandler(&node.nodes[variableNode.0]!, params: &params, generator: &generator)
  92. }
  93. if let _ = node.nodes[pathToken] {
  94. return findHandler(&node.nodes[pathToken]!, params: &params, generator: &generator)
  95. }
  96. if let _ = node.nodes["*"] {
  97. return findHandler(&node.nodes["*"]!, params: &params, generator: &generator)
  98. }
  99. return nil
  100. }
  101. private func stripQuery(path: String) -> String {
  102. if let path = path.split("?").first {
  103. return path
  104. }
  105. return path
  106. }
  107. }