182 lines
5.4 KiB
Swift
182 lines
5.4 KiB
Swift
//
|
|
// Copyright 2021 Signal Messenger, LLC
|
|
// SPDX-License-Identifier: AGPL-3.0-only
|
|
//
|
|
|
|
public enum OWSHTTPError: Error, CustomDebugStringConvertible, IsRetryableProvider, UserErrorDescriptionProvider {
|
|
case wrappedFailure(any Error)
|
|
/// Request failed without a response from the service.
|
|
case networkFailure(NetworkErrorType)
|
|
/// Request failed with a response from the service.
|
|
case serviceResponse(ServiceResponse)
|
|
|
|
// MARK: -
|
|
|
|
public enum NetworkErrorType: CustomDebugStringConvertible {
|
|
case invalidResponseStatus
|
|
case unknownNetworkFailure
|
|
case genericTimeout
|
|
case genericFailure
|
|
case wrappedFailure(any Error)
|
|
|
|
public var isTimeoutImpl: Bool {
|
|
switch self {
|
|
case .invalidResponseStatus, .unknownNetworkFailure, .genericFailure:
|
|
return false
|
|
case .genericTimeout:
|
|
return true
|
|
case .wrappedFailure:
|
|
return true
|
|
}
|
|
}
|
|
|
|
public var debugDescription: String {
|
|
switch self {
|
|
case .invalidResponseStatus: return "Invalid response status"
|
|
case .unknownNetworkFailure: return "Unknown network failure"
|
|
case .genericTimeout: return "Generic timeout"
|
|
case .genericFailure: return "Generic failure"
|
|
case .wrappedFailure(let wrappedError):
|
|
return "networkFailureOrTimeout(\(wrappedError.localizedDescription))"
|
|
}
|
|
}
|
|
}
|
|
|
|
public struct ServiceResponse {
|
|
let requestUrl: URL
|
|
let responseStatus: Int
|
|
let responseHeaders: HttpHeaders
|
|
let responseData: Data?
|
|
|
|
var is5xx: Bool {
|
|
switch responseStatus {
|
|
case 500..<600: true
|
|
default: false
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: - NSError bridging
|
|
|
|
public static var errorDomain: String {
|
|
return "OWSHTTPError"
|
|
}
|
|
|
|
public var errorUserInfo: [String: Any] {
|
|
var result = [String: Any]()
|
|
result[NSLocalizedDescriptionKey] = localizedDescription
|
|
return result
|
|
}
|
|
|
|
// MARK: -
|
|
|
|
public var localizedDescription: String {
|
|
switch self {
|
|
case .networkFailure:
|
|
OWSLocalizedString(
|
|
"ERROR_DESCRIPTION_REQUEST_FAILED",
|
|
comment: "Error indicating that a socket request failed.",
|
|
)
|
|
case .serviceResponse(let serviceResponse) where serviceResponse.responseStatus == 429:
|
|
OWSLocalizedString(
|
|
"REGISTER_RATE_LIMITING_ERROR",
|
|
comment: "",
|
|
)
|
|
case .wrappedFailure, .serviceResponse:
|
|
OWSLocalizedString(
|
|
"ERROR_DESCRIPTION_RESPONSE_FAILED",
|
|
comment: "Error indicating that a socket response failed.",
|
|
)
|
|
}
|
|
}
|
|
|
|
public var debugDescription: String {
|
|
switch self {
|
|
case .wrappedFailure(let error):
|
|
return "wrappedFailure(\(error))"
|
|
case .networkFailure:
|
|
return "networkFailure"
|
|
case .serviceResponse(let serviceResponse):
|
|
return "HTTP \(serviceResponse.responseStatus); \(serviceResponse.responseHeaders))"
|
|
}
|
|
}
|
|
|
|
// MARK: - IsRetryableProvider
|
|
|
|
public var isRetryableProvider: Bool {
|
|
switch self {
|
|
case .wrappedFailure:
|
|
return true
|
|
case .networkFailure:
|
|
return true
|
|
case .serviceResponse(let serviceResponse):
|
|
return serviceResponse.is5xx
|
|
}
|
|
}
|
|
|
|
// MARK: -
|
|
|
|
public var responseStatusCode: Int {
|
|
switch self {
|
|
case .wrappedFailure, .networkFailure:
|
|
return 0
|
|
case .serviceResponse(let serviceResponse):
|
|
return Int(serviceResponse.responseStatus)
|
|
}
|
|
}
|
|
|
|
public var responseHeaders: HttpHeaders? {
|
|
switch self {
|
|
case .wrappedFailure, .networkFailure:
|
|
return nil
|
|
case .serviceResponse(let serviceResponse):
|
|
return serviceResponse.responseHeaders
|
|
}
|
|
}
|
|
|
|
public var responseBodyData: Data? {
|
|
switch self {
|
|
case .wrappedFailure, .networkFailure:
|
|
return nil
|
|
case .serviceResponse(let serviceResponse):
|
|
return serviceResponse.responseData
|
|
}
|
|
}
|
|
|
|
public var isNetworkFailureImpl: Bool {
|
|
switch self {
|
|
case .wrappedFailure:
|
|
return false
|
|
case .networkFailure(let wrappedError):
|
|
switch wrappedError {
|
|
case .invalidResponseStatus, .unknownNetworkFailure, .genericFailure:
|
|
return true
|
|
case .genericTimeout:
|
|
return false
|
|
case .wrappedFailure(let wrappedError):
|
|
return wrappedError.isNetworkFailure
|
|
}
|
|
case .serviceResponse:
|
|
return false
|
|
}
|
|
}
|
|
|
|
public var isTimeoutImpl: Bool {
|
|
switch self {
|
|
case .wrappedFailure:
|
|
return false
|
|
case .networkFailure(let wrappedError):
|
|
switch wrappedError {
|
|
case .invalidResponseStatus, .unknownNetworkFailure, .genericFailure:
|
|
return false
|
|
case .genericTimeout:
|
|
return true
|
|
case .wrappedFailure(let wrappedError):
|
|
return wrappedError.isTimeout
|
|
}
|
|
case .serviceResponse:
|
|
return false
|
|
}
|
|
}
|
|
}
|