Reputation: 108
I am using a NWConnection
to make a request using an http proxy that requires authorization. The only way I found to make the request with this proxy is with an NWConnection
. The request also specifies that it should accept encodings of types: Br, Deflate, and Gzip
. I am using 3 different packages to decode each of the three type. How do I know which type the response is encoded with so I can pick the correct decoding type?
NWConnection
returns the response headers with the body as Data. Im assuming the encoding type is in those headers, but I am unsure how to separate the headers from the request body.
Function to decode
import Foundation
import Gzip
import SwiftyDeflate
import SwiftBrotli
func decodeRequest(data: Data, encodingType: String?) -> String? {
var encoding = ""
if let encodingType {
encoding = encodingType
}
switch encoding.lowercased() {
case "gzip":
do {
let decompressedData = try data.gunzipped()
return String(data: decompressedData, encoding: .utf8) ?? ""
} catch {
return nil
}
case "deflate":
do {
let decompressedData = try data.deflateDecompress()
return String(data: decompressedData, encoding: .utf8) ?? ""
} catch {
return nil
}
case "br", "brotli":
let brotli = Brotli()
switch brotli.decompress(data) {
case .success(let decompressedData):
return String(data: decompressedData, encoding: .utf8) ?? ""
case .failure:
return nil
}
default:
return String(data: data, encoding: .utf8) ?? ""
}
}
Request
func updateOrderStatusProxy(proxy: String, completion: @escaping (Bool) -> Void) {
let orderLink = "some link"
guard let url = URL(string: orderLink) else {
completion(true)
return
}
let proxyDetails = proxy.split(separator: ":").map(String.init)
guard proxyDetails.count == 4, let port = UInt16(proxyDetails[1]) else {
completion(false)
print("Invalid proxy format")
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.tls
let privacyContext = NWParameters.PrivacyContext(description: "ProxyConfig")
privacyContext.proxyConfigurations = [proxyConfig]
parameters.setPrivacyContext(privacyContext)
let host = url.host ?? ""
let path = url.path.isEmpty ? "/" : url.path
let query = url.query ?? ""
let fullPath = query.isEmpty ? path : "\(path)?\(query)"
let connection = NWConnection(
to: .hostPort(
host: .init(host),
port: .init(integerLiteral: UInt16(url.port ?? 443))
),
using: parameters
)
connection.stateUpdateHandler = { state in
switch state {
case .ready:
let httpRequest = "GET \(fullPath) HTTP/1.1\r\nHost: \(host)\r\nConnection: close\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\nUser-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\nAccept-Language: en-US,en;q=0.9\r\nAccept-Encoding: gzip, deflate, br\r\nSec-Fetch-Dest: document\r\nSec-Fetch-Mode: navigate\r\nSec-Fetch-Site: none\r\nPriority: u=0, i\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)")
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 body = decodeRequest(data: data, encodingType: nil) {
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)
}
}
}
Upvotes: 0
Views: 31