HttpResponse.swift 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. //
  2. // HttpResponse.swift
  3. // Swifter
  4. // Copyright (c) 2014 Damian Kołakowski. All rights reserved.
  5. //
  6. import Foundation
  7. public enum HttpResponseBody {
  8. case JSON(AnyObject)
  9. case XML(AnyObject)
  10. case PLIST(AnyObject)
  11. case HTML(String)
  12. case STRING(String)
  13. func data() -> String? {
  14. switch self {
  15. case .JSON(let object):
  16. if NSJSONSerialization.isValidJSONObject(object) {
  17. do {
  18. let json = try NSJSONSerialization.dataWithJSONObject(object, options: NSJSONWritingOptions.PrettyPrinted)
  19. if let nsString = NSString(data: json, encoding: NSUTF8StringEncoding) {
  20. return nsString as String
  21. }
  22. } catch let serializationError as NSError {
  23. return "Serialisation error: \(serializationError)"
  24. }
  25. }
  26. return "Invalid object to serialise."
  27. case .XML(_):
  28. return "XML serialization not supported."
  29. case .PLIST(let object):
  30. let format = NSPropertyListFormat.XMLFormat_v1_0
  31. if NSPropertyListSerialization.propertyList(object, isValidForFormat: format) {
  32. do {
  33. let plist = try NSPropertyListSerialization.dataWithPropertyList(object, format: format, options: 0)
  34. if let nsString = NSString(data: plist, encoding: NSUTF8StringEncoding) {
  35. return nsString as String
  36. }
  37. } catch let serializationError as NSError {
  38. return "Serialisation error: \(serializationError)"
  39. }
  40. }
  41. return "Invalid object to serialise."
  42. case .STRING(let body):
  43. return body
  44. case .HTML(let body):
  45. return "<html><meta charset=\"UTF-8\"><body>\(body)</body></html>"
  46. }
  47. }
  48. }
  49. public enum HttpResponse {
  50. case OK(HttpResponseBody), Created, Accepted
  51. case MovedPermanently(String)
  52. case BadRequest, Unauthorized, Forbidden, NotFound
  53. case InternalServerError
  54. case RAW(Int, String, [String:String]?, NSData)
  55. func statusCode() -> Int {
  56. switch self {
  57. case .OK(_) : return 200
  58. case .Created : return 201
  59. case .Accepted : return 202
  60. case .MovedPermanently : return 301
  61. case .BadRequest : return 400
  62. case .Unauthorized : return 401
  63. case .Forbidden : return 403
  64. case .NotFound : return 404
  65. case .InternalServerError : return 500
  66. case .RAW(let code,_,_,_) : return code
  67. }
  68. }
  69. func reasonPhrase() -> String {
  70. switch self {
  71. case .OK(_) : return "OK"
  72. case .Created : return "Created"
  73. case .Accepted : return "Accepted"
  74. case .MovedPermanently : return "Moved Permanently"
  75. case .BadRequest : return "Bad Request"
  76. case .Unauthorized : return "Unauthorized"
  77. case .Forbidden : return "Forbidden"
  78. case .NotFound : return "Not Found"
  79. case .InternalServerError : return "Internal Server Error"
  80. case .RAW(_,let pharse,_,_) : return pharse
  81. }
  82. }
  83. func headers() -> [String: String] {
  84. var headers = [String:String]()
  85. headers["Server"] = "Swifter \(HttpServer.VERSION)"
  86. switch self {
  87. case .OK(let body):
  88. switch body {
  89. case .JSON(_) : headers["Content-Type"] = "application/json"
  90. case .PLIST(_) : headers["Content-Type"] = "application/xml"
  91. case .XML(_) : headers["Content-Type"] = "application/xml"
  92. // 'application/xml' or 'text/xml' ?
  93. // From RFC: http://www.rfc-editor.org/rfc/rfc3023.txt - "If an XML document -- that is, the unprocessed,
  94. // source XML document -- is readable by casual users, text/xml is preferable to application/xml.
  95. // MIME user agents (and web user agents) that do not have explicit support for text/xml will treat it as text/plain,
  96. // for example, by displaying the XML MIME entity as plain text.
  97. // Application/xml is preferable when the XML MIME entity is unreadable by casual users."
  98. case .HTML(_) : headers["Content-Type"] = "text/html"
  99. default:[]
  100. }
  101. case .MovedPermanently(let location): headers["Location"] = location
  102. case .RAW(_,_, let rawHeaders,_):
  103. if let rawHeaders = rawHeaders {
  104. for (k, v) in rawHeaders {
  105. headers.updateValue(v, forKey: k)
  106. }
  107. }
  108. default:[]
  109. }
  110. return headers
  111. }
  112. func body() -> NSData? {
  113. switch self {
  114. case .OK(let body) : return body.data()?.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)
  115. case .RAW(_,_,_, let data) : return data
  116. default : return nil
  117. }
  118. }
  119. }
  120. /**
  121. Makes it possible to compare handler responses with '==', but
  122. ignores any associated values. This should generally be what
  123. you want. E.g.:
  124. let resp = handler(updatedRequest)
  125. if resp == .NotFound {
  126. print("Client requested not found: \(request.url)")
  127. }
  128. */
  129. func ==(inLeft: HttpResponse, inRight: HttpResponse) -> Bool {
  130. return inLeft.statusCode() == inRight.statusCode()
  131. }