news 2026/11/3 2:32:38

WebUI X: Portable 模块开发指南(下):SuPathHandler、WebrootPathHandler 与 KernelSU I/O API 路径处理机制详解

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
WebUI X: Portable 模块开发指南(下):SuPathHandler、WebrootPathHandler 与 KernelSU I/O API 路径处理机制详解

【免费下载链接】WebUI-X-Portable

WebUI X from MMRL but portable!

项目地址:https://gitcode.com/gh_mirrors/we/WebUI-X-Portable
点击查看免费下载

WebUI X: Portable(简称 WXP)是运行在 Android 上的 WebUI 运行时宿主与模块开发环境,支持在 KernelSU、Magisk、APatch 及非 Root ADB 环境下运行、调试模块 WebUI。本文是模块开发指南的下篇,带你吃透三大核心机制——SuPathHandler、WebrootPathHandler与KernelSU I/O API,搞懂 WebUI X 是如何把手机上的真实文件“安全地”喂给前端的。

一、WebUI X 路径处理架构总览 💡

理解 WXP 的路径处理,先记住一句话:所有文件访问最终都汇聚到SuFile(dev.mmrlx.nio.SuFile)这个 su 权限的文件抽象上,再向上分三条对外出口。

出口触发方式核心文件
HTTP 路由浏览器请求mui.kernelsu.org/...SuPathHandler.kt、WebrootPathHandler.kt
JSksu.io()前端调用File/FileInputStream/RandomAccessFileKsuIO.kt
JSfs类 Node.js 的readFileSync/writeFileFileSystemInterface.kt

三条线共用同一个“万能工具函数”sufile,它负责把路径参数安全地转换成一个SuFile实例:

// [ExtraOptions.kt#L37-L45](https://link.gitcode.com/i/d9a3e63608f668c529cc97777ac28148) fun WebUI.sufile(vararg paths: Any): SuFile { val f = file(*paths) if (f !is SuFile) throw IllegalArgumentException("Provided file factory is not a `dev.mmrlx.nio.SuFile`") return f }

为什么用SuFile而不是java.io.File?因为 WXP 需要以 su 身份访问/data/adb等受保护目录,普通File拿不到权限。整个 I/O 层围绕它构建,是“Portable”的关键。

二、SuPathHandler:把 su 目录挂载成 URL 路由 🚀

SuPathHandler 只干一件事:把一个文件系统目录,映射成一个可被浏览器访问的 URL 前缀。它的实现极其精简,几乎只有一行核心逻辑。

// [SuPathHandler.kt#L9-L21](https://link.gitcode.com/i/008390535e1273d1f6fd27d21a5de789#L9-L21) fun RouteRegistry.suRoute(name: String, directory: String) { route(name) { try { sufile(directory, request.path).asResponse() } catch (e: IOException) { response(status = ResponseStatus.BAD_REQUEST, data = null) } } }

四个预置挂载点

在 WebUIScreen.kt#L157-L173 中,WXP 一口气注册了四条suRoute,这也是你在开发者工具网络面板里看到的那些“隐藏路径”的由来:

URL 前缀映射目标用途
/.{moduleId}/module.path.moduleDir模块根目录(按模块隔离)
/.adb/adbPath.baseDirADB 基础目录
/.config/adbPath.configDir全局配置(.config)
/.local/adbPath.localDir本地数据(.local)

这些/.xxx前缀配合asResponse()(见 Responses.kt#L103-L142)把SuFile转成带正确 MIME、CORS 头的WebResourceResponse返回给前端。

三、WebrootPathHandler:WebUI 前端的静态资源服务器 🌐

如果说 SuPathHandler 是“通用管道”,那么 WebrootPathHandler.kt 就是真正服务模块 WebUI 前端(webroot/index.html等)的核心服务器,它承担了安全防护、样式注入、缓存与回退等复杂职责。

1)路径穿越防护(安全核心)⚠️

前端请求的request.path绝不能直接拼到目录后读取,否则会出现../../越权访问。WXP 用“规范化后必须仍是子路径”的校验来兜底:

// [WebrootPathHandler.kt#L80-L86](https://link.gitcode.com/i/e09c56f827b5e00fc8dbdc284784d438#L80-L86) val file = directory.getCanonicalFileIfChild(path) ?: run { Log.e("webrootPathHandler", "The requested file: $path is outside the mounted directory: $directory") return@route forbiddenResponse // 403 }

2)historyFallback 单页应用回退

对于 SPA(单页应用),当请求的路由路径没有对应文件时,可回退到index.html。这一行为由 WebrootConfig.kt#L293-L297 的historyFallback/historyFallbackFile两个配置项控制。

3)CSS / JS 自动注入 ✨

WXP 会自动扫描config目录,把开发者准备的样式与脚本“无感注入”进 HTML:

  • config/style/*.css→ 注入<link rel="stylesheet">
  • config/js/head/*.js→ 注入到</head>前
  • config/js/body/*.js→ 注入到</body>前,且.mjs文件会自动补上type="module"
// [WebrootPathHandler.kt#L42-L59](https://link.gitcode.com/i/e09c56f827b5e00fc8dbdc284784d438#L42-L59) if (it.extension == "mjs") append(" type=\"module\"")

4)CSP 合并 + 缓存 + ETag

  • 内容安全策略:默认 CSP 定义在 WebrootConfig.kt#L299-L303,允许'unsafe-inline'以兼容常见前端写法,同时限制connect-src *。
  • 静态资源缓存:对js / css / png / svg / woff2等扩展名下发Cache-Control: public, max-age=...(cachingMaxAge默认 86400 秒)。
  • ETag 校验:ETag = 文件大小-最后修改时间,让前端能做增量校验。

四、KernelSU I/O API:让 JS 直接读写文件 📁

这是 WXP 最具“扩展性”的部分。通过 KsuIO.kt 与 KernelSUInterface.kt#L307-L309,前端只需ksu.io()即可拿到一组受权限保护的文件对象。

四大核心对象

// [KsuIO.kt#L48-L53](https://link.gitcode.com/i/f0ac5c3caa337533a98d1f164319c4d8#L48-L53) fun JavaScriptScope.ksuIO(io: KsuIO) { function("File") { newObject { fileInterface(io, checkString(0)) } } function("FileInputStream") { newObject { fileInputStreamInterface(io) } } function("FileOutputStream") { newObject { fileOutputStreamInterface(io) } } function("RandomAccessFile") { newObject { randomAccessFileInterface(io) } } }
对象典型能力底层
Fileexists / list / mkdirs / renameTo / length / getUsableSpaceSuFile
FileInputStreamopen / read / available / close(分块读取,Base64 传输)ManagedInputStream
FileOutputStreamopen / write / writeByte / flush / close(支持追加)SuFileOutputStream
RandomAccessFilereadInt / writeUTF / seek / length / setLength等随机访问SuRandomAccessFile

资源生命周期统一管理 🔒

KsuIO用三个ConcurrentHashMap分别追踪打开的输入流、输出流与随机文件,并在 WebUI 销毁时统一closeAll,避免 su 权限下的文件句柄泄漏:

// [KsuIO.kt#L30-L36](https://link.gitcode.com/i/f0ac5c3caa337533a98d1f164319c4d8#L30-L36) onDestroy { closeAll(inputs) { it.close() } closeAll(outputs) { it.close() } closeAll(randomFiles) { it.close() } }

fs 接口:类 Node.js 的文件读写

除了ksu.io(),WXP 还提供更接近 Node.js 习惯的fs命名空间(见 FileSystemInterface.kt#L20-L81),暴露readFile / readFileSync / writeFile / access / inputstream / outputstream等方法,并通过fs.constants提供O_RDONLY、O_CREAT等打开标志,方便从 Node 环境移植代码。

五、整体串联:一次请求的完整旅程 🧭

把所有模块串起来,就能看懂 WebUIScreen.kt 的装配顺序:

  1. 注册 I/O 工厂(WebUIScreen.kt#L83-L105):把inputStreamFactory、outputStreamFactory、randomAccessFileFactory、fileFactory绑定到SuFile/SuRandomAccessFile。
  2. 注册 JS 接口(WebUIScreen.kt#L126-L135):挂载mod、ksu、fs等命名空间。
  3. 注册路由(WebUIScreen.kt#L151-L176):iconPathHandler→internalPathHandler→ 四条suRoute→webrootPathHandler。
  4. 启动 WebView:WebUIView(wstate)渲染并接管。

开发提示:路由注册有先后顺序,webrootPathHandler放在最后作为“兜底”,确保它接管所有未匹配的常规路径请求。

六、模块路径模型:ModulePath 与 AdbPath 📍

上面反复出现的moduleDir、webrootDir、configDir等,都由 ModulePaths.kt 统一推导,避免开发者手拼路径出错。

属性相对位置说明
moduleDir{baseDir}/modules/{id}模块根目录
webrootDir{moduleDir}/webroot前端静态资源目录
webrootConfig{moduleDir}/webroot/config.jsonWebUI 配置文件
configDir{baseDir}/.config/{id}模块私有配置
serviceFilesservice.sh/action.sh/sepolicy.rule等生命周期脚本

这些路径既会通过mod.path/mod.adbPath暴露给 JS,也会以LuaTable形式喂给 Lua 插件(见 ModulePaths.kt#L113-L139),实现跨脚本语言的统一访问。

七、开发实战清单 ✅

  • 前端静态资源:放在webroot/,自定义样式放.config/{id}/style/,脚本放.config/{id}/js/head|body/。
  • SPA 路由:在config.json开启historyFallback: true,避免刷新 404。
  • 读写系统文件:优先用fs.readFileSync/ksu.io().RandomAccessFile,注意close释放句柄。
  • 安全红线:任何路径拼接都要经过“规范化 + 子路径校验”,不要绕过getCanonicalFileIfChild。
  • 权限申请:危险接口受Permissions.KSU.IO/MX.IO约束(见 Permissions.kt#L34-L42),调用前确保config.json已声明相应权限。

想跑起来体验?克隆仓库后用./gradlew assembleDebug构建即可:git clone https://gitcode.com/gh_mirrors/we/WebUI-X-Portable


小结:WebUI X: Portable 的路径处理,本质是“一个SuFile抽象 + 三条受控出口”。SuPathHandler负责把目录变成 URL,WebrootPathHandler负责把 WebUI 前端安全地端出来,KernelSU I/O API则把 su 级别的文件能力安全地交给 JS。理解了这条主线,你就能自信地为模块编写前后端联动的 WebUI 了。

【免费下载链接】WebUI-X-Portable

WebUI X from MMRL but portable!

项目地址:https://gitcode.com/gh_mirrors/we/WebUI-X-Portable
点击查看免费下载

相关推荐

上一篇:隐私与合规指南:Qwen2.5-0.5B-Instruct_rai_1.8.0_hybrid的MIT+Apache-2.0双许可与第三方声明解读
下一篇:如何用MOSS-Image-Base训练LoRA:定制化风格迁移的完整实战教程

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/11/3 2:30:19

supertree API完整参考:SuperTree、Node与TreeData三大核心类详解

【免费下载链接】supertree Impress your boss with interactive Decision Tree visualization 项目地址&#xff1a; https://gitcode.com/gh_mirrors/su/supertree 点击查看 免费下载 supertree 是一个在 Jupyter 中实现交互式决策树可视化的 Python 库&#xff0c;支持 sc…

作者头像 李华