I am trying to use a proxy with auth in Swift. I finally got it working using NWConnection
. And I can see from my proxy server that it's actually getting the request from my iOS app when I call this function. However I get the error response below. I am assuming something about my request to httpbin.org
is wrong but I have not been able to figure it out. The proxy in the array is working and feel free to use it if you want to test.
Connected to proxy: resi.wealthproxies.com
Response from proxy resi.wealthproxies.com: HTTP/1.1 400 Bad Request
Server: awselb/2.0
Date: Thu, 13 Feb 2025 21:31:28 GMT
Content-Type: text/html
Content-Length: 122
Connection: close
<html>
<head><title>400 Bad Request</title></head>
<body>
<center><h1>400 Bad Request</h1></center>
</body>
</html>
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 = ["resi.wealthproxies.com:8000:akzaidan:x0if46jo-country-US-session-niwtz2y7-duration-60"]
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 {
completion(0, "Invalid proxy format")
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 http://httpbin.org/get HTTP/1.1\r\n
Host: httpbin.org\r\n
Connection: close\r\n
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n
Accept-Encoding: gzip, deflate\r\n
Accept-Language: en-US,en;q=0.9\r\n
Upgrade-Insecure-Requests: 1\r\n
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0.1 Safari/605.1.15\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 {
// Receive the response
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
lock.lock()
totalTime += Int64(duration)
successCount += 1
lock.unlock()
} 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) {
if successCount == 0 {
completion(0, "Proxies Failed")
} else {
let averageTime = Int(Double(totalTime) / Double(successCount))
completion(averageTime, "")
}
}
}
The issue was the formatting of the HTTP request within the string. The correct format is:
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"
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.
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()