URLSessionWebSocketTaskでプロキシを通せなくて困ったんだが？

# 結論
`URLSession.webSocketTask(with:)`の引数にURLRequestではなくURLを渡せばおk

# なぜか通せないプロキシ、URLSessionWebSocketTaskとの戦い
URLSessionWebSocketTaskという便利な公式APIが公開されていたのでWebSocketクライアントを置き換えよう、てなわけで

### ChatGPTに書いてもらう
まずはこいつに書かせればええやん?

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/661548/7a153ae1-91f9-29b1-4f05-000a8b1bbc0d.png)

今日はこれで良いや(本筋ではないので)

<details><summary>生成されたコード</summary><div>

```swift
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)
    }
}
```

</div></details>

しかしこのままではCharles等のアプリケーションで通信内容が覗けないので、プロキシに対応する

```swift
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を渡すようにしてみると...通った
```swift
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にしても問題はないのでこれでしばらく食いつなぐかあ