You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift中HTTP代理认证问题:请求无报错但未走代理的解决方法

Swift中带认证代理的请求转发与测速问题

我需要在Swift中通过代理转发HTTP请求,以此批量测量一组代理的平均往返时间。查阅了多篇Stack Overflow相关帖子,但内容均已过时。
代理格式:host:port:username:password

更新1

  • 调整group.leave()的位置,并按要求添加Info.plist配置项后,弹出了如下系统授权菜单。关闭该菜单后再次调用下方函数,函数无报错运行,但请求并未通过代理转发。
  • 推测问题出在未在系统设置中输入授权菜单要求的密码,但我的用户需要测试大量不同主机/端口的代理速度,每次手动在设置中输入密码完全不合理。如何解决这个问题?

系统代理授权弹窗

初始实现代码

func averageProxyGroupSpeed(proxies: [String], completion: @escaping (Int, String) -> Void) {
    let numProxies = proxies.count
    if numProxies == 0 {
        completion(0, "No proxies")
        return
    }

    var totalTime: Int64 = 0
    var successCount = 0
    let group = DispatchGroup()
    let queue = DispatchQueue(label: "proxyQueue", attributes: .concurrent)
    let lock = NSLock()

    let shuffledProxies = proxies.shuffled()
    let selectedProxies = Array(shuffledProxies.prefix(25))

    for proxy in selectedProxies {
        group.enter()
        queue.async {
            let proxyDetails = proxy.split(separator: ":").map(String.init)
            guard proxyDetails.count == 4,
                  let port = Int(proxyDetails[1]),
                  let url = URL(string: "http://httpbin.org/get") else {
                completion(0, "Invalid proxy format")
                group.leave()
                return
            }

            var request = URLRequest(url: url)
            request.timeoutInterval = 15

            let configuration = URLSessionConfiguration.default
            configuration.connectionProxyDictionary = [
                AnyHashable("HTTPEnable"): true,
                AnyHashable("HTTPProxy"): proxyDetails[0],
                AnyHashable("HTTPPort"): port,
                AnyHashable("HTTPSEnable"): false,
                AnyHashable("HTTPUser"): proxyDetails[2],
                AnyHashable("HTTPPassword"): proxyDetails[3]
            ]

            let session = URLSession(configuration: configuration)
            let start = Date()

            let task = session.dataTask(with: request) { _, _, error in
                defer { group.leave() }
                
                if let error = error {
                    print("Error: \(error.localizedDescription)")
                } else {
                    let duration = Date().timeIntervalSince(start) * 1000

                    lock.lock()
                    totalTime += Int64(duration)
                    successCount += 1
                    lock.unlock()
                }
            }
            task.resume()
        }
    }

    group.notify(queue: DispatchQueue.main) {
        if successCount == 0 {
            completion(0, "Proxies Failed")
        } else {
            let averageTime = Int(Double(totalTime) / Double(successCount))
            completion(averageTime, "")
        }
    }
}

更新2 - 尝试使用URLSessionDelegate

  • 即使实现了代理认证逻辑,仍然弹出相同的系统授权菜单

带Delegate的实现代码

class ProxySessionDelegate: NSObject, URLSessionDelegate {
    let username: String
    let password: String

    init(username: String, password: String) {
        self.username = username
        self.password = password
    }

    func urlSession(_ session: URLSession, task: URLSessionTask, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
        if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodHTTPBasic {
            let credential = URLCredential(user: self.username, password: self.password, persistence: .forSession)
            completionHandler(.useCredential, credential)
        } else {
            completionHandler(.performDefaultHandling, nil)
        }
    }
}

func averageProxyGroupSpeed(proxies: [String], completion: @escaping (Int, String) -> Void) {
    let numProxies = proxies.count
    if numProxies == 0 {
        completion(0, "No proxies")
        return
    }

    var totalTime: Int64 = 0
    var successCount = 0
    let group = DispatchGroup()
    let queue = DispatchQueue(label: "proxyQueue", attributes: .concurrent)
    let lock = NSLock()

    let shuffledProxies = proxies.shuffled()
    let selectedProxies = Array(shuffledProxies.prefix(25))

    for proxy in selectedProxies {
        group.enter()
        queue.async {
            let proxyDetails = proxy.split(separator: ":").map(String.init)
            guard proxyDetails.count == 4,
                  let port = Int(proxyDetails[1]),
                  let url = URL(string: "http://httpbin.org/get") else {
                completion(0, "Invalid proxy format")
                group.leave()
                return
            }

            var request = URLRequest(url: url)
            request.timeoutInterval = 15

            let configuration = URLSessionConfiguration.default
            configuration.connectionProxyDictionary = [
                AnyHashable("HTTPEnable"): true,
                AnyHashable("HTTPProxy"): proxyDetails[0],
                AnyHashable("HTTPPort"): port,
                AnyHashable("HTTPSEnable"): false,
                AnyHashable("HTTPUser"): proxyDetails[2],
                AnyHashable("HTTPPassword"): proxyDetails[3]
            ]

            let delegate = ProxySessionDelegate(username: proxyDetails[2], password: proxyDetails[3])
            let session = URLSession(configuration: configuration, delegate: delegate, delegateQueue: nil)
            let start = Date()

            let task = session.dataTask(with: request) { _, _, error in
                defer { group.leave() }
                
                if let error = error {
                    print("Error: \(error.localizedDescription)")
                } else {
                    let duration = Date().timeIntervalSince(start) * 1000

                    lock.lock()
                    totalTime += Int64(duration)
                    successCount += 1
                    lock.unlock()
                }
            }
            task.resume()
        }
    }

    group.notify(queue: DispatchQueue.main) {
        if successCount == 0 {
            completion(0, "Proxies Failed")
        } else {
            let averageTime = Int(Double(totalTime) / Double(successCount))
            completion(averageTime, "")
        }
    }
}

内容的提问来源于stack exchange,提问作者Ahmed Zaidan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 14:17:15