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