首页 最新 热门 推荐

  • 首页
  • 最新
  • 热门
  • 推荐

WebKit URL Cache 与网络拦截

  • 25-04-23 10:21
  • 2967
  • 9040
juejin.cn

Network 进程 URL Cache

是在 WKNetworkSessionDelegate 使用 NSURLSession 处理网络请求,没有实现其 URLSession:dataTask:willCacheResponse:completionHandler:代理,底层会默认写 NSURLCache 缓存。

也就是说网络请求的缓存会存在于 Networking 进程里面。

SchemeHandler 如何网络拦截

  • 调用 -[WKWebViewConfiguration -setURLSchemeHandler:forURLScheme:] 注入处理 http / https 的自定义 handler ;
  • Hook -[WKWebView handlesURLScheme:] 允许处理 http / https 的 scheme;
  • 在自定义 handler 里面实现网络请求并回传;

SchemeHandler 底层链路

SchemeHandler 存储链路

arduino
代码解读
复制代码
-[WKWebViewConfiguration -setURLSchemeHandler:forURLScheme:] 调用: PageConfiguration::setURLSchemeHandlerForURLScheme 存储到这里: HashMapString, Ref> m_urlSchemeHandlers;

网络请求发起链路

有网络请求时会走到 SchemeHandler 的 -webView:startURLSchemeTask: 方法,由此在 WebKit 找到调用链路:

arduino
代码解读
复制代码
//WebContent 进程 DocumentLoader::loadMainResource CachedResourceLoader::requestResource CachedResource::load … WebLoaderStrategy::loadResource WebLoaderStrategy::tryLoadingUsingURLSchemeHandler WebURLSchemeHandlerProxy::startNewTask WebURLSchemeTaskProxy::startLoading IPC: Messages::WebPageProxy::StartURLSchemeTask //APP进程 WebPageProxy::startURLSchemeTask WebPageProxy::startURLSchemeTaskShared WebURLSchemeHandler::startTask WebURLSchemeHandlerCocoa::platformStartTask -[WKURLSchemeHandler webView:startURLSchemeTask:]

自定义 WKURLSchemeHandler 网络回包后回传链路

arduino
代码解读
复制代码
//APP 进程 -[WKURLSchemeTask didReceiveResponse:] WebURLSchemeTask::didReceiveResponse IPC: Messages::WebPage::URLSchemeTaskDidReceiveResponse //WebContent 进程 WebPage::URLSchemeTaskDidReceiveResponse WebURLSchemeHandlerProxy::taskDidReceiveResponse WebURLSchemeTaskProxy::didReceiveResponse ResourceLoader::didReceiveResponse ResourceLoadNotifier::didReceiveResponse DocumentLoader::addResponse

看起来没有传送到 Networking 进程。

Web 进程资源缓存

加载网页资源时会在 CachedResourceLoader 寻找是否有可用缓存:

arduino
代码解读
复制代码
DocumentLoader::loadMainResource CachedResourceLoader::requestResource CachedResourceLoader::determineRevalidationPolicy

若没有命中缓存会执行:

scss
代码解读
复制代码
…CachedResourceLoader::loadResource(…) { … auto resource = createResource(type, WTFMove(request), sessionID, &cookieJar, settings); if (resource->allowsCaching()) memoryCache.add(*resource); … }

createResource(…)实际上就会返回继承 CachedResource 抽象类的资源对象,能缓存则存入 memoryCache,比如 CachedImage / CachedCSSStyleSheet / CachedScript / CachedSVGDocument,后续链路会发起网络请求。

WKURLSchemeHandler 与 CachedResourceLoader 交叉点

由上面的源码分析知,CachedResourceLoader 会先查找 memoryCache,不可用时调用CachedResource::load加载资源,后续会走到 SchemeHandler 处理链路。

SchemeHandler 资源数据是否利用到了 Web 进程资源缓存

对于服务器返回的 response 和不拦截情况没差异,但如果是客户端构建的 response 可能会有一些问题,最直观的就是响应 http header 可能不一致。

所以我们需要知道客户端构建 response 回传到 Web 进程使用与缓存后,第二次访问到是否还有效。

导致 CachedResourceLoader 的 memoryCache 失效原因很多,在最后一部分发现了端倪(有些资源不会走到这个链路,比如图片资源多半没这个判定):

