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

Swift使用带认证代理时出现HTTP 400错误请求问题求助

Swift代理请求返回HTTP 400 Bad Request问题排查与解决

问题概述

在Swift中使用带认证的HTTP代理,通过NWConnection已成功建立与代理服务器的连接,但向httpbin.org发送请求时收到HTTP/1.1 400 Bad Request响应,需排查请求格式问题。

错误响应

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

问题原因与解决方法

核心问题:HTTP请求格式错误

你构造的多行HTTP请求字符串中,每行开头包含了代码缩进带来的多余空格,导致实际发送的请求行和请求头前面存在无效空格,触发代理服务器的400错误。

另外,对于HTTP代理(目标端口为80),请求行使用相对路径(GET /get HTTP/1.1)比完整URL更规范,部分代理服务器可能对此有严格要求。

修正后的HTTP请求构造

将请求字符串改为无缩进的格式,或使用字符串拼接避免多余空格:

let httpRequest = "GET /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"

或者使用多行字符串时去掉所有缩进:

let httpRequest = """
GET /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
"""

其他注意点

  • 确认代理服务器支持HTTP代理模式(你当前使用的httpCONNECTProxy是用于HTTPS的CONNECT方法,若目标是HTTP,部分代理可能需要调整配置,但从连接成功来看,认证是有效的,主要问题还是请求格式)。
  • 若后续请求HTTPS目标,需使用CONNECT方法建立隧道,但当前目标是HTTP,直接发送普通HTTP请求即可。

内容的提问来源于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 08:57:00