Sources/Instrumentation/WKWebView/URLProtocol/WKWebViewHtmlURLProtocol.swift (102 lines of code) (raw):

// // Copyright 2023 aliyun-sls Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. import Foundation import WebKit // MARK: - URLProtocol class WKWebViewHtmlURLProtocol: URLProtocol { static var WKWebViewProtocolKey = "kOTelJSBridgeNSURLProtocolKey_1" var customTask : URLSessionDataTask? override class func canInit(with request: URLRequest) -> Bool { if let bool: Bool = URLProtocol.property(forKey: WKWebViewHtmlURLProtocol.WKWebViewProtocolKey, in: request) as? Bool, bool { return false } return true } override class func canonicalRequest(for request: URLRequest) -> URLRequest { return request } override class func requestIsCacheEquivalent(_ a: URLRequest, to b: URLRequest) -> Bool { return super.requestIsCacheEquivalent(a, to: b) } override func startLoading() { guard let mutableRequest = (self.request as NSURLRequest).mutableCopy() as? NSMutableURLRequest else { return } URLProtocol.setProperty(true, forKey: WKWebViewHtmlURLProtocol.WKWebViewProtocolKey, in: mutableRequest) // sync cookie var request = self.request as URLRequest WebViewCookieManager.syncRequestCookie(request: &request) // print("WKWebViewHtmlURLProtocol. method: \(request.httpMethod), url: \(request.url!), path: \(request.url!.path),cookie: \(request.value(forHTTPHeaderField: "Cookie") ?? "")") let session = URLSession.init(configuration: URLSessionConfiguration.default, delegate: self, delegateQueue: nil) self.customTask = session.dataTask(with: request) self.customTask?.resume() } override func stopLoading() { if let _ = customTask { customTask!.cancel() customTask = nil } } } // MARK: - URLSessionDataDelegate for URLSession extension WKWebViewHtmlURLProtocol : URLSessionDataDelegate { func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) { if let e = error { self.client?.urlProtocol(self, didFailWithError: e) } else { self.client?.urlProtocolDidFinishLoading(self) } } func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive response: URLResponse, completionHandler: @escaping (URLSession.ResponseDisposition) -> Void) { self.client?.urlProtocol(self, didReceive: response, cacheStoragePolicy: .allowed) completionHandler(.allow) } func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) { self.client?.urlProtocol(self, didLoad: data) } } // MARK: - register & unregister extension WKWebViewHtmlURLProtocol { static func register() { registerScheme(scheme: "http") registerScheme(scheme: "https") URLProtocol.registerClass(WKWebViewHtmlURLProtocol.self) } static func unregister() { unregisterScheme(scheme: "http") unregisterScheme(scheme: "https") URLProtocol.unregisterClass(WKWebViewHtmlURLProtocol.self) } } extension WKWebViewHtmlURLProtocol { // encode with base64 for protection private static var registerString: String { return String(data: Data(base64Encoded: "cmVnaXN0ZXJTY2hlbWVGb3JDdXN0b21Qcm90b2NvbDo=")!, encoding: .utf8)! } // encode with base64 for protection private static var unregisterString: String { return String(data: Data(base64Encoded: "dW5yZWdpc3RlclNjaGVtZUZvckN1c3RvbVByb3RvY29sOg==")!, encoding: .utf8)! } // encode with base64 for protection private static var controllerString: String { return String(data: Data(base64Encoded: "YnJvd3NpbmdDb250ZXh0Q29udHJvbGxlcg==")!, encoding: .utf8)! } private static var browsingContextController : NSObject.Type? { guard let instance = WKWebView().value(forKey: controllerString) else { return nil } return type(of: instance) as? NSObject.Type } private static var registerSchemeForCustomProtocol : Selector { return Selector((registerString)) } private static var unregisterSchemeForCustomProtocol : Selector { return Selector((unregisterString)) } static func registerScheme(scheme: String) { guard let controller = browsingContextController else { return } if (controller.responds(to: registerSchemeForCustomProtocol)) { controller.perform(registerSchemeForCustomProtocol, with: scheme) } } static func unregisterScheme(scheme: String) { guard let controller = browsingContextController else { return } if (controller.responds(to: unregisterSchemeForCustomProtocol)) { controller.perform(unregisterSchemeForCustomProtocol, with: scheme) } } }