HttpResponse.swift 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. //
  2. // HttpResponse.swift
  3. // Swifter
  4. //
  5. // Copyright (c) 2014-2016 Damian Kołakowski. All rights reserved.
  6. //
  7. import Foundation
  8. public enum SerializationError: Error {
  9. case invalidObject
  10. case notSupported
  11. }
  12. public protocol HttpResponseBodyWriter {
  13. func write(_ file: String.File) throws
  14. func write(_ data: [UInt8]) throws
  15. func write(_ data: ArraySlice<UInt8>) throws
  16. func write(_ data: NSData) throws
  17. func write(_ data: Data) throws
  18. }
  19. public enum HttpResponseBody {
  20. case json(Any)
  21. case html(String)
  22. case htmlBody(String)
  23. case text(String)
  24. case data(Data, contentType: String? = nil)
  25. case custom(Any, (Any) throws -> String)
  26. func content() -> (Int, ((HttpResponseBodyWriter) throws -> Void)?) {
  27. do {
  28. switch self {
  29. case .json(let object):
  30. guard JSONSerialization.isValidJSONObject(object) else {
  31. throw SerializationError.invalidObject
  32. }
  33. let data = try JSONSerialization.data(withJSONObject: object)
  34. return (data.count, {
  35. try $0.write(data)
  36. })
  37. case .text(let body):
  38. let data = [UInt8](body.utf8)
  39. return (data.count, {
  40. try $0.write(data)
  41. })
  42. case .html(let html):
  43. let data = [UInt8](html.utf8)
  44. return (data.count, {
  45. try $0.write(data)
  46. })
  47. case .htmlBody(let body):
  48. let serialized = "<html><meta charset=\"UTF-8\"><body>\(body)</body></html>"
  49. let data = [UInt8](serialized.utf8)
  50. return (data.count, {
  51. try $0.write(data)
  52. })
  53. case .data(let data, _):
  54. return (data.count, {
  55. try $0.write(data)
  56. })
  57. case .custom(let object, let closure):
  58. let serialized = try closure(object)
  59. let data = [UInt8](serialized.utf8)
  60. return (data.count, {
  61. try $0.write(data)
  62. })
  63. }
  64. } catch {
  65. let data = [UInt8]("Serialization error: \(error)".utf8)
  66. return (data.count, {
  67. try $0.write(data)
  68. })
  69. }
  70. }
  71. }
  72. // swiftlint:disable cyclomatic_complexity
  73. public enum HttpResponse {
  74. case switchProtocols([String: String], (Socket) -> Void)
  75. case ok(HttpResponseBody, [String: String] = [:]), created, accepted
  76. case movedPermanently(String)
  77. case movedTemporarily(String)
  78. case badRequest(HttpResponseBody?), unauthorized(HttpResponseBody?), forbidden(HttpResponseBody?), notFound(HttpResponseBody? = nil), notAcceptable(HttpResponseBody?), tooManyRequests(HttpResponseBody?), internalServerError(HttpResponseBody?)
  79. case raw(Int, String, [String: String]?, ((HttpResponseBodyWriter) throws -> Void)? )
  80. public var statusCode: Int {
  81. switch self {
  82. case .switchProtocols : return 101
  83. case .ok : return 200
  84. case .created : return 201
  85. case .accepted : return 202
  86. case .movedPermanently : return 301
  87. case .movedTemporarily : return 307
  88. case .badRequest : return 400
  89. case .unauthorized : return 401
  90. case .forbidden : return 403
  91. case .notFound : return 404
  92. case .notAcceptable : return 406
  93. case .tooManyRequests : return 429
  94. case .internalServerError : return 500
  95. case .raw(let code, _, _, _) : return code
  96. }
  97. }
  98. public var reasonPhrase: String {
  99. switch self {
  100. case .switchProtocols : return "Switching Protocols"
  101. case .ok : return "OK"
  102. case .created : return "Created"
  103. case .accepted : return "Accepted"
  104. case .movedPermanently : return "Moved Permanently"
  105. case .movedTemporarily : return "Moved Temporarily"
  106. case .badRequest : return "Bad Request"
  107. case .unauthorized : return "Unauthorized"
  108. case .forbidden : return "Forbidden"
  109. case .notFound : return "Not Found"
  110. case .notAcceptable : return "Not Acceptable"
  111. case .tooManyRequests : return "Too Many Requests"
  112. case .internalServerError : return "Internal Server Error"
  113. case .raw(_, let phrase, _, _) : return phrase
  114. }
  115. }
  116. public func headers() -> [String: String] {
  117. var headers = ["Server": "Swifter \(HttpServer.VERSION)"]
  118. switch self {
  119. case .switchProtocols(let switchHeaders, _):
  120. for (key, value) in switchHeaders {
  121. headers[key] = value
  122. }
  123. case .ok(let body, let customHeaders):
  124. for (key, value) in customHeaders {
  125. headers.updateValue(value, forKey: key)
  126. }
  127. switch body {
  128. case .json: headers["Content-Type"] = "application/json"
  129. case .html, .htmlBody: headers["Content-Type"] = "text/html"
  130. case .text: headers["Content-Type"] = "text/plain"
  131. case .data(_, let contentType): headers["Content-Type"] = contentType
  132. default:break
  133. }
  134. case .movedPermanently(let location):
  135. headers["Location"] = location
  136. case .movedTemporarily(let location):
  137. headers["Location"] = location
  138. case .raw(_, _, let rawHeaders, _):
  139. if let rawHeaders = rawHeaders {
  140. for (key, value) in rawHeaders {
  141. headers.updateValue(value, forKey: key)
  142. }
  143. }
  144. default:break
  145. }
  146. return headers
  147. }
  148. func content() -> (length: Int, write: ((HttpResponseBodyWriter) throws -> Void)?) {
  149. switch self {
  150. case .ok(let body, _) : return body.content()
  151. case .badRequest(let body), .unauthorized(let body), .forbidden(let body), .notFound(let body), .tooManyRequests(let body), .internalServerError(let body) : return body?.content() ?? (-1, nil)
  152. case .raw(_, _, _, let writer) : return (-1, writer)
  153. default : return (-1, nil)
  154. }
  155. }
  156. func socketSession() -> ((Socket) -> Void)? {
  157. switch self {
  158. case .switchProtocols(_, let handler) : return handler
  159. default: return nil
  160. }
  161. }
  162. }
  163. /**
  164. Makes it possible to compare handler responses with '==', but
  165. ignores any associated values. This should generally be what
  166. you want. E.g.:
  167. let resp = handler(updatedRequest)
  168. if resp == .NotFound {
  169. print("Client requested not found: \(request.url)")
  170. }
  171. */
  172. func == (inLeft: HttpResponse, inRight: HttpResponse) -> Bool {
  173. return inLeft.statusCode == inRight.statusCode
  174. }