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

Swift中URLSession转NWConnection代理请求无响应问题求助

解决NWConnection通过认证代理发起HTTPS请求无响应的问题

针对你将URLSession GET请求转换为NWConnection时遇到的无响应问题,以下是核心问题排查和修正方案:

核心问题分析

  1. HTTPS端口错误:目标URL为HTTPS协议,默认端口应为443,但原代码使用url.port ?? 80,导致连接到非HTTPS端口,无法建立正确加密连接。
  2. 缺少TLS配置:原代码使用NWParameters.tcp未启用TLS加密,无法与HTTPS服务器通信。
  3. HTTP请求换行符不规范:原请求使用\r作为换行,不符合HTTP标准的\r\n,可能导致代理或服务器无法正确解析请求。
  4. 代理配置关联逻辑问题:原代码中PrivacyContext的设置方式未确保代理配置生效,需调整参数关联逻辑。

修正后的完整代码

func updateOrderStatusProxy(completion: @escaping (Bool) -> Void) {
    let orderLink = "https://shop.ccs.com/51913883831/orders/f3ef2745f2b06c6b410e2aa8a6135847"

    guard let url = URL(string: orderLink) else {
        completion(true)
        return
    }
    
    let proxy = "resi.wealthproxies.com:8000:akzaidan:x0if46jo-country-US-session-7cz6bpzy-duration-60"
    let proxyDetails = proxy.split(separator: ":").map(String.init)
    guard proxyDetails.count == 4, let port = UInt16(proxyDetails[1]) else {
        print("Invalid proxy format")
        completion(false)
        return
    }

    // 配置代理端点和认证信息
    let proxyEndpoint = NWEndpoint.hostPort(host: NWEndpoint.Host(proxyDetails[0]),
                                            port: NWEndpoint.Port(integerLiteral: port))
    let proxyConfig = NWParameters.ProxyConfiguration(httpCONNECTProxy: proxyEndpoint, tlsOptions: nil)
    proxyConfig.applyCredential(username: proxyDetails[2], password: proxyDetails[3])

    // 配置TLS参数,适配HTTPS请求
    let tlsOptions = NWProtocolTLS.Options()
    sec_protocol_options_set_verify_block(tlsOptions.securityProtocolOptions, { (sec_protocol_metadata, sec_trust, sec_protocol_verify_complete) in
        let trust = sec_trust_copy_ref(sec_trust).takeRetainedValue()
        var error: CFError?
        if SecTrustEvaluateWithError(trust, &error) {
            sec_protocol_verify_complete(true)
        } else {
            sec_protocol_verify_complete(false)
        }
    }, DispatchQueue.global())
    
    let parameters = NWParameters.tls(options: tlsOptions)
    // 直接关联代理配置到参数
    parameters.proxyConfigurations = [proxyConfig]

    let host = url.host ?? ""
    let path = url.path.isEmpty ? "/" : url.path
    let query = url.query ?? ""
    let fullPath = query.isEmpty ? path : "\(path)?\(query)"

    // 使用HTTPS默认端口443
    let connection = NWConnection(
        to: .hostPort(
            host: NWEndpoint.Host(host),
            port: NWEndpoint.Port(integerLiteral: UInt16(url.port ?? 443))
        ),
        using: parameters
    )

    connection.stateUpdateHandler = { state in
        switch state {
        case .ready:
            print("Connected to proxy: \(proxyDetails[0])")

            // 使用标准HTTP换行符\r\n
            let httpRequest = """
            GET \(fullPath) HTTP/1.1\r\n
            Host: \(host)\r\n
            Connection: close\r\n
            Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\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
            Accept-Language: en-US,en;q=0.9\r\n
            Accept-Encoding: gzip, deflate, br\r\n
            Sec-Fetch-Dest: document\r\n
            Sec-Fetch-Mode: navigate\r\n
            Sec-Fetch-Site: none\r\n
            Priority: u=0, i\r\n
            \r\n
            """

            guard let requestData = httpRequest.data(using: .utf8) else {
                print("Failed to convert request to data")
                completion(false)
                return
            }

            connection.send(content: requestData, completion: .contentProcessed({ error in
                if let error = error {
                    print("Failed to send request: \(error)")
                    completion(false)
                    return
                }

                // 读取响应数据
                self.readAllData(connection: connection) { finalData, readError in
                    if let readError = readError {
                        print("Failed to receive response: \(readError)")
                        completion(false)
                        return
                    }

                    guard let data = finalData else {
                        print("No data received or unable to read data.")
                        completion(false)
                        return
                    }

                    // 处理压缩的响应
                    if let responseString = self.decompressResponse(data: data) {
                        print("Received \(data.count) bytes")
                        print("\n\nBody is \(responseString)")
                        completion(true)
                    } else if let body = String(data: data, encoding: .utf8) {
                        print("Received \(data.count) bytes (uncompressed)")
                        print("\n\nBody is \(body)")
                        completion(true)
                    } else {
                        print("Unable to decode response body.")
                        completion(false)
                    }
                }
            }))

        case .failed(let error):
            print("Connection failed for proxy \(proxyDetails[0]): \(error)")
            completion(false)

        case .cancelled:
            print("Connection cancelled for proxy \(proxyDetails[0])")
            completion(false)

        case .waiting(let error):
            print("Connection waiting for proxy \(proxyDetails[0]): \(error)")
            completion(false)

        default:
            break
        }
    }

    connection.start(queue: .global())
}

private func readAllData(connection: NWConnection,
                         accumulatedData: Data = Data(),
                         completion: @escaping (Data?, Error?) -> Void) {
    
    connection.receive(minimumIncompleteLength: 1, maximumLength: 65536) { data, context, isComplete, error in
        
        if let error = error {
            completion(nil, error)
            return
        }
        
        let newAccumulatedData = accumulatedData + (data ?? Data())

        if isComplete {
            completion(newAccumulatedData, nil)
        } else {
            self.readAllData(connection: connection,
                             accumulatedData: newAccumulatedData,
                             completion: completion)
        }
    }
}

// 处理gzip/deflate/br压缩的响应
private func decompressResponse(data: Data) -> String? {
    guard let responseString = String(data: data, encoding: .utf8),
          let headerEndIndex = responseString.range(of: "\r\n\r\n")?.upperBound else {
        return nil
    }
    
    let headersString = String(responseString[..<headerEndIndex])
    let bodyData = data.subdata(in: headerEndIndex.utf16Offset(in: responseString)..<data.count)
    
    if headersString.contains("Content-Encoding: gzip") {
        do {
            let decompressedData = try Data(contentsOf: GzipArchive(data: bodyData))
            return String(data: decompressedData, encoding: .utf8)
        } catch {
            print("Failed to decompress gzip data: \(error)")
            return nil
        }
    } else if headersString.contains("Content-Encoding: deflate") {
        do {
            let decompressedData = try Data(contentsOf: DeflateArchive(data: bodyData))
            return String(data: decompressedData, encoding: .utf8)
        } catch {
            print("Failed to decompress deflate data: \(error)")
            return nil
        }
    } else if headersString.contains("Content-Encoding: br") {
        do {
            let decompressedData = try BrotliDecoder.decode(bodyData)
            return String(data: decompressedData, encoding: .utf8)
        } catch {
            print("Failed to decompress br data: \(error)")
            return nil
        }
    }
    
    return nil
}

关键修改说明

  • HTTPS端口修正:将目标端口改为443,适配HTTPS协议要求。
  • 添加TLS配置:创建支持加密的NWParameters,确保与HTTPS服务器的安全通信。
  • 规范HTTP请求格式:统一使用\r\n作为换行符,符合HTTP协议标准。
  • 调整代理配置方式:直接将代理配置关联到NWParameters,确保代理生效。
  • 添加压缩响应处理:针对请求头中的Accept-Encoding,新增gzip/deflate/br解压逻辑,正确解析服务器返回的压缩内容。

内容的提问来源于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 07:05:02