-
Notifications
You must be signed in to change notification settings - Fork 84
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Adds Swift interfaces for responses within Envoy. These should be at parity with the interfaces being added for Kotlin in #260. Resolves #118. Doc for reference: https://docs.google.com/document/d/1N0ZFJktK8m01uqqgfDRVB9mpC1iEn9dqkQaa_yMn_kE/edit#heading=h.i6ky65xaa9va Signed-off-by: Michael Rebello <[email protected]>
- Loading branch information
Showing
7 changed files
with
187 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
import Foundation | ||
|
||
/// A result returned from Envoy. | ||
@objcMembers | ||
public final class Result: NSObject { | ||
/// The response returned from the server. | ||
public let response: Response? | ||
/// An error that was encountered on the network (i.e., going offline). | ||
public let error: NetworkError? | ||
|
||
/// Designated initializer. | ||
public init(response: Response?, | ||
error: NetworkError?) | ||
{ | ||
self.response = response | ||
self.error = error | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import Foundation | ||
|
||
/// Error representing cases when no response was received from the server. | ||
/// I.e., the client went offline or became disconnected. | ||
@objcMembers | ||
public final class NetworkError: NSObject, Swift.Error { | ||
/// Message describing this error. | ||
public let message: String? | ||
|
||
/// Designated initializer. | ||
public init(message: String?) { | ||
self.message = message | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
import Foundation | ||
|
||
/// Represents an Envoy HTTP response. Use `ResponseBuilder` to construct new instances. | ||
@objcMembers | ||
public final class Response: NSObject { | ||
/// Status code returned with the response. | ||
public let statusCode: Int | ||
/// Headers returned with the response. | ||
/// Multiple values for a given name are valid, and will be sent as comma-separated values. | ||
public let headers: [String: [String]] | ||
/// Trailers returned with the response. | ||
/// Multiple values for a given name are valid, and will be sent as comma-separated values. | ||
public let trailers: [String: [String]] | ||
/// Serialized data returned as the body of the response. | ||
public let body: Data? | ||
|
||
/// Converts the response back to a builder so that it can be modified (i.e., by a filter). | ||
/// | ||
/// - returns: A new builder including all the properties of this response. | ||
public func newBuilder() -> ResponseBuilder { | ||
return ResponseBuilder(response: self) | ||
} | ||
|
||
/// Internal initializer called from the builder to create a new response. | ||
init(statusCode: Int, | ||
headers: [String: [String]] = [:], | ||
trailers: [String: [String]] = [:], | ||
body: Data?) | ||
{ | ||
self.statusCode = statusCode | ||
self.headers = headers | ||
self.trailers = trailers | ||
self.body = body | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
import Foundation | ||
|
||
/// Builder used for constructing instances of `Response` types. | ||
@objcMembers | ||
public final class ResponseBuilder: NSObject { | ||
/// Status code returned with the response. | ||
public private(set) var statusCode: Int = 200 | ||
/// Headers returned with the response. | ||
/// Multiple values for a given name are valid, and will be sent as comma-separated values. | ||
public private(set) var headers: [String: [String]] = [:] | ||
/// Trailers returned with the response. | ||
/// Multiple values for a given name are valid, and will be sent as comma-separated values. | ||
public private(set) var trailers: [String: [String]] = [:] | ||
/// Serialized data returned as the body of the response. | ||
public private(set) var body: Data? | ||
|
||
// MARK: - Initializers | ||
|
||
/// Internal initializer used for converting a response back to a builder. | ||
convenience init(response: Response) { | ||
self.init() | ||
self.statusCode = response.statusCode | ||
self.headers = response.headers | ||
self.trailers = response.trailers | ||
self.body = response.body | ||
} | ||
|
||
// MARK: - Builder functions | ||
|
||
@discardableResult | ||
public func addStatusCode(_ statusCode: Int) -> ResponseBuilder { | ||
self.statusCode = statusCode | ||
return self | ||
} | ||
|
||
@discardableResult | ||
public func addHeader(name: String, value: String) -> ResponseBuilder { | ||
self.headers[name, default: []].append(value) | ||
return self | ||
} | ||
|
||
@discardableResult | ||
public func removeHeaders(name: String) -> ResponseBuilder { | ||
self.headers.removeValue(forKey: name) | ||
return self | ||
} | ||
|
||
@discardableResult | ||
public func removeHeader(name: String, value: String) -> ResponseBuilder { | ||
self.headers[name]?.removeAll(where: { $0 == value }) | ||
if self.headers[name]?.isEmpty == true { | ||
self.headers.removeValue(forKey: name) | ||
} | ||
|
||
return self | ||
} | ||
|
||
@discardableResult | ||
public func addTrailer(name: String, value: String) -> ResponseBuilder { | ||
self.trailers[name, default: []].append(value) | ||
return self | ||
} | ||
|
||
@discardableResult | ||
public func removeTrailer(name: String) -> ResponseBuilder { | ||
self.trailers.removeValue(forKey: name) | ||
return self | ||
} | ||
|
||
@discardableResult | ||
public func removeTrailers(named name: String, value: String) -> ResponseBuilder { | ||
self.trailers[name]?.removeAll(where: { $0 == value }) | ||
if self.trailers[name]?.isEmpty == true { | ||
self.trailers.removeValue(forKey: name) | ||
} | ||
|
||
return self | ||
} | ||
|
||
@discardableResult | ||
public func addBody(_ body: Data?) -> ResponseBuilder { | ||
self.body = body | ||
return self | ||
} | ||
|
||
public func build() -> Response { | ||
return Response(statusCode: self.statusCode, | ||
headers: self.headers, | ||
trailers: self.trailers, | ||
body: self.body) | ||
} | ||
} | ||
|
||
// MARK: - Objective-C helpers | ||
|
||
extension Response { | ||
/// Convenience builder function to allow for cleaner Objective-C syntax. | ||
/// | ||
/// For example: | ||
/// | ||
/// Response *res = [Response withBuild:^(ResponseBuilder *builder) { | ||
/// [builder addBody:bodyData]; | ||
/// [builder addHeaderWithName:@"x-some-header" value:@"foo"]; | ||
/// [builder addTrailerWithName:@"x-some-trailer" value:@"foo"]; | ||
/// }]; | ||
@objc | ||
public static func with(build: (ResponseBuilder) -> Void) -> Response { | ||
let builder = ResponseBuilder() | ||
build(builder) | ||
return builder.build() | ||
} | ||
} |