HttpResponse.swift 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  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: ErrorProtocol {
  9. case InvalidObject
  10. case NotSupported
  11. }
  12. public protocol HttpResponseBodyWriter {
  13. func write(_ file: File)
  14. func write(_ data: [UInt8])
  15. func write(_ data: ArraySlice<UInt8>)
  16. }
  17. public enum HttpResponseBody {
  18. case Json(AnyObject)
  19. case Html(String)
  20. case Text(String)
  21. case Data([UInt8])
  22. case Custom(Any, (Any) throws -> String)
  23. func content() -> (Int, ((HttpResponseBodyWriter) throws -> Void)?) {
  24. do {
  25. switch self {
  26. case .Json(let object):
  27. #if os(Linux)
  28. let data = [UInt8]("Not ready for Linux.".utf8)
  29. return (data.count, {
  30. $0.write(data)
  31. })
  32. #else
  33. guard JSONSerialization.isValidJSONObject(object) else {
  34. throw SerializationError.InvalidObject
  35. }
  36. let json = try JSONSerialization.data(withJSONObject: object, options: JSONSerialization.WritingOptions.prettyPrinted)
  37. let data = json.withUnsafeBytes({ (body: UnsafePointer<UInt8>) -> Array<UInt8> in
  38. return Array(UnsafeBufferPointer(start: body, count: json.count))
  39. })
  40. return (data.count, {
  41. $0.write(data)
  42. })
  43. #endif
  44. case .Text(let body):
  45. let data = [UInt8](body.utf8)
  46. return (data.count, {
  47. $0.write(data)
  48. })
  49. case .Html(let body):
  50. let serialised = "<html><meta charset=\"UTF-8\"><body>\(body)</body></html>"
  51. let data = [UInt8](serialised.utf8)
  52. return (data.count, {
  53. $0.write(data)
  54. })
  55. case .Data(let body):
  56. return (body.count, {
  57. $0.write(body)
  58. })
  59. case .Custom(let object, let closure):
  60. let serialised = try closure(object)
  61. let data = [UInt8](serialised.utf8)
  62. return (data.count, {
  63. $0.write(data)
  64. })
  65. }
  66. } catch {
  67. let data = [UInt8]("Serialisation error: \(error)".utf8)
  68. return (data.count, {
  69. $0.write(data)
  70. })
  71. }
  72. }
  73. }
  74. public enum HttpResponse {
  75. case SwitchProtocols([String: String], (Socket) -> Void)
  76. case OK(HttpResponseBody), Created, Accepted
  77. case MovedPermanently(String)
  78. case BadRequest(HttpResponseBody?), Unauthorized, Forbidden, NotFound
  79. case InternalServerError
  80. case RAW(Int, String, [String:String]?, ((HttpResponseBodyWriter) -> Void)? )
  81. func statusCode() -> Int {
  82. switch self {
  83. case .SwitchProtocols(_, _) : return 101
  84. case .OK(_) : return 200
  85. case .Created : return 201
  86. case .Accepted : return 202
  87. case .MovedPermanently : return 301
  88. case .BadRequest(_) : return 400
  89. case .Unauthorized : return 401
  90. case .Forbidden : return 403
  91. case .NotFound : return 404
  92. case .InternalServerError : return 500
  93. case .RAW(let code, _ , _, _) : return code
  94. }
  95. }
  96. func reasonPhrase() -> String {
  97. switch self {
  98. case .SwitchProtocols(_, _) : return "Switching Protocols"
  99. case .OK(_) : return "OK"
  100. case .Created : return "Created"
  101. case .Accepted : return "Accepted"
  102. case .MovedPermanently : return "Moved Permanently"
  103. case .BadRequest(_) : return "Bad Request"
  104. case .Unauthorized : return "Unauthorized"
  105. case .Forbidden : return "Forbidden"
  106. case .NotFound : return "Not Found"
  107. case .InternalServerError : return "Internal Server Error"
  108. case .RAW(_, let phrase, _, _) : return phrase
  109. }
  110. }
  111. func headers() -> [String: String] {
  112. var headers = ["Server" : "Swifter \(HttpServer.VERSION)"]
  113. switch self {
  114. case .SwitchProtocols(let switchHeaders, _):
  115. for (key, value) in switchHeaders {
  116. headers[key] = value
  117. }
  118. case .OK(let body):
  119. switch body {
  120. case .Text(_) : headers["Content-Type"] = "text/plain"
  121. case .Json(_) : headers["Content-Type"] = "application/json"
  122. case .Html(_) : headers["Content-Type"] = "text/html"
  123. case .Data(_) : headers["Content-Type"] = "application/octet-stream"
  124. default:break
  125. }
  126. case .MovedPermanently(let location):
  127. headers["Location"] = location
  128. case .RAW(_, _, let rawHeaders, _):
  129. if let rawHeaders = rawHeaders {
  130. for (k, v) in rawHeaders {
  131. headers.updateValue(v, forKey: k)
  132. }
  133. }
  134. default:break
  135. }
  136. return headers
  137. }
  138. func content() -> (length: Int, write: ((HttpResponseBodyWriter) throws -> Void)?) {
  139. switch self {
  140. case .OK(let body) : return body.content()
  141. case .BadRequest(let body) : return body?.content() ?? (-1, nil)
  142. case .RAW(_, _, _, let writer) : return (-1, writer)
  143. default : return (-1, nil)
  144. }
  145. }
  146. func socketSession() -> ((Socket) -> Void)? {
  147. switch self {
  148. case SwitchProtocols(_, let handler) : return handler
  149. default: return nil
  150. }
  151. }
  152. }
  153. /**
  154. Makes it possible to compare handler responses with '==', but
  155. ignores any associated values. This should generally be what
  156. you want. E.g.:
  157. let resp = handler(updatedRequest)
  158. if resp == .NotFound {
  159. print("Client requested not found: \(request.url)")
  160. }
  161. */
  162. func ==(inLeft: HttpResponse, inRight: HttpResponse) -> Bool {
  163. return inLeft.statusCode() == inRight.statusCode()
  164. }