HttpResponse.swift 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. //
  2. // HttpResponse.swift
  3. // Swifter
  4. // Copyright (c) 2015 Damian Kołakowski. All rights reserved.
  5. //
  6. import Foundation
  7. public enum SerializationError: ErrorType {
  8. case InvalidObject
  9. case NotSupported
  10. case EncodingError
  11. }
  12. public protocol Serializer {
  13. func serialize(object: Any) throws -> String
  14. }
  15. public class JSONSerializer: Serializer {
  16. public func serialize(object: Any) throws -> String {
  17. guard let obj = object as? AnyObject where NSJSONSerialization.isValidJSONObject(obj) else {
  18. throw SerializationError.InvalidObject
  19. }
  20. let json = try NSJSONSerialization.dataWithJSONObject(obj, options: NSJSONWritingOptions.PrettyPrinted)
  21. guard let string = String(data: json, encoding: NSUTF8StringEncoding) else {
  22. throw SerializationError.EncodingError
  23. }
  24. return string
  25. }
  26. private static func serialize(object: Any) throws -> String {
  27. let serializer = JSONSerializer()
  28. return try serializer.serialize(object)
  29. }
  30. }
  31. public class XMLSerializer: Serializer {
  32. public func serialize(object: Any) throws -> String {
  33. throw SerializationError.NotSupported
  34. }
  35. private static func serialize(object: Any) throws -> String {
  36. let serializer = XMLSerializer()
  37. return try serializer.serialize(object)
  38. }
  39. }
  40. public class PLISTSerializer: Serializer {
  41. public func serialize(object: Any) throws -> String {
  42. let format = NSPropertyListFormat.XMLFormat_v1_0
  43. guard let obj = object as? AnyObject where NSPropertyListSerialization.propertyList(obj, isValidForFormat: format) else {
  44. throw SerializationError.InvalidObject
  45. }
  46. let plist = try NSPropertyListSerialization.dataWithPropertyList(obj, format: format, options: 0)
  47. guard let string = String(data: plist, encoding: NSUTF8StringEncoding) else {
  48. throw SerializationError.EncodingError
  49. }
  50. return string
  51. }
  52. private static func serialize(object: Any) throws -> String {
  53. let serializer = PLISTSerializer()
  54. return try serializer.serialize(object)
  55. }
  56. }
  57. public enum HttpResponseBody {
  58. case Json(AnyObject)
  59. case Xml(AnyObject)
  60. case Plist(AnyObject)
  61. case Html(String)
  62. case Text(String)
  63. case Custom(Serializer, Any)
  64. func data() -> [UInt8]? {
  65. do {
  66. switch self {
  67. case .Json(let object):
  68. let serialised = try JSONSerializer.serialize(object)
  69. return [UInt8](serialised.utf8)
  70. case .Xml(let object):
  71. let serialised = try XMLSerializer.serialize(object)
  72. return [UInt8](serialised.utf8)
  73. case .Plist(let object):
  74. let serialised = try PLISTSerializer.serialize(object)
  75. return [UInt8](serialised.utf8)
  76. case .Text(let body):
  77. let serialised = body
  78. return [UInt8](serialised.utf8)
  79. case .Html(let body):
  80. let serialised = "<html><meta charset=\"UTF-8\"><body>\(body)</body></html>"
  81. return [UInt8](serialised.utf8)
  82. case .Custom(let serializer, let object):
  83. let serialised = try serializer.serialize(object)
  84. return [UInt8](serialised.utf8)
  85. }
  86. } catch {
  87. return [UInt8]("Serialisation error: \(error)".utf8)
  88. }
  89. }
  90. }
  91. public enum HttpResponse {
  92. case OK(HttpResponseBody), Created, Accepted
  93. case MovedPermanently(String)
  94. case BadRequest, Unauthorized, Forbidden, NotFound
  95. case InternalServerError
  96. case RAW(Int, String, [String:String]?, [UInt8]?)
  97. func statusCode() -> Int {
  98. switch self {
  99. case .OK(_) : return 200
  100. case .Created : return 201
  101. case .Accepted : return 202
  102. case .MovedPermanently : return 301
  103. case .BadRequest : return 400
  104. case .Unauthorized : return 401
  105. case .Forbidden : return 403
  106. case .NotFound : return 404
  107. case .InternalServerError : return 500
  108. case .RAW(let code, _ , _, _) : return code
  109. }
  110. }
  111. func reasonPhrase() -> String {
  112. switch self {
  113. case .OK(_) : return "OK"
  114. case .Created : return "Created"
  115. case .Accepted : return "Accepted"
  116. case .MovedPermanently : return "Moved Permanently"
  117. case .BadRequest : return "Bad Request"
  118. case .Unauthorized : return "Unauthorized"
  119. case .Forbidden : return "Forbidden"
  120. case .NotFound : return "Not Found"
  121. case .InternalServerError : return "Internal Server Error"
  122. case .RAW(_, let phrase, _, _) : return phrase
  123. }
  124. }
  125. func headers() -> [String: String] {
  126. var headers = [String:String]()
  127. headers["Server"] = "Swifter \(HttpServer.VERSION)"
  128. switch self {
  129. case .OK(let body):
  130. switch body {
  131. case .Json(_) : headers["Content-Type"] = "application/json"
  132. case .Plist(_) : headers["Content-Type"] = "application/xml"
  133. case .Xml(_) : headers["Content-Type"] = "application/xml"
  134. // 'application/xml' or 'text/xml' ?
  135. // From RFC: http://www.rfc-editor.org/rfc/rfc3023.txt - "If an XML document -- that is, the unprocessed,
  136. // source XML document -- is readable by casual users, text/xml is preferable to application/xml.
  137. // MIME user agents (and web user agents) that do not have explicit support for text/xml will treat it as text/plain,
  138. // for example, by displaying the XML MIME entity as plain text.
  139. case .Html(_) : headers["Content-Type"] = "text/html"
  140. default:break
  141. }
  142. case .MovedPermanently(let location): headers["Location"] = location
  143. case .RAW(_, _, let rawHeaders, _):
  144. if let rawHeaders = rawHeaders {
  145. for (k, v) in rawHeaders {
  146. headers.updateValue(v, forKey: k)
  147. }
  148. }
  149. default:break
  150. }
  151. return headers
  152. }
  153. func body() -> [UInt8]? {
  154. switch self {
  155. case .OK(let body) : return body.data()
  156. case .RAW(_, _, _, let data) : return data
  157. default : return nil
  158. }
  159. }
  160. }
  161. /**
  162. Makes it possible to compare handler responses with '==', but
  163. ignores any associated values. This should generally be what
  164. you want. E.g.:
  165. let resp = handler(updatedRequest)
  166. if resp == .NotFound {
  167. print("Client requested not found: \(request.url)")
  168. }
  169. */
  170. func ==(inLeft: HttpResponse, inRight: HttpResponse) -> Bool {
  171. return inLeft.statusCode() == inRight.statusCode()
  172. }