1
0

HttpResponse.swift 5.8 KB

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