1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

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

Posted at

結論

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

なぜか通せないプロキシ、URLSessionWebSocketTaskとの戦い

URLSessionWebSocketTaskという便利な公式APIが公開されていたのでWebSocketクライアントを置き換えよう、てなわけで

ChatGPTに書いてもらう

まずはこいつに書かせればええやん?

image.png

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

生成されたコード
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にしても問題はないのでこれでしばらく食いつなぐかあ

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?