結論
URLSession.webSocketTask(with:)
の引数にURLRequestではなくURLを渡せばおk
なぜか通せないプロキシ、URLSessionWebSocketTaskとの戦い
URLSessionWebSocketTaskという便利な公式APIが公開されていたのでWebSocketクライアントを置き換えよう、てなわけで
ChatGPTに書いてもらう
まずはこいつに書かせればええやん?
今日はこれで良いや(本筋ではないので)
生成されたコード
import Foundation
class WebSocketClient {
private var task: URLSessionWebSocketTask?
private let queue = DispatchQueue(label: "WebSocketClientQueue", attributes: .concurrent)
func connect(request: URLRequest) async throws {
let session = URLSession(configuration: .default)
task = try await session.webSocketTask(with: request)
task?.resume()
try await receiveMessage()
}
func send(message: String) async throws {
guard let task = task else {
throw NSError(domain: "WebSocketTask not found", code: 0, userInfo: nil)
}
let message = URLSessionWebSocketTask.Message.string(message)
try await task.send(message)
}
private func receiveMessage() async throws {
while true {
let result = try await task?.receive()
switch result {
case .failure(let error):
throw error
case .success(let message):
switch message {
case .string(let text):
print("Received message: \(text)")
// Process the received message
case .data(let data):
print("Received data: \(data)")
// Process the received data
@unknown default:
print("Unknown message type received")
}
}
}
}
func disconnect() async throws {
task?.cancel(with: .normalClosure, reason: nil)
}
}
しかしこのままではCharles等のアプリケーションで通信内容が覗けないので、プロキシに対応する
func connect(request: URLRequest) async throws {
if let proxy = Proxy(from: CFNetworkCopySystemProxySettings()?.takeRetainedValue() as? [String: Any]) {
configuration.connectionProxyDictionary = [
kCFNetworkProxiesHTTPProxy: proxy.host,
kCFNetworkProxiesHTTPPort: proxy.port,
kCFNetworkProxiesHTTPEnable: 1,
"HTTPSProxy": proxy.host,
"HTTPSPort": proxy.port,
"HTTPSEnable": 1,
]
}
let urlSession = URLSession(configuration: configuration, delegate: self, delegateQueue: queue)
task = try await session.webSocketTask(with: request)
task?.resume()
try await receiveMessage()
}
こちらを参照(iOSではHTTPS proxy用のkeyが公開されていないらしい)
https://github.com/shiguredo/sora-ios-sdk/blob/fa4fe236a331534d63384c6b7aac847411932a6e/Sora/URLSessionWebSocketChannel.swift#L29-L52
そしてCharlesを通して動かしてみると、「指定されたホストが存在しません」のエラー
困った...とあれこれ調べるも解決策になりそうなものは見当たらない...と思ったその矢先
For some reason, URLSessionWebSocketTask will only respect the proxy configuration if started with a URL and not a URLRequest. As a temporary
workaround, port header information from the request to the session.
https://github.com/signalapp/Signal-iOS/blob/eaed4da06347a3a1af45662a6424d6bc99bf1bf8/SignalServiceKit/src/Network/SSKWebSocket.swift#L194-L196
ふぅん
下のように引数にURLRequestを渡していたのでURLを渡すようにしてみると...通った
func connect(to url: URL) async throws {
...
let urlSession = URLSession(configuration: configuration, delegate: self, delegateQueue: queue)
task = try await session.webSocketTask(with: url) // ここを変えた
...
}
バグだろうか?
requestに設定できる諸々はURLSessionConfigurationに設定できるのでURL指定のI/Fにしても問題はないのでこれでしばらく食いつなぐかあ