mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-10-13 10:26:38 +08:00
feat(openresty): render origin error page directives
Wire origin error page into OpenResty proxy route rendering: ConfigSnapshot fields, default HTML template SupportFile, proxy_intercept_errors + error_page with status-preserving internal Lua location, and Agent placeholder substitution for __OPENFLARE_ERROR_PAGE_TMPL__. Pages routes are excluded.
This commit is contained in:
@@ -522,6 +522,9 @@ func (m *Manager) CurrentChecksum() (string, error) {
|
|||||||
}
|
}
|
||||||
normalizedRoute := string(data)
|
normalizedRoute := string(data)
|
||||||
if m.NginxCertDir != "" {
|
if m.NginxCertDir != "" {
|
||||||
|
// Longer error-page path must be restored before the cert-dir prefix rewrite.
|
||||||
|
errorPagePath := filepath.ToSlash(filepath.Join(m.NginxCertDir, openrestyrender.OriginErrorPageSupportPath))
|
||||||
|
normalizedRoute = strings.ReplaceAll(normalizedRoute, errorPagePath, openrestyrender.ErrorPageTmplPlaceholder)
|
||||||
normalizedRoute = strings.ReplaceAll(normalizedRoute, m.NginxCertDir, openrestyrender.CertDirPlaceholder)
|
normalizedRoute = strings.ReplaceAll(normalizedRoute, m.NginxCertDir, openrestyrender.CertDirPlaceholder)
|
||||||
}
|
}
|
||||||
if luaDir := m.luaRuntimePath(); luaDir != "" {
|
if luaDir := m.luaRuntimePath(); luaDir != "" {
|
||||||
@@ -1375,6 +1378,8 @@ func (m *Manager) renderRouteConfig(content string) string {
|
|||||||
rendered := content
|
rendered := content
|
||||||
if m.NginxCertDir != "" {
|
if m.NginxCertDir != "" {
|
||||||
rendered = strings.ReplaceAll(rendered, openrestyrender.CertDirPlaceholder, m.NginxCertDir)
|
rendered = strings.ReplaceAll(rendered, openrestyrender.CertDirPlaceholder, m.NginxCertDir)
|
||||||
|
errorPagePath := filepath.ToSlash(filepath.Join(m.NginxCertDir, openrestyrender.OriginErrorPageSupportPath))
|
||||||
|
rendered = strings.ReplaceAll(rendered, openrestyrender.ErrorPageTmplPlaceholder, errorPagePath)
|
||||||
}
|
}
|
||||||
if luaDir := m.luaRuntimePath(); luaDir != "" {
|
if luaDir := m.luaRuntimePath(); luaDir != "" {
|
||||||
rendered = strings.ReplaceAll(rendered, openrestyrender.LuaDirPlaceholder, luaDir)
|
rendered = strings.ReplaceAll(rendered, openrestyrender.LuaDirPlaceholder, luaDir)
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import (
|
|||||||
|
|
||||||
"github.com/Rain-kl/Wavelet/internal/apps/agent/protocol"
|
"github.com/Rain-kl/Wavelet/internal/apps/agent/protocol"
|
||||||
sharedprotocol "github.com/Rain-kl/Wavelet/pkg/protocol"
|
sharedprotocol "github.com/Rain-kl/Wavelet/pkg/protocol"
|
||||||
|
openrestyrender "github.com/Rain-kl/Wavelet/pkg/render/openresty"
|
||||||
)
|
)
|
||||||
|
|
||||||
type runCall struct {
|
type runCall struct {
|
||||||
@@ -299,13 +300,17 @@ func TestManagerApplyWritesSupportFilesAndReplacesPlaceholder(t *testing.T) {
|
|||||||
if !strings.Contains(string(routeData), "/etc/nginx/openflare-certs/1.crt") {
|
if !strings.Contains(string(routeData), "/etc/nginx/openflare-certs/1.crt") {
|
||||||
t.Fatalf("expected placeholder replacement in route config, got %s", string(routeData))
|
t.Fatalf("expected placeholder replacement in route config, got %s", string(routeData))
|
||||||
}
|
}
|
||||||
renderedRoute := manager.renderRouteConfig("access_by_lua_file __OPENFLARE_LUA_DIR__/pow/check.lua;\nlocation /.within.website/x/cmd/anubis/static/ { alias __OPENFLARE_POW_STATIC_DIR__/; }\n")
|
renderedRoute := manager.renderRouteConfig("access_by_lua_file __OPENFLARE_LUA_DIR__/pow/check.lua;\nlocation /.within.website/x/cmd/anubis/static/ { alias __OPENFLARE_POW_STATIC_DIR__/; }\nio.open(\"__OPENFLARE_ERROR_PAGE_TMPL__\", \"r\")\n")
|
||||||
if !strings.Contains(renderedRoute, "access_by_lua_file /etc/nginx/openflare-lua/pow/check.lua;") {
|
if !strings.Contains(renderedRoute, "access_by_lua_file /etc/nginx/openflare-lua/pow/check.lua;") {
|
||||||
t.Fatalf("expected lua dir placeholder replacement in route config, got %s", renderedRoute)
|
t.Fatalf("expected lua dir placeholder replacement in route config, got %s", renderedRoute)
|
||||||
}
|
}
|
||||||
if !strings.Contains(renderedRoute, "alias /etc/nginx/openflare-lua/pow/static/;") {
|
if !strings.Contains(renderedRoute, "alias /etc/nginx/openflare-lua/pow/static/;") {
|
||||||
t.Fatalf("expected pow static dir placeholder replacement in route config, got %s", renderedRoute)
|
t.Fatalf("expected pow static dir placeholder replacement in route config, got %s", renderedRoute)
|
||||||
}
|
}
|
||||||
|
wantErrorPagePath := filepath.ToSlash(filepath.Join(manager.NginxCertDir, openrestyrender.OriginErrorPageSupportPath))
|
||||||
|
if !strings.Contains(renderedRoute, `io.open("`+wantErrorPagePath+`", "r")`) {
|
||||||
|
t.Fatalf("expected error page template placeholder replacement, got %s", renderedRoute)
|
||||||
|
}
|
||||||
mainData, err := os.ReadFile(manager.MainConfigPath)
|
mainData, err := os.ReadFile(manager.MainConfigPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to read main config: %v", err)
|
t.Fatalf("failed to read main config: %v", err)
|
||||||
|
|||||||
@@ -0,0 +1,172 @@
|
|||||||
|
package openresty
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// OriginErrorPageSupportPath is the SupportFile path for the origin error HTML template.
|
||||||
|
OriginErrorPageSupportPath = "error_pages/origin_error.html.tmpl"
|
||||||
|
|
||||||
|
// OriginErrorPageInternalLocation is the internal nginx location that serves the error body.
|
||||||
|
OriginErrorPageInternalLocation = "/__openflare_origin_error"
|
||||||
|
|
||||||
|
defaultOriginErrorPageStatusTag = "500-599"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DefaultOriginErrorPageHTML is the built-in Cloudflare-style origin error page.
|
||||||
|
// Placeholders {{status}} and {{host}} are substituted at request time by Lua.
|
||||||
|
const DefaultOriginErrorPageHTML = `<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>{{status}} | 源站暂时无法提供服务</title>
|
||||||
|
<style>
|
||||||
|
:root { color-scheme: light dark; }
|
||||||
|
* { box-sizing: border-box; }
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
min-height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif;
|
||||||
|
background: #f6f7f9;
|
||||||
|
color: #1f2937;
|
||||||
|
}
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
body { background: #0f1419; color: #e5e7eb; }
|
||||||
|
.card { background: #1a2332; border-color: #2d3a4d; box-shadow: none; }
|
||||||
|
.status { color: #f3f4f6; }
|
||||||
|
.muted { color: #9ca3af; }
|
||||||
|
.host { background: #243044; color: #d1d5db; }
|
||||||
|
}
|
||||||
|
.card {
|
||||||
|
width: min(32rem, calc(100% - 2rem));
|
||||||
|
padding: 2.5rem 2rem;
|
||||||
|
border-radius: 12px;
|
||||||
|
background: #fff;
|
||||||
|
border: 1px solid #e5e7eb;
|
||||||
|
box-shadow: 0 10px 30px rgba(15, 23, 42, 0.06);
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.status {
|
||||||
|
margin: 0;
|
||||||
|
font-size: clamp(3.5rem, 12vw, 5.5rem);
|
||||||
|
font-weight: 700;
|
||||||
|
letter-spacing: -0.04em;
|
||||||
|
line-height: 1;
|
||||||
|
color: #111827;
|
||||||
|
}
|
||||||
|
h1 {
|
||||||
|
margin: 1rem 0 0.5rem;
|
||||||
|
font-size: 1.25rem;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
p { margin: 0.4rem 0; line-height: 1.6; }
|
||||||
|
.muted { color: #6b7280; font-size: 0.95rem; }
|
||||||
|
.host {
|
||||||
|
display: inline-block;
|
||||||
|
margin-top: 1.25rem;
|
||||||
|
padding: 0.35rem 0.75rem;
|
||||||
|
border-radius: 999px;
|
||||||
|
background: #f3f4f6;
|
||||||
|
color: #374151;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main class="card" role="main">
|
||||||
|
<p class="status" aria-label="HTTP status">{{status}}</p>
|
||||||
|
<h1>源站暂时无法提供服务</h1>
|
||||||
|
<p class="muted">网关已拦截源站错误响应。请稍后重试;若问题持续,请联系站点管理员。</p>
|
||||||
|
<p class="host">{{host}}</p>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
`
|
||||||
|
|
||||||
|
// EffectiveOriginErrorPageHTML returns custom HTML when set, otherwise the built-in default.
|
||||||
|
func EffectiveOriginErrorPageHTML(cfg ConfigSnapshot) string {
|
||||||
|
if strings.TrimSpace(cfg.OriginErrorPageHTML) == "" {
|
||||||
|
return DefaultOriginErrorPageHTML
|
||||||
|
}
|
||||||
|
return cfg.OriginErrorPageHTML
|
||||||
|
}
|
||||||
|
|
||||||
|
func effectiveOriginErrorPageStatusTags(cfg ConfigSnapshot) []string {
|
||||||
|
if len(cfg.OriginErrorPageStatusCodes) == 0 {
|
||||||
|
return []string{defaultOriginErrorPageStatusTag}
|
||||||
|
}
|
||||||
|
return cfg.OriginErrorPageStatusCodes
|
||||||
|
}
|
||||||
|
|
||||||
|
func originErrorPageSupportFile(cfg ConfigSnapshot) SupportFile {
|
||||||
|
return SupportFile{
|
||||||
|
Path: OriginErrorPageSupportPath,
|
||||||
|
Content: EffectiveOriginErrorPageHTML(cfg),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func renderOriginErrorPageIntercept(cfg ConfigSnapshot) string {
|
||||||
|
if !cfg.OriginErrorPageEnabled {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
if _, err := ExpandStatusCodeTags(effectiveOriginErrorPageStatusTags(cfg)); err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return " proxy_intercept_errors on;\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
// renderOriginErrorPageServerBits emits server-level error_page + internal location.
|
||||||
|
// Returns empty string when disabled, expand fails, or no codes remain.
|
||||||
|
func renderOriginErrorPageServerBits(cfg ConfigSnapshot) string {
|
||||||
|
if !cfg.OriginErrorPageEnabled {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
codes, err := ExpandStatusCodeTags(effectiveOriginErrorPageStatusTags(cfg))
|
||||||
|
if err != nil || len(codes) == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
parts := make([]string, len(codes))
|
||||||
|
for i, code := range codes {
|
||||||
|
parts[i] = strconv.Itoa(code)
|
||||||
|
}
|
||||||
|
var builder strings.Builder
|
||||||
|
fmt.Fprintf(&builder, " error_page %s = %s;\n", strings.Join(parts, " "), OriginErrorPageInternalLocation)
|
||||||
|
builder.WriteString(renderOriginErrorPageInternalLocation())
|
||||||
|
return builder.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func renderOriginErrorPageInternalLocation() string {
|
||||||
|
return fmt.Sprintf(` location = %s {
|
||||||
|
internal;
|
||||||
|
default_type text/html;
|
||||||
|
charset utf-8;
|
||||||
|
content_by_lua_block {
|
||||||
|
local f = io.open("%s", "r")
|
||||||
|
if not f then
|
||||||
|
ngx.status = ngx.status
|
||||||
|
ngx.say("Error ", ngx.status)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local body = f:read("*a")
|
||||||
|
f:close()
|
||||||
|
local status = tostring(ngx.status)
|
||||||
|
local host = ngx.var.host or ""
|
||||||
|
body = body:gsub("{{status}}", status, 1)
|
||||||
|
body = body:gsub("{{host}}", host, 1)
|
||||||
|
body = body:gsub("{{status}}", status)
|
||||||
|
body = body:gsub("{{host}}", host)
|
||||||
|
ngx.header["Content-Type"] = "text/html; charset=utf-8"
|
||||||
|
ngx.say(body)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`, OriginErrorPageInternalLocation, ErrorPageTmplPlaceholder)
|
||||||
|
}
|
||||||
@@ -0,0 +1,185 @@
|
|||||||
|
package openresty
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestRenderOriginErrorPageEnabled(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
doc := Document{
|
||||||
|
Routes: []Route{{
|
||||||
|
ID: 1, SiteName: "ex", Domains: []string{"ex.test"},
|
||||||
|
OriginURL: "http://127.0.0.1:9", Enabled: true,
|
||||||
|
}},
|
||||||
|
OpenRestyConfig: ConfigSnapshot{
|
||||||
|
OriginErrorPageEnabled: true,
|
||||||
|
OriginErrorPageStatusCodes: []string{"500-599"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
out, err := RenderRouteConfig(doc, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if !strings.Contains(out, "proxy_intercept_errors on") {
|
||||||
|
t.Fatal("missing intercept")
|
||||||
|
}
|
||||||
|
if !strings.Contains(out, "error_page") || !strings.Contains(out, "/__openflare_origin_error") {
|
||||||
|
t.Fatal("missing error_page")
|
||||||
|
}
|
||||||
|
if !strings.Contains(out, "error_page 500") {
|
||||||
|
t.Fatalf("expected expanded status codes in error_page, got:\n%s", out)
|
||||||
|
}
|
||||||
|
if !strings.Contains(out, "= /__openflare_origin_error") {
|
||||||
|
t.Fatal("error_page must keep original status via = redirect form")
|
||||||
|
}
|
||||||
|
if !strings.Contains(out, ErrorPageTmplPlaceholder) {
|
||||||
|
t.Fatal("missing error page template placeholder")
|
||||||
|
}
|
||||||
|
res, err := Render(doc, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
found := false
|
||||||
|
for _, f := range res.SupportFiles {
|
||||||
|
if f.Path == OriginErrorPageSupportPath {
|
||||||
|
found = true
|
||||||
|
if !strings.Contains(f.Content, "{{status}}") {
|
||||||
|
t.Fatal("template missing placeholder")
|
||||||
|
}
|
||||||
|
if !strings.Contains(f.Content, "{{host}}") {
|
||||||
|
t.Fatal("template missing host placeholder")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
t.Fatal("missing support file")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRenderOriginErrorPageDisabled(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
doc := Document{
|
||||||
|
Routes: []Route{{
|
||||||
|
ID: 1, SiteName: "ex", Domains: []string{"ex.test"},
|
||||||
|
OriginURL: "http://127.0.0.1:9", Enabled: true,
|
||||||
|
}},
|
||||||
|
OpenRestyConfig: ConfigSnapshot{OriginErrorPageEnabled: false},
|
||||||
|
}
|
||||||
|
out, err := RenderRouteConfig(doc, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if strings.Contains(out, "proxy_intercept_errors") {
|
||||||
|
t.Fatal("should not intercept when disabled")
|
||||||
|
}
|
||||||
|
if strings.Contains(out, "/__openflare_origin_error") {
|
||||||
|
t.Fatal("should not emit internal error location when disabled")
|
||||||
|
}
|
||||||
|
res, err := Render(doc, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
for _, f := range res.SupportFiles {
|
||||||
|
if f.Path == OriginErrorPageSupportPath {
|
||||||
|
t.Fatal("should not emit support file when disabled")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRenderOriginErrorPageDefaultsEmptyHTMLAndStatusCodes(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
doc := Document{
|
||||||
|
Routes: []Route{{
|
||||||
|
ID: 1, SiteName: "ex", Domains: []string{"ex.test"},
|
||||||
|
OriginURL: "http://127.0.0.1:9", Enabled: true,
|
||||||
|
}},
|
||||||
|
OpenRestyConfig: ConfigSnapshot{
|
||||||
|
OriginErrorPageEnabled: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
out, err := RenderRouteConfig(doc, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if !strings.Contains(out, "error_page 500") {
|
||||||
|
t.Fatalf("empty status codes should default to 500-599, got:\n%s", out)
|
||||||
|
}
|
||||||
|
html := EffectiveOriginErrorPageHTML(doc.OpenRestyConfig)
|
||||||
|
if html != DefaultOriginErrorPageHTML {
|
||||||
|
t.Fatal("empty HTML should use default template")
|
||||||
|
}
|
||||||
|
if !strings.Contains(html, "{{status}}") || !strings.Contains(html, "{{host}}") {
|
||||||
|
t.Fatal("default HTML must include placeholders")
|
||||||
|
}
|
||||||
|
if !strings.Contains(html, "源站暂时无法提供服务") {
|
||||||
|
t.Fatal("default HTML missing neutral copy")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRenderOriginErrorPageCustomHTMLInSupportFile(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
custom := "<html><body>custom {{status}} @ {{host}}</body></html>"
|
||||||
|
doc := Document{
|
||||||
|
Routes: []Route{{
|
||||||
|
ID: 1, SiteName: "ex", Domains: []string{"ex.test"},
|
||||||
|
OriginURL: "http://127.0.0.1:9", Enabled: true,
|
||||||
|
}},
|
||||||
|
OpenRestyConfig: ConfigSnapshot{
|
||||||
|
OriginErrorPageEnabled: true,
|
||||||
|
OriginErrorPageStatusCodes: []string{"502"},
|
||||||
|
OriginErrorPageHTML: custom,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
res, err := Render(doc, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
found := false
|
||||||
|
for _, f := range res.SupportFiles {
|
||||||
|
if f.Path == OriginErrorPageSupportPath {
|
||||||
|
found = true
|
||||||
|
if f.Content != custom {
|
||||||
|
t.Fatalf("support file content = %q, want custom HTML", f.Content)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
t.Fatal("missing support file")
|
||||||
|
}
|
||||||
|
out, err := RenderRouteConfig(doc, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if !strings.Contains(out, "error_page 502 = /__openflare_origin_error") {
|
||||||
|
t.Fatalf("expected single 502 error_page, got:\n%s", out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRenderOriginErrorPageSkipsPagesRoutes(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
doc := Document{
|
||||||
|
Routes: []Route{{
|
||||||
|
ID: 1, SiteName: "pages", Domains: []string{"pages.test"},
|
||||||
|
UpstreamType: "pages", Enabled: true,
|
||||||
|
PagesDeployment: &PagesDeployment{
|
||||||
|
ProjectID: 1, LocalRoot: PagesDirPlaceholder + "/projects/1/current",
|
||||||
|
EntryFile: "index.html",
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
OpenRestyConfig: ConfigSnapshot{
|
||||||
|
OriginErrorPageEnabled: true,
|
||||||
|
OriginErrorPageStatusCodes: []string{"500-599"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
out, err := RenderRouteConfig(doc, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if strings.Contains(out, "proxy_intercept_errors") {
|
||||||
|
t.Fatal("pages routes must not get proxy_intercept_errors")
|
||||||
|
}
|
||||||
|
if strings.Contains(out, "/__openflare_origin_error") {
|
||||||
|
t.Fatal("pages routes must not get origin error location")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -46,6 +46,9 @@ func Render(doc Document, certificateFiles []SupportFile) (*Result, error) {
|
|||||||
}
|
}
|
||||||
files := append([]SupportFile(nil), certificateFiles...)
|
files := append([]SupportFile(nil), certificateFiles...)
|
||||||
files = append(files, SupportFile{Path: "waf_config.json", Content: wafConfig})
|
files = append(files, SupportFile{Path: "waf_config.json", Content: wafConfig})
|
||||||
|
if doc.OpenRestyConfig.OriginErrorPageEnabled {
|
||||||
|
files = append(files, originErrorPageSupportFile(doc.OpenRestyConfig))
|
||||||
|
}
|
||||||
files = DedupeSupportFiles(files)
|
files = DedupeSupportFiles(files)
|
||||||
return &Result{
|
return &Result{
|
||||||
MainConfig: mainConfig,
|
MainConfig: mainConfig,
|
||||||
@@ -270,7 +273,7 @@ func renderOpenRestyObservabilityTemplateBlock() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func renderHTTPProxyServer(serverNames string, siteName string, originURL string, originHost string, customHeaders []CustomHeader, cacheConfig routeCacheConfig, limitConfig routeLimitConfig, upstreamConfig routeUpstreamConfig, powEnabled bool, basicAuthEnabled bool, basicAuthUsername string, basicAuthPassword string, cfg ConfigSnapshot) string {
|
func renderHTTPProxyServer(serverNames string, siteName string, originURL string, originHost string, customHeaders []CustomHeader, cacheConfig routeCacheConfig, limitConfig routeLimitConfig, upstreamConfig routeUpstreamConfig, powEnabled bool, basicAuthEnabled bool, basicAuthUsername string, basicAuthPassword string, cfg ConfigSnapshot) string {
|
||||||
return fmt.Sprintf("server {\n listen 80;\n server_name %s;\n%s%s location / {\n%s%s%s%s%s }\n%s}\n\n", serverNames, renderAccessBlock(siteName, powEnabled), renderPowLocationBlocks(powEnabled), renderBasicAuthBlock(basicAuthEnabled, basicAuthUsername, basicAuthPassword), renderProxyHeaderBlock(originURL, originHost, customHeaders, upstreamConfig, cfg), renderRouteLimitBlock(limitConfig), renderRouteCacheBlock(cacheConfig, cfg), renderProxyPassBlock(originURL, upstreamConfig), renderPowStaticLocationBlock(powEnabled))
|
return fmt.Sprintf("server {\n listen 80;\n server_name %s;\n%s%s location / {\n%s%s%s%s%s%s }\n%s%s}\n\n", serverNames, renderAccessBlock(siteName, powEnabled), renderPowLocationBlocks(powEnabled), renderBasicAuthBlock(basicAuthEnabled, basicAuthUsername, basicAuthPassword), renderProxyHeaderBlock(originURL, originHost, customHeaders, upstreamConfig, cfg), renderRouteLimitBlock(limitConfig), renderRouteCacheBlock(cacheConfig, cfg), renderOriginErrorPageIntercept(cfg), renderProxyPassBlock(originURL, upstreamConfig), renderOriginErrorPageServerBits(cfg), renderPowStaticLocationBlock(powEnabled))
|
||||||
}
|
}
|
||||||
|
|
||||||
func renderPagesAPIProxyLocationBlock(deployment *PagesDeployment) string {
|
func renderPagesAPIProxyLocationBlock(deployment *PagesDeployment) string {
|
||||||
@@ -333,7 +336,7 @@ func renderHTTPSServer(serverNames string, siteName string, originURL string, or
|
|||||||
h3Listen = " listen 443 quic;\n"
|
h3Listen = " listen 443 quic;\n"
|
||||||
h3Header = " add_header Alt-Svc 'h3=\":443\"; ma=86400';\n"
|
h3Header = " add_header Alt-Svc 'h3=\":443\"; ma=86400';\n"
|
||||||
}
|
}
|
||||||
return fmt.Sprintf("server {\n listen 443 ssl;\n%s http2 on;\n server_name %s;\n ssl_certificate %s;\n ssl_certificate_key %s;\n%s%s%s location / {\n%s%s%s%s%s }\n%s}\n\n", h3Listen, serverNames, certPath, keyPath, h3Header, renderAccessBlock(siteName, powEnabled), renderPowLocationBlocks(powEnabled), renderBasicAuthBlock(basicAuthEnabled, basicAuthUsername, basicAuthPassword), renderProxyHeaderBlock(originURL, originHost, customHeaders, upstreamConfig, cfg), renderRouteLimitBlock(limitConfig), renderRouteCacheBlock(cacheConfig, cfg), renderProxyPassBlock(originURL, upstreamConfig), renderPowStaticLocationBlock(powEnabled))
|
return fmt.Sprintf("server {\n listen 443 ssl;\n%s http2 on;\n server_name %s;\n ssl_certificate %s;\n ssl_certificate_key %s;\n%s%s%s location / {\n%s%s%s%s%s%s }\n%s%s}\n\n", h3Listen, serverNames, certPath, keyPath, h3Header, renderAccessBlock(siteName, powEnabled), renderPowLocationBlocks(powEnabled), renderBasicAuthBlock(basicAuthEnabled, basicAuthUsername, basicAuthPassword), renderProxyHeaderBlock(originURL, originHost, customHeaders, upstreamConfig, cfg), renderRouteLimitBlock(limitConfig), renderRouteCacheBlock(cacheConfig, cfg), renderOriginErrorPageIntercept(cfg), renderProxyPassBlock(originURL, upstreamConfig), renderOriginErrorPageServerBits(cfg), renderPowStaticLocationBlock(powEnabled))
|
||||||
}
|
}
|
||||||
|
|
||||||
func renderHTTPSPagesServer(serverNames string, siteName string, certificateID uint, deployment *PagesDeployment, limitConfig routeLimitConfig, powEnabled bool, basicAuthEnabled bool, basicAuthUsername string, basicAuthPassword string, cfg ConfigSnapshot) string {
|
func renderHTTPSPagesServer(serverNames string, siteName string, certificateID uint, deployment *PagesDeployment, limitConfig routeLimitConfig, powEnabled bool, basicAuthEnabled bool, basicAuthUsername string, basicAuthPassword string, cfg ConfigSnapshot) string {
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ const (
|
|||||||
ObservabilityPortPlaceholder = "__OPENFLARE_OBSERVABILITY_PORT__"
|
ObservabilityPortPlaceholder = "__OPENFLARE_OBSERVABILITY_PORT__"
|
||||||
PowStaticDirPlaceholder = "__OPENFLARE_POW_STATIC_DIR__"
|
PowStaticDirPlaceholder = "__OPENFLARE_POW_STATIC_DIR__"
|
||||||
PagesDirPlaceholder = "__OPENFLARE_PAGES_DIR__"
|
PagesDirPlaceholder = "__OPENFLARE_PAGES_DIR__"
|
||||||
|
ErrorPageTmplPlaceholder = "__OPENFLARE_ERROR_PAGE_TMPL__"
|
||||||
|
|
||||||
SourceConfigFileName = "openresty_config.json"
|
SourceConfigFileName = "openresty_config.json"
|
||||||
)
|
)
|
||||||
@@ -273,47 +274,50 @@ type WAFDocument struct {
|
|||||||
// ConfigSnapshot holds the full set of OpenResty tuning parameters that are
|
// ConfigSnapshot holds the full set of OpenResty tuning parameters that are
|
||||||
// rendered into the nginx main configuration template.
|
// rendered into the nginx main configuration template.
|
||||||
type ConfigSnapshot struct {
|
type ConfigSnapshot struct {
|
||||||
DefaultServerReturnStatus int `json:"default_server_return_status"`
|
DefaultServerReturnStatus int `json:"default_server_return_status"`
|
||||||
WorkerProcesses string `json:"worker_processes"`
|
WorkerProcesses string `json:"worker_processes"`
|
||||||
WorkerConnections int `json:"worker_connections"`
|
WorkerConnections int `json:"worker_connections"`
|
||||||
WorkerRlimitNofile int `json:"worker_rlimit_nofile"`
|
WorkerRlimitNofile int `json:"worker_rlimit_nofile"`
|
||||||
EventsUse string `json:"events_use,omitempty"`
|
EventsUse string `json:"events_use,omitempty"`
|
||||||
EventsMultiAcceptEnabled bool `json:"events_multi_accept_enabled"`
|
EventsMultiAcceptEnabled bool `json:"events_multi_accept_enabled"`
|
||||||
KeepaliveTimeout int `json:"keepalive_timeout"`
|
KeepaliveTimeout int `json:"keepalive_timeout"`
|
||||||
KeepaliveRequests int `json:"keepalive_requests"`
|
KeepaliveRequests int `json:"keepalive_requests"`
|
||||||
ClientHeaderTimeout int `json:"client_header_timeout"`
|
ClientHeaderTimeout int `json:"client_header_timeout"`
|
||||||
ClientBodyTimeout int `json:"client_body_timeout"`
|
ClientBodyTimeout int `json:"client_body_timeout"`
|
||||||
ClientMaxBodySize string `json:"client_max_body_size"`
|
ClientMaxBodySize string `json:"client_max_body_size"`
|
||||||
LargeClientHeaderBuffers string `json:"large_client_header_buffers"`
|
LargeClientHeaderBuffers string `json:"large_client_header_buffers"`
|
||||||
SendTimeout int `json:"send_timeout"`
|
SendTimeout int `json:"send_timeout"`
|
||||||
ProxyConnectTimeout int `json:"proxy_connect_timeout"`
|
ProxyConnectTimeout int `json:"proxy_connect_timeout"`
|
||||||
ProxySendTimeout int `json:"proxy_send_timeout"`
|
ProxySendTimeout int `json:"proxy_send_timeout"`
|
||||||
ProxyReadTimeout int `json:"proxy_read_timeout"`
|
ProxyReadTimeout int `json:"proxy_read_timeout"`
|
||||||
WebsocketEnabled bool `json:"websocket_enabled"`
|
WebsocketEnabled bool `json:"websocket_enabled"`
|
||||||
HTTP3Enabled bool `json:"http3_enabled"`
|
HTTP3Enabled bool `json:"http3_enabled"`
|
||||||
ProxyRequestBuffering bool `json:"proxy_request_buffering"`
|
ProxyRequestBuffering bool `json:"proxy_request_buffering"`
|
||||||
ProxyBufferingEnabled bool `json:"proxy_buffering_enabled"`
|
ProxyBufferingEnabled bool `json:"proxy_buffering_enabled"`
|
||||||
ProxyBuffers string `json:"proxy_buffers"`
|
ProxyBuffers string `json:"proxy_buffers"`
|
||||||
ProxyBufferSize string `json:"proxy_buffer_size"`
|
ProxyBufferSize string `json:"proxy_buffer_size"`
|
||||||
ProxyBusyBuffersSize string `json:"proxy_busy_buffers_size"`
|
ProxyBusyBuffersSize string `json:"proxy_busy_buffers_size"`
|
||||||
GzipEnabled bool `json:"gzip_enabled"`
|
GzipEnabled bool `json:"gzip_enabled"`
|
||||||
GzipMinLength int `json:"gzip_min_length"`
|
GzipMinLength int `json:"gzip_min_length"`
|
||||||
GzipCompLevel int `json:"gzip_comp_level"`
|
GzipCompLevel int `json:"gzip_comp_level"`
|
||||||
Resolvers string `json:"resolvers,omitempty"`
|
Resolvers string `json:"resolvers,omitempty"`
|
||||||
CacheEnabled bool `json:"cache_enabled"`
|
CacheEnabled bool `json:"cache_enabled"`
|
||||||
CachePath string `json:"cache_path,omitempty"`
|
CachePath string `json:"cache_path,omitempty"`
|
||||||
CacheLevels string `json:"cache_levels"`
|
CacheLevels string `json:"cache_levels"`
|
||||||
CacheInactive string `json:"cache_inactive"`
|
CacheInactive string `json:"cache_inactive"`
|
||||||
CacheMaxSize string `json:"cache_max_size"`
|
CacheMaxSize string `json:"cache_max_size"`
|
||||||
CacheKeyTemplate string `json:"cache_key_template"`
|
CacheKeyTemplate string `json:"cache_key_template"`
|
||||||
CacheLockEnabled bool `json:"cache_lock_enabled"`
|
CacheLockEnabled bool `json:"cache_lock_enabled"`
|
||||||
CacheLockTimeout string `json:"cache_lock_timeout"`
|
CacheLockTimeout string `json:"cache_lock_timeout"`
|
||||||
CacheUseStale string `json:"cache_use_stale"`
|
CacheUseStale string `json:"cache_use_stale"`
|
||||||
MainConfigTemplate string `json:"main_config_template,omitempty"`
|
MainConfigTemplate string `json:"main_config_template,omitempty"`
|
||||||
DefaultLimitConnPerServer int `json:"default_limit_conn_per_server,omitempty"`
|
DefaultLimitConnPerServer int `json:"default_limit_conn_per_server,omitempty"`
|
||||||
DefaultLimitConnPerIP int `json:"default_limit_conn_per_ip,omitempty"`
|
DefaultLimitConnPerIP int `json:"default_limit_conn_per_ip,omitempty"`
|
||||||
DefaultLimitRate string `json:"default_limit_rate,omitempty"`
|
DefaultLimitRate string `json:"default_limit_rate,omitempty"`
|
||||||
DefaultLimitReqPerIP string `json:"default_limit_req_per_ip,omitempty"`
|
DefaultLimitReqPerIP string `json:"default_limit_req_per_ip,omitempty"`
|
||||||
|
OriginErrorPageEnabled bool `json:"origin_error_page_enabled"`
|
||||||
|
OriginErrorPageStatusCodes []string `json:"origin_error_page_status_codes,omitempty"`
|
||||||
|
OriginErrorPageHTML string `json:"origin_error_page_html,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Document is the top-level input structure for the OpenResty renderer,
|
// Document is the top-level input structure for the OpenResty renderer,
|
||||||
|
|||||||
Reference in New Issue
Block a user