Skip to content
This repository has been archived by the owner. It is now read-only.
Permalink
firefox-merge-…
Switch branches/tags
Go to file
 
 
Cannot retrieve contributors at this time
executable file 187 lines (155 sloc) 6.96 KB
/* 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
}
}