Files
panel/pkg/webserver/apache/query.go
T
耗子 ded7909fdb refactor: 重写 apache/nginx 配置解析器为 AST 驱动
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>
2026-06-08 00:00:48 +08:00

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
}