1
0

Http.swift 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  1. //
  2. // Http.swift
  3. // Swifter
  4. //
  5. // Copyright © 2016 kolakowski. All rights reserved.
  6. //
  7. import Foundation
  8. open class Request {
  9. public enum HttpVersion { case http10, http11 }
  10. public var httpVersion = HttpVersion.http10
  11. public var method = ""
  12. public var path = ""
  13. public var query = [(String, String)]()
  14. public var headers = [(String, String)]()
  15. public var body = [UInt8]()
  16. public var contentLength = 0
  17. }
  18. open class Response: ExpressibleByIntegerLiteral {
  19. public init() { }
  20. public required init(integerLiteral value: Int) {
  21. self.status = value
  22. }
  23. public init(_ status: Status = Status.ok) {
  24. self.status = status.rawValue
  25. }
  26. public init(_ status: Int = Status.ok.rawValue) {
  27. self.status = status
  28. }
  29. public init(_ body: Array<UInt8>) {
  30. self.body.append(contentsOf: body)
  31. }
  32. public init(_ body: ArraySlice<UInt8>) {
  33. self.body.append(contentsOf: body)
  34. }
  35. public var status = Status.ok.rawValue
  36. public var headers = [(String, String)]()
  37. public var body = [UInt8]()
  38. public var processingSuccesor: IncomingDataProcessor? = nil
  39. }
  40. public class TextResponse: Response, ExpressibleByStringLiteral {
  41. public required init(integerLiteral value: Int) {
  42. super.init(200)
  43. self.headers.append(("Content-Type", "text/plain"))
  44. }
  45. public required init(stringLiteral value: String) {
  46. super.init(200)
  47. self.headers.append(("Content-Type", "text/plain"))
  48. self.body = [UInt8](value.utf8)
  49. }
  50. public required init(unicodeScalarLiteral value: String) {
  51. super.init(200)
  52. self.headers.append(("Content-Type", "text/plain"))
  53. self.body = [UInt8](value.utf8)
  54. }
  55. public required init(extendedGraphemeClusterLiteral value: String) {
  56. super.init(200)
  57. self.headers.append(("Content-Type", "text/plain"))
  58. self.body = [UInt8](value.utf8)
  59. }
  60. public init(_ status: Int = Status.ok.rawValue, _ text: String) {
  61. super.init(status)
  62. self.headers.append(("Content-Type", "text/plain"))
  63. self.body = [UInt8](text.utf8)
  64. }
  65. }
  66. public enum Status: Int {
  67. case `continue` = 100
  68. case switchingProtocols = 101
  69. case ok = 200
  70. case created = 201
  71. case accepted = 202
  72. case noContent = 204
  73. case resetContent = 205
  74. case partialContent = 206
  75. case movedPerm = 301
  76. case notModified = 304
  77. case badRequest = 400
  78. case unauthorized = 401
  79. case forbidden = 403
  80. case notFound = 404
  81. case internalServerError = 500
  82. }
  83. public class HttpIncomingDataPorcessor: Hashable, IncomingDataProcessor {
  84. private enum State {
  85. case waitingForHeaders
  86. case waitingForBody
  87. }
  88. private var state = State.waitingForHeaders
  89. private let socket: Int32
  90. private var buffer = Array<UInt8>()
  91. private var request = Request()
  92. private let callback: ((Request) throws -> Void)
  93. public init(_ socket: Int32, _ closure: @escaping ((Request) throws -> Void)) {
  94. self.socket = socket
  95. self.callback = closure
  96. }
  97. public static func == (lhs: HttpIncomingDataPorcessor, rhs: HttpIncomingDataPorcessor) -> Bool {
  98. return lhs.socket == rhs.socket
  99. }
  100. public var hashValue: Int { return Int(self.socket) }
  101. public func process(_ chunk: ArraySlice<UInt8>) throws {
  102. switch self.state {
  103. case .waitingForHeaders:
  104. guard self.buffer.count + chunk.count < 4096 else {
  105. throw SwifterError.httpError("Headers size exceeds the limit.")
  106. }
  107. var iterator = chunk.makeIterator()
  108. while let byte = iterator.next() {
  109. if byte != UInt8.cr {
  110. buffer.append(byte)
  111. }
  112. if buffer.count >= 2 && buffer[buffer.count-1] == UInt8.lf && buffer[buffer.count-2] == UInt8.lf {
  113. self.buffer.removeLast(2)
  114. self.request = try self.consumeHeader(buffer)
  115. self.buffer.removeAll(keepingCapacity: true)
  116. let left = [UInt8](iterator)
  117. self.state = .waitingForBody
  118. try self.process(left[0..<left.count])
  119. break
  120. }
  121. }
  122. case .waitingForBody:
  123. guard self.request.body.count + chunk.count <= request.contentLength else {
  124. throw SwifterError.httpError("Peer sent more data then required ('Content-Length' = \(request.contentLength).")
  125. }
  126. request.body.append(contentsOf: chunk)
  127. if request.body.count == request.contentLength {
  128. self.state = .waitingForHeaders
  129. try self.callback(request)
  130. }
  131. }
  132. }
  133. private func consumeHeader(_ data: [UInt8]) throws -> Request {
  134. let lines = data.split(separator: UInt8.lf)
  135. guard let requestLine = lines.first else {
  136. throw SwifterError.httpError("No status line.")
  137. }
  138. let requestLineTokens = requestLine.split(separator: UInt8.space)
  139. guard requestLineTokens.count >= 3 else {
  140. throw SwifterError.httpError("Invalid status line.")
  141. }
  142. let request = Request()
  143. if requestLineTokens[2] == [0x48, 0x54, 0x54, 0x50, 0x2f, 0x31, 0x2e, 0x30] {
  144. request.httpVersion = .http10
  145. } else if requestLineTokens[2] == [0x48, 0x54, 0x54, 0x50, 0x2f, 0x31, 0x2e, 0x31] {
  146. request.httpVersion = .http11
  147. } else {
  148. throw SwifterError.httpError("Invalid http version: \(requestLineTokens[2])")
  149. }
  150. request.headers = lines
  151. .dropFirst()
  152. .map { line in
  153. let headerTokens = line.split(separator: UInt8.colon, maxSplits: 1)
  154. if let name = headerTokens.first, let value = headerTokens.last {
  155. if let nameString = String(bytes: name, encoding: String.Encoding.ascii),
  156. let valueString = String(bytes: value, encoding: String.Encoding.ascii) {
  157. return (nameString.lowercased(), valueString.trimmingCharacters(in: CharacterSet.whitespaces))
  158. }
  159. }
  160. return ("", "")
  161. }
  162. if let (_, value) = request.headers
  163. .filter({ $0.0 == "content-length" })
  164. .first {
  165. guard let contentLength = Int(value) else {
  166. throw SwifterError.httpError("Invalid 'Content-Length' header value \(value).")
  167. }
  168. request.contentLength = contentLength
  169. }
  170. guard let method = String(bytes: requestLineTokens[0], encoding: .ascii) else {
  171. throw SwifterError.httpError("Invalid 'method' value \(requestLineTokens[0]).")
  172. }
  173. request.method = method
  174. guard let path = String(bytes: requestLineTokens[1], encoding: .ascii) else {
  175. throw SwifterError.httpError("Invalid 'path' value \(requestLineTokens[1]).")
  176. }
  177. if let firstQuestionMark = path.characters.index(of: "?") {
  178. request.path = String(path.characters[path.startIndex..<firstQuestionMark])
  179. let queryStartIndex = path.characters.index(after: firstQuestionMark)
  180. if path.endIndex > queryStartIndex {
  181. let query = String(path.characters[queryStartIndex..<path.endIndex])
  182. request.query = query
  183. .components(separatedBy: "&")
  184. .reduce([(String, String)]()) { (c, s) -> [(String, String)] in
  185. let tokens = s.components(separatedBy: "=")
  186. if let name = tokens.first, let value = tokens.last {
  187. if let nameDecoded = name.removingPercentEncoding, let valueDecoded = value.removingPercentEncoding {
  188. return c + [(nameDecoded, tokens.count > 1 ? valueDecoded : "")]
  189. }
  190. }
  191. return c
  192. }
  193. }
  194. } else {
  195. request.path = path
  196. }
  197. return request
  198. }
  199. }