I'd like to add a proxy to all networks calls coming from my app. Something like
func intercept(request: URLRequest) {
if isOk(request) {
return // the request continues as normally
} else if isIntercepted(request) {
let res = HTTPURLResponse(url: url, statusCode: 200, httpVersion: "HTTP/2", headerFields: ["Content-Type": "application/json"])
res.value = "{\"ok\":true}" // not that exactly work, but you get the idea
request.end(res)
} else {
let res = HTTPURLResponse(url: url, statusCode: 401, httpVersion: "HTTP/2")
res.value = "forbidden"
request.end(res)
}
}
I'd like it to be applied to all calls coming from my app. Ie from my code and from all libraries and frameworks I'm using. Is it possible?
I've found questions about reading traffic of other apps (not possible) and setting delegates on calls started from my code. I'd like to go further with something that 1) would automatically apply to all traffic and 2) include traffic from 3rd parties
That's a job for the URLProtocol
class.
From Apple's docs:
An NSURLProtocol object handles the loading of protocol-specific URL data. The NSURLProtocol class itself is an abstract class that provides the infrastructure for processing URLs with a specific URL scheme. You create subclasses for any custom protocols or URL schemes that your app supports.
You need to implement your own subclass of URLProtocol
and register it with the app for it to be used. Thereafter all connections initialized from the App will make use of the protocol, and you will be able to handle/block whichever request you want.
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool
{
guard URLProtocol.registerClass(MyURLProtocol.self) else
{
abort()
}
return true
}
Also, if you are using URLSession
(and you should!), then you also have to register your class through the session configurations:
func getURLSessionConfiguration() -> URLSessionConfiguration
{
let configuration = URLSessionConfiguration.default
configuration.protocolClasses = [
MyURLProtocol.self
]
return configuration
}
let session = URLSession(configuration: getURLSessionConfiguration())
Then you can manage whatever you want to block from the startLoading()
method of your URLProtocol
subclass:
override func startLoading()
{
if !self.isOK()
{
let error = NSError(domain: "GuardURLProtocol", code: 10, userInfo: [NSLocalizedDescriptionKey: "Connection denied by guard"])
self.client?.urlProtocol(self, didFailWithError: error)
}
else if let task = self.task
{
task.resume()
}
}
There are more methods you have to implement, and you should read the documentation from Apple.
But as a gizmo (and exercise for myself), I have written a generic blocker protocol, and you should check it out to see how it works. In the bottom of the GuardURLProtocol.swift
file there is an example subclass (BlockFPTURLSession
) where all FTP requests are blocked by schema.
If you use the class I linked above, and try to open an FTP connection, you will see the following errors:
2017-02-16 23:09:45.846 URLProtocol[83111:7456862] Error: Error Domain=GuardURLProtocol Code=10 "Connection denied by guard" UserInfo={NSLocalizedDescription=Connection denied by guard}
Have fun!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With