Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Support to set ignored URLProtocol classes #155

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 48 additions & 4 deletions Sources/Atlantis.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@ public final class Atlantis: NSObject {
private(set) var configuration: Configuration = Configuration.default()
private var packages: [String: TrafficPackage] = [:]
private lazy var waitingWebsocketPackages: [String: [TrafficPackage]] = [:]
private var ignoreProtocols: [AnyClass] = []
private let queue = DispatchQueue(label: "com.proxyman.atlantis")
private var ignoredRequestIds: Set<String> = []

// MARK: - Variables

Expand Down Expand Up @@ -138,6 +140,11 @@ public final class Atlantis: NSObject {
public class func setDelegate(_ delegate: AtlantisDelegate) {
Atlantis.shared.delegate = delegate
}

/// Set list of URLProtocol classes that cause the duplicate records
public class func setIgnoreProtocols(_ protocols: [AnyClass]) {
Atlantis.shared.ignoreProtocols = protocols
}
}

// MARK: - Private
Expand Down Expand Up @@ -199,23 +206,60 @@ extension Atlantis {
}
#endif
}

private func checkShouldIgnore(on request: URLRequest) -> Bool {
// Get the BBHTTPProtocolHandler class by name
for cls in ignoreProtocols {

// Get the canInitWithRequest: selector
let selector = NSSelectorFromString("canInitWithRequest:")

// Ensure the class responds to the selector
guard let method = class_getClassMethod(cls, selector) else {
print("[Atlantis] ❓ Warn: canInitWithRequest: method not found.")
return false
}

// Cast the method implementation to the correct function signature
typealias CanInitWithRequestFunction = @convention(c) (AnyClass, Selector, URLRequest) -> Bool
let canInitWithRequest = unsafeBitCast(method_getImplementation(method), to: CanInitWithRequestFunction.self)

// Call the method with the request
if canInitWithRequest(cls, selector, request) {
return true
}
}
return false
}

private func getPackage(_ taskOrConnection: AnyObject) -> TrafficPackage? {
private func getPackage(_ taskOrConnection: AnyObject, isCompleted: Bool = false) -> TrafficPackage? {
// This method should be called from our queue

// Receive package from the cache
let id = PackageIdentifier.getID(taskOrConnection: taskOrConnection)
guard !ignoredRequestIds.contains(id) else {
if isCompleted {
ignoredRequestIds.remove(id)
}
return nil
}
if let package = packages[id] {
return package
}

// If not found, just generate and cache
switch taskOrConnection {
case let task as URLSessionTask:
guard let package = TrafficPackage.buildRequest(sessionTask: task, id: id) else {
guard let request = task.currentRequest,
let package = TrafficPackage.buildRequest(sessionTask: task, id: id) else {
print("[Atlantis] ❌ Error: Should build package from URLSessionTask")
return nil
}

if checkShouldIgnore(on: request) {
ignoredRequestIds.insert(id)
return nil
}

packages[id] = package
return package
default:
Expand Down Expand Up @@ -353,7 +397,7 @@ extension Atlantis {
private func handleDidFinish(_ taskOrConnection: AnyObject, error: Error?) {
queue.sync {
guard Atlantis.isEnabled.value else { return }
guard let package = getPackage(taskOrConnection) else {
guard let package = getPackage(taskOrConnection, isCompleted: true) else {
return
}

Expand Down