HttpResponse.swift 6.7 KB

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