How to inspect websocket traffic with charlesproxy for iOS simulator/devices

旧时模样 提交于 2019-12-02 16:14:22

I finally found the answer.

Charles 3.11.2 works perfectly with WebSocket.

I use socketIO, so I've already seen http requests sent during the negotiation phase, but I missed websockets traffic.

In the beginning, socketIO try to use polling then switches to use websockets.

The websocket traffic is visible when you go to the request with status: "Sending request body" which is actually wss:// request.

You even have a dedicated tab for this kind of traffic. The rest of messages will appear right there.

PS1. Ensure you're connected to socket properly then it appears in Charles.
PS2. I suggest using socketIO it's a great enhancement for full-duplex traffic like websockets.

UPDATE: Apparently socket.io-client-swift v15.1.0 now properly supports SOCKS proxy. I have not yet tried it, but it would mean that these manual edits to Starscream are no longer required.


The accepted answer does not seem to work with Socket.IO on iOS devices.

The latest version of Socket.IO-Client-Swift (15.0.0 at the time of writing) uses Starscream for WebSockets on iOS/OS X.

The good news is that Starscream supports SOCKS proxying however:

  1. Socket.IO does not expose the Starscream websocket or provide any API for enabling the SOCKS proxying behaviour.

  2. The SOCKS proxying built into Starscream uses the OS SOCKS proxy settings which are cumbersome to setup (at least for iOS).

If I get some time I might propose a PR to address this more thoroughly, but given that it requires work to both Starscream and Socket.IO-Client-Swift, this is not entirely straightforward.

The easiest way to hack around this for temporary debugging purposes (which is the use case for Charles!), is to edit the WebSocket.swift file as part of Starscream, and replace this code:

if enableSOCKSProxy {
    let proxyDict = CFNetworkCopySystemProxySettings()
    let socksConfig = CFDictionaryCreateMutableCopy(nil, 0, proxyDict!.takeRetainedValue())
    let propertyKey = CFStreamPropertyKey(rawValue: kCFStreamPropertySOCKSProxy)
    CFWriteStreamSetProperty(outputStream, propertyKey, socksConfig)
    CFReadStreamSetProperty(inputStream, propertyKey, socksConfig)
}

with this code:

let socksConfig = CFDictionaryCreateMutableCopy(nil, 0, CFNetworkCopySystemProxySettings()!.takeRetainedValue()) as! [String: Any]
let propertyKey = CFStreamPropertyKey(rawValue: kCFStreamPropertySOCKSProxy)
let ip = socksConfig["HTTPSProxy"]
let proxySocksConfig = ["SOCKSProxy": ip, "SOCKSPort": 8889, "SOCKSEnable": true] as CFDictionary // Where 8889 is the SOCKS proxy port in Charles
CFWriteStreamSetProperty(outputStream, propertyKey, proxySocksConfig)
CFReadStreamSetProperty(inputStream, propertyKey, proxySocksConfig)

This will ensure the SOCKS proxy is enabled by default, and will route all the websockets traffic via Charles.

You then need to ensure that the HTTP proxy settings are configured in iOS (since the same IP will be used for both HTTP and SOCKS), SOCKS proxy is enabled in Charles, and that the port matches the port in the code above (by default 8889).

Thanks for your very very helpful answer Jonathan Ellis! I'm using Pusher and this worked great!

However, I found socksConfig to not always contain valid data and didn't work or would crash the app when I pulled the IP from there. Since the only thing we are getting from there is the localhost IP I just replaced the following in WebSocket.swift

if enableSOCKSProxy {
    let proxyDict = CFNetworkCopySystemProxySettings()
    let socksConfig = CFDictionaryCreateMutableCopy(nil, 0, proxyDict!.takeRetainedValue())
    let propertyKey = CFStreamPropertyKey(rawValue: kCFStreamPropertySOCKSProxy)
    CFWriteStreamSetProperty(outputStream, propertyKey, socksConfig)
    CFReadStreamSetProperty(inputStream, propertyKey, socksConfig)
}

with this:

let propertyKey = CFStreamPropertyKey(rawValue: kCFStreamPropertySOCKSProxy)
let proxySocksConfig = ["SOCKSProxy": "127.0.0.1", "SOCKSPort": 8889, "SOCKSEnable": true] as CFDictionary // Where 8889 is the SOCKS proxy port in Charles
CFWriteStreamSetProperty(outputStream, propertyKey, proxySocksConfig)
CFReadStreamSetProperty(inputStream, propertyKey, proxySocksConfig)

And then enabled the socks proxy in Charles as you described.

Thanks Again!

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!