swifthttpproxyrequest

Authenticate HTTP proxy for request


I'm simply trying to use a proxy to route a http request in Swift to measure the average round trip time of a list of proxies. I've went through multiple Stack Overflow threads on this topic but they are all super old / outdated.

format: host:port:username:password

Update

Why does the request work (throws no errors) but does not use the proxies? I'm assuming it's because the password isn't entered in the settings as the alert said. My users will want to test proxy speeds for many different Hosts/Ports, it doesn't make sense to enter the password in settings every time. How can I fix this issue?

image1

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, "")
        }
    }
}


Solution

  • This code enables requests in Swift to utilize a Proxy with Auth. The only way I found to Authenticate a proxy in swift was with NWConnection. All the other native methods didn't work or I was using them wrong.

    import Foundation
    import Network
    
    func averageProxyGroupSpeed() {
        var successCount = 0
        let group = DispatchGroup()
        let queue = DispatchQueue(label: "proxyQueue", attributes: .concurrent)
        let lock = NSLock()
    
        let selectedProxies = ["107.180.132.32:3128:slgayamo:xxeeikxt"]
    
        for proxy in selectedProxies {
            group.enter()
            queue.async {
                let proxyDetails = proxy.split(separator: ":").map(String.init)
                guard proxyDetails.count == 4,
                      let port = UInt16(proxyDetails[1]) else {
                    group.leave()
                    return
                }
    
                let proxyEndpoint = NWEndpoint.hostPort(host: .init(proxyDetails[0]), port: NWEndpoint.Port(integerLiteral: port))
                let proxyConfig = ProxyConfiguration(httpCONNECTProxy: proxyEndpoint, tlsOptions: nil)
                proxyConfig.applyCredential(username: proxyDetails[2], password: proxyDetails[3])
    
                let parameters = NWParameters.tcp
                let privacyContext = NWParameters.PrivacyContext(description: "ProxyConfig")
                privacyContext.proxyConfigurations = [proxyConfig]
                parameters.setPrivacyContext(privacyContext)
    
                let connection = NWConnection(to: .hostPort(host: "httpbin.org", port: 80), using: parameters)
                
                let start = Date()
                
                connection.stateUpdateHandler = { state in
                    switch state {
                    case .ready:
                        print("Connected to proxy: \(proxyDetails[0])")
                                            
                        let httpRequest = "GET /get HTTP/1.1\r\nHost: httpbin.org\r\nConnection: close\r\nAccept: */*\r\nUser-Agent: MySwiftApp/1.0\r\n\r\n"
                                            
                        
                        connection.send(content: httpRequest.data(using: .utf8), completion: .contentProcessed({ error in
                            if let error = error {
                                print("Failed to send request: \(error)")
                                group.leave()
                            } else {
                                connection.receive(minimumIncompleteLength: 1, maximumLength: 4096) { data, _, isComplete, error in
                                    defer { group.leave() }
                                    
                                    if let error = error {
                                        print("Failed to receive response: \(error)")
                                    } else if isComplete {
                                        print("complete")
                                        
                                        if let data {
                                            let responseString = String(data: data, encoding: .utf8) ?? ""
                                            print("Response from proxy \(proxyDetails[0]): \(responseString)")
                                        } else {
                                            print("data len is \(data?.count ?? -6)")
                                        }
                                        
                                        let duration = Date().timeIntervalSince(start) * 1000 // Convert to milliseconds
                                        
                                    } else if let data {
                                        let responseString = String(data: data, encoding: .utf8) ?? ""
                                        print("Response from proxy \(proxyDetails[0]): \(responseString)")
                                    } else {
                                        print("Not complete")
                                    }
                                }
                            }
                        }))
                    case .failed(let error):
                        print("Connection failed for proxy \(proxyDetails[0]): \(error)")
                        group.leave()
                    case .cancelled:
                        print("Connection cancelled for proxy \(proxyDetails[0])")
                        group.leave()
                    case .waiting(let error):
                        print("Connection waiting for proxy \(proxyDetails[0]): \(error)")
                        group.leave()
                    default:
                        break
                    }
                }
    
                connection.start(queue: queue)
            }
        }
    
        group.notify(queue: DispatchQueue.main) {
            // Handle completion if needed
        }
    }
    
    averageProxyGroupSpeed()