mirror of
https://github.com/truewhile/MeBox.git
synced 2026-10-06 05:16:38 +08:00
feat(reader): P2——goja JS 引擎接入 + Go 原生 java.* 桥接
- rule/goja.go:纯 Go JS 运行时(goja),脚本编译缓存 + 超时中断(默认10s),
绑定集合对齐 legado evalJS(java/cookie/cache/source/book/result/baseUrl/
chapter/title/src/page/key)
- rule/bridge.go:java 对象函数名与 legado JsExtensions 一一对应(存量书源
硬编码这些名字),全部 Go 原生实现:
网络 ajax/ajaxAll/connect/get/post/head(经 MeBox HTTP 客户端,响应对象
带 body()/code()/url()/header());规则回调 getString/getStringList/
getElements/put/get(桥回当前解析器);编解码 base64/hex/md5(16/32)/
digest/HMac/encodeURI/timeFormat(SimpleDateFormat 转 Go 布局)
- 对称加密:createSymmetricCrypto + aes/des/3des 全家桶,transformation
三段式解析(AES/DES/3DES × CBC/ECB/CTR/OFB/CFB × PKCS5/7/Zero/NoPadding,
ECB 手工块循环),密钥长度自动补齐,decryptStr 自动识别 Base64/Hex
- get(url,headers) 与 get(key) 按 arity 分发(对齐 Kotlin 重载)
- 浏览器/文件/压缩包类函数明确抛不支持(服务端沙箱边界)
- URL 规则 JS:analyzeJs(@result 链式)、{{js}} 内嵌、option js/bodyJs
- execute 记录 Set-Cookie 进 cookie jar(java/cookie.getCookie 可读)
- 测试:编码/摘要/AES 向量/CBC+ECB 往返/ajax httptest/URL JS/沙箱边界,
全部通过;修复 java.get 双注册、测试缺 SetContent 与 md5Encode16 期望值
This commit is contained in:
@@ -39,14 +39,23 @@ type Request struct {
|
||||
IsForm bool // Body 为已编码的 form 数据
|
||||
IsJSON bool // 以 application/json 发送
|
||||
Charset string
|
||||
// BodyJsFn 对应 UrlOption.bodyJs:响应体二次处理(JS 执行闭包)。
|
||||
BodyJsFn func(body string) string
|
||||
// Unsupported 非 nil 表示该请求依赖当前阶段不支持的能力,
|
||||
// 值为对应错误(webView/js/type)。
|
||||
// 值为对应错误(webView/type;JS 在接入 runner 后已支持)。
|
||||
Unsupported error
|
||||
}
|
||||
|
||||
// ParseAnalyzeUrl 对应 AnalyzeUrl.init:URL 规则 → 可执行请求。
|
||||
// key/page 对应搜索关键词与页码({{key}}/{{page}}/<1,2,3>)。
|
||||
func ParseAnalyzeUrl(mUrl, key string, page int, baseUrl string) (*Request, error) {
|
||||
return ParseAnalyzeUrlWithJS(mUrl, key, page, baseUrl, nil)
|
||||
}
|
||||
|
||||
// ParseAnalyzeUrlWithJS 在 ParseAnalyzeUrl 基础上支持 JS:
|
||||
// URL 中的 <js>/@js: 块、{{js}} 内嵌、选项里的 js/bodyJs。
|
||||
// runner 为 nil 时遇到 JS 标记 Unsupported(P0 兼容路径)。
|
||||
func ParseAnalyzeUrlWithJS(mUrl, key string, page int, baseUrl string, runner *JSRunner) (*Request, error) {
|
||||
req := &Request{Method: "GET", Headers: map[string]string{}}
|
||||
// baseUrl 自身可能带 ",{...}" 选项段,先截断(对应 init 中的 paramPattern)
|
||||
if st, _, ok := findParamSplit(baseUrl); ok {
|
||||
@@ -54,37 +63,87 @@ func ParseAnalyzeUrl(mUrl, key string, page int, baseUrl string) (*Request, erro
|
||||
}
|
||||
ruleUrl := mUrl
|
||||
|
||||
// ── analyzeJs:URL 中的 <js>/@js:(P0 不支持) ──
|
||||
// ── analyzeJs:URL 中的 <js>/@js:,@result 引用前序结果 ──
|
||||
if jsPatternRe.MatchString(ruleUrl) {
|
||||
req.Unsupported = ErrJsUnsupported
|
||||
// 移除 JS 块继续解析,便于调试接口展示其余部分
|
||||
ruleUrl = jsPatternRe.ReplaceAllString(ruleUrl, "")
|
||||
if runner == nil {
|
||||
req.Unsupported = ErrJsUnsupported
|
||||
// 移除 JS 块继续解析,便于调试接口展示其余部分
|
||||
ruleUrl = jsPatternRe.ReplaceAllString(ruleUrl, "")
|
||||
} else {
|
||||
// 对应 AnalyzeUrl.analyzeJs
|
||||
start := 0
|
||||
result := ruleUrl
|
||||
for _, g := range jsPatternRe.FindAllStringSubmatchIndex(ruleUrl, -1) {
|
||||
if g[0] > start {
|
||||
if seg := strings.TrimSpace(ruleUrl[start:g[0]]); seg != "" {
|
||||
result = strings.ReplaceAll(seg, "@result", anyToString(result))
|
||||
}
|
||||
}
|
||||
jsBody := ""
|
||||
if g[2] >= 0 {
|
||||
jsBody = ruleUrl[g[2]:g[3]]
|
||||
} else if g[4] >= 0 {
|
||||
jsBody = ruleUrl[g[4]:g[5]]
|
||||
}
|
||||
v, err := runner.Run(nil, jsBody, result, baseUrl)
|
||||
if err != nil {
|
||||
return req, err
|
||||
}
|
||||
result = anyToString(v)
|
||||
start = g[1]
|
||||
}
|
||||
if len(ruleUrl) > start {
|
||||
if seg := strings.TrimSpace(ruleUrl[start:]); seg != "" {
|
||||
result = strings.ReplaceAll(seg, "@result", result)
|
||||
}
|
||||
}
|
||||
ruleUrl = result
|
||||
}
|
||||
}
|
||||
|
||||
// ── replaceKeyPageJs:{{...}} 与 <页码列表> ──
|
||||
if strings.Contains(ruleUrl, "{{") && strings.Contains(ruleUrl, "}}") {
|
||||
var subErr error
|
||||
ra := NewRuleAnalyzer(ruleUrl, false)
|
||||
out := ra.InnerRule2("{{", "}}", func(inner string) string {
|
||||
trimmed := strings.TrimSpace(inner)
|
||||
switch trimmed {
|
||||
case "key":
|
||||
return key
|
||||
case "page":
|
||||
p := page
|
||||
if p < 1 {
|
||||
p = 1
|
||||
if runner == nil {
|
||||
ra := NewRuleAnalyzer(ruleUrl, false)
|
||||
out := ra.InnerRule2("{{", "}}", func(inner string) string {
|
||||
trimmed := strings.TrimSpace(inner)
|
||||
switch trimmed {
|
||||
case "key":
|
||||
return key
|
||||
case "page":
|
||||
p := page
|
||||
if p < 1 {
|
||||
p = 1
|
||||
}
|
||||
return anyToString(float64(p))
|
||||
default:
|
||||
subErr = ErrJsUnsupported
|
||||
return ""
|
||||
}
|
||||
return anyToString(float64(p))
|
||||
default:
|
||||
subErr = ErrJsUnsupported
|
||||
return ""
|
||||
})
|
||||
if subErr != nil {
|
||||
req.Unsupported = subErr
|
||||
} else if out != "" {
|
||||
ruleUrl = out
|
||||
}
|
||||
} else {
|
||||
// 对应 legado:{{...}} 一律按 JS 执行(key/page 为绑定变量)
|
||||
ra := NewRuleAnalyzer(ruleUrl, false)
|
||||
out := ra.InnerRule2("{{", "}}", func(inner string) string {
|
||||
v, err := runner.Run(nil, strings.TrimSpace(inner), nil, baseUrl)
|
||||
if err != nil {
|
||||
subErr = err
|
||||
return ""
|
||||
}
|
||||
return anyToString(v)
|
||||
})
|
||||
if subErr != nil {
|
||||
return req, subErr
|
||||
}
|
||||
if out != "" {
|
||||
ruleUrl = out
|
||||
}
|
||||
})
|
||||
if subErr != nil {
|
||||
req.Unsupported = subErr
|
||||
} else if out != "" {
|
||||
ruleUrl = out
|
||||
}
|
||||
}
|
||||
if page >= 1 {
|
||||
@@ -148,10 +207,26 @@ func ParseAnalyzeUrl(mUrl, key string, page int, baseUrl string) (*Request, erro
|
||||
if useWebView(option.WebView) && req.Unsupported == nil {
|
||||
req.Unsupported = ErrWebJSUnsupported
|
||||
}
|
||||
// 对应 AnalyzeUrl:option.js 在解析完成后执行,结果覆盖 url
|
||||
if option.Js != "" && req.Unsupported == nil {
|
||||
req.Unsupported = ErrJsUnsupported
|
||||
if runner == nil {
|
||||
req.Unsupported = ErrJsUnsupported
|
||||
} else if v, err := runner.Run(nil, option.Js, req.URL, baseUrl); err != nil {
|
||||
return req, err
|
||||
} else if s := anyToString(v); s != "" {
|
||||
req.URL = s
|
||||
}
|
||||
}
|
||||
if option.BodyJs != "" && req.Unsupported == nil {
|
||||
// 对应 AnalyzeUrl:bodyJs 在响应后执行,结果作为 body
|
||||
if option.BodyJs != "" && req.Unsupported == nil && runner != nil {
|
||||
req.BodyJsFn = func(body string) string {
|
||||
v, err := runner.Run(nil, option.BodyJs, body, baseUrl)
|
||||
if err != nil {
|
||||
return body
|
||||
}
|
||||
return anyToString(v)
|
||||
}
|
||||
} else if option.BodyJs != "" && req.Unsupported == nil {
|
||||
req.Unsupported = ErrJsUnsupported
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user