2305 lines
55 KiB
Go
2305 lines
55 KiB
Go
// 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)
|
||
}
|
||
})
|
||
}
|