mirror of
https://github.com/tnb-labs/panel.git
synced 2026-08-31 01:12:17 +08:00
ded7909fdb
apache 包废弃脆弱的字符级 lexer,重写为三层单遍解析器(逻辑行合并续行 + shell 分词 + 递归下降)+ 统一有序 AST(VirtualHost 并入 Block、参数分离 引号信息)。修复嵌套块被静默丢弃、不支持续行符、特殊字符粘连、order 表 VirtualHost 排序等缺陷;proxy/redirect/vhost 读写全部改为 AST 驱动,删除 所有正则反解析与脆弱的 Sscanf/strings.Index。 nginx 包片段读端(proxy/redirect/upstream)从正则反解析改为 gonginx AST 遍历。 types.Vhost 接口清理:用 ConfigScope 类型常量替代 stringly-typed 的 "site"/"shared",用 SetRawConfig 独立方法替代 skipComment 可变布尔参数。 新增 robustness_test.go:用 apache 官方及社区真实配置验证语义无损、幂等、边界容错。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
222 lines
5.4 KiB
Go
222 lines
5.4 KiB
Go
package apache
|
|
|
|
import "strings"
|
|
|
|
// 以下查询/修改方法挂在 *nodeList 上,Config 与 Block 通过嵌入共享,名称比较一律大小写不敏感
|
|
|
|
// Get 返回首个匹配名称的指令
|
|
func (l *nodeList) Get(name string) *Directive {
|
|
for _, n := range l.Nodes {
|
|
if d, ok := n.(*Directive); ok && strings.EqualFold(d.Name, name) {
|
|
return d
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// GetAll 返回所有匹配名称的指令
|
|
func (l *nodeList) GetAll(name string) []*Directive {
|
|
var out []*Directive
|
|
for _, n := range l.Nodes {
|
|
if d, ok := n.(*Directive); ok && strings.EqualFold(d.Name, name) {
|
|
out = append(out, d)
|
|
}
|
|
}
|
|
return out
|
|
}
|
|
|
|
// Has 判断是否存在指定指令
|
|
func (l *nodeList) Has(name string) bool {
|
|
return l.Get(name) != nil
|
|
}
|
|
|
|
// Value 返回指令首个参数值,不存在返回空串
|
|
func (l *nodeList) Value(name string) string {
|
|
if d := l.Get(name); d != nil && len(d.Args) > 0 {
|
|
return d.Args[0].Value
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// Values 返回指令全部参数值
|
|
func (l *nodeList) Values(name string) []string {
|
|
if d := l.Get(name); d != nil {
|
|
return argValues(d.Args)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Add 追加一条指令,参数自动判断引号,返回新指令
|
|
func (l *nodeList) Add(name string, args ...string) *Directive {
|
|
d := Dir(name, args...)
|
|
l.Nodes = append(l.Nodes, d)
|
|
return d
|
|
}
|
|
|
|
// Set 设置指令:存在则更新参数,否则追加
|
|
func (l *nodeList) Set(name string, args ...string) *Directive {
|
|
if d := l.Get(name); d != nil {
|
|
d.Args = argsOf(args...)
|
|
return d
|
|
}
|
|
return l.Add(name, args...)
|
|
}
|
|
|
|
// Remove 删除首个匹配名称的指令,返回是否删除
|
|
func (l *nodeList) Remove(name string) bool {
|
|
for i, n := range l.Nodes {
|
|
if d, ok := n.(*Directive); ok && strings.EqualFold(d.Name, name) {
|
|
l.Nodes = append(l.Nodes[:i], l.Nodes[i+1:]...)
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// RemoveAll 删除所有匹配名称的指令,返回删除数量
|
|
func (l *nodeList) RemoveAll(name string) int {
|
|
return l.RemoveFunc(name, func(*Directive) bool { return true })
|
|
}
|
|
|
|
// RemoveFunc 删除满足谓词的匹配名称指令,返回删除数量
|
|
func (l *nodeList) RemoveFunc(name string, pred func(*Directive) bool) int {
|
|
kept := l.Nodes[:0]
|
|
count := 0
|
|
for _, n := range l.Nodes {
|
|
if d, ok := n.(*Directive); ok && strings.EqualFold(d.Name, name) && pred(d) {
|
|
count++
|
|
continue
|
|
}
|
|
kept = append(kept, n)
|
|
}
|
|
l.Nodes = kept
|
|
return count
|
|
}
|
|
|
|
// GetBlock 返回首个匹配类型(及可选参数)的块
|
|
func (l *nodeList) GetBlock(name string, args ...string) *Block {
|
|
for _, n := range l.Nodes {
|
|
if b, ok := n.(*Block); ok && strings.EqualFold(b.Name, name) {
|
|
if len(args) == 0 || argsEqual(b.Args, args) {
|
|
return b
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// AddBlock 追加一个块,返回新块
|
|
func (l *nodeList) AddBlock(name string, args ...string) *Block {
|
|
b := Blk(name, args...)
|
|
l.Nodes = append(l.Nodes, b)
|
|
return b
|
|
}
|
|
|
|
// VirtualHosts 返回所有 VirtualHost 块
|
|
func (l *nodeList) VirtualHosts() []*Block {
|
|
var out []*Block
|
|
for _, n := range l.Nodes {
|
|
if b, ok := n.(*Block); ok && strings.EqualFold(b.Name, "VirtualHost") {
|
|
out = append(out, b)
|
|
}
|
|
}
|
|
return out
|
|
}
|
|
|
|
// AddVirtualHost 追加一个 VirtualHost 块
|
|
func (l *nodeList) AddVirtualHost(args ...string) *Block {
|
|
return l.AddBlock("VirtualHost", args...)
|
|
}
|
|
|
|
// Find 按点路径查找指令,跨越嵌套块,如 Find("IfModule.Proxy.BalancerMember")
|
|
func (l *nodeList) Find(path string) []*Directive {
|
|
return findDirectives(l.Nodes, strings.Split(path, "."))
|
|
}
|
|
|
|
// FindOne 返回点路径命中的首个指令,无命中返回 nil
|
|
func (l *nodeList) FindOne(path string) *Directive {
|
|
if ds := l.Find(path); len(ds) > 0 {
|
|
return ds[0]
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// FindBlocks 按点路径查找块,如 FindBlocks("IfModule.Proxy")
|
|
func (l *nodeList) FindBlocks(path string) []*Block {
|
|
return findBlocks(l.Nodes, strings.Split(path, "."))
|
|
}
|
|
|
|
// SetArgs 替换块参数,自动判断引号
|
|
func (b *Block) SetArgs(args ...string) {
|
|
b.Args = argsOf(args...)
|
|
}
|
|
|
|
// AppendArg 追加一个块参数
|
|
func (b *Block) AppendArg(value string) {
|
|
b.Args = append(b.Args, arg(value))
|
|
}
|
|
|
|
// ArgValues 返回块参数的纯值切片
|
|
func (b *Block) ArgValues() []string {
|
|
return argValues(b.Args)
|
|
}
|
|
|
|
// findDirectives 沿点路径递归查找指令
|
|
func findDirectives(nodes []Node, parts []string) []*Directive {
|
|
if len(parts) == 0 {
|
|
return nil
|
|
}
|
|
head := parts[0]
|
|
if len(parts) == 1 {
|
|
var out []*Directive
|
|
for _, n := range nodes {
|
|
if d, ok := n.(*Directive); ok && strings.EqualFold(d.Name, head) {
|
|
out = append(out, d)
|
|
}
|
|
}
|
|
return out
|
|
}
|
|
var out []*Directive
|
|
for _, n := range nodes {
|
|
if b, ok := n.(*Block); ok && strings.EqualFold(b.Name, head) {
|
|
out = append(out, findDirectives(b.Nodes, parts[1:])...)
|
|
}
|
|
}
|
|
return out
|
|
}
|
|
|
|
// findBlocks 沿点路径递归查找块
|
|
func findBlocks(nodes []Node, parts []string) []*Block {
|
|
if len(parts) == 0 {
|
|
return nil
|
|
}
|
|
head := parts[0]
|
|
last := len(parts) == 1
|
|
var out []*Block
|
|
for _, n := range nodes {
|
|
b, ok := n.(*Block)
|
|
if !ok || !strings.EqualFold(b.Name, head) {
|
|
continue
|
|
}
|
|
if last {
|
|
out = append(out, b)
|
|
} else {
|
|
out = append(out, findBlocks(b.Nodes, parts[1:])...)
|
|
}
|
|
}
|
|
return out
|
|
}
|
|
|
|
// argsEqual 比较参数值与字符串切片是否逐项相等
|
|
func argsEqual(args []Argument, want []string) bool {
|
|
if len(args) != len(want) {
|
|
return false
|
|
}
|
|
for i, a := range args {
|
|
if a.Value != want[i] {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|