diff --git a/openflare_server/common/constants.go b/openflare_server/common/constants.go index be663d74..f563f0da 100644 --- a/openflare_server/common/constants.go +++ b/openflare_server/common/constants.go @@ -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 diff --git a/openflare_server/controller/option.go b/openflare_server/controller/option.go index aa500427..80f68a3b 100644 --- a/openflare_server/controller/option.go +++ b/openflare_server/controller/option.go @@ -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 diff --git a/openflare_server/controller/option_test.go b/openflare_server/controller/option_test.go index 17b25c2f..a8b9d17b 100644 --- a/openflare_server/controller/option_test.go +++ b/openflare_server/controller/option_test.go @@ -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}, diff --git a/openflare_server/model/option.go b/openflare_server/model/option.go index 2a99c6df..776b33c1 100644 --- a/openflare_server/model/option.go +++ b/openflare_server/model/option.go @@ -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 diff --git a/openflare_server/service/config_version.go b/openflare_server/service/config_version.go index b95d22ca..231885a5 100644 --- a/openflare_server/service/config_version.go +++ b/openflare_server/service/config_version.go @@ -135,41 +135,42 @@ type snapshotWAFDocument struct { } type openRestyConfigSnapshot struct { - WorkerProcesses string `json:"worker_processes"` - WorkerConnections int `json:"worker_connections"` - WorkerRlimitNofile int `json:"worker_rlimit_nofile"` - EventsUse string `json:"events_use,omitempty"` - EventsMultiAcceptEnabled bool `json:"events_multi_accept_enabled"` - KeepaliveTimeout int `json:"keepalive_timeout"` - KeepaliveRequests int `json:"keepalive_requests"` - ClientHeaderTimeout int `json:"client_header_timeout"` - ClientBodyTimeout int `json:"client_body_timeout"` - ClientMaxBodySize string `json:"client_max_body_size"` - LargeClientHeaderBuffers string `json:"large_client_header_buffers"` - SendTimeout int `json:"send_timeout"` - ProxyConnectTimeout int `json:"proxy_connect_timeout"` - ProxySendTimeout int `json:"proxy_send_timeout"` - ProxyReadTimeout int `json:"proxy_read_timeout"` - WebsocketEnabled bool `json:"websocket_enabled"` - ProxyRequestBuffering bool `json:"proxy_request_buffering"` - ProxyBufferingEnabled bool `json:"proxy_buffering_enabled"` - ProxyBuffers string `json:"proxy_buffers"` - ProxyBufferSize string `json:"proxy_buffer_size"` - ProxyBusyBuffersSize string `json:"proxy_busy_buffers_size"` - GzipEnabled bool `json:"gzip_enabled"` - GzipMinLength int `json:"gzip_min_length"` - GzipCompLevel int `json:"gzip_comp_level"` - Resolvers string `json:"resolvers,omitempty"` - CacheEnabled bool `json:"cache_enabled"` - CachePath string `json:"cache_path,omitempty"` - CacheLevels string `json:"cache_levels"` - CacheInactive string `json:"cache_inactive"` - CacheMaxSize string `json:"cache_max_size"` - CacheKeyTemplate string `json:"cache_key_template"` - CacheLockEnabled bool `json:"cache_lock_enabled"` - CacheLockTimeout string `json:"cache_lock_timeout"` - CacheUseStale string `json:"cache_use_stale"` - MainConfigTemplate string `json:"main_config_template,omitempty"` + DefaultServerReturnStatus int `json:"default_server_return_status"` + WorkerProcesses string `json:"worker_processes"` + WorkerConnections int `json:"worker_connections"` + WorkerRlimitNofile int `json:"worker_rlimit_nofile"` + EventsUse string `json:"events_use,omitempty"` + EventsMultiAcceptEnabled bool `json:"events_multi_accept_enabled"` + KeepaliveTimeout int `json:"keepalive_timeout"` + KeepaliveRequests int `json:"keepalive_requests"` + ClientHeaderTimeout int `json:"client_header_timeout"` + ClientBodyTimeout int `json:"client_body_timeout"` + ClientMaxBodySize string `json:"client_max_body_size"` + LargeClientHeaderBuffers string `json:"large_client_header_buffers"` + SendTimeout int `json:"send_timeout"` + ProxyConnectTimeout int `json:"proxy_connect_timeout"` + ProxySendTimeout int `json:"proxy_send_timeout"` + ProxyReadTimeout int `json:"proxy_read_timeout"` + WebsocketEnabled bool `json:"websocket_enabled"` + ProxyRequestBuffering bool `json:"proxy_request_buffering"` + ProxyBufferingEnabled bool `json:"proxy_buffering_enabled"` + ProxyBuffers string `json:"proxy_buffers"` + ProxyBufferSize string `json:"proxy_buffer_size"` + ProxyBusyBuffersSize string `json:"proxy_busy_buffers_size"` + GzipEnabled bool `json:"gzip_enabled"` + GzipMinLength int `json:"gzip_min_length"` + GzipCompLevel int `json:"gzip_comp_level"` + Resolvers string `json:"resolvers,omitempty"` + CacheEnabled bool `json:"cache_enabled"` + CachePath string `json:"cache_path,omitempty"` + CacheLevels string `json:"cache_levels"` + CacheInactive string `json:"cache_inactive"` + CacheMaxSize string `json:"cache_max_size"` + CacheKeyTemplate string `json:"cache_key_template"` + CacheLockEnabled bool `json:"cache_lock_enabled"` + CacheLockTimeout string `json:"cache_lock_timeout"` + CacheUseStale string `json:"cache_use_stale"` + MainConfigTemplate string `json:"main_config_template,omitempty"` } type snapshotDocument struct { @@ -934,41 +935,42 @@ func stringSliceEqual(left []string, right []string) bool { func buildOpenRestyConfigSnapshot() openRestyConfigSnapshot { return openRestyConfigSnapshot{ - WorkerProcesses: common.OpenRestyWorkerProcesses, - WorkerConnections: common.OpenRestyWorkerConnections, - WorkerRlimitNofile: common.OpenRestyWorkerRlimitNofile, - EventsUse: common.OpenRestyEventsUse, - EventsMultiAcceptEnabled: common.OpenRestyEventsMultiAcceptEnabled, - KeepaliveTimeout: common.OpenRestyKeepaliveTimeout, - KeepaliveRequests: common.OpenRestyKeepaliveRequests, - ClientHeaderTimeout: common.OpenRestyClientHeaderTimeout, - ClientBodyTimeout: common.OpenRestyClientBodyTimeout, - ClientMaxBodySize: common.OpenRestyClientMaxBodySize, - LargeClientHeaderBuffers: common.OpenRestyLargeClientHeaderBuffers, - SendTimeout: common.OpenRestySendTimeout, - ProxyConnectTimeout: common.OpenRestyProxyConnectTimeout, - ProxySendTimeout: common.OpenRestyProxySendTimeout, - ProxyReadTimeout: common.OpenRestyProxyReadTimeout, - WebsocketEnabled: common.OpenRestyWebsocketEnabled, - ProxyRequestBuffering: common.OpenRestyProxyRequestBufferingEnabled, - ProxyBufferingEnabled: common.OpenRestyProxyBufferingEnabled, - ProxyBuffers: common.OpenRestyProxyBuffers, - ProxyBufferSize: common.OpenRestyProxyBufferSize, - ProxyBusyBuffersSize: common.OpenRestyProxyBusyBuffersSize, - GzipEnabled: common.OpenRestyGzipEnabled, - GzipMinLength: common.OpenRestyGzipMinLength, - GzipCompLevel: common.OpenRestyGzipCompLevel, - Resolvers: common.OpenRestyResolvers, - CacheEnabled: common.OpenRestyCacheEnabled, - CachePath: common.OpenRestyCachePath, - CacheLevels: common.OpenRestyCacheLevels, - CacheInactive: common.OpenRestyCacheInactive, - CacheMaxSize: common.OpenRestyCacheMaxSize, - CacheKeyTemplate: common.OpenRestyCacheKeyTemplate, - CacheLockEnabled: common.OpenRestyCacheLockEnabled, - CacheLockTimeout: common.OpenRestyCacheLockTimeout, - CacheUseStale: common.OpenRestyCacheUseStale, - MainConfigTemplate: common.OpenRestyMainConfigTemplate, + DefaultServerReturnStatus: common.OpenRestyDefaultServerReturnStatus, + WorkerProcesses: common.OpenRestyWorkerProcesses, + WorkerConnections: common.OpenRestyWorkerConnections, + WorkerRlimitNofile: common.OpenRestyWorkerRlimitNofile, + EventsUse: common.OpenRestyEventsUse, + EventsMultiAcceptEnabled: common.OpenRestyEventsMultiAcceptEnabled, + KeepaliveTimeout: common.OpenRestyKeepaliveTimeout, + KeepaliveRequests: common.OpenRestyKeepaliveRequests, + ClientHeaderTimeout: common.OpenRestyClientHeaderTimeout, + ClientBodyTimeout: common.OpenRestyClientBodyTimeout, + ClientMaxBodySize: common.OpenRestyClientMaxBodySize, + LargeClientHeaderBuffers: common.OpenRestyLargeClientHeaderBuffers, + SendTimeout: common.OpenRestySendTimeout, + ProxyConnectTimeout: common.OpenRestyProxyConnectTimeout, + ProxySendTimeout: common.OpenRestyProxySendTimeout, + ProxyReadTimeout: common.OpenRestyProxyReadTimeout, + WebsocketEnabled: common.OpenRestyWebsocketEnabled, + ProxyRequestBuffering: common.OpenRestyProxyRequestBufferingEnabled, + ProxyBufferingEnabled: common.OpenRestyProxyBufferingEnabled, + ProxyBuffers: common.OpenRestyProxyBuffers, + ProxyBufferSize: common.OpenRestyProxyBufferSize, + ProxyBusyBuffersSize: common.OpenRestyProxyBusyBuffersSize, + GzipEnabled: common.OpenRestyGzipEnabled, + GzipMinLength: common.OpenRestyGzipMinLength, + GzipCompLevel: common.OpenRestyGzipCompLevel, + Resolvers: common.OpenRestyResolvers, + CacheEnabled: common.OpenRestyCacheEnabled, + CachePath: common.OpenRestyCachePath, + CacheLevels: common.OpenRestyCacheLevels, + CacheInactive: common.OpenRestyCacheInactive, + CacheMaxSize: common.OpenRestyCacheMaxSize, + CacheKeyTemplate: common.OpenRestyCacheKeyTemplate, + CacheLockEnabled: common.OpenRestyCacheLockEnabled, + CacheLockTimeout: common.OpenRestyCacheLockTimeout, + CacheUseStale: common.OpenRestyCacheUseStale, + MainConfigTemplate: common.OpenRestyMainConfigTemplate, } } @@ -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", diff --git a/openflare_server/service/https_phase1_test.go b/openflare_server/service/https_phase1_test.go index 1fe08fce..2c6d232b 100644 --- a/openflare_server/service/https_phase1_test.go +++ b/openflare_server/service/https_phase1_test.go @@ -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") diff --git a/openflare_server/utils/render/openresty/render.go b/openflare_server/utils/render/openresty/render.go index 884ba3ff..fd94f465 100644 --- a/openflare_server/utils/render/openresty/render.go +++ b/openflare_server/utils/render/openresty/render.go @@ -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 { diff --git a/openflare_server/utils/render/openresty/types.go b/openflare_server/utils/render/openresty/types.go index c8306b8c..42be01a9 100644 --- a/openflare_server/utils/render/openresty/types.go +++ b/openflare_server/utils/render/openresty/types.go @@ -158,41 +158,42 @@ type WAFDocument struct { } type ConfigSnapshot struct { - WorkerProcesses string `json:"worker_processes"` - WorkerConnections int `json:"worker_connections"` - WorkerRlimitNofile int `json:"worker_rlimit_nofile"` - EventsUse string `json:"events_use,omitempty"` - EventsMultiAcceptEnabled bool `json:"events_multi_accept_enabled"` - KeepaliveTimeout int `json:"keepalive_timeout"` - KeepaliveRequests int `json:"keepalive_requests"` - ClientHeaderTimeout int `json:"client_header_timeout"` - ClientBodyTimeout int `json:"client_body_timeout"` - ClientMaxBodySize string `json:"client_max_body_size"` - LargeClientHeaderBuffers string `json:"large_client_header_buffers"` - SendTimeout int `json:"send_timeout"` - ProxyConnectTimeout int `json:"proxy_connect_timeout"` - ProxySendTimeout int `json:"proxy_send_timeout"` - ProxyReadTimeout int `json:"proxy_read_timeout"` - WebsocketEnabled bool `json:"websocket_enabled"` - ProxyRequestBuffering bool `json:"proxy_request_buffering"` - ProxyBufferingEnabled bool `json:"proxy_buffering_enabled"` - ProxyBuffers string `json:"proxy_buffers"` - ProxyBufferSize string `json:"proxy_buffer_size"` - ProxyBusyBuffersSize string `json:"proxy_busy_buffers_size"` - GzipEnabled bool `json:"gzip_enabled"` - GzipMinLength int `json:"gzip_min_length"` - GzipCompLevel int `json:"gzip_comp_level"` - Resolvers string `json:"resolvers,omitempty"` - CacheEnabled bool `json:"cache_enabled"` - CachePath string `json:"cache_path,omitempty"` - CacheLevels string `json:"cache_levels"` - CacheInactive string `json:"cache_inactive"` - CacheMaxSize string `json:"cache_max_size"` - CacheKeyTemplate string `json:"cache_key_template"` - CacheLockEnabled bool `json:"cache_lock_enabled"` - CacheLockTimeout string `json:"cache_lock_timeout"` - CacheUseStale string `json:"cache_use_stale"` - MainConfigTemplate string `json:"main_config_template,omitempty"` + DefaultServerReturnStatus int `json:"default_server_return_status"` + WorkerProcesses string `json:"worker_processes"` + WorkerConnections int `json:"worker_connections"` + WorkerRlimitNofile int `json:"worker_rlimit_nofile"` + EventsUse string `json:"events_use,omitempty"` + EventsMultiAcceptEnabled bool `json:"events_multi_accept_enabled"` + KeepaliveTimeout int `json:"keepalive_timeout"` + KeepaliveRequests int `json:"keepalive_requests"` + ClientHeaderTimeout int `json:"client_header_timeout"` + ClientBodyTimeout int `json:"client_body_timeout"` + ClientMaxBodySize string `json:"client_max_body_size"` + LargeClientHeaderBuffers string `json:"large_client_header_buffers"` + SendTimeout int `json:"send_timeout"` + ProxyConnectTimeout int `json:"proxy_connect_timeout"` + ProxySendTimeout int `json:"proxy_send_timeout"` + ProxyReadTimeout int `json:"proxy_read_timeout"` + WebsocketEnabled bool `json:"websocket_enabled"` + ProxyRequestBuffering bool `json:"proxy_request_buffering"` + ProxyBufferingEnabled bool `json:"proxy_buffering_enabled"` + ProxyBuffers string `json:"proxy_buffers"` + ProxyBufferSize string `json:"proxy_buffer_size"` + ProxyBusyBuffersSize string `json:"proxy_busy_buffers_size"` + GzipEnabled bool `json:"gzip_enabled"` + GzipMinLength int `json:"gzip_min_length"` + GzipCompLevel int `json:"gzip_comp_level"` + Resolvers string `json:"resolvers,omitempty"` + CacheEnabled bool `json:"cache_enabled"` + CachePath string `json:"cache_path,omitempty"` + CacheLevels string `json:"cache_levels"` + CacheInactive string `json:"cache_inactive"` + CacheMaxSize string `json:"cache_max_size"` + CacheKeyTemplate string `json:"cache_key_template"` + CacheLockEnabled bool `json:"cache_lock_enabled"` + CacheLockTimeout string `json:"cache_lock_timeout"` + CacheUseStale string `json:"cache_use_stale"` + MainConfigTemplate string `json:"main_config_template,omitempty"` } type Document struct { diff --git a/openflare_server/web/features/performance/components/performance-page.tsx b/openflare_server/web/features/performance/components/performance-page.tsx index 594c6311..85b026a2 100644 --- a/openflare_server/web/features/performance/components/performance-page.tsx +++ b/openflare_server/web/features/performance/components/performance-page.tsx @@ -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 = { + 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() { } /> + + + setPerformanceFields((previous) => ({ + ...previous, + OpenRestyDefaultServerReturnStatus: event.target.value, + })) + } + /> + diff --git a/openflare_server/web/features/settings/components/settings-page.tsx b/openflare_server/web/features/settings/components/settings-page.tsx index 2c49f424..d8b4984c 100644 --- a/openflare_server/web/features/settings/components/settings-page.tsx +++ b/openflare_server/web/features/settings/components/settings-page.tsx @@ -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',