mirror of
https://github.com/ProxymanApp/atlantis.git
synced 2026-05-20 20:20:35 +00:00
129 lines
4.4 KiB
Swift
129 lines
4.4 KiB
Swift
//
|
|
// NetworkInjector.swift
|
|
// atlantis
|
|
//
|
|
// Created by Nghia Tran on 10/23/20.
|
|
// Copyright © 2020 Proxyman. All rights reserved.
|
|
//
|
|
|
|
import Foundation
|
|
|
|
struct NetworkConfiguration {
|
|
|
|
/// Whether or not Atlantis should perform the Method Swizzling on WS/WSS connection.
|
|
let shouldCaptureWebSocketTraffic: Bool
|
|
|
|
// init
|
|
init(shouldCaptureWebSocketTraffic: Bool = true) {
|
|
self.shouldCaptureWebSocketTraffic = shouldCaptureWebSocketTraffic
|
|
}
|
|
}
|
|
|
|
protocol Injector {
|
|
|
|
var delegate: InjectorDelegate? { get set }
|
|
func injectAllNetworkClasses(config: NetworkConfiguration)
|
|
}
|
|
|
|
protocol InjectorDelegate: AnyObject {
|
|
|
|
// For URLSession
|
|
func injectorSessionDidCallResume(task: URLSessionTask)
|
|
func injectorSessionDidReceiveResponse(dataTask: URLSessionTask, response: URLResponse)
|
|
func injectorSessionDidReceiveData(dataTask: URLSessionTask, data: Data)
|
|
func injectorSessionDidComplete(task: URLSessionTask, error: Error?)
|
|
func injectorSessionDidUpload(task: URLSessionTask, request: NSURLRequest, data: Data?)
|
|
|
|
// Websocket
|
|
func injectorSessionWebSocketDidSendMessage(task: URLSessionTask, message: URLSessionWebSocketTask.Message)
|
|
func injectorSessionWebSocketDidReceive(task: URLSessionTask, message: URLSessionWebSocketTask.Message)
|
|
func injectorSessionWebSocketDidSendPingPong(task: URLSessionTask)
|
|
func injectorSessionWebSocketDidSendCancelWithReason(task: URLSessionTask, closeCode: URLSessionWebSocketTask.CloseCode, reason: Data?)
|
|
}
|
|
|
|
final class NetworkInjector: Injector {
|
|
|
|
// MARK: - Variables
|
|
|
|
weak var delegate: InjectorDelegate?
|
|
|
|
// MARK: - Internal
|
|
|
|
func injectAllNetworkClasses(config: NetworkConfiguration = NetworkConfiguration()) {
|
|
// Make sure we swizzle *ONCE*
|
|
DispatchQueue.once {
|
|
injectAllURLSession(config)
|
|
}
|
|
}
|
|
}
|
|
|
|
extension NetworkInjector {
|
|
|
|
private func injectAllURLSession(_ config: NetworkConfiguration) {
|
|
|
|
// iOS 8: __NSCFURLSessionConnection
|
|
// iOS 9, 10, 11, 12, 13, 14: __NSCFURLLocalSessionConnection
|
|
// This approach works with delegate or complete block from URLSession
|
|
let sessionClass: AnyClass? = NSClassFromString("__NSCFURLLocalSessionConnection") ?? NSClassFromString("__NSCFURLSessionConnection")
|
|
|
|
if let anySessionClass = sessionClass {
|
|
injectIntoURLSessionDelegate(anyClass: anySessionClass)
|
|
}
|
|
|
|
// Resume
|
|
injectURLSessionResume()
|
|
|
|
// Upload
|
|
injectURLSessionUploadTasks()
|
|
|
|
// Websocket
|
|
// Able to opt-out the WS/WSS if needed
|
|
if config.shouldCaptureWebSocketTraffic {
|
|
injectURLSessionWebsocketTasks()
|
|
}
|
|
}
|
|
|
|
private func injectIntoURLSessionDelegate(anyClass: AnyClass) {
|
|
_swizzleURLSessionDataTaskDidReceiveResponse(baseClass: anyClass)
|
|
_swizzleURLSessionDataTaskDidReceiveData(baseClass: anyClass)
|
|
_swizzleURLSessionTaskDidCompleteWithError(baseClass: anyClass)
|
|
}
|
|
|
|
private func injectURLSessionResume() {
|
|
// In iOS 7 resume lives in __NSCFLocalSessionTask
|
|
// In iOS 8 resume lives in NSURLSessionTask
|
|
// In iOS 9 resume lives in __NSCFURLSessionTask
|
|
// In iOS 14 resume lives in NSURLSessionTask
|
|
var baseResumeClass: AnyClass? = nil;
|
|
if !ProcessInfo.processInfo.responds(to: #selector(getter: ProcessInfo.operatingSystemVersion)) {
|
|
baseResumeClass = NSClassFromString("__NSCFLocalSessionTask")
|
|
} else {
|
|
#if targetEnvironment(macCatalyst) || os(macOS)
|
|
baseResumeClass = URLSessionTask.self
|
|
#else
|
|
let majorVersion = ProcessInfo.processInfo.operatingSystemVersion.majorVersion
|
|
if majorVersion < 9 || majorVersion >= 14 {
|
|
baseResumeClass = URLSessionTask.self
|
|
} else {
|
|
baseResumeClass = NSClassFromString("__NSCFURLSessionTask")
|
|
}
|
|
#endif
|
|
}
|
|
|
|
guard let resumeClass = baseResumeClass else {
|
|
assertionFailure("Could not find URLSessionTask. Please open support ticket at https://github.com/ProxymanApp/atlantis")
|
|
return
|
|
}
|
|
|
|
_swizzleURLSessionResumeSelector(baseClass: resumeClass)
|
|
}
|
|
|
|
private func injectURLSessionUploadTasks() {
|
|
_swizzleURLSessionUploadSelector(baseClass: URLSession.self)
|
|
}
|
|
|
|
private func injectURLSessionWebsocketTasks() {
|
|
_swizzleURLSessionWebsocketSelector()
|
|
}
|
|
}
|