HttpResponse.swift 5.8 KB

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