[优化] 自定义不存在页面返回状态码

This commit is contained in:
ryan
2026-06-02 08:14:15 +08:00
parent d21643feed
commit 3825a7f29a
10 changed files with 196 additions and 110 deletions
+1
View File
@@ -59,6 +59,7 @@ var DatabaseAutoCleanupRetentionDays = 30
// V5 OpenResty performance settings (hot-reloadable via Option table)
var OpenRestyDefaultServerReturnStatus = 421
var OpenRestyWorkerProcesses = "auto"
var OpenRestyWorkerConnections = 4096
var OpenRestyWorkerRlimitNofile = 65535
+9
View File
@@ -104,6 +104,15 @@ func validateOpenRestyOption(key string, value string) error {
trimmed := strings.TrimSpace(value)
switch key {
case "OpenRestyDefaultServerReturnStatus":
if err := validatePositiveIntegerOption(key, trimmed); err != nil {
return err
}
statusCode, _ := strconv.Atoi(trimmed)
if statusCode < 100 || statusCode > 999 {
return fmt.Errorf("%s 必须在 100 到 999 之间", key)
}
return nil
case "OpenRestyWorkerProcesses":
if trimmed == "auto" {
return nil
@@ -9,6 +9,11 @@ func TestValidateOpenRestyOption(t *testing.T) {
value string
wantErr bool
}{
{name: "default server status valid 421", key: "OpenRestyDefaultServerReturnStatus", value: "421"},
{name: "default server status valid 200", key: "OpenRestyDefaultServerReturnStatus", value: "200"},
{name: "default server status invalid 99", key: "OpenRestyDefaultServerReturnStatus", value: "99", wantErr: true},
{name: "default server status invalid 1000", key: "OpenRestyDefaultServerReturnStatus", value: "1000", wantErr: true},
{name: "default server status invalid abc", key: "OpenRestyDefaultServerReturnStatus", value: "abc", wantErr: true},
{name: "worker processes auto", key: "OpenRestyWorkerProcesses", value: "auto"},
{name: "worker processes number", key: "OpenRestyWorkerProcesses", value: "8"},
{name: "worker processes invalid", key: "OpenRestyWorkerProcesses", value: "0", wantErr: true},
+5
View File
@@ -53,6 +53,7 @@ func InitOptionMap() {
common.OptionMap["GeoIPProvider"] = common.GeoIPProvider
common.OptionMap["DatabaseAutoCleanupEnabled"] = strconv.FormatBool(common.DatabaseAutoCleanupEnabled)
common.OptionMap["DatabaseAutoCleanupRetentionDays"] = strconv.Itoa(common.DatabaseAutoCleanupRetentionDays)
common.OptionMap["OpenRestyDefaultServerReturnStatus"] = strconv.Itoa(common.OpenRestyDefaultServerReturnStatus)
common.OptionMap["OpenRestyWorkerProcesses"] = common.OpenRestyWorkerProcesses
common.OptionMap["OpenRestyWorkerConnections"] = strconv.Itoa(common.OpenRestyWorkerConnections)
common.OptionMap["OpenRestyWorkerRlimitNofile"] = strconv.Itoa(common.OpenRestyWorkerRlimitNofile)
@@ -213,6 +214,10 @@ func updateOptionMap(key string, value string) {
if v, err := strconv.Atoi(value); err == nil && v >= 1 {
common.DatabaseAutoCleanupRetentionDays = v
}
case "OpenRestyDefaultServerReturnStatus":
if v, err := strconv.Atoi(value); err == nil && v >= 100 && v <= 999 {
common.OpenRestyDefaultServerReturnStatus = v
}
case "OpenRestyWorkerProcesses":
if strings.TrimSpace(value) != "" {
common.OpenRestyWorkerProcesses = value
@@ -135,6 +135,7 @@ type snapshotWAFDocument struct {
}
type openRestyConfigSnapshot struct {
DefaultServerReturnStatus int `json:"default_server_return_status"`
WorkerProcesses string `json:"worker_processes"`
WorkerConnections int `json:"worker_connections"`
WorkerRlimitNofile int `json:"worker_rlimit_nofile"`
@@ -934,6 +935,7 @@ func stringSliceEqual(left []string, right []string) bool {
func buildOpenRestyConfigSnapshot() openRestyConfigSnapshot {
return openRestyConfigSnapshot{
DefaultServerReturnStatus: common.OpenRestyDefaultServerReturnStatus,
WorkerProcesses: common.OpenRestyWorkerProcesses,
WorkerConnections: common.OpenRestyWorkerConnections,
WorkerRlimitNofile: common.OpenRestyWorkerRlimitNofile,
@@ -1052,6 +1054,7 @@ func diffOpenRestyOptionDetails(left openRestyConfigSnapshot, right openRestyCon
CurrentValue: current,
})
}
appendIfChanged("OpenRestyDefaultServerReturnStatus", fmt.Sprintf("%d", left.DefaultServerReturnStatus), fmt.Sprintf("%d", right.DefaultServerReturnStatus))
appendIfChanged("OpenRestyWorkerProcesses", left.WorkerProcesses, right.WorkerProcesses)
appendIfChanged("OpenRestyWorkerConnections", fmt.Sprintf("%d", left.WorkerConnections), fmt.Sprintf("%d", right.WorkerConnections))
appendIfChanged("OpenRestyWorkerRlimitNofile", fmt.Sprintf("%d", left.WorkerRlimitNofile), fmt.Sprintf("%d", right.WorkerRlimitNofile))
@@ -1099,6 +1102,7 @@ func extractOptionDiffKeys(details []ConfigOptionDiffItem) []string {
func openRestyOptionKeys() []string {
return []string{
"OpenRestyDefaultServerReturnStatus",
"OpenRestyWorkerProcesses",
"OpenRestyWorkerConnections",
"OpenRestyWorkerRlimitNofile",
@@ -1193,8 +1193,8 @@ func TestRenderConfigUsesDefaultServerFallback(t *testing.T) {
if !strings.Contains(preview.MainConfig, "server_name _;") {
t.Fatal("expected preview main config to include default server_name")
}
if !strings.Contains(preview.MainConfig, "return 404;") {
t.Fatal("expected preview main config to return 404 for unmatched hosts")
if !strings.Contains(preview.MainConfig, "return 421;") {
t.Fatal("expected preview main config to return 421 for unmatched hosts")
}
if !strings.Contains(preview.MainConfig, "ssl_reject_handshake on;") {
t.Fatal("expected preview main config to reject unmatched https handshakes")
@@ -305,7 +305,7 @@ func renderMainConfigTemplate(templateText string, cfg ConfigSnapshot) string {
"{{OpenRestyWorkerConnections}}", fmt.Sprintf("%d", cfg.WorkerConnections),
"{{OpenRestyWorkerRlimitNofile}}", fmt.Sprintf("%d", cfg.WorkerRlimitNofile),
"{{OpenRestyConnectionUpgradeMap}}", renderConnectionUpgradeMap(),
"{{OpenRestyDefaultServerBlock}}", renderDefaultServerBlock(),
"{{OpenRestyDefaultServerBlock}}", renderDefaultServerBlock(cfg.DefaultServerReturnStatus),
"{{OpenRestyAccessLogPath}}", AccessLogPlaceholder,
"{{OpenRestyErrorLogPath}}", ErrorLogPlaceholder,
"{{OpenRestyEventsUseDirective}}", renderTemplateDirective(cfg.EventsUse != "", fmt.Sprintf("use %s;", cfg.EventsUse)),
@@ -586,8 +586,26 @@ func renderConnectionUpgradeMap() string {
return " map $http_upgrade $connection_upgrade {\n default upgrade;\n '' \"\";\n }\n\n"
}
func renderDefaultServerBlock() string {
return strings.Join([]string{" server {", " listen 80 default_server;", " server_name _;", "", " return 404;", " }", "", " server {", " listen 443 ssl default_server;", " server_name _;", "", " ssl_reject_handshake on;", " }", ""}, "\n")
func renderDefaultServerBlock(statusCode int) string {
if statusCode <= 0 {
statusCode = 421
}
return strings.Join([]string{
" server {",
" listen 80 default_server;",
" server_name _;",
"",
fmt.Sprintf(" return %d;", statusCode),
" }",
"",
" server {",
" listen 443 ssl default_server;",
" server_name _;",
"",
" ssl_reject_handshake on;",
" }",
"",
}, "\n")
}
func normalizedRouteDomains(route Route) []string {
@@ -158,6 +158,7 @@ type WAFDocument struct {
}
type ConfigSnapshot struct {
DefaultServerReturnStatus int `json:"default_server_return_status"`
WorkerProcesses string `json:"worker_processes"`
WorkerConnections int `json:"worker_connections"`
WorkerRlimitNofile int `json:"worker_rlimit_nofile"`
@@ -31,6 +31,7 @@ const templatePanelClassName =
'h-[560px] w-full box-border overflow-auto rounded-2xl border border-[var(--border-default)] bg-[var(--surface-elevated)] px-4 py-3 font-mono text-xs leading-6';
const defaultPerformanceFields = {
OpenRestyDefaultServerReturnStatus: '421',
OpenRestyWorkerProcesses: 'auto',
OpenRestyWorkerConnections: '4096',
OpenRestyWorkerRlimitNofile: '65535',
@@ -69,6 +70,8 @@ const defaultPerformanceFields = {
};
const performanceFieldTooltips: Record<string, string> = {
default_server_return_status:
'当客户端请求未匹配任何已配置的网站域名时,默认服务器返回的 HTTP 状态码。通常保持 421 拒绝访问。',
worker_processes:
'Nginx worker 进程数。通常建议保持 auto,让 OpenResty 按 CPU 核数自动分配。',
worker_connections:
@@ -201,6 +204,7 @@ export function PerformancePage() {
const optionMap = optionsToMap(optionsQuery.data);
setPerformanceFields({
OpenRestyDefaultServerReturnStatus: optionMap.OpenRestyDefaultServerReturnStatus ?? '421',
OpenRestyWorkerProcesses: optionMap.OpenRestyWorkerProcesses ?? 'auto',
OpenRestyWorkerConnections:
optionMap.OpenRestyWorkerConnections ?? '4096',
@@ -301,6 +305,10 @@ export function PerformancePage() {
}
const integerFields = [
[
'default_server_return_status',
performanceFields.OpenRestyDefaultServerReturnStatus,
],
['worker_connections', performanceFields.OpenRestyWorkerConnections],
['worker_rlimit_nofile', performanceFields.OpenRestyWorkerRlimitNofile],
['keepalive_timeout', performanceFields.OpenRestyKeepaliveTimeout],
@@ -323,6 +331,17 @@ export function PerformancePage() {
'client_max_body_size 必须为整数或带 k/m/g 单位的大小值。',
);
}
const defaultServerReturnStatusVal = Number.parseInt(
performanceFields.OpenRestyDefaultServerReturnStatus,
10,
);
if (
Number.isNaN(defaultServerReturnStatusVal) ||
defaultServerReturnStatusVal < 100 ||
defaultServerReturnStatusVal > 999
) {
throw new Error('空白页面返回状态码必须在 100 到 999 之间。');
}
if (
!isProxyBuffersValue(
performanceFields.OpenRestyLargeClientHeaderBuffers,
@@ -333,6 +352,10 @@ export function PerformancePage() {
await saveOptionEntries(
[
[
'OpenRestyDefaultServerReturnStatus',
performanceFields.OpenRestyDefaultServerReturnStatus.trim(),
],
[
'OpenRestyWorkerProcesses',
performanceFields.OpenRestyWorkerProcesses.trim(),
@@ -893,6 +916,24 @@ export function PerformancePage() {
}
/>
</ResourceField>
<ResourceField
label="空白页面返回状态码"
hint="默认 421,当请求未匹配任何已有反代网站时,默认服务器返回的状态码。"
tooltip={performanceFieldTooltips.default_server_return_status}
>
<ResourceInput
type="number"
min="100"
max="999"
value={performanceFields.OpenRestyDefaultServerReturnStatus}
onChange={(event) =>
setPerformanceFields((previous) => ({
...previous,
OpenRestyDefaultServerReturnStatus: event.target.value,
}))
}
/>
</ResourceField>
</div>
</AppCard>
@@ -87,6 +87,7 @@ const defaultOperationFields = {
NodeOfflineThreshold: '120000',
AgentUpdateRepo: 'Rain-kl/OpenFlare',
GeoIPProvider: 'ipinfo',
OpenRestyDefaultServerReturnStatus: '421',
OpenRestyWorkerProcesses: 'auto',
OpenRestyWorkerConnections: '4096',
OpenRestyWorkerRlimitNofile: '65535',
@@ -373,6 +374,7 @@ export function SettingsPage() {
NodeOfflineThreshold: optionMap.NodeOfflineThreshold ?? '120000',
AgentUpdateRepo: optionMap.AgentUpdateRepo ?? 'Rain-kl/OpenFlare',
GeoIPProvider: optionMap.GeoIPProvider ?? 'ipinfo',
OpenRestyDefaultServerReturnStatus: optionMap.OpenRestyDefaultServerReturnStatus ?? '421',
OpenRestyWorkerProcesses: optionMap.OpenRestyWorkerProcesses ?? 'auto',
OpenRestyWorkerConnections:
optionMap.OpenRestyWorkerConnections ?? '4096',