From d6ea72532994bf9cd2e1e6eaa7d0d84ba367eb29 Mon Sep 17 00:00:00 2001 From: Joseph Mattello Date: Sun, 6 Nov 2022 17:49:06 -0500 Subject: [PATCH] Add Network service and default sessions Signed-off-by: Joseph Mattello --- AltStore.xcodeproj/project.pbxproj | 12 +++ AltStore/My Apps/MyAppsViewController.swift | 2 +- .../Operations/DownloadAppOperation.swift | 4 +- .../FetchAnisetteDataOperation.swift | 49 +++++------ .../Operations/FetchSourceOperation.swift | 8 +- .../FetchTrustedSourcesOperation.swift | 4 +- .../Operations/UpdatePatronsOperation.swift | 2 +- AltStore/Services/NetworkService.swift | 81 +++++++++++++++++++ 8 files changed, 125 insertions(+), 37 deletions(-) create mode 100644 AltStore/Services/NetworkService.swift diff --git a/AltStore.xcodeproj/project.pbxproj b/AltStore.xcodeproj/project.pbxproj index 92c94e88..004d4920 100644 --- a/AltStore.xcodeproj/project.pbxproj +++ b/AltStore.xcodeproj/project.pbxproj @@ -43,6 +43,7 @@ B39575F5284F29E20080B4FF /* Roxas.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B39575F4284F29E20080B4FF /* Roxas.framework */; }; B39F16132918D7C5002E9404 /* Consts.swift in Sources */ = {isa = PBXBuildFile; fileRef = B39F16122918D7C5002E9404 /* Consts.swift */; }; B39F16152918D7DA002E9404 /* Consts+Proxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = B39F16142918D7DA002E9404 /* Consts+Proxy.swift */; }; + B3C053B7295F921E0079DB81 /* NetworkService.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3C053B6295F921E0079DB81 /* NetworkService.swift */; }; B3C395F1284F2DE700DA9E2F /* KeychainAccess in Frameworks */ = {isa = PBXBuildFile; productRef = B3C395F0284F2DE700DA9E2F /* KeychainAccess */; }; B3C395F4284F35DD00DA9E2F /* Nuke in Frameworks */ = {isa = PBXBuildFile; productRef = B3C395F3284F35DD00DA9E2F /* Nuke */; }; B3C395F7284F362400DA9E2F /* AppCenterAnalytics in Frameworks */ = {isa = PBXBuildFile; productRef = B3C395F6284F362400DA9E2F /* AppCenterAnalytics */; }; @@ -533,6 +534,7 @@ B39575F4284F29E20080B4FF /* Roxas.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Roxas.framework; sourceTree = BUILT_PRODUCTS_DIR; }; B39F16122918D7C5002E9404 /* Consts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Consts.swift; sourceTree = ""; }; B39F16142918D7DA002E9404 /* Consts+Proxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Consts+Proxy.swift"; sourceTree = ""; }; + B3C053B6295F921E0079DB81 /* NetworkService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkService.swift; sourceTree = ""; }; B3C39606284F4C8400DA9E2F /* CodeSigning.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = CodeSigning.xcconfig; sourceTree = ""; }; B3C39607284F4C8400DA9E2F /* Build.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Build.xcconfig; sourceTree = ""; }; B3C39608284F4C8400DA9E2F /* CodeSigning.xcconfig.sample */ = {isa = PBXFileReference; lastKnownFileType = text; path = CodeSigning.xcconfig.sample; sourceTree = ""; }; @@ -1026,6 +1028,14 @@ path = Consts; sourceTree = ""; }; + B3C053B5295F921E0079DB81 /* Services */ = { + isa = PBXGroup; + children = ( + B3C053B6295F921E0079DB81 /* NetworkService.swift */, + ); + path = Services; + sourceTree = ""; + }; BF0DCA642433BDE200E3A595 /* Analytics */ = { isa = PBXGroup; children = ( @@ -1560,6 +1570,7 @@ BF3D648922E79A7700E9056B /* Types */, BFD2479D2284FBBD00981D42 /* Extensions */, BFD247962284D7C100981D42 /* Resources */, + B3C053B5295F921E0079DB81 /* Services */, BF6C8FA8242935CA00125131 /* Dependencies */, BFD247972284D7D800981D42 /* Supporting Files */, 1920B04E2924AC8300744F60 /* Settings.bundle */, @@ -2473,6 +2484,7 @@ BF88F97224F8727D00BB75DF /* AppManagerErrors.swift in Sources */, B39F16152918D7DA002E9404 /* Consts+Proxy.swift in Sources */, BF6C8FAE2429597900125131 /* BannerCollectionViewCell.swift in Sources */, + B3C053B7295F921E0079DB81 /* NetworkService.swift in Sources */, BF6F439223644C6E00A0B879 /* RefreshAltStoreViewController.swift in Sources */, BFE60742231B07E6002B0E8E /* SettingsHeaderFooterView.swift in Sources */, BFE338E822F10E56002E24B9 /* LaunchViewController.swift in Sources */, diff --git a/AltStore/My Apps/MyAppsViewController.swift b/AltStore/My Apps/MyAppsViewController.swift index 9f968e16..89099c11 100644 --- a/AltStore/My Apps/MyAppsViewController.swift +++ b/AltStore/My Apps/MyAppsViewController.swift @@ -754,7 +754,7 @@ private extension MyAppsViewController { let downloadProgress = Progress.discreteProgress(totalUnitCount: 100) downloadOperation = RSTAsyncBlockOperation { (operation) in - let downloadTask = URLSession.shared.downloadTask(with: url) { (fileURL, response, error) in + let downloadTask = AppServices.network.session.downloadTask(with: url) { (fileURL, response, error) in do { let (fileURL, _) = try Result((fileURL, response), error).get() diff --git a/AltStore/Operations/DownloadAppOperation.swift b/AltStore/Operations/DownloadAppOperation.swift index 83820388..569d652d 100644 --- a/AltStore/Operations/DownloadAppOperation.swift +++ b/AltStore/Operations/DownloadAppOperation.swift @@ -39,8 +39,8 @@ final class DownloadAppOperation: ResultOperation private var sourceURL: URL? private let destinationURL: URL - private let session = URLSession(configuration: .default) - private let temporaryDirectory = FileManager.default.uniqueTemporaryURL() + private let session: URLSession = AppServices.network.backgroundSession + private let temporaryDirectory: URL = FileManager.default.uniqueTemporaryURL() init(app: AppProtocol, destinationURL: URL, context: AppOperationContext) { diff --git a/AltStore/Operations/FetchAnisetteDataOperation.swift b/AltStore/Operations/FetchAnisetteDataOperation.swift index 4a121751..61b66002 100644 --- a/AltStore/Operations/FetchAnisetteDataOperation.swift +++ b/AltStore/Operations/FetchAnisetteDataOperation.swift @@ -34,30 +34,31 @@ final class FetchAnisetteDataOperation: ResultOperation let url = AnisetteManager.currentURL DLOG("Anisette URL: %@", url.absoluteString) - - let task = URLSession.shared.dataTask(with: url) { data, response, error in - guard let data = data, error == nil else { return } - - do { - // make sure this JSON is in the format we expect - // convert data to json - if let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String: String] { - // try to read out a dictionary - //for some reason serial number isn't needed but it doesn't work unless it has a value - let formattedJSON: [String: String] = ["machineID": json["X-Apple-I-MD-M"]!, "oneTimePassword": json["X-Apple-I-MD"]!, "localUserID": json["X-Apple-I-MD-LU"]!, "routingInfo": json["X-Apple-I-MD-RINFO"]!, "deviceUniqueIdentifier": json["X-Mme-Device-Id"]!, "deviceDescription": json["X-MMe-Client-Info"]!, "date": json["X-Apple-I-Client-Time"]!, "locale": json["X-Apple-Locale"]!, "timeZone": json["X-Apple-I-TimeZone"]!, "deviceSerialNumber": "1"] - - if let anisette = ALTAnisetteData(json: formattedJSON) { + + let task = AppServices.network.session.dataTask(with: url) { data, response, error in + + guard let data = data, error == nil else { return } + + do { + // make sure this JSON is in the format we expect + // convert data to json + if let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String: String] { + // try to read out a dictionary + //for some reason serial number isn't needed but it doesn't work unless it has a value + let formattedJSON: [String: String] = ["machineID": json["X-Apple-I-MD-M"]!, "oneTimePassword": json["X-Apple-I-MD"]!, "localUserID": json["X-Apple-I-MD-LU"]!, "routingInfo": json["X-Apple-I-MD-RINFO"]!, "deviceUniqueIdentifier": json["X-Mme-Device-Id"]!, "deviceDescription": json["X-MMe-Client-Info"]!, "date": json["X-Apple-I-Client-Time"]!, "locale": json["X-Apple-Locale"]!, "timeZone": json["X-Apple-I-TimeZone"]!, "deviceSerialNumber": "1"] + + if let anisette = ALTAnisetteData(json: formattedJSON) { DLOG("Anisette used: %@", formattedJSON) - self.finish(.success(anisette)) - } - } - } catch let error as NSError { - print("Failed to load: \(error.localizedDescription)") - self.finish(.failure(error)) - } - - } - - task.resume() + self.finish(.success(anisette)) + } + } + } catch let error as NSError { + print("Failed to load: \(error.localizedDescription)") + self.finish(.failure(error)) + } + + } + + task.resume() } } diff --git a/AltStore/Operations/FetchSourceOperation.swift b/AltStore/Operations/FetchSourceOperation.swift index 443cd037..250fbb6c 100644 --- a/AltStore/Operations/FetchSourceOperation.swift +++ b/AltStore/Operations/FetchSourceOperation.swift @@ -18,7 +18,7 @@ final class FetchSourceOperation: ResultOperation let sourceURL: URL let managedObjectContext: NSManagedObjectContext - private let session: URLSession + private let session: URLSession = AppServices.network.sessionNoCache private lazy var dateFormatter: ISO8601DateFormatter = { let dateFormatter = ISO8601DateFormatter() @@ -29,12 +29,6 @@ final class FetchSourceOperation: ResultOperation { self.sourceURL = sourceURL self.managedObjectContext = managedObjectContext - - let configuration = URLSessionConfiguration.default - configuration.requestCachePolicy = .reloadIgnoringLocalCacheData - configuration.urlCache = nil - - self.session = URLSession(configuration: configuration) } override func main() diff --git a/AltStore/Operations/FetchTrustedSourcesOperation.swift b/AltStore/Operations/FetchTrustedSourcesOperation.swift index 9fced831..07bef5ae 100644 --- a/AltStore/Operations/FetchTrustedSourcesOperation.swift +++ b/AltStore/Operations/FetchTrustedSourcesOperation.swift @@ -37,8 +37,8 @@ final class FetchTrustedSourcesOperation: ResultOperation<[FetchTrustedSourcesOp override func main() { super.main() - - let dataTask = URLSession.shared.dataTask(with: .trustedSources) { (data, response, error) in + let session: URLSession = AppServices.network.session + let dataTask = session.dataTask(with: .trustedSources) { (data, response, error) in do { if let response = response as? HTTPURLResponse diff --git a/AltStore/Operations/UpdatePatronsOperation.swift b/AltStore/Operations/UpdatePatronsOperation.swift index 8e4064b8..f170ff37 100644 --- a/AltStore/Operations/UpdatePatronsOperation.swift +++ b/AltStore/Operations/UpdatePatronsOperation.swift @@ -43,7 +43,7 @@ final class UpdatePatronsOperation: ResultOperation { super.main() - let dataTask = URLSession.shared.dataTask(with: .patreonInfo) { (data, response, error) in + let dataTask = AppServices.network.session.dataTask(with: .patreonInfo) { (data, response, error) in do { if let response = response as? HTTPURLResponse diff --git a/AltStore/Services/NetworkService.swift b/AltStore/Services/NetworkService.swift new file mode 100644 index 00000000..7ff19a95 --- /dev/null +++ b/AltStore/Services/NetworkService.swift @@ -0,0 +1,81 @@ +// +// NetworkService.swift +// SideStore +// +// Created by Joseph Mattiello on 11/6/22. +// Copyright © 2022 Riley Testut. All rights reserved. +// + +import Foundation + +public protocol Services { + var network: any NetworkService { get } +} + +public protocol NetworkService { + var session: URLSession { get } + var sessionNoCache: URLSession { get } + var backgroundSession: URLSession { get } +} + +public struct DefaultServices: Services { + public var network: NetworkService = AltNetworkService() +} + +let AppServices = DefaultServices() + +final public class AltNetworkDelegate: NSObject, URLSessionTaskDelegate { + public struct Options: OptionSet { + public let rawValue: Int + public init(rawValue: Int) { + self.rawValue = rawValue + } + + public static let redirect = Options(rawValue: 1 << 0) + public static let all: Options = [.redirect] + } + + var options: Options = .all + +// func urlSession(_ session: URLSession, task: URLSessionTask, willPerformHTTPRedirection response: HTTPURLResponse, newRequest request: URLRequest, completionHandler: @escaping (URLRequest?) -> Void) { +// if options.contains(.redirect) { +// completionHandler(request) +// } else { +// completionHandler(nil) +// } +// } + + public func urlSession(_ session: URLSession, task: URLSessionTask, willPerformHTTPRedirection response: HTTPURLResponse, newRequest request: URLRequest) async -> URLRequest? { + if options.contains(.redirect) { + return request + } else { + return nil + } + } +} + +public final class AltNetworkService: NetworkService { + public let session: URLSession = { + let configuration: URLSessionConfiguration = URLSessionConfiguration.default + configuration.httpShouldSetCookies = true + configuration.httpShouldUsePipelining = true + let session = URLSession.init(configuration: configuration, delegate: AltNetworkDelegate(), delegateQueue: nil) + return session + }() + + public let sessionNoCache: URLSession = { + let configuration: URLSessionConfiguration = URLSessionConfiguration.default + configuration.requestCachePolicy = .reloadIgnoringLocalCacheData + configuration.urlCache = nil + let session = URLSession.init(configuration: configuration, delegate: AltNetworkDelegate(), delegateQueue: nil) + return session + }() + + static let backgroundSessionIdentifier = "SideStoreBackgroundSession" + + public let backgroundSession: URLSession = { + let configuration: URLSessionConfiguration = URLSessionConfiguration.background(withIdentifier: AltNetworkService.backgroundSessionIdentifier) + let session = URLSession.init(configuration: configuration, delegate: AltNetworkDelegate(), delegateQueue: nil) + return session + }() +}