mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-08-20 19:33:08 +09:00
187 lines
7 KiB
Swift
187 lines
7 KiB
Swift
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||
|
||
import Foundation
|
||
import Alamofire
|
||
import Deferred
|
||
import Shared
|
||
import SwiftyJSON
|
||
|
||
//public struct PushRemoteError {
|
||
// static let MissingNecessaryCryptoKeys: Int32 = 101
|
||
// static let InvalidURLEndpoint: Int32 = 102
|
||
// static let ExpiredURLEndpoint: Int32 = 103
|
||
// static let DataPayloadTooLarge: Int32 = 104
|
||
// static let EndpointBecameUnavailable: Int32 = 105
|
||
// static let InvalidSubscription: Int32 = 106
|
||
// static let RouterTypeIsInvalid: Int32 = 108
|
||
// static let InvalidAuthentication: Int32 = 109
|
||
// static let InvalidCryptoKeysSpecified: Int32 = 110
|
||
// static let MissingRequiredHeader: Int32 = 111
|
||
// static let InvalidTTLHeaderValue: Int32 = 112
|
||
// static let UnknownError: Int32 = 999
|
||
//}
|
||
|
||
public let PushClientErrorDomain = "org.mozilla.push.error"
|
||
private let PushClientUnknownError = NSError(domain: PushClientErrorDomain, code: 999,
|
||
userInfo: [NSLocalizedDescriptionKey: "Invalid server response"])
|
||
private let log = Logger.browserLogger
|
||
|
||
/// Bug 1364403 – This is to be put into the push registration
|
||
private let apsEnvironment: [String: Any] = [
|
||
"mutable-content": 1,
|
||
"alert": [
|
||
"title": " ",
|
||
"body": " "
|
||
],
|
||
]
|
||
|
||
public struct PushRemoteError {
|
||
let code: Int
|
||
let errno: Int
|
||
let error: String
|
||
let message: String?
|
||
|
||
public static func from(json: JSON) -> PushRemoteError? {
|
||
guard let code = json["code"].int,
|
||
let errno = json["errno"].int,
|
||
let error = json["error"].string else {
|
||
return nil
|
||
}
|
||
|
||
let message = json["message"].string
|
||
return PushRemoteError(code: code, errno: errno, error: error, message: message)
|
||
}
|
||
}
|
||
|
||
public enum PushClientError: MaybeErrorType {
|
||
case Remote(PushRemoteError)
|
||
case Local(Error)
|
||
|
||
public var description: String {
|
||
switch self {
|
||
case let .Remote(error):
|
||
let errorString = error.error
|
||
let messageString = error.message ?? ""
|
||
return "<FxAClientError.Remote \(error.code)/\(error.errno): \(errorString) (\(messageString))>"
|
||
case let .Local(error):
|
||
return "<FxAClientError.Local Error \"\(error.localizedDescription)\">"
|
||
}
|
||
}
|
||
}
|
||
|
||
public class PushClient {
|
||
let endpointURL: NSURL
|
||
let experimentalMode: Bool
|
||
|
||
lazy fileprivate var alamofire: SessionManager = {
|
||
let ua = UserAgent.fxaUserAgent
|
||
let configuration = URLSessionConfiguration.ephemeral
|
||
var defaultHeaders = SessionManager.default.session.configuration.httpAdditionalHeaders ?? [:]
|
||
defaultHeaders["User-Agent"] = ua
|
||
configuration.httpAdditionalHeaders = defaultHeaders
|
||
return SessionManager(configuration: configuration)
|
||
}()
|
||
|
||
public init(endpointURL: NSURL, experimentalMode: Bool = false) {
|
||
self.endpointURL = endpointURL
|
||
self.experimentalMode = experimentalMode
|
||
}
|
||
}
|
||
|
||
public extension PushClient {
|
||
public func register(_ apnsToken: String) -> Deferred<Maybe<PushRegistration>> {
|
||
// POST /v1/{type}/{app_id}/registration
|
||
let registerURL = endpointURL.appendingPathComponent("registration")!
|
||
|
||
var mutableURLRequest = URLRequest(url: registerURL)
|
||
mutableURLRequest.httpMethod = HTTPMethod.post.rawValue
|
||
|
||
mutableURLRequest.setValue("application/json", forHTTPHeaderField: "Content-Type")
|
||
let parameters: [String: Any]
|
||
if experimentalMode {
|
||
parameters = [
|
||
"token": apnsToken,
|
||
"aps": apsEnvironment,
|
||
]
|
||
} else {
|
||
parameters = ["token": apnsToken]
|
||
}
|
||
|
||
mutableURLRequest.httpBody = JSON(parameters).stringValue()?.utf8EncodedData
|
||
|
||
if experimentalMode {
|
||
log.info("curl -X POST \(registerURL.absoluteString) --data '\(JSON(parameters).stringValue()!)'")
|
||
}
|
||
|
||
return send(request: mutableURLRequest) >>== { json in
|
||
guard let response = PushRegistration.from(json: json) else {
|
||
return deferMaybe(PushClientError.Local(PushClientUnknownError))
|
||
}
|
||
|
||
return deferMaybe(response)
|
||
}
|
||
}
|
||
|
||
public func updateUAID(_ apnsToken: String, withRegistration creds: PushRegistration) -> Deferred<Maybe<PushRegistration>> {
|
||
// PUT /v1/{type}/{app_id}/registration/{uaid}
|
||
let registerURL = endpointURL.appendingPathComponent("registration/\(creds.uaid)")!
|
||
var mutableURLRequest = URLRequest(url: registerURL)
|
||
|
||
mutableURLRequest.httpMethod = HTTPMethod.put.rawValue
|
||
mutableURLRequest.addValue("Bearer \(creds.secret)", forHTTPHeaderField: "Authorization")
|
||
|
||
mutableURLRequest.setValue("application/json", forHTTPHeaderField: "Content-Type")
|
||
let parameters = ["token": apnsToken]
|
||
mutableURLRequest.httpBody = JSON(parameters).stringValue()?.utf8EncodedData
|
||
|
||
return send(request: mutableURLRequest) >>== { json in
|
||
return deferMaybe(creds)
|
||
}
|
||
}
|
||
|
||
public func unregister(_ creds: PushRegistration) -> Success {
|
||
// DELETE /v1/{type}/{app_id}/registration/{uaid}
|
||
let unregisterURL = endpointURL.appendingPathComponent("registration/\(creds.uaid)")
|
||
|
||
var mutableURLRequest = URLRequest(url: unregisterURL!)
|
||
mutableURLRequest.httpMethod = HTTPMethod.delete.rawValue
|
||
mutableURLRequest.addValue("Bearer \(creds.secret)", forHTTPHeaderField: "Authorization")
|
||
|
||
return send(request: mutableURLRequest) >>> succeed
|
||
}
|
||
}
|
||
|
||
/// Utilities
|
||
extension PushClient {
|
||
fileprivate func send(request: URLRequest) -> Deferred<Maybe<JSON>> {
|
||
log.info("\(request.httpMethod!) \(request.url?.absoluteString ?? "nil")")
|
||
let deferred = Deferred<Maybe<JSON>>()
|
||
alamofire.request(request)
|
||
.validate(contentType: ["application/json"])
|
||
.responseJSON { response in
|
||
// Don't cancel requests just because our client is deallocated.
|
||
withExtendedLifetime(self.alamofire) {
|
||
let result = response.result
|
||
if let error = result.error {
|
||
return deferred.fill(Maybe(failure: PushClientError.Local(error)))
|
||
}
|
||
|
||
guard let data = response.data else {
|
||
return deferred.fill(Maybe(failure: PushClientError.Local(PushClientUnknownError)))
|
||
}
|
||
|
||
let json = JSON(data: data)
|
||
|
||
if let remoteError = PushRemoteError.from(json: json) {
|
||
return deferred.fill(Maybe(failure: PushClientError.Remote(remoteError)))
|
||
}
|
||
|
||
deferred.fill(Maybe(success: json))
|
||
}
|
||
}
|
||
|
||
return deferred
|
||
}
|
||
}
|