lolly/internal/server/vhost_test.go

2305 lines
55 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Package server 提供虚拟主机管理器的测试。
package server
import (
"os"
"testing"
"time"
"github.com/valyala/fasthttp"
"rua.plus/lolly/internal/config"
)
// mockHandler 创建一个记录调用的 mock handler
func mockHandler(name string, called *bool) fasthttp.RequestHandler {
return func(ctx *fasthttp.RequestCtx) {
*called = true
_, _ = ctx.WriteString(name)
}
}
// TestVHostManager_Handler 测试虚拟主机选择器功能。
func TestVHostManager_Handler(t *testing.T) {
t.Run("匹配已知主机", func(t *testing.T) {
manager := NewVHostManager()
hostCalled := false
_ = manager.AddHost("example.com", mockHandler("example", &hostCalled))
handler := manager.Handler()
ctx := &fasthttp.RequestCtx{}
ctx.Request.SetHost("example.com")
handler(ctx)
if !hostCalled {
t.Error("期望 example.com handler 被调用,但未被调用")
}
if string(ctx.Response.Body()) != "example" {
t.Errorf("响应体 = %q, want %q", string(ctx.Response.Body()), "example")
}
})
t.Run("匹配带端口的主机", func(t *testing.T) {
manager := NewVHostManager()
hostCalled := false
_ = manager.AddHost("example.com", mockHandler("example", &hostCalled))
handler := manager.Handler()
ctx := &fasthttp.RequestCtx{}
ctx.Request.SetHost("example.com:8080")
handler(ctx)
if !hostCalled {
t.Error("期望 example.com handler 被调用(端口应被忽略),但未被调用")
}
if string(ctx.Response.Body()) != "example" {
t.Errorf("响应体 = %q, want %q", string(ctx.Response.Body()), "example")
}
})
t.Run("无匹配使用默认主机", func(t *testing.T) {
manager := NewVHostManager()
exampleCalled := false
defaultCalled := false
_ = manager.AddHost("example.com", mockHandler("example", &exampleCalled))
manager.SetDefault(mockHandler("default", &defaultCalled))
handler := manager.Handler()
ctx := &fasthttp.RequestCtx{}
ctx.Request.SetHost("unknown.com")
handler(ctx)
if exampleCalled {
t.Error("不期望 example.com handler 被调用")
}
if !defaultCalled {
t.Error("期望默认 handler 被调用,但未被调用")
}
if string(ctx.Response.Body()) != "default" {
t.Errorf("响应体 = %q, want %q", string(ctx.Response.Body()), "default")
}
})
t.Run("无匹配无默认返回404", func(t *testing.T) {
manager := NewVHostManager()
exampleCalled := false
_ = manager.AddHost("example.com", mockHandler("example", &exampleCalled))
handler := manager.Handler()
ctx := &fasthttp.RequestCtx{}
ctx.Request.SetHost("unknown.com")
handler(ctx)
if exampleCalled {
t.Error("不期望 example.com handler 被调用")
}
if ctx.Response.StatusCode() != fasthttp.StatusNotFound {
t.Errorf("状态码 = %d, want %d", ctx.Response.StatusCode(), fasthttp.StatusNotFound)
}
})
t.Run("IPv6地址Host", func(t *testing.T) {
manager := NewVHostManager()
ipv6Called := false
_ = manager.AddHost("[::1]", mockHandler("ipv6", &ipv6Called))
handler := manager.Handler()
ctx := &fasthttp.RequestCtx{}
ctx.Request.SetHost("[::1]:8080")
handler(ctx)
if !ipv6Called {
t.Error("期望 [::1] handler 被调用,但未被调用")
}
if string(ctx.Response.Body()) != "ipv6" {
t.Errorf("响应体 = %q, want %q", string(ctx.Response.Body()), "ipv6")
}
})
t.Run("空Host使用默认", func(t *testing.T) {
manager := NewVHostManager()
defaultCalled := false
manager.SetDefault(mockHandler("default", &defaultCalled))
handler := manager.Handler()
ctx := &fasthttp.RequestCtx{}
ctx.Request.SetHost("")
handler(ctx)
if !defaultCalled {
t.Error("期望默认 handler 被调用,但未被调用")
}
if string(ctx.Response.Body()) != "default" {
t.Errorf("响应体 = %q, want %q", string(ctx.Response.Body()), "default")
}
})
t.Run("空Host无默认返回404", func(t *testing.T) {
manager := NewVHostManager()
handler := manager.Handler()
ctx := &fasthttp.RequestCtx{}
ctx.Request.SetHost("")
handler(ctx)
if ctx.Response.StatusCode() != fasthttp.StatusNotFound {
t.Errorf("状态码 = %d, want %d", ctx.Response.StatusCode(), fasthttp.StatusNotFound)
}
})
}
// TestVHostManager_AddHost 测试添加虚拟主机功能。
func TestVHostManager_AddHost(t *testing.T) {
t.Run("添加单个主机", func(t *testing.T) {
manager := NewVHostManager()
called := false
_ = manager.AddHost("test.com", mockHandler("test", &called))
handler := manager.Handler()
ctx := &fasthttp.RequestCtx{}
ctx.Request.SetHost("test.com")
handler(ctx)
if !called {
t.Error("期望添加的主机 handler 被调用")
}
})
t.Run("添加多个主机", func(t *testing.T) {
manager := NewVHostManager()
host1Called := false
host2Called := false
_ = manager.AddHost("host1.com", mockHandler("host1", &host1Called))
_ = manager.AddHost("host2.com", mockHandler("host2", &host2Called))
handler := manager.Handler()
// 测试 host1
ctx1 := &fasthttp.RequestCtx{}
ctx1.Request.SetHost("host1.com")
handler(ctx1)
if !host1Called {
t.Error("期望 host1 handler 被调用")
}
// 测试 host2
ctx2 := &fasthttp.RequestCtx{}
ctx2.Request.SetHost("host2.com")
handler(ctx2)
if !host2Called {
t.Error("期望 host2 handler 被调用")
}
})
t.Run("覆盖已存在的主机", func(t *testing.T) {
manager := NewVHostManager()
firstCalled := false
secondCalled := false
_ = manager.AddHost("test.com", mockHandler("first", &firstCalled))
_ = manager.AddHost("test.com", mockHandler("second", &secondCalled))
handler := manager.Handler()
ctx := &fasthttp.RequestCtx{}
ctx.Request.SetHost("test.com")
handler(ctx)
if firstCalled {
t.Error("不期望第一个 handler 被调用(应被覆盖)")
}
if !secondCalled {
t.Error("期望第二个 handler 被调用")
}
})
}
// TestVHostManager_SetDefault 测试设置默认主机功能。
func TestVHostManager_SetDefault(t *testing.T) {
t.Run("设置默认主机", func(t *testing.T) {
manager := NewVHostManager()
defaultCalled := false
manager.SetDefault(mockHandler("default", &defaultCalled))
handler := manager.Handler()
ctx := &fasthttp.RequestCtx{}
ctx.Request.SetHost("nonexistent.com")
handler(ctx)
if !defaultCalled {
t.Error("期望默认 handler 被调用")
}
})
t.Run("覆盖默认主机", func(t *testing.T) {
manager := NewVHostManager()
firstCalled := false
secondCalled := false
manager.SetDefault(mockHandler("first", &firstCalled))
manager.SetDefault(mockHandler("second", &secondCalled))
handler := manager.Handler()
ctx := &fasthttp.RequestCtx{}
ctx.Request.SetHost("unknown.com")
handler(ctx)
if firstCalled {
t.Error("不期望第一个默认 handler 被调用(应被覆盖)")
}
if !secondCalled {
t.Error("期望第二个默认 handler 被调用")
}
})
}
// TestVHostManager_WildcardPrefix 测试前缀通配符 *.example.com。
func TestVHostManager_WildcardPrefix(t *testing.T) {
tests := []struct {
name string
pattern string
host string
shouldMatch bool
}{
{"exact subdomain", "*.example.com", "www.example.com", true},
{"nested subdomain", "*.example.com", "api.www.example.com", true},
{"no subdomain", "*.example.com", "example.com", false},
{"different domain", "*.example.com", "www.other.com", false},
{"longest match", "*.b.example.com", "a.b.example.com", true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
manager := NewVHostManager()
called := false
_ = manager.AddHost(tt.pattern, mockHandler("wildcard", &called))
handler := manager.Handler()
ctx := &fasthttp.RequestCtx{}
ctx.Request.SetHost(tt.host)
handler(ctx)
if called != tt.shouldMatch {
t.Errorf("expected match %v, got %v", tt.shouldMatch, called)
}
})
}
}
// TestVHostManager_WildcardSuffix 测试后缀通配符 example.*。
func TestVHostManager_WildcardSuffix(t *testing.T) {
tests := []struct {
name string
pattern string
host string
shouldMatch bool
}{
{"match com", "example.*", "example.com", true},
{"match net", "example.*", "example.net", true},
{"match org", "example.*", "example.org", true},
{"no match subdomain", "example.*", "www.example.com", false},
{"no match different prefix", "example.*", "other.com", false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
manager := NewVHostManager()
called := false
_ = manager.AddHost(tt.pattern, mockHandler("suffix", &called))
handler := manager.Handler()
ctx := &fasthttp.RequestCtx{}
ctx.Request.SetHost(tt.host)
handler(ctx)
if called != tt.shouldMatch {
t.Errorf("expected match %v, got %v", tt.shouldMatch, called)
}
})
}
}
// TestVHostManager_Regex 测试正则匹配。
func TestVHostManager_Regex(t *testing.T) {
tests := []struct {
name string
pattern string
host string
shouldMatch bool
wantErr bool
}{
{"match digits", "~^api[0-9]+\\.example\\.com$", "api1.example.com", true, false},
{"match digits 2", "~^api[0-9]+\\.example\\.com$", "api99.example.com", true, false},
{"no match letters", "~^api[0-9]+\\.example\\.com$", "apiX.example.com", false, false},
{"invalid regex", "~[invalid", "", false, true},
{"match any subdomain", "~.*\\.example\\.com", "www.example.com", true, false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
manager := NewVHostManager()
called := false
err := manager.AddHost(tt.pattern, mockHandler("regex", &called))
if tt.wantErr {
if err == nil {
t.Error("expected error for invalid regex")
}
return
}
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
handler := manager.Handler()
ctx := &fasthttp.RequestCtx{}
ctx.Request.SetHost(tt.host)
handler(ctx)
if called != tt.shouldMatch {
t.Errorf("expected match %v, got %v", tt.shouldMatch, called)
}
})
}
}
// TestVHostManager_MatchPriority 测试匹配优先级。
func TestVHostManager_MatchPriority(t *testing.T) {
t.Run("exact over wildcard", func(t *testing.T) {
manager := NewVHostManager()
exactCalled := false
wildcardCalled := false
_ = manager.AddHost("www.example.com", mockHandler("exact", &exactCalled))
_ = manager.AddHost("*.example.com", mockHandler("wildcard", &wildcardCalled))
handler := manager.Handler()
ctx := &fasthttp.RequestCtx{}
ctx.Request.SetHost("www.example.com")
handler(ctx)
if !exactCalled {
t.Error("expected exact match to be called")
}
if wildcardCalled {
t.Error("expected wildcard to NOT be called when exact match exists")
}
})
t.Run("longest wildcard prefix", func(t *testing.T) {
manager := NewVHostManager()
shortCalled := false
longCalled := false
_ = manager.AddHost("*.example.com", mockHandler("short", &shortCalled))
_ = manager.AddHost("*.b.example.com", mockHandler("long", &longCalled))
handler := manager.Handler()
ctx := &fasthttp.RequestCtx{}
ctx.Request.SetHost("a.b.example.com")
handler(ctx)
if shortCalled {
t.Error("expected short wildcard to NOT be called")
}
if !longCalled {
t.Error("expected longest wildcard match to be called")
}
})
}
// TestVHostManager_FindHost 测试 FindHost 方法。
func TestVHostManager_FindHost(t *testing.T) {
manager := NewVHostManager()
_ = manager.AddHost("exact.com", mockHandler("exact", new(bool)))
_ = manager.AddHost("*.wildcard.com", mockHandler("wildcard", new(bool)))
_ = manager.AddHost("suffix.*", mockHandler("suffix", new(bool)))
_ = manager.AddHost("~^regex.*", mockHandler("regex", new(bool)))
manager.SetDefault(mockHandler("default", new(bool)))
tests := []struct {
host string
wantName string
}{
{"exact.com", "exact.com"},
{"www.wildcard.com", "*.wildcard.com"},
{"suffix.net", "suffix.*"},
{"regex123", "~^regex.*"},
{"unknown.com", "default"},
}
for _, tt := range tests {
t.Run(tt.host, func(t *testing.T) {
vhost := manager.FindHost(tt.host)
if vhost == nil {
t.Fatal("expected non-nil vhost")
}
if vhost.name != tt.wantName {
t.Errorf("expected name %q, got %q", tt.wantName, vhost.name)
}
})
}
}
// TestVHostManager_FindHost_NilDefault 测试无默认主机时返回 nil。
func TestVHostManager_FindHost_NilDefault(t *testing.T) {
manager := NewVHostManager()
_ = manager.AddHost("example.com", mockHandler("example", new(bool)))
vhost := manager.FindHost("unknown.com")
if vhost != nil {
t.Error("expected nil when no default host and no match")
}
}
// TestVHostManager_AddHost_InvalidRegex 测试无效正则表达式。
func TestVHostManager_AddHost_InvalidRegex(t *testing.T) {
manager := NewVHostManager()
err := manager.AddHost("~[invalid(regex", mockHandler("test", new(bool)))
if err == nil {
t.Error("expected error for invalid regex pattern")
}
}
// TestVHostManager_PortStripping 测试端口剥离逻辑。
func TestVHostManager_PortStripping(t *testing.T) {
tests := []struct {
name string
input string
expected string
}{
{"无端口", "example.com", "example.com"},
{"标准HTTP端口", "example.com:80", "example.com"},
{"标准HTTPS端口", "example.com:443", "example.com"},
{"自定义端口", "example.com:8080", "example.com"},
{"IPv6 localhost带端口", "[localhost]:8080", "[localhost]"},
{"IPv6 loopback带端口", "[::1]:8080", "[::1]"},
{"IPv6完整地址带端口", "[2001:db8::1]:443", "[2001:db8::1]"},
{"IPv6无端口", "[::1]", "[::1]"},
{"IPv6完整地址无端口", "[2001:db8::1]", "[2001:db8::1]"},
{"空字符串", "", ""},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
manager := NewVHostManager()
called := false
_ = manager.AddHost(tt.expected, mockHandler("matched", &called))
handler := manager.Handler()
ctx := &fasthttp.RequestCtx{}
ctx.Request.SetHost(tt.input)
handler(ctx)
if !called {
t.Errorf("Host %q 期望匹配 %q但未匹配", tt.input, tt.expected)
}
})
}
// IPv6 数字地址测试
t.Run("IPv6数字地址", func(t *testing.T) {
manager := NewVHostManager()
ipv6Called := false
manager.AddHost("[::1]", mockHandler("ipv6", &ipv6Called))
handler := manager.Handler()
ctx := &fasthttp.RequestCtx{}
ctx.Request.SetHost("[::1]:8080")
handler(ctx)
if !ipv6Called {
t.Error("期望 [::1] handler 被调用,但未被调用")
}
if string(ctx.Response.Body()) != "ipv6" {
t.Errorf("响应体 = %q, want %q", string(ctx.Response.Body()), "ipv6")
}
})
}
// TestStartVHostMode_MultipleHosts 测试多虚拟主机配置。
func TestStartVHostMode_MultipleHosts(t *testing.T) {
cfg := &config.Config{
Mode: config.ServerModeVHost,
Servers: []config.ServerConfig{
{
Name: "api.example.com",
Listen: "127.0.0.1:0",
ServerNames: []string{"api.example.com", "api2.example.com"},
},
{
Name: "www.example.com",
Listen: "127.0.0.1:0",
ServerNames: []string{"www.example.com"},
},
},
}
s := New(cfg)
if s == nil {
t.Fatal("Expected non-nil server")
}
// 验证多虚拟主机配置
if len(s.config.Servers) != 2 {
t.Errorf("Expected 2 servers, got %d", len(s.config.Servers))
}
// 验证 server_names 配置
if len(s.config.Servers[0].ServerNames) != 2 {
t.Errorf("Expected 2 server_names for first server, got %d", len(s.config.Servers[0].ServerNames))
}
}
// TestStartVHostMode_DefaultHost 测试默认主机配置。
func TestStartVHostMode_DefaultHost(t *testing.T) {
cfg := &config.Config{
Mode: config.ServerModeVHost,
Servers: []config.ServerConfig{
{
Name: "api.example.com",
Listen: "127.0.0.1:0",
Default: false,
},
{
Name: "default.example.com",
Listen: "127.0.0.1:0",
Default: true,
},
},
}
s := New(cfg)
if s == nil {
t.Fatal("Expected non-nil server")
}
// 验证默认主机配置
defaultServer := cfg.GetDefaultServerFromList()
if defaultServer == nil {
t.Error("Expected non-nil default server")
return
}
if defaultServer.Name != "default.example.com" {
t.Errorf("Expected default server name 'default.example.com', got %q", defaultServer.Name)
}
}
// TestStartVHostMode_NoDefaultHost 测试无默认主机配置。
func TestStartVHostMode_NoDefaultHost(t *testing.T) {
cfg := &config.Config{
Mode: config.ServerModeVHost,
Servers: []config.ServerConfig{
{
Name: "api.example.com",
Listen: "127.0.0.1:0",
Default: false,
},
{
Name: "www.example.com",
Listen: "127.0.0.1:0",
Default: false,
},
},
}
s := New(cfg)
if s == nil {
t.Fatal("Expected non-nil server")
}
// 验证无默认主机
defaultServer := cfg.GetDefaultServerFromList()
if defaultServer != nil {
t.Error("Expected nil default server when none marked as default")
}
}
// TestStartVHostMode_WithProxy 测试带代理配置的虚拟主机。
func TestStartVHostMode_WithProxy(t *testing.T) {
cfg := &config.Config{
Mode: config.ServerModeVHost,
Servers: []config.ServerConfig{
{
Name: "api.example.com",
Listen: "127.0.0.1:0",
Proxy: []config.ProxyConfig{
{
Path: "/api",
Targets: []config.ProxyTarget{
{URL: "http://127.0.0.1:8081", Weight: 1},
},
},
},
},
{
Name: "www.example.com",
Listen: "127.0.0.1:0",
Proxy: []config.ProxyConfig{
{
Path: "/www",
Targets: []config.ProxyTarget{
{URL: "http://127.0.0.1:8082", Weight: 1},
},
},
},
},
},
}
s := New(cfg)
if s == nil {
t.Fatal("Expected non-nil server")
}
// 验证代理配置
if len(s.config.Servers[0].Proxy) != 1 {
t.Errorf("Expected 1 proxy for first server, got %d", len(s.config.Servers[0].Proxy))
}
if len(s.config.Servers[1].Proxy) != 1 {
t.Errorf("Expected 1 proxy for second server, got %d", len(s.config.Servers[1].Proxy))
}
}
// TestStartVHostMode_WithStaticFiles 测试带静态文件配置的虚拟主机。
func TestStartVHostMode_WithStaticFiles(t *testing.T) {
tempDir := t.TempDir()
cfg := &config.Config{
Mode: config.ServerModeVHost,
Servers: []config.ServerConfig{
{
Name: "static.example.com",
Listen: "127.0.0.1:0",
Static: []config.StaticConfig{
{
Path: "/static",
Root: tempDir,
Index: []string{"index.html"},
},
},
},
},
}
s := New(cfg)
if s == nil {
t.Fatal("Expected non-nil server")
}
// 验证静态文件配置
if len(s.config.Servers[0].Static) != 1 {
t.Errorf("Expected 1 static config, got %d", len(s.config.Servers[0].Static))
}
}
// TestStartVHostMode_WithMiddleware 测试带中间件配置的虚拟主机。
func TestStartVHostMode_WithMiddleware(t *testing.T) {
cfg := &config.Config{
Mode: config.ServerModeVHost,
Servers: []config.ServerConfig{
{
Name: "secure.example.com",
Listen: "127.0.0.1:0",
Security: config.SecurityConfig{
Headers: config.SecurityHeaders{
XFrameOptions: "DENY",
XContentTypeOptions: "nosniff",
},
},
},
},
}
s := New(cfg)
if s == nil {
t.Fatal("Expected non-nil server")
}
// 验证中间件配置
if s.config.Servers[0].Security.Headers.XFrameOptions != "DENY" {
t.Error("Expected XFrameOptions to be DENY")
}
}
// TestStartVHostMode_ServerNamesFallback 测试 server_names 回退到 Name 字段。
func TestStartVHostMode_ServerNamesFallback(t *testing.T) {
cfg := &config.Config{
Mode: config.ServerModeVHost,
Servers: []config.ServerConfig{
{
Name: "fallback.example.com",
Listen: "127.0.0.1:0",
ServerNames: nil, // 无 server_names应回退到 Name
},
},
}
s := New(cfg)
if s == nil {
t.Fatal("Expected non-nil server")
}
// 验证配置
if s.config.Servers[0].Name != "fallback.example.com" {
t.Errorf("Expected name 'fallback.example.com', got %q", s.config.Servers[0].Name)
}
}
// TestStartVHostMode_MultipleServerNames 测试多个 server_names。
func TestStartVHostMode_MultipleServerNames(t *testing.T) {
cfg := &config.Config{
Mode: config.ServerModeVHost,
Servers: []config.ServerConfig{
{
Name: "multi.example.com",
Listen: "127.0.0.1:0",
ServerNames: []string{
"example.com",
"www.example.com",
"api.example.com",
"*.example.com",
},
},
},
}
s := New(cfg)
if s == nil {
t.Fatal("Expected non-nil server")
}
// 验证多个 server_names
if len(s.config.Servers[0].ServerNames) != 4 {
t.Errorf("Expected 4 server_names, got %d", len(s.config.Servers[0].ServerNames))
}
}
// TestStartVHostMode_WildcardServerNames 测试通配符 server_names。
func TestStartVHostMode_WildcardServerNames(t *testing.T) {
tests := []struct {
name string
serverName string
requestHost string
shouldMatch bool
}{
{"前缀通配匹配", "*.example.com", "www.example.com", true},
{"前缀通配匹配子域名", "*.example.com", "api.www.example.com", true},
{"前缀通配不匹配根域", "*.example.com", "example.com", false},
{"后缀通配匹配", "example.*", "example.com", true},
{"后缀通配匹配其他TLD", "example.*", "example.net", true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
manager := NewVHostManager()
called := false
_ = manager.AddHost(tt.serverName, mockHandler("wildcard", &called))
handler := manager.Handler()
ctx := &fasthttp.RequestCtx{}
ctx.Request.SetHost(tt.requestHost)
handler(ctx)
if called != tt.shouldMatch {
t.Errorf("Expected match %v for %q against %q, got %v",
tt.shouldMatch, tt.requestHost, tt.serverName, called)
}
})
}
}
// TestStartVHostMode_WithCompression 测试带压缩配置的虚拟主机。
func TestStartVHostMode_WithCompression(t *testing.T) {
cfg := &config.Config{
Mode: config.ServerModeVHost,
Servers: []config.ServerConfig{
{
Name: "compressed.example.com",
Listen: "127.0.0.1:0",
Compression: config.CompressionConfig{
Type: "gzip",
Level: 6,
},
},
},
}
s := New(cfg)
if s == nil {
t.Fatal("Expected non-nil server")
}
// 验证压缩配置
if s.config.Servers[0].Compression.Type != "gzip" {
t.Error("Expected compression type to be gzip")
}
}
// TestStartVHostMode_WithRewrite 测试带重写配置的虚拟主机。
func TestStartVHostMode_WithRewrite(t *testing.T) {
cfg := &config.Config{
Mode: config.ServerModeVHost,
Servers: []config.ServerConfig{
{
Name: "rewrite.example.com",
Listen: "127.0.0.1:0",
Rewrite: []config.RewriteRule{
{
Pattern: "^/old/(.*)$",
Replacement: "/new/$1",
Flag: "last",
},
},
},
},
}
s := New(cfg)
if s == nil {
t.Fatal("Expected non-nil server")
}
// 验证重写配置
if len(s.config.Servers[0].Rewrite) != 1 {
t.Errorf("Expected 1 rewrite rule, got %d", len(s.config.Servers[0].Rewrite))
}
}
// TestStartVHostMode_WithSecurity 测试带安全配置的虚拟主机。
func TestStartVHostMode_WithSecurity(t *testing.T) {
cfg := &config.Config{
Mode: config.ServerModeVHost,
Servers: []config.ServerConfig{
{
Name: "secure.example.com",
Listen: "127.0.0.1:0",
Security: config.SecurityConfig{
Access: config.AccessConfig{
Allow: []string{"127.0.0.1"},
Deny: []string{"10.0.0.0/8"},
},
RateLimit: config.RateLimitConfig{
RequestRate: 100,
Burst: 200,
},
},
},
},
}
s := New(cfg)
if s == nil {
t.Fatal("Expected non-nil server")
}
// 验证安全配置
if len(s.config.Servers[0].Security.Access.Allow) != 1 {
t.Error("Expected 1 allowed IP")
}
if s.config.Servers[0].Security.RateLimit.RequestRate != 100 {
t.Error("Expected request rate 100")
}
}
// TestStartVHostMode_PerformanceConfig 测试性能配置。
func TestStartVHostMode_PerformanceConfig(t *testing.T) {
cfg := &config.Config{
Mode: config.ServerModeVHost,
Servers: []config.ServerConfig{
{
Name: "perf.example.com",
Listen: "127.0.0.1:0",
},
},
Performance: config.PerformanceConfig{
GoroutinePool: config.GoroutinePoolConfig{
Enabled: true,
MaxWorkers: 100,
MinWorkers: 10,
IdleTimeout: 30 * time.Second,
},
FileCache: config.FileCacheConfig{
MaxEntries: 1000,
MaxSize: 100 * 1024 * 1024,
},
},
}
s := New(cfg)
if s == nil {
t.Fatal("Expected non-nil server")
}
// 验证性能配置
if !s.config.Performance.GoroutinePool.Enabled {
t.Error("Expected GoroutinePool to be enabled")
}
}
// TestStartVHostMode_ServerOptions 测试服务器选项配置。
func TestStartVHostMode_ServerOptions(t *testing.T) {
cfg := &config.Config{
Mode: config.ServerModeVHost,
Servers: []config.ServerConfig{
{
Name: "options.example.com",
Listen: "127.0.0.1:0",
ReadTimeout: 30 * time.Second,
WriteTimeout: 30 * time.Second,
IdleTimeout: 60 * time.Second,
MaxConnsPerIP: 100,
MaxRequestsPerConn: 1000,
Concurrency: 1000,
ReadBufferSize: 16384,
WriteBufferSize: 16384,
},
},
}
s := New(cfg)
if s == nil {
t.Fatal("Expected non-nil server")
}
// 验证服务器选项
if s.config.Servers[0].ReadTimeout != 30*time.Second {
t.Error("Expected ReadTimeout 30s")
}
if s.config.Servers[0].MaxConnsPerIP != 100 {
t.Error("Expected MaxConnsPerIP 100")
}
}
// TestStartVHostMode_ServerTokens 测试 ServerTokens 配置。
func TestStartVHostMode_ServerTokens(t *testing.T) {
tests := []struct {
name string
serverTokens bool
expectedVersion bool
}{
{"显示版本", true, true},
{"隐藏版本", false, false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
cfg := &config.Config{
Mode: config.ServerModeVHost,
Servers: []config.ServerConfig{
{
Name: "tokens.example.com",
Listen: "127.0.0.1:0",
ServerTokens: tt.serverTokens,
},
},
}
s := New(cfg)
if s == nil {
t.Fatal("Expected non-nil server")
}
// 验证 ServerTokens 配置
if s.config.Servers[0].ServerTokens != tt.serverTokens {
t.Errorf("Expected ServerTokens %v, got %v", tt.serverTokens, s.config.Servers[0].ServerTokens)
}
})
}
}
// TestStartVHostMode_MonitoringConfig 测试监控配置。
func TestStartVHostMode_MonitoringConfig(t *testing.T) {
cfg := &config.Config{
Mode: config.ServerModeVHost,
Servers: []config.ServerConfig{
{
Name: "monitor.example.com",
Listen: "127.0.0.1:0",
Default: true,
},
},
Monitoring: config.MonitoringConfig{
Status: config.StatusConfig{
Enabled: true,
Path: "/status",
Format: "json",
Allow: []string{"127.0.0.1"},
},
Pprof: config.PprofConfig{
Enabled: true,
Path: "/debug/pprof",
},
},
}
s := New(cfg)
if s == nil {
t.Fatal("Expected non-nil server")
}
// 验证监控配置
if !s.config.Monitoring.Status.Enabled {
t.Error("Expected Status monitoring to be enabled")
}
if s.config.Monitoring.Status.Path != "/status" {
t.Error("Expected status path /status")
}
}
// TestStartVHostMode_CacheAPI 测试缓存 API 配置。
func TestStartVHostMode_CacheAPI(t *testing.T) {
cfg := &config.Config{
Mode: config.ServerModeVHost,
Servers: []config.ServerConfig{
{
Name: "cache.example.com",
Listen: "127.0.0.1:0",
Default: true,
CacheAPI: &config.CacheAPIConfig{
Enabled: true,
Path: "/_cache/purge",
Allow: []string{"127.0.0.1"},
},
},
},
}
s := New(cfg)
if s == nil {
t.Fatal("Expected non-nil server")
}
// 验证缓存 API 配置
if s.config.Servers[0].CacheAPI == nil || !s.config.Servers[0].CacheAPI.Enabled {
t.Error("Expected CacheAPI to be enabled")
}
}
// TestStartVHostMode_ErrorPage 测试错误页面配置。
func TestStartVHostMode_ErrorPage(t *testing.T) {
tempDir := t.TempDir()
errorPagePath := tempDir + "/404.html"
_ = os.WriteFile(errorPagePath, []byte("<html>Not Found</html>"), 0o644)
cfg := &config.Config{
Mode: config.ServerModeVHost,
Servers: []config.ServerConfig{
{
Name: "errors.example.com",
Listen: "127.0.0.1:0",
Security: config.SecurityConfig{
ErrorPage: config.ErrorPageConfig{
Pages: map[int]string{
404: errorPagePath,
},
Default: errorPagePath,
},
},
},
},
}
s := New(cfg)
if s == nil {
t.Fatal("Expected non-nil server")
}
// 验证错误页面配置
if s.config.Servers[0].Security.ErrorPage.Pages == nil {
t.Error("Expected error pages to be configured")
}
}
// TestStartVHostMode_LuaConfig 测试 Lua 配置。
func TestStartVHostMode_LuaConfig(t *testing.T) {
cfg := &config.Config{
Mode: config.ServerModeVHost,
Servers: []config.ServerConfig{
{
Name: "lua.example.com",
Listen: "127.0.0.1:0",
Lua: &config.LuaMiddlewareConfig{
Enabled: true,
GlobalSettings: config.LuaGlobalSettings{
MaxConcurrentCoroutines: 100,
CoroutineTimeout: 30 * time.Second,
},
},
},
},
}
s := New(cfg)
if s == nil {
t.Fatal("Expected non-nil server")
}
// 验证 Lua 配置
if s.config.Servers[0].Lua == nil || !s.config.Servers[0].Lua.Enabled {
t.Error("Expected Lua to be enabled")
}
}
// TestStartVHostMode_AuthConfig 测试认证配置。
func TestStartVHostMode_AuthConfig(t *testing.T) {
cfg := &config.Config{
Mode: config.ServerModeVHost,
Servers: []config.ServerConfig{
{
Name: "auth.example.com",
Listen: "127.0.0.1:0",
Security: config.SecurityConfig{
Auth: config.AuthConfig{
Type: "basic",
Realm: "Protected Area",
Users: []config.User{
{Name: "admin", Password: "$2a$10$hash"},
},
},
},
},
},
}
s := New(cfg)
if s == nil {
t.Fatal("Expected non-nil server")
}
// 验证认证配置
if s.config.Servers[0].Security.Auth.Type != "basic" {
t.Error("Expected auth type basic")
}
}
// TestStartVHostMode_AuthRequestConfig 测试外部认证配置。
func TestStartVHostMode_AuthRequestConfig(t *testing.T) {
cfg := &config.Config{
Mode: config.ServerModeVHost,
Servers: []config.ServerConfig{
{
Name: "authreq.example.com",
Listen: "127.0.0.1:0",
Security: config.SecurityConfig{
AuthRequest: config.AuthRequestConfig{
Enabled: true,
URI: "/auth",
Method: "GET",
Timeout: 5 * time.Second,
},
},
},
},
}
s := New(cfg)
if s == nil {
t.Fatal("Expected non-nil server")
}
// 验证外部认证配置
if !s.config.Servers[0].Security.AuthRequest.Enabled {
t.Error("Expected AuthRequest to be enabled")
}
}
// TestStartVHostMode_ConnLimiter 测试连接限制配置。
func TestStartVHostMode_ConnLimiter(t *testing.T) {
cfg := &config.Config{
Mode: config.ServerModeVHost,
Servers: []config.ServerConfig{
{
Name: "limited.example.com",
Listen: "127.0.0.1:0",
Security: config.SecurityConfig{
RateLimit: config.RateLimitConfig{
ConnLimit: 100,
Key: "ip",
},
},
},
},
}
s := New(cfg)
if s == nil {
t.Fatal("Expected non-nil server")
}
// 验证连接限制配置
if s.config.Servers[0].Security.RateLimit.ConnLimit != 100 {
t.Error("Expected ConnLimit 100")
}
}
// TestStartVHostMode_BodyLimit 测试请求体限制配置。
func TestStartVHostMode_BodyLimit(t *testing.T) {
cfg := &config.Config{
Mode: config.ServerModeVHost,
Servers: []config.ServerConfig{
{
Name: "bodylimit.example.com",
Listen: "127.0.0.1:0",
ClientMaxBodySize: "10MB",
},
},
}
s := New(cfg)
if s == nil {
t.Fatal("Expected non-nil server")
}
// 验证请求体限制配置
if s.config.Servers[0].ClientMaxBodySize != "10MB" {
t.Error("Expected ClientMaxBodySize 10MB")
}
}
// TestStartVHostMode_MixedConfig 测试混合配置场景。
func TestStartVHostMode_MixedConfig(t *testing.T) {
tempDir := t.TempDir()
cfg := &config.Config{
Mode: config.ServerModeVHost,
Servers: []config.ServerConfig{
{
Name: "api.example.com",
Listen: "127.0.0.1:0",
ServerNames: []string{"api.example.com", "api-alias.example.com"},
Proxy: []config.ProxyConfig{
{
Path: "/v1",
Targets: []config.ProxyTarget{
{URL: "http://backend1:8080", Weight: 1},
},
},
},
Security: config.SecurityConfig{
Headers: config.SecurityHeaders{
XFrameOptions: "DENY",
},
},
},
{
Name: "static.example.com",
Listen: "127.0.0.1:0",
Default: true,
Static: []config.StaticConfig{
{
Path: "/",
Root: tempDir,
Index: []string{"index.html"},
},
},
Compression: config.CompressionConfig{
Type: "gzip",
},
},
},
Performance: config.PerformanceConfig{
GoroutinePool: config.GoroutinePoolConfig{
Enabled: true,
MaxWorkers: 50,
},
},
}
s := New(cfg)
if s == nil {
t.Fatal("Expected non-nil server")
}
// 验证混合配置
if len(s.config.Servers) != 2 {
t.Errorf("Expected 2 servers, got %d", len(s.config.Servers))
}
// 验证第一个服务器配置
if len(s.config.Servers[0].Proxy) != 1 {
t.Error("Expected 1 proxy for api server")
}
// 验证第二个服务器配置
if len(s.config.Servers[1].Static) != 1 {
t.Error("Expected 1 static config for static server")
}
if !s.config.Servers[1].Default {
t.Error("Expected second server to be default")
}
}
// TestStartVHostMode_ModeDetection 测试模式自动检测。
func TestStartVHostMode_ModeDetection(t *testing.T) {
tests := []struct {
name string
servers []config.ServerConfig
expectedMode config.ServerMode
}{
{
name: "单服务器模式",
servers: []config.ServerConfig{
{Listen: ":8080"},
},
expectedMode: config.ServerModeSingle,
},
{
name: "虚拟主机模式(相同监听地址)",
servers: []config.ServerConfig{
{Listen: ":8080", Name: "api"},
{Listen: ":8080", Name: "www"},
},
expectedMode: config.ServerModeVHost,
},
{
name: "多服务器模式(不同监听地址)",
servers: []config.ServerConfig{
{Listen: ":8080", Name: "api"},
{Listen: ":8081", Name: "www"},
},
expectedMode: config.ServerModeMultiServer,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
cfg := &config.Config{
Mode: config.ServerModeAuto,
Servers: tt.servers,
}
mode := cfg.GetMode()
if mode != tt.expectedMode {
t.Errorf("Expected mode %s, got %s", tt.expectedMode, mode)
}
})
}
}
// TestStartVHostMode_StartIntegration 测试 startVHostMode 启动集成。
// TestStartVHostMode_VHostManagerCreation 测试 VHostManager 创建逻辑。
func TestStartVHostMode_VHostManagerCreation(t *testing.T) {
manager := NewVHostManager()
// 添加多个虚拟主机
hosts := []struct {
name string
handler fasthttp.RequestHandler
}{
{"api.example.com", mockHandler("api", new(bool))},
{"www.example.com", mockHandler("www", new(bool))},
{"*.example.com", mockHandler("wildcard", new(bool))},
}
for _, h := range hosts {
if err := manager.AddHost(h.name, h.handler); err != nil {
t.Errorf("Failed to add host %s: %v", h.name, err)
}
}
// 设置默认主机
manager.SetDefault(mockHandler("default", new(bool)))
// 验证主机查找
tests := []struct {
host string
expected string
}{
{"api.example.com", "api.example.com"},
{"www.example.com", "www.example.com"},
{"sub.example.com", "*.example.com"},
{"unknown.example.com", "*.example.com"},
{"other.com", "default"},
}
for _, tt := range tests {
t.Run(tt.host, func(t *testing.T) {
vhost := manager.FindHost(tt.host)
if vhost == nil {
t.Fatalf("Expected non-nil vhost for %s", tt.host)
}
if vhost.name != tt.expected {
t.Errorf("Expected vhost name %s, got %s", tt.expected, vhost.name)
}
})
}
}
// TestStartVHostMode_StatsTracking 测试虚拟主机模式下的请求统计。
func TestStartVHostMode_StatsTracking(t *testing.T) {
cfg := &config.Config{
Mode: config.ServerModeVHost,
Servers: []config.ServerConfig{
{
Name: "stats.example.com",
Listen: "127.0.0.1:0",
},
},
}
s := New(cfg)
// 测试统计追踪包装器
handler := func(ctx *fasthttp.RequestCtx) {
ctx.SetBodyString("test response")
}
wrappedHandler := s.trackStats(handler)
ctx := &fasthttp.RequestCtx{}
ctx.Init(&fasthttp.Request{}, nil, nil)
ctx.Request.SetBody([]byte("test request"))
wrappedHandler(ctx)
if s.requests.Load() != 1 {
t.Errorf("Expected 1 request, got %d", s.requests.Load())
}
if s.bytesReceived.Load() != int64(len("test request")) {
t.Errorf("Expected bytesReceived %d, got %d", len("test request"), s.bytesReceived.Load())
}
if s.bytesSent.Load() != int64(len("test response")) {
t.Errorf("Expected bytesSent %d, got %d", len("test response"), s.bytesSent.Load())
}
}
// TestStartVHostMode_MiddlewareChainBuilding 测试虚拟主机模式的中间件链构建。
func TestStartVHostMode_MiddlewareChainBuilding(t *testing.T) {
cfg := &config.Config{
Mode: config.ServerModeVHost,
Servers: []config.ServerConfig{
{
Name: "middleware.example.com",
Listen: "127.0.0.1:0",
Security: config.SecurityConfig{
Headers: config.SecurityHeaders{
XFrameOptions: "DENY",
XContentTypeOptions: "nosniff",
},
RateLimit: config.RateLimitConfig{
RequestRate: 100,
Burst: 200,
},
},
Compression: config.CompressionConfig{
Type: "gzip",
Level: 6,
},
},
},
}
s := New(cfg)
// 为虚拟主机构建中间件链
chain, err := s.buildMiddlewareChain(&cfg.Servers[0])
if err != nil {
t.Fatalf("Failed to build middleware chain: %v", err)
}
if chain == nil {
t.Fatal("Expected non-nil middleware chain")
}
}
// TestStartVHostMode_GetServerName 测试服务器名称获取。
func TestStartVHostMode_GetServerName(t *testing.T) {
tests := []struct {
name string
serverToken bool
expectFull bool
}{
{"显示版本", false, false}, // ServerTokens=false 隐藏版本
{"隐藏版本", true, true}, // ServerTokens=true 显示版本
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
cfg := &config.Config{
Mode: config.ServerModeVHost,
Servers: []config.ServerConfig{
{
Name: "name.example.com",
Listen: "127.0.0.1:0",
ServerTokens: tt.serverToken,
},
},
}
s := New(cfg)
serverName := s.getServerName(&cfg.Servers[0])
if tt.expectFull {
// 应包含版本号
if len(serverName) < 6 {
t.Errorf("Expected server name with version, got %s", serverName)
}
} else {
// 应该只有 "lolly"
if serverName != "lolly" {
t.Errorf("Expected server name 'lolly', got %s", serverName)
}
}
})
}
}
// TestStartVHostMode_CreateListener 测试虚拟主机模式下的监听器创建。
func TestStartVHostMode_CreateListener(t *testing.T) {
cfg := &config.Config{
Mode: config.ServerModeVHost,
Servers: []config.ServerConfig{
{
Name: "listener.example.com",
Listen: "127.0.0.1:0", // 随机端口
},
},
}
s := New(cfg)
// 创建 TCP 监听器
ln, err := s.createListener(&cfg.Servers[0])
if err != nil {
t.Fatalf("Failed to create listener: %v", err)
}
defer ln.Close()
// 验证监听器类型
if ln.Addr().Network() != "tcp" {
t.Errorf("Expected tcp network, got %s", ln.Addr().Network())
}
// 验证可以获取地址
if ln.Addr() == nil {
t.Error("Expected non-nil listener address")
}
}
// TestStartVHostMode_RegisterRoutes 测试虚拟主机路由注册。
func TestStartVHostMode_RegisterRoutes(t *testing.T) {
cfg := &config.Config{
Mode: config.ServerModeVHost,
Servers: []config.ServerConfig{
{
Name: "routes.example.com",
Listen: "127.0.0.1:0",
Proxy: []config.ProxyConfig{
{
Path: "/api",
Targets: []config.ProxyTarget{
{URL: "http://backend:8080", Weight: 1},
},
},
},
Static: []config.StaticConfig{
{
Path: "/static",
Root: "/tmp",
Index: []string{"index.html"},
},
},
},
},
}
s := New(cfg)
// 验证代理和静态配置
if len(s.config.Servers[0].Proxy) != 1 {
t.Errorf("Expected 1 proxy config, got %d", len(s.config.Servers[0].Proxy))
}
if len(s.config.Servers[0].Static) != 1 {
t.Errorf("Expected 1 static config, got %d", len(s.config.Servers[0].Static))
}
}
// TestStartVHostMode_DefaultHostSetup 测试默认主机设置。
func TestStartVHostMode_DefaultHostSetup(t *testing.T) {
// 测试有默认主机的情况
t.Run("with default host", func(t *testing.T) {
cfg := &config.Config{
Mode: config.ServerModeVHost,
Servers: []config.ServerConfig{
{
Name: "api.example.com",
Listen: "127.0.0.1:0",
Default: false,
},
{
Name: "default.example.com",
Listen: "127.0.0.1:0",
Default: true,
},
},
}
defaultSrv := cfg.GetDefaultServerFromList()
if defaultSrv == nil {
t.Fatal("Expected non-nil default server")
}
if defaultSrv.Name != "default.example.com" {
t.Errorf("Expected default server name 'default.example.com', got %s", defaultSrv.Name)
}
})
// 测试无默认主机的情况
t.Run("without default host", func(t *testing.T) {
cfg := &config.Config{
Mode: config.ServerModeVHost,
Servers: []config.ServerConfig{
{
Name: "api.example.com",
Listen: "127.0.0.1:0",
Default: false,
},
{
Name: "www.example.com",
Listen: "127.0.0.1:0",
Default: false,
},
},
}
defaultSrv := cfg.GetDefaultServerFromList()
if defaultSrv != nil {
t.Errorf("Expected nil default server, got %v", defaultSrv)
}
})
}
// TestStartVHostMode_MultiServerNames 测试每个服务器有多个 server_names。
func TestStartVHostMode_MultiServerNames(t *testing.T) {
manager := NewVHostManager()
// 模拟 startVHostMode 中的主机注册逻辑
serverNames := []string{"example.com", "www.example.com", "example.org"}
for _, name := range serverNames {
if err := manager.AddHost(name, mockHandler(name, new(bool))); err != nil {
t.Errorf("Failed to add host %s: %v", name, err)
}
}
// 验证每个主机名都能找到
for _, name := range serverNames {
vhost := manager.FindHost(name)
if vhost == nil {
t.Errorf("Expected to find vhost for %s", name)
}
if vhost.name != name {
t.Errorf("Expected vhost name %s, got %s", name, vhost.name)
}
}
}
// TestStartVHostMode_ComplexWildcardSetup 测试复杂的通配符配置。
func TestStartVHostMode_ComplexWildcardSetup(t *testing.T) {
manager := NewVHostManager()
// 添加精确匹配
_ = manager.AddHost("exact.example.com", mockHandler("exact", new(bool)))
// 添加前缀通配
_ = manager.AddHost("*.example.com", mockHandler("wildcard", new(bool)))
// 添加后缀通配
_ = manager.AddHost("test.*", mockHandler("suffix", new(bool)))
// 设置默认
manager.SetDefault(mockHandler("default", new(bool)))
// 验证匹配优先级
tests := []struct {
host string
expected string
}{
// 精确匹配优先
{"exact.example.com", "exact.example.com"},
// 前缀通配匹配
{"sub.example.com", "*.example.com"},
{"deep.sub.example.com", "*.example.com"},
// 后缀通配匹配
{"test.org", "test.*"},
{"test.net", "test.*"},
// 默认主机
{"other.com", "default"},
}
for _, tt := range tests {
t.Run(tt.host, func(t *testing.T) {
vhost := manager.FindHost(tt.host)
if vhost == nil {
t.Fatalf("Expected non-nil vhost for %s", tt.host)
}
if vhost.name != tt.expected {
t.Errorf("Expected %s, got %s", tt.expected, vhost.name)
}
})
}
}
// TestStartVHostMode_ActualExecution 测试 startVHostMode 实际执行路径。
func TestStartVHostMode_ActualExecution(t *testing.T) {
if testing.Short() {
t.Skip("Skipping integration test in short mode")
}
t.Run("基本虚拟主机模式启动", func(t *testing.T) {
cfg := &config.Config{
Mode: config.ServerModeVHost,
Servers: []config.ServerConfig{
{
Name: "api.example.com",
Listen: "127.0.0.1:0",
ServerNames: []string{"api.example.com"},
},
},
}
s := New(cfg)
// 启动服务器(在 goroutine 中)
errCh := make(chan error, 1)
go func() {
errCh <- s.Start()
}()
// 等待一小段时间让服务器启动
time.Sleep(50 * time.Millisecond)
// 停止服务器
_ = s.GracefulStop(1 * time.Second)
// 检查启动是否成功(服务器应该阻塞直到停止)
select {
case err := <-errCh:
// 服务器已停止,这是正常的
if err != nil {
t.Logf("Server stopped with: %v", err)
}
default:
// 服务器仍在运行,关闭它
_ = s.StopWithTimeout(1 * time.Second)
}
})
}
// TestStartVHostMode_MultipleVirtualHosts 测试多个虚拟主机的实际执行。
func TestStartVHostMode_MultipleVirtualHosts(t *testing.T) {
if testing.Short() {
t.Skip("Skipping integration test in short mode")
}
cfg := &config.Config{
Mode: config.ServerModeVHost,
Servers: []config.ServerConfig{
{
Name: "api.example.com",
Listen: "127.0.0.1:0",
ServerNames: []string{"api.example.com", "api2.example.com"},
},
{
Name: "www.example.com",
Listen: "127.0.0.1:0",
ServerNames: []string{"www.example.com"},
Default: true,
},
},
}
s := New(cfg)
// 验证配置
if len(s.config.Servers) != 2 {
t.Errorf("Expected 2 servers, got %d", len(s.config.Servers))
}
// 验证 server_names
if len(s.config.Servers[0].ServerNames) != 2 {
t.Errorf("Expected 2 server_names for first server, got %d", len(s.config.Servers[0].ServerNames))
}
// 验证默认主机
defaultSrv := cfg.GetDefaultServerFromList()
if defaultSrv == nil || defaultSrv.Name != "www.example.com" {
t.Error("Expected www.example.com to be default server")
}
}
// TestStartVHostMode_ServerNamesFallbackToName 测试 server_names 回退到 Name 字段。
func TestStartVHostMode_ServerNamesFallbackToName(t *testing.T) {
if testing.Short() {
t.Skip("Skipping integration test in short mode")
}
cfg := &config.Config{
Mode: config.ServerModeVHost,
Servers: []config.ServerConfig{
{
Name: "fallback.example.com",
Listen: "127.0.0.1:0",
ServerNames: nil, // 未设置,应回退到 Name
},
},
}
s := New(cfg)
// 验证 Name 字段正确设置
if s.config.Servers[0].Name != "fallback.example.com" {
t.Errorf("Expected Name 'fallback.example.com', got %s", s.config.Servers[0].Name)
}
}
// TestStartVHostMode_WithMonitoringEndpoints 测试监控端点配置。
func TestStartVHostMode_WithMonitoringEndpoints(t *testing.T) {
cfg := &config.Config{
Mode: config.ServerModeVHost,
Servers: []config.ServerConfig{
{
Name: "default.example.com",
Listen: "127.0.0.1:0",
Default: true,
},
},
Monitoring: config.MonitoringConfig{
Status: config.StatusConfig{
Enabled: true,
Path: "/_status",
Format: "json",
Allow: []string{"127.0.0.1"},
},
Pprof: config.PprofConfig{
Enabled: true,
Path: "/debug/pprof",
},
},
}
s := New(cfg)
// 验证监控配置
if !s.config.Monitoring.Status.Enabled {
t.Error("Expected status monitoring enabled")
}
if !s.config.Monitoring.Pprof.Enabled {
t.Error("Expected pprof enabled")
}
}
// TestStartVHostMode_WithCacheAPIEndpoint 测试缓存 API 端点配置。
func TestStartVHostMode_WithCacheAPIEndpoint(t *testing.T) {
cfg := &config.Config{
Mode: config.ServerModeVHost,
Servers: []config.ServerConfig{
{
Name: "default.example.com",
Listen: "127.0.0.1:0",
Default: true,
CacheAPI: &config.CacheAPIConfig{
Enabled: true,
Path: "/_cache/purge",
Allow: []string{"127.0.0.1"},
},
},
},
}
s := New(cfg)
// 验证缓存 API 配置
if s.config.Servers[0].CacheAPI == nil || !s.config.Servers[0].CacheAPI.Enabled {
t.Error("Expected cache API enabled")
}
}
// TestStartVHostMode_WithProxyConfig 测试代理配置。
func TestStartVHostMode_WithProxyConfig(t *testing.T) {
cfg := &config.Config{
Mode: config.ServerModeVHost,
Servers: []config.ServerConfig{
{
Name: "api.example.com",
Listen: "127.0.0.1:0",
Proxy: []config.ProxyConfig{
{
Path: "/api",
Targets: []config.ProxyTarget{
{URL: "http://127.0.0.1:8081", Weight: 1},
},
},
},
},
},
}
s := New(cfg)
// 验证代理配置
if len(s.config.Servers[0].Proxy) != 1 {
t.Errorf("Expected 1 proxy config, got %d", len(s.config.Servers[0].Proxy))
}
if s.config.Servers[0].Proxy[0].Path != "/api" {
t.Errorf("Expected proxy path /api, got %s", s.config.Servers[0].Proxy[0].Path)
}
}
// TestStartVHostMode_WithStaticFiles2 测试静态文件配置。
func TestStartVHostMode_WithStaticFiles2(t *testing.T) {
tempDir := t.TempDir()
cfg := &config.Config{
Mode: config.ServerModeVHost,
Servers: []config.ServerConfig{
{
Name: "static.example.com",
Listen: "127.0.0.1:0",
Static: []config.StaticConfig{
{
Path: "/static",
Root: tempDir,
Index: []string{"index.html"},
},
},
},
},
}
s := New(cfg)
// 验证静态文件配置
if len(s.config.Servers[0].Static) != 1 {
t.Errorf("Expected 1 static config, got %d", len(s.config.Servers[0].Static))
}
}
// TestStartVHostMode_WithGoroutinePool 测试 GoroutinePool 配置。
func TestStartVHostMode_WithGoroutinePool(t *testing.T) {
cfg := &config.Config{
Mode: config.ServerModeVHost,
Servers: []config.ServerConfig{
{
Name: "pool.example.com",
Listen: "127.0.0.1:0",
},
},
Performance: config.PerformanceConfig{
GoroutinePool: config.GoroutinePoolConfig{
Enabled: true,
MaxWorkers: 100,
MinWorkers: 10,
IdleTimeout: 30 * time.Second,
},
},
}
s := New(cfg)
// 验证性能配置
if !s.config.Performance.GoroutinePool.Enabled {
t.Error("Expected GoroutinePool enabled")
}
}
// TestStartVHostMode_InvalidRegexServerName 测试无效正则表达式的 server_name。
func TestStartVHostMode_InvalidRegexServerName(t *testing.T) {
manager := NewVHostManager()
// 添加无效正则表达式应该返回错误
err := manager.AddHost("~[invalid(regex", mockHandler("test", new(bool)))
if err == nil {
t.Error("Expected error for invalid regex pattern")
}
}
// TestStartVHostMode_NoServers 测试无服务器配置。
func TestStartVHostMode_NoServers(t *testing.T) {
cfg := &config.Config{
Mode: config.ServerModeVHost,
Servers: []config.ServerConfig{},
}
s := New(cfg)
// 验证空配置
if len(s.config.Servers) != 0 {
t.Errorf("Expected 0 servers, got %d", len(s.config.Servers))
}
}
// TestStartVHostMode_SingleServer 测试单服务器虚拟主机模式。
func TestStartVHostMode_SingleServer(t *testing.T) {
cfg := &config.Config{
Mode: config.ServerModeVHost,
Servers: []config.ServerConfig{
{
Name: "single.example.com",
Listen: "127.0.0.1:0",
ServerNames: []string{"single.example.com"},
Default: true,
},
},
}
s := New(cfg)
// 验证配置
if len(s.config.Servers) != 1 {
t.Errorf("Expected 1 server, got %d", len(s.config.Servers))
}
// 验证默认主机
defaultSrv := cfg.GetDefaultServerFromList()
if defaultSrv == nil {
t.Error("Expected default server")
}
}
// TestStartVHostMode_MixedProxyAndStatic 测试代理和静态文件混合配置。
func TestStartVHostMode_MixedProxyAndStatic(t *testing.T) {
tempDir := t.TempDir()
cfg := &config.Config{
Mode: config.ServerModeVHost,
Servers: []config.ServerConfig{
{
Name: "mixed.example.com",
Listen: "127.0.0.1:0",
Proxy: []config.ProxyConfig{
{
Path: "/api",
Targets: []config.ProxyTarget{
{URL: "http://backend:8080", Weight: 1},
},
},
},
Static: []config.StaticConfig{
{
Path: "/static",
Root: tempDir,
Index: []string{"index.html"},
},
},
},
},
}
s := New(cfg)
// 验证代理配置
if len(s.config.Servers[0].Proxy) != 1 {
t.Errorf("Expected 1 proxy config, got %d", len(s.config.Servers[0].Proxy))
}
// 验证静态文件配置
if len(s.config.Servers[0].Static) != 1 {
t.Errorf("Expected 1 static config, got %d", len(s.config.Servers[0].Static))
}
}
// TestStartVHostMode_ActualServerStart 测试 startVHostMode 实际服务器启动。
func TestStartVHostMode_ActualServerStart(t *testing.T) {
if testing.Short() {
t.Skip("Skipping integration test in short mode")
}
t.Run("带默认主机启动", func(t *testing.T) {
cfg := &config.Config{
Mode: config.ServerModeVHost,
Servers: []config.ServerConfig{
{
Name: "api.example.com",
Listen: "127.0.0.1:0",
ServerNames: []string{"api.example.com"},
},
{
Name: "default.example.com",
Listen: "127.0.0.1:0",
ServerNames: []string{"default.example.com"},
Default: true,
},
},
}
s := New(cfg)
errCh := make(chan error, 1)
go func() {
errCh <- s.Start()
}()
time.Sleep(50 * time.Millisecond)
_ = s.GracefulStop(1 * time.Second)
select {
case <-errCh:
default:
_ = s.StopWithTimeout(1 * time.Second)
}
})
t.Run("带代理配置启动", func(t *testing.T) {
cfg := &config.Config{
Mode: config.ServerModeVHost,
Servers: []config.ServerConfig{
{
Name: "proxy.example.com",
Listen: "127.0.0.1:0",
ServerNames: []string{"proxy.example.com"},
Proxy: []config.ProxyConfig{
{
Path: "/api",
Targets: []config.ProxyTarget{
{URL: "http://127.0.0.1:8081", Weight: 1},
},
},
},
},
},
}
s := New(cfg)
errCh := make(chan error, 1)
go func() {
errCh <- s.Start()
}()
time.Sleep(50 * time.Millisecond)
_ = s.GracefulStop(1 * time.Second)
select {
case <-errCh:
default:
_ = s.StopWithTimeout(1 * time.Second)
}
})
t.Run("带监控端点启动", func(t *testing.T) {
cfg := &config.Config{
Mode: config.ServerModeVHost,
Servers: []config.ServerConfig{
{
Name: "monitor.example.com",
Listen: "127.0.0.1:0",
Default: true,
},
},
Monitoring: config.MonitoringConfig{
Status: config.StatusConfig{
Enabled: true,
Path: "/_status",
},
Pprof: config.PprofConfig{
Enabled: true,
Path: "/debug/pprof",
},
},
}
s := New(cfg)
errCh := make(chan error, 1)
go func() {
errCh <- s.Start()
}()
time.Sleep(50 * time.Millisecond)
_ = s.GracefulStop(1 * time.Second)
select {
case <-errCh:
default:
_ = s.StopWithTimeout(1 * time.Second)
}
})
t.Run("带缓存API启动", func(t *testing.T) {
cfg := &config.Config{
Mode: config.ServerModeVHost,
Servers: []config.ServerConfig{
{
Name: "cache.example.com",
Listen: "127.0.0.1:0",
Default: true,
CacheAPI: &config.CacheAPIConfig{
Enabled: true,
Path: "/_cache/purge",
},
},
},
}
s := New(cfg)
errCh := make(chan error, 1)
go func() {
errCh <- s.Start()
}()
time.Sleep(50 * time.Millisecond)
_ = s.GracefulStop(1 * time.Second)
select {
case <-errCh:
default:
_ = s.StopWithTimeout(1 * time.Second)
}
})
t.Run("带GoroutinePool启动", func(t *testing.T) {
cfg := &config.Config{
Mode: config.ServerModeVHost,
Servers: []config.ServerConfig{
{
Name: "pool.example.com",
Listen: "127.0.0.1:0",
ServerNames: []string{"pool.example.com"},
},
},
Performance: config.PerformanceConfig{
GoroutinePool: config.GoroutinePoolConfig{
Enabled: true,
MaxWorkers: 10,
MinWorkers: 2,
IdleTimeout: 5 * time.Second,
},
},
}
s := New(cfg)
errCh := make(chan error, 1)
go func() {
errCh <- s.Start()
}()
time.Sleep(50 * time.Millisecond)
_ = s.GracefulStop(1 * time.Second)
select {
case <-errCh:
default:
_ = s.StopWithTimeout(1 * time.Second)
}
})
}