I'm developing a iOS app and im using the SocketIO Swift SDK for connecting to a server with Sails(v0.12.11). I want to know how i can emit to a url (e.g I want to broadcast to all users) i tried:
socket.emit("post", ["data": ["message": "Hello World"], "url": "/user/broadcast"])
Unfortunately, it won't trigger Sails to trigger the controller/action. When I make the same request via HTTP I see a new message in the log file.
Does someone know how to make the virtual get request?
I remember our iOS developers struggled with this also...
After a while, we realised sails socket need to be connected with additional query params...
This is c/p from one of the projects where we connect to sails socket...
let socket: SocketIOClient = SocketIOClient(socketURL: NSURL(string: socketURL)!, options: Set(arrayLiteral: SocketIOClientOption.Log(true), .ConnectParams(["__sails_io_sdk_version": "0.11.0"])))
As you can see we are passing __sails_io_sdk_version": "0.11.0" to connection...
Try sending this and check if it works...
I'm trying to use the Google Translate API for a simple iOS app I'm building. I'm essentially stuck in trying to use SSL for this request.
According to Google Translate API, it supports SSL. So now my question is how do I even do this? Here's some code that I'm trying right now. I have an API from the translate API.
func translateRequest(text: String, fromLang: String, toLang: String) {
let httpsURL = NSURL(string: "https://www.googleapis.com/language/translate/v2?key=<MYAPIKEYHERE>&source=\(fromLang)&target=\(toLang)&q=\(text)&format=text")
let sessionConfiguration = NSURLSessionConfiguration.defaultSessionConfiguration()
sessionConfiguration.timeoutIntervalForRequest = NSTimeInterval(10)
let session = NSURLSession(configuration: sessionConfiguration, delegate: self, delegateQueue: nil)
let translateTask = session.dataTaskWithURL(httpsURL!, completionHandler: completionHandlerTranslate)
translateTask.resume()
}
func completionHandlerTranslate(dataOpt: NSData?, responseOpt: NSURLResponse?, errorOpt: NSError?) {
// A helper function to do something with responses
}
Now since I'm using NSURLSession (instead of NSURLConnection), I'm also having my controller by the NSURLSessionDelegate. According to Apple's docs (in the Configuring Authentication section), for NSURLSession I only need to implement didReceiveChallenge function. The signature of the function is:
func URLSession(session: NSURLSession, didReceiveChallenge challenge: NSURLAuthenticationChallenge, completionHandler: (NSURLSessionAuthChallengeDisposition, NSURLCredential?) -> Void) {
}
But I have no idea what should go in there. I've seen a lot of examples of folks trying to verify the server etc, but I'm still confused.
Here are my questions:
SSL is used to encrypt data, so what do I need to do in didReceiveChallenge for encrypted communication to happen between client and server?
How can I make sure that didReceiveChallenge rejects all other certificates (like placing a proxy like CharlesProxy and trying to sniff the data)?
It usually isn't a good idea to prevent use of Charles Proxy, because it isn't practical for someone to use it against someone else. It only works because the user has explicitly installed certificates to tell the OS to trust the fake certs that Charles Proxy produces. Users generally like the ability to see what data your app is sending on their own devices, and preventing them from doing so will probably create the impression that your app is doing something shady.
With that said, if you really want to prevent HTTPS proxies from working, the way you would do that is with key pinning. Instead of trusting the certificate, you write a didReceiveChallenge handler that requests default handling for everything except for server trust. Then, for server trust:
Extract the proposed credential from the challenge object.
Extract the first (leaf) certificate from the credential's certificate chain; IIRC, this is at index 0, but don't quote me on that.
Extract the public key from the certificate.
Add a copy of your server's public key in your app bundle somewhere.
Compare the two public keys. If they match, return/call the callback in such a way that tells it to use the provided certificate.
If they don't match, cancel the entire connection/task.
That's the basic idea, anyway. With that change, the only way to sniff the traffic is to replace the public key in your app bundle and re-sign the app.
I would like to inspect network traffic going through web sockets, I have no control over the networking code as this is a binary lib for which I do not have the source code, so I cannot do any log/breakpoint in the networking part of the code.
I have tried using the latest version of CharlesProxy which claim to be able to sniff websockets however when I tried the url and apis using websockets were not even mentionned in the list of endpoints called from my iPhone.
I have verified that CharlesProxy is configured correctly as I am able to inspect non-websocket traffic even under SSL.
So my question is: did anyone find a solution to inspect traffic going through websockets with CharlesProxy?
Note: I have ATS disabled when using iOS9
Thanks!
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 JUNE 2022: Whilst socket.io-client-swift has an API to enableSOCKSProxy, it seems Starscream v4 has actually removed the built-in support for SOCKS proxying, so this option doesn't actually do anything!
So it looks like we're back to manually patching Starscream to enable SOCKS proxying.
Just to recap: we've gone from Starscream supporting SOCKS proxy but socket.io-client-swift not having an API to enable it, to now socket.io-client-swift having an API to enable SOCKS proxying, which Starscream no longer supports 🤣🙄.
UPDATE JUNE 2019: 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:
Socket.IO does not expose the Starscream websocket or provide any API for enabling the SOCKS proxying behaviour.
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!
Found a workaround solution:
After trying, I conclude that although iOS simulators follow system proxy settings for HTTP, WebSocket is not followed. Socks5 proxy settings is not followed, either.
However, we can use Reverse Proxies in Charles to force the simulator to use it.
In Charles, click Proxy -> Reverse Proxies, set up a local address to be your WebSocket server's transparent proxy, then use that address in your new WebSocket(<address>) (Well in the case of React Native), then you can see your WebSocket connections appear in Charles
CharlesProxy with iOS WebSocket connection
var rd: InputStream
var wr: OutputStream
let dict: NSDictionary = [
StreamSOCKSProxyConfiguration.hostKey.rawValue : <ip>,
StreamSOCKSProxyConfiguration.portKey.rawValue : <port>
]
rd.setProperty(dict, forKey: Stream.PropertyKey.socksProxyConfigurationKey)
wr.setProperty(dict, forKey: Stream.PropertyKey.socksProxyConfigurationKey)
or
let proxyDict = CFNetworkCopySystemProxySettings()
let prop = CFDictionaryCreateMutableCopy(nil, 0, proxyDict!.takeRetainedValue())
rd.setProperty(prop, forKey: Stream.PropertyKey(rawValue: kCFStreamPropertySOCKSProxy as String as String))
wr.setProperty(prop, forKey: Stream.PropertyKey(rawValue: kCFStreamPropertySOCKSProxy as String as String))
CharlesProxy setup
Proxy -> Proxy Settings... -> Proxies -> Enable SOCKS Proxy
also you can check/review socket connection using websocket-tester
Please note that it should be used only in debug mode for security reasons and there is can be additional logic(e.g. checking signature...) which prevents intercept a traffic
I'm new in iOS and Swift development, and i'm not used to manage the network reachability in my usual programs.
I found AFNetworking and Alamofire which are great frameworks to ease the API calls in iOS.
But i have difficulties to figure out how to make some requests to wait until network is back if i'm loosing it.
Typically during session login or getting some json lists or downloading images.
Do you have some tutorials or examples i can rely on to improve my knowledge and be able to build a strong application resilient to network availability ?
now Alamofire has just included a Network status listener
https://github.com/Alamofire/Alamofire/pull/1053
Alamofire now has a network reachability manager
This is from their github page -
let manager = NetworkReachabilityManager(host: "your.server.url.com")
manager?.listener = { status in
print("Network Status Changed: \(status)") }
manager?.startListening()
Is there any difference between NSURLRequest class on Mac OS and iOS?
Apparently there is one.
Here how the class description looks on Mac OS:
<NSURLRequest: 0x60000000f290> { URL: http://www.google.com/ }
And here how it looks on iOS:
<NSURLRequest http://www.google.com/>
As it appears there is a big difference for my project as it work perfectly on iOS and connection fails for Mac OS version.
Does anyone know how to make Mac OS app init the NSURLRequest class object exactly the way it does for iOS?
UPD 1
Same code for initialisation of the object for both platforms
NSURLRequest* aRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:#"http://www.google.com/"]];
I tried same to use NSURLRequest in both AFNetworking and NSURLConnection getting same result - successful connection on iOS and 403 error for Mac OS
successful connection on iOS and 403 error for Mac OS
403 is HTTP's Forbidden response. The fact that you're getting a 403 means that a) your request is making a proper HTTP connection, but b) the server doesn't want to talk to you for some reason. A good strategy for diagnosing the issue is to use a proxy like Charles to look at the successful and unsuccessful requests and note the differences. The user agent parameter is one thing that's likely to differ between the two platforms, but you'll probably find other differences as well. Once you know what's different between the two requests, you'll have a better idea of what you need to change to make the failing requests work.