/
ClientResponse.swift
127 lines (113 loc) · 4.44 KB
/
ClientResponse.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
public struct ClientResponse {
public var status: HTTPStatus
public var headers: HTTPHeaders
public var body: ByteBuffer?
public init(status: HTTPStatus = .ok, headers: HTTPHeaders = [:], body: ByteBuffer? = nil) {
self.status = status
self.headers = headers
self.body = body
}
}
extension ClientResponse {
private struct _ContentContainer: ContentContainer {
var body: ByteBuffer?
var headers: HTTPHeaders
var contentType: HTTPMediaType? {
return self.headers.contentType
}
mutating func encode<E>(_ encodable: E, using encoder: ContentEncoder) throws where E : Encodable {
var body = ByteBufferAllocator().buffer(capacity: 0)
try encoder.encode(encodable, to: &body, headers: &self.headers)
self.body = body
}
func decode<D>(_ decodable: D.Type, using decoder: ContentDecoder) throws -> D where D : Decodable {
guard let body = self.body else {
throw Abort(.lengthRequired)
}
return try decoder.decode(D.self, from: body, headers: self.headers)
}
mutating func encode<C>(_ content: C, using encoder: ContentEncoder) throws where C : Content {
var body = ByteBufferAllocator().buffer(capacity: 0)
var content = content
try content.beforeEncode()
try encoder.encode(content, to: &body, headers: &self.headers)
self.body = body
}
func decode<C>(_ content: C.Type, using decoder: ContentDecoder) throws -> C where C : Content {
guard let body = self.body else {
throw Abort(.lengthRequired)
}
var decoded = try decoder.decode(C.self, from: body, headers: self.headers)
try decoded.afterDecode()
return decoded
}
}
public var content: ContentContainer {
get {
return _ContentContainer(body: self.body, headers: self.headers)
}
set {
let container = (newValue as! _ContentContainer)
self.body = container.body
self.headers = container.headers
}
}
}
extension ClientResponse: CustomStringConvertible {
public var description: String {
var desc = ["HTTP/1.1 \(status.code) \(status.reasonPhrase)"]
desc += self.headers.map { "\($0.name): \($0.value)" }
if var body = self.body {
let string = body.readString(length: body.readableBytes) ?? ""
desc += ["", string]
}
return desc.joined(separator: "\n")
}
}
extension ClientResponse: ResponseEncodable {
public func encodeResponse(for request: Request) -> EventLoopFuture<Response> {
let body: Response.Body
if let buffer = self.body {
body = .init(buffer: buffer)
} else {
body = .empty
}
let response = Response(
status: self.status,
headers: self.headers,
body: body
)
return request.eventLoop.makeSucceededFuture(response)
}
}
extension ClientResponse: Codable {
private enum CodingKeys: String, CodingKey {
case status = "status"
case headers = "headers"
case body = "body"
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.status = try container.decode(HTTPStatus.self, forKey: .status)
self.headers = try container.decode(HTTPHeaders.self, forKey: .headers)
let bodyString = try container.decode(String?.self, forKey: .body)
guard let s = bodyString, let bodyData = Data(base64Encoded: s) else {
throw Abort(.internalServerError, reason: "Could not decode client response body from base64 string")
}
var body = ByteBufferAllocator().buffer(capacity: 0)
body.writeBytes(bodyData)
self.body = body
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(self.status, forKey: .status)
try container.encode(self.headers, forKey: .headers)
if let body = self.body {
let string = Data(body.readableBytesView).base64EncodedString()
try container.encode(string, forKey: .body)
} else {
try container.encodeNil(forKey: .body)
}
}
}
extension ClientResponse: Equatable { }