feat(converter): 支持 server 级别的 root 和 index 指令转换
- 在 convertServerBlock 中收集 server 级别的 root/index 指令 - 如果没有显式的 location / 静态配置,创建默认静态配置 - 如果 location / 是 proxy 类型,不创建静态配置 - 添加 3 个测试用例覆盖不同场景 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
b290cea0f6
commit
78ca32748c
@ -200,6 +200,8 @@ func convertUpstreamServer(d *Directive) config.ProxyTarget {
|
|||||||
func convertServerBlock(d *Directive, upstreams map[string]*upstreamInfo, result *ConvertResult) config.ServerConfig {
|
func convertServerBlock(d *Directive, upstreams map[string]*upstreamInfo, result *ConvertResult) config.ServerConfig {
|
||||||
server := config.ServerConfig{}
|
server := config.ServerConfig{}
|
||||||
var sslDetected bool
|
var sslDetected bool
|
||||||
|
var serverRoot string
|
||||||
|
var serverIndex []string
|
||||||
|
|
||||||
for i := range d.Block {
|
for i := range d.Block {
|
||||||
bd := &d.Block[i]
|
bd := &d.Block[i]
|
||||||
@ -211,6 +213,12 @@ func convertServerBlock(d *Directive, upstreams map[string]*upstreamInfo, result
|
|||||||
}
|
}
|
||||||
case "server_name":
|
case "server_name":
|
||||||
parseServerName(bd, &server)
|
parseServerName(bd, &server)
|
||||||
|
case "root":
|
||||||
|
if len(bd.Args) > 0 {
|
||||||
|
serverRoot = bd.Args[0]
|
||||||
|
}
|
||||||
|
case "index":
|
||||||
|
serverIndex = append(serverIndex, bd.Args...)
|
||||||
case "ssl_certificate":
|
case "ssl_certificate":
|
||||||
if len(bd.Args) > 0 {
|
if len(bd.Args) > 0 {
|
||||||
server.SSL.Cert = bd.Args[0]
|
server.SSL.Cert = bd.Args[0]
|
||||||
@ -273,6 +281,36 @@ func convertServerBlock(d *Directive, upstreams map[string]*upstreamInfo, result
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If server-level root is defined but no explicit location / static config exists,
|
||||||
|
// create a default static configuration for "/".
|
||||||
|
// However, if location / is a proxy, don't create static config.
|
||||||
|
if serverRoot != "" {
|
||||||
|
hasRootLocation := false
|
||||||
|
// Check if there's already a static config for "/"
|
||||||
|
for _, s := range server.Static {
|
||||||
|
if s.Path == "/" {
|
||||||
|
hasRootLocation = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Check if location / is a proxy
|
||||||
|
if !hasRootLocation {
|
||||||
|
for _, p := range server.Proxy {
|
||||||
|
if p.Path == "/" {
|
||||||
|
hasRootLocation = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !hasRootLocation {
|
||||||
|
server.Static = append(server.Static, config.StaticConfig{
|
||||||
|
Path: "/",
|
||||||
|
Root: serverRoot,
|
||||||
|
Index: serverIndex,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Warn if SSL was detected (listen ... ssl) but cert/key are not configured.
|
// Warn if SSL was detected (listen ... ssl) but cert/key are not configured.
|
||||||
if sslDetected && (server.SSL.Cert == "" || server.SSL.Key == "") {
|
if sslDetected && (server.SSL.Cert == "" || server.SSL.Key == "") {
|
||||||
result.Warnings = append(result.Warnings, Warning{
|
result.Warnings = append(result.Warnings, Warning{
|
||||||
|
|||||||
@ -5,6 +5,8 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"rua.plus/lolly/internal/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
// helper: parse nginx config string and convert.
|
// helper: parse nginx config string and convert.
|
||||||
@ -127,6 +129,112 @@ http {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestConvertServerLevelRoot(t *testing.T) {
|
||||||
|
input := `
|
||||||
|
http {
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
root /var/www/html;
|
||||||
|
index index.html index.htm index.php;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
result, err := convertString(t, input)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("convert error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
s := result.Config.Servers[0]
|
||||||
|
if len(s.Static) != 1 {
|
||||||
|
t.Fatalf("expected 1 static, got %d", len(s.Static))
|
||||||
|
}
|
||||||
|
|
||||||
|
st := s.Static[0]
|
||||||
|
if st.Path != "/" {
|
||||||
|
t.Errorf("expected path /, got %s", st.Path)
|
||||||
|
}
|
||||||
|
if st.Root != "/var/www/html" {
|
||||||
|
t.Errorf("expected root /var/www/html, got %s", st.Root)
|
||||||
|
}
|
||||||
|
if len(st.Index) != 3 || st.Index[0] != "index.html" || st.Index[1] != "index.htm" || st.Index[2] != "index.php" {
|
||||||
|
t.Errorf("expected [index.html index.htm index.php], got %v", st.Index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConvertServerLevelRootWithLocation(t *testing.T) {
|
||||||
|
// When both server-level root and location /static exist, both should be converted
|
||||||
|
input := `
|
||||||
|
http {
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
root /var/www/html;
|
||||||
|
index index.html;
|
||||||
|
location /static/ {
|
||||||
|
root /var/www/static;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
result, err := convertString(t, input)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("convert error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
s := result.Config.Servers[0]
|
||||||
|
if len(s.Static) != 2 {
|
||||||
|
t.Fatalf("expected 2 static, got %d", len(s.Static))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the root location
|
||||||
|
var rootStatic, staticLoc *config.StaticConfig
|
||||||
|
for i := range s.Static {
|
||||||
|
if s.Static[i].Path == "/" {
|
||||||
|
rootStatic = &s.Static[i]
|
||||||
|
} else if s.Static[i].Path == "/static/" {
|
||||||
|
staticLoc = &s.Static[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if rootStatic == nil {
|
||||||
|
t.Error("expected root static config at path /")
|
||||||
|
} else if rootStatic.Root != "/var/www/html" {
|
||||||
|
t.Errorf("expected root /var/www/html, got %s", rootStatic.Root)
|
||||||
|
}
|
||||||
|
|
||||||
|
if staticLoc == nil {
|
||||||
|
t.Error("expected static config at path /static/")
|
||||||
|
} else if staticLoc.Root != "/var/www/static" {
|
||||||
|
t.Errorf("expected root /var/www/static, got %s", staticLoc.Root)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConvertServerLevelRootNotCreatedForProxy(t *testing.T) {
|
||||||
|
// When location / is a proxy, server-level root should NOT create a static config
|
||||||
|
input := `
|
||||||
|
http {
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
root /var/www/html;
|
||||||
|
location / {
|
||||||
|
proxy_pass http://backend;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
result, err := convertString(t, input)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("convert error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
s := result.Config.Servers[0]
|
||||||
|
if len(s.Static) != 0 {
|
||||||
|
t.Errorf("expected 0 static (location / is proxy), got %d", len(s.Static))
|
||||||
|
}
|
||||||
|
if len(s.Proxy) != 1 {
|
||||||
|
t.Errorf("expected 1 proxy, got %d", len(s.Proxy))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestConvertUpstream(t *testing.T) {
|
func TestConvertUpstream(t *testing.T) {
|
||||||
input := `
|
input := `
|
||||||
upstream backend {
|
upstream backend {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user