rust
代码解读
复制代码
… CachedResourceLoader::determineRevalidationPolicy(…) { … auto revalidationDecision = existingResource->makeRevalidationDecision(cachePolicy); // Check if the cache headers requires us to revalidate (cache expiration for example). if (revalidationDecision != CachedResource::RevalidationDecision::No) { // See if the resource has usable ETag or Last-modified headers. if (existingResource->canUseCacheValidator()) { return Revalidate; } // No, must reload. return Reload; } return Use; }

一看就是对重用缓存的有效性判定,客户端构建不会加 ETag / Last-modified 所以不用管,也就是说只要 revalidationDecision 不是 No 就得 Reload,无法返回 Use 让上层直接复用。看下如何判定的:

ruby
代码解读
复制代码
CachedResource::RevalidationDecision CachedResource::makeRevalidationDecision(CachePolicy cachePolicy) const { … if (m_response.cacheControlContainsNoCache()) return RevalidationDecision::YesDueToNoCache; // FIXME: Cache-Control:no-store should prevent storing, not reuse. if (m_response.cacheControlContainsNoStore()) return RevalidationDecision::YesDueToNoStore; if (isExpired()) return RevalidationDecision::YesDueToExpired; return RevalidationDecision::No; … }

isExpired()实际上就是根据响应头判定资源是否过期,获取响应头新鲜度的代码是这样的:

arduino
代码解读
复制代码
Seconds computeFreshnessLifetimeForHTTPFamily(…) { … // Freshness Lifetime: // http://tools.ietf.org/html/rfc7234#section-4.2.1 auto maxAge = response.cacheControlMaxAge(); if (maxAge) return *maxAge; auto date = response.date(); auto effectiveDate = date.value_or(responseTime); if (auto expires = response.expires()) return *expires - effectiveDate; // Implicit lifetime. switch (response.httpStatusCode()) { case 301: // Moved Permanently case 410: // Gone // These are semantically permanent and so get long implicit lifetime. return 24_h * 365; default: // Heuristic Freshness: // http://tools.ietf.org/html/rfc7234#section-4.2.2 if (auto lastModified = response.lastModified()) return (effectiveDate - *lastModified) * 0.1; return 0_us; } }

逻辑很清晰,先后获取了多个响应头:Cache-Control : max-age、Expires、Date,后面是特殊响应状态码判定,返回值都是有效时间戳与 responseTime 的时间差。

可见,若想让 URL 资源可被 WebContent 进程复用,客户端构建 response 需要设置有效的 max-age 或 Expires 或 Last-Modified 响应头即可。

总结

对于+[WKBrowsingContextController registerSchemeForCustomProtocol:]拦截方案,它是向 Network 进程的 NSURLSession 注册 NSURLProtocol,对于客户端构建的响应包,同样会由 Network 进程传递到 Web 进程,走上面的 CachedResourceLoader 链路。

明确了有无 WebKit 网络拦截时网络请求和缓存的链路,这将在我们做优化决策时起到作用。

比如通过客户端构建网络回包的优化方向,包括离线包方案、自定义 URL Cache、Web 资源预请求、前端 link preload 等。

注:本文转载自juejin.cn的波儿菜的文章"https://juejin.cn/post/7462019042633990144"。版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。如有侵权,请联系我们删除。
复制链接
复制链接
相关推荐
发表评论
登录后才能发表评论和回复 注册

/ 登录

评论记录:

未查询到任何数据!
回复评论:

分类栏目

后端 (14832) 前端 (14280) 移动开发 (3760) 编程语言 (3851) Java (3904) Python (3298) 人工智能 (10119) AIGC (2810) 大数据 (3499) 数据库 (3945) 数据结构与算法 (3757) 音视频 (2669) 云原生 (3145) 云平台 (2965) 前沿技术 (2993) 开源 (2160) 小程序 (2860) 运维 (2533) 服务器 (2698) 操作系统 (2325) 硬件开发 (2492) 嵌入式 (2955) 微软技术 (2769) 软件工程 (2056) 测试 (2865) 网络空间安全 (2948) 网络与通信 (2797) 用户体验设计 (2592) 学习和成长 (2593) 搜索 (2744) 开发工具 (7108) 游戏 (2829) HarmonyOS (2935) 区块链 (2782) 数学 (3112) 3C硬件 (2759) 资讯 (2909) Android (4709) iOS (1850) 代码人生 (3043) 阅读 (2841)

热门文章

141
iOS
关于我们 隐私政策 免责声明 联系我们
Copyright © 2020-2024 蚁人论坛 (iYenn.com) All Rights Reserved.
Scroll to Top