mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-10-07 08:06:37 +08:00
[功能] 添加OpenResty解析器指令支持,增强配置模板和运行时解析能力
This commit is contained in:
@@ -73,6 +73,7 @@ func main() {
|
|||||||
NginxLuaDir: cfg.OpenrestyLuaDir,
|
NginxLuaDir: cfg.OpenrestyLuaDir,
|
||||||
OpenrestyObservabilityListen: nginx.ObservabilityListenAddress(cfg.OpenrestyPath, cfg.OpenrestyObservabilityPort),
|
OpenrestyObservabilityListen: nginx.ObservabilityListenAddress(cfg.OpenrestyPath, cfg.OpenrestyObservabilityPort),
|
||||||
OpenrestyObservabilityPort: cfg.OpenrestyObservabilityPort,
|
OpenrestyObservabilityPort: cfg.OpenrestyObservabilityPort,
|
||||||
|
OpenrestyResolverDirective: nginx.ResolverDirective(cfg.OpenrestyPath),
|
||||||
Executor: nginx.NewExecutor(nginx.ExecutorOptions{
|
Executor: nginx.NewExecutor(nginx.ExecutorOptions{
|
||||||
NginxPath: cfg.OpenrestyPath,
|
NginxPath: cfg.OpenrestyPath,
|
||||||
DockerBinary: cfg.DockerBinary,
|
DockerBinary: cfg.DockerBinary,
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ const AccessLogPlaceholder = "__OPENFLARE_ACCESS_LOG__"
|
|||||||
const LuaDirPlaceholder = "__OPENFLARE_LUA_DIR__"
|
const LuaDirPlaceholder = "__OPENFLARE_LUA_DIR__"
|
||||||
const ObservabilityListenPlaceholder = "__OPENFLARE_OBSERVABILITY_LISTEN__"
|
const ObservabilityListenPlaceholder = "__OPENFLARE_OBSERVABILITY_LISTEN__"
|
||||||
const ObservabilityPortPlaceholder = "__OPENFLARE_OBSERVABILITY_PORT__"
|
const ObservabilityPortPlaceholder = "__OPENFLARE_OBSERVABILITY_PORT__"
|
||||||
|
const ResolverDirectivePlaceholder = "__OPENFLARE_RESOLVER_DIRECTIVE__"
|
||||||
const DockerMainConfigPath = "/usr/local/openresty/nginx/conf/nginx.conf"
|
const DockerMainConfigPath = "/usr/local/openresty/nginx/conf/nginx.conf"
|
||||||
const DockerRouteConfigPath = "/etc/nginx/conf.d/openflare_routes.conf"
|
const DockerRouteConfigPath = "/etc/nginx/conf.d/openflare_routes.conf"
|
||||||
const DockerAccessLogPath = "/etc/nginx/conf.d/openflare_access.log"
|
const DockerAccessLogPath = "/etc/nginx/conf.d/openflare_access.log"
|
||||||
@@ -171,7 +172,7 @@ func (e *DockerExecutor) CheckHealth(ctx context.Context) error {
|
|||||||
return fmt.Errorf("docker inspect openresty failed: %w: %s", err, string(output))
|
return fmt.Errorf("docker inspect openresty failed: %w: %s", err, string(output))
|
||||||
}
|
}
|
||||||
if strings.TrimSpace(string(output)) != "true" {
|
if strings.TrimSpace(string(output)) != "true" {
|
||||||
return errors.New("docker openresty container is not running")
|
return e.containerNotRunningError(ctx)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -235,6 +236,46 @@ func (e *DockerExecutor) validateMountSources() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (e *DockerExecutor) containerNotRunningError(ctx context.Context) error {
|
||||||
|
inspectSummary := ""
|
||||||
|
inspectOutput, inspectErr := e.Runner.Run(ctx, e.DockerBinary, "inspect", "-f", "status={{.State.Status}} exit_code={{.State.ExitCode}} error={{printf \"%q\" .State.Error}} oom_killed={{.State.OOMKilled}} finished_at={{.State.FinishedAt}}", e.ContainerName)
|
||||||
|
if inspectErr == nil {
|
||||||
|
inspectSummary = strings.TrimSpace(string(inspectOutput))
|
||||||
|
}
|
||||||
|
|
||||||
|
logTail := ""
|
||||||
|
logOutput, logErr := e.Runner.Run(ctx, e.DockerBinary, "logs", "--tail", "50", e.ContainerName)
|
||||||
|
if logErr == nil {
|
||||||
|
logTail = strings.TrimSpace(string(logOutput))
|
||||||
|
}
|
||||||
|
|
||||||
|
message := "docker openresty container is not running"
|
||||||
|
if inspectSummary != "" {
|
||||||
|
message += ": " + inspectSummary
|
||||||
|
}
|
||||||
|
if logTail != "" {
|
||||||
|
message += "; recent logs: " + compactDiagnosticText(logTail)
|
||||||
|
}
|
||||||
|
return errors.New(message)
|
||||||
|
}
|
||||||
|
|
||||||
|
func compactDiagnosticText(text string) string {
|
||||||
|
trimmed := strings.TrimSpace(text)
|
||||||
|
if trimmed == "" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
lines := strings.Split(trimmed, "\n")
|
||||||
|
if len(lines) > 8 {
|
||||||
|
lines = lines[len(lines)-8:]
|
||||||
|
}
|
||||||
|
joined := strings.Join(lines, " | ")
|
||||||
|
joined = strings.Join(strings.Fields(joined), " ")
|
||||||
|
if len(joined) > 800 {
|
||||||
|
return joined[len(joined)-800:]
|
||||||
|
}
|
||||||
|
return joined
|
||||||
|
}
|
||||||
|
|
||||||
func ensureRegularFile(path string, label string) error {
|
func ensureRegularFile(path string, label string) error {
|
||||||
cleanPath := strings.TrimSpace(path)
|
cleanPath := strings.TrimSpace(path)
|
||||||
if cleanPath == "" {
|
if cleanPath == "" {
|
||||||
@@ -281,6 +322,7 @@ type Manager struct {
|
|||||||
NginxLuaDir string
|
NginxLuaDir string
|
||||||
OpenrestyObservabilityListen string
|
OpenrestyObservabilityListen string
|
||||||
OpenrestyObservabilityPort int
|
OpenrestyObservabilityPort int
|
||||||
|
OpenrestyResolverDirective string
|
||||||
Executor Executor
|
Executor Executor
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -406,6 +448,9 @@ func (m *Manager) CurrentChecksum() (string, error) {
|
|||||||
if m.OpenrestyObservabilityPort > 0 {
|
if m.OpenrestyObservabilityPort > 0 {
|
||||||
normalizedMain = strings.ReplaceAll(normalizedMain, fmt.Sprintf("%d", m.OpenrestyObservabilityPort), ObservabilityPortPlaceholder)
|
normalizedMain = strings.ReplaceAll(normalizedMain, fmt.Sprintf("%d", m.OpenrestyObservabilityPort), ObservabilityPortPlaceholder)
|
||||||
}
|
}
|
||||||
|
if resolverDirective := strings.TrimSpace(m.OpenrestyResolverDirective); resolverDirective != "" {
|
||||||
|
normalizedMain = strings.ReplaceAll(normalizedMain, resolverDirective, ResolverDirectivePlaceholder)
|
||||||
|
}
|
||||||
normalizedRoute := string(data)
|
normalizedRoute := string(data)
|
||||||
if m.NginxCertDir != "" {
|
if m.NginxCertDir != "" {
|
||||||
normalizedRoute = strings.ReplaceAll(normalizedRoute, m.NginxCertDir, CertDirPlaceholder)
|
normalizedRoute = strings.ReplaceAll(normalizedRoute, m.NginxCertDir, CertDirPlaceholder)
|
||||||
@@ -807,6 +852,9 @@ func (m *Manager) renderMainConfig(content string) string {
|
|||||||
if m.OpenrestyObservabilityPort > 0 {
|
if m.OpenrestyObservabilityPort > 0 {
|
||||||
rendered = strings.ReplaceAll(rendered, ObservabilityPortPlaceholder, fmt.Sprintf("%d", m.OpenrestyObservabilityPort))
|
rendered = strings.ReplaceAll(rendered, ObservabilityPortPlaceholder, fmt.Sprintf("%d", m.OpenrestyObservabilityPort))
|
||||||
}
|
}
|
||||||
|
if resolverDirective := strings.TrimSpace(m.OpenrestyResolverDirective); resolverDirective != "" {
|
||||||
|
rendered = strings.ReplaceAll(rendered, ResolverDirectivePlaceholder, resolverDirective)
|
||||||
|
}
|
||||||
return rendered
|
return rendered
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -820,6 +868,43 @@ func ObservabilityListenAddress(openrestyPath string, port int) string {
|
|||||||
return fmt.Sprintf("%d", port)
|
return fmt.Sprintf("%d", port)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ResolverDirective(openrestyPath string) string {
|
||||||
|
resolvers := resolverAddresses(openrestyPath)
|
||||||
|
if len(resolvers) == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return fmt.Sprintf(" resolver %s valid=30s ipv6=off;\n resolver_timeout 5s;\n", strings.Join(resolvers, " "))
|
||||||
|
}
|
||||||
|
|
||||||
|
func resolverAddresses(openrestyPath string) []string {
|
||||||
|
if strings.TrimSpace(openrestyPath) == "" {
|
||||||
|
return []string{"127.0.0.11"}
|
||||||
|
}
|
||||||
|
data, err := os.ReadFile("/etc/resolv.conf")
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
lines := strings.Split(string(data), "\n")
|
||||||
|
resolvers := make([]string, 0, 2)
|
||||||
|
seen := make(map[string]struct{})
|
||||||
|
for _, line := range lines {
|
||||||
|
fields := strings.Fields(strings.TrimSpace(line))
|
||||||
|
if len(fields) < 2 || fields[0] != "nameserver" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
addr := strings.TrimSpace(fields[1])
|
||||||
|
if addr == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if _, ok := seen[addr]; ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
seen[addr] = struct{}{}
|
||||||
|
resolvers = append(resolvers, addr)
|
||||||
|
}
|
||||||
|
return resolvers
|
||||||
|
}
|
||||||
|
|
||||||
func (m *Manager) routeConfigIncludePath() string {
|
func (m *Manager) routeConfigIncludePath() string {
|
||||||
if strings.TrimSpace(m.RuntimeRouteConfigPath) != "" {
|
if strings.TrimSpace(m.RuntimeRouteConfigPath) != "" {
|
||||||
return strings.TrimSpace(m.RuntimeRouteConfigPath)
|
return strings.TrimSpace(m.RuntimeRouteConfigPath)
|
||||||
|
|||||||
@@ -113,6 +113,15 @@ func TestPathExecutorRestartIgnoresMissingPID(t *testing.T) {
|
|||||||
func TestDockerExecutorCheckHealthFailsWhenContainerStopped(t *testing.T) {
|
func TestDockerExecutorCheckHealthFailsWhenContainerStopped(t *testing.T) {
|
||||||
runner := &fakeRunner{
|
runner := &fakeRunner{
|
||||||
runFn: func(name string, args ...string) ([]byte, error) {
|
runFn: func(name string, args ...string) ([]byte, error) {
|
||||||
|
if len(args) >= 4 && args[0] == "inspect" && args[2] == "{{.State.Running}}" {
|
||||||
|
return []byte("false"), nil
|
||||||
|
}
|
||||||
|
if len(args) >= 4 && args[0] == "inspect" {
|
||||||
|
return []byte("status=exited exit_code=1 error=\"\" oom_killed=false finished_at=2026-03-18T10:08:30Z"), nil
|
||||||
|
}
|
||||||
|
if len(args) >= 1 && args[0] == "logs" {
|
||||||
|
return []byte("nginx: [emerg] host not found in upstream \"c1\" in /etc/nginx/conf.d/openflare_routes.conf:30"), nil
|
||||||
|
}
|
||||||
return []byte("false"), nil
|
return []byte("false"), nil
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -130,6 +139,14 @@ func TestDockerExecutorCheckHealthFailsWhenContainerStopped(t *testing.T) {
|
|||||||
}
|
}
|
||||||
if err := executor.CheckHealth(context.Background()); err == nil {
|
if err := executor.CheckHealth(context.Background()); err == nil {
|
||||||
t.Fatal("expected CheckHealth to fail when container is not running")
|
t.Fatal("expected CheckHealth to fail when container is not running")
|
||||||
|
} else {
|
||||||
|
text := err.Error()
|
||||||
|
if !strings.Contains(text, "exit_code=1") {
|
||||||
|
t.Fatalf("expected exit code in health error, got %v", err)
|
||||||
|
}
|
||||||
|
if !strings.Contains(text, "host not found in upstream") {
|
||||||
|
t.Fatalf("expected recent docker logs in health error, got %v", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -610,10 +627,11 @@ func TestManagerApplyWritesSupportFilesAndReplacesPlaceholder(t *testing.T) {
|
|||||||
LuaDir: filepath.Join(tempDir, "lua"),
|
LuaDir: filepath.Join(tempDir, "lua"),
|
||||||
NginxLuaDir: "/etc/nginx/openflare-lua",
|
NginxLuaDir: "/etc/nginx/openflare-lua",
|
||||||
OpenrestyObservabilityListen: "18081",
|
OpenrestyObservabilityListen: "18081",
|
||||||
|
OpenrestyResolverDirective: " resolver 127.0.0.11 valid=30s ipv6=off;\n resolver_timeout 5s;\n",
|
||||||
Executor: &fakeExecutor{},
|
Executor: &fakeExecutor{},
|
||||||
}
|
}
|
||||||
|
|
||||||
err := manager.Apply(context.Background(), "include __OPENFLARE_ROUTE_CONFIG__;\nserver { listen __OPENFLARE_OBSERVABILITY_LISTEN__; }", "ssl_certificate __OPENFLARE_CERT_DIR__/1.crt;", []protocol.SupportFile{
|
err := manager.Apply(context.Background(), "include __OPENFLARE_ROUTE_CONFIG__;\n__OPENFLARE_RESOLVER_DIRECTIVE__server { listen __OPENFLARE_OBSERVABILITY_LISTEN__; }", "ssl_certificate __OPENFLARE_CERT_DIR__/1.crt;", []protocol.SupportFile{
|
||||||
{Path: "1.crt", Content: "cert-data"},
|
{Path: "1.crt", Content: "cert-data"},
|
||||||
{Path: "1.key", Content: "key-data"},
|
{Path: "1.key", Content: "key-data"},
|
||||||
})
|
})
|
||||||
@@ -635,6 +653,9 @@ func TestManagerApplyWritesSupportFilesAndReplacesPlaceholder(t *testing.T) {
|
|||||||
if !strings.Contains(string(mainData), "listen 18081;") {
|
if !strings.Contains(string(mainData), "listen 18081;") {
|
||||||
t.Fatalf("expected observability listen placeholder replacement in main config, got %s", string(mainData))
|
t.Fatalf("expected observability listen placeholder replacement in main config, got %s", string(mainData))
|
||||||
}
|
}
|
||||||
|
if !strings.Contains(string(mainData), "resolver 127.0.0.11 valid=30s ipv6=off;") {
|
||||||
|
t.Fatalf("expected resolver directive placeholder replacement in main config, got %s", string(mainData))
|
||||||
|
}
|
||||||
certData, err := os.ReadFile(filepath.Join(manager.CertDir, "1.crt"))
|
certData, err := os.ReadFile(filepath.Join(manager.CertDir, "1.crt"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to read cert file: %v", err)
|
t.Fatalf("failed to read cert file: %v", err)
|
||||||
@@ -651,6 +672,13 @@ func TestManagerApplyWritesSupportFilesAndReplacesPlaceholder(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestResolverDirectiveForDockerMode(t *testing.T) {
|
||||||
|
got := ResolverDirective("")
|
||||||
|
if !strings.Contains(got, "resolver 127.0.0.11") {
|
||||||
|
t.Fatalf("expected docker resolver directive, got %q", got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestCertFileMode(t *testing.T) {
|
func TestCertFileMode(t *testing.T) {
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
path string
|
path string
|
||||||
|
|||||||
@@ -125,7 +125,7 @@ http {
|
|||||||
gzip {{OpenRestyGzip}};
|
gzip {{OpenRestyGzip}};
|
||||||
gzip_min_length {{OpenRestyGzipMinLength}};
|
gzip_min_length {{OpenRestyGzipMinLength}};
|
||||||
gzip_comp_level {{OpenRestyGzipCompLevel}};
|
gzip_comp_level {{OpenRestyGzipCompLevel}};
|
||||||
{{OpenRestyCacheBlock}} include {{OpenRestyRouteConfigInclude}};
|
{{OpenRestyResolverDirective}}{{OpenRestyCacheBlock}} include {{OpenRestyRouteConfigInclude}};
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
|
|||||||
@@ -124,6 +124,7 @@ const (
|
|||||||
nginxLuaDirPlaceholder = "__OPENFLARE_LUA_DIR__"
|
nginxLuaDirPlaceholder = "__OPENFLARE_LUA_DIR__"
|
||||||
nginxObservabilityListenPlaceholder = "__OPENFLARE_OBSERVABILITY_LISTEN__"
|
nginxObservabilityListenPlaceholder = "__OPENFLARE_OBSERVABILITY_LISTEN__"
|
||||||
nginxObservabilityPortPlaceholder = "__OPENFLARE_OBSERVABILITY_PORT__"
|
nginxObservabilityPortPlaceholder = "__OPENFLARE_OBSERVABILITY_PORT__"
|
||||||
|
nginxResolverDirectivePlaceholder = "__OPENFLARE_RESOLVER_DIRECTIVE__"
|
||||||
)
|
)
|
||||||
|
|
||||||
var requiredMainConfigTemplatePlaceholders = []string{
|
var requiredMainConfigTemplatePlaceholders = []string{
|
||||||
@@ -151,6 +152,7 @@ var requiredMainConfigTemplatePlaceholders = []string{
|
|||||||
"{{OpenRestyGzip}}",
|
"{{OpenRestyGzip}}",
|
||||||
"{{OpenRestyGzipMinLength}}",
|
"{{OpenRestyGzipMinLength}}",
|
||||||
"{{OpenRestyGzipCompLevel}}",
|
"{{OpenRestyGzipCompLevel}}",
|
||||||
|
"{{OpenRestyResolverDirective}}",
|
||||||
"{{OpenRestyCacheBlock}}",
|
"{{OpenRestyCacheBlock}}",
|
||||||
"{{OpenRestyRouteConfigInclude}}",
|
"{{OpenRestyRouteConfigInclude}}",
|
||||||
}
|
}
|
||||||
@@ -671,6 +673,7 @@ func renderMainConfigTemplate(templateText string, cfg openRestyConfigSnapshot)
|
|||||||
"{{OpenRestyGzip}}", onOff(cfg.GzipEnabled),
|
"{{OpenRestyGzip}}", onOff(cfg.GzipEnabled),
|
||||||
"{{OpenRestyGzipMinLength}}", fmt.Sprintf("%d", cfg.GzipMinLength),
|
"{{OpenRestyGzipMinLength}}", fmt.Sprintf("%d", cfg.GzipMinLength),
|
||||||
"{{OpenRestyGzipCompLevel}}", fmt.Sprintf("%d", cfg.GzipCompLevel),
|
"{{OpenRestyGzipCompLevel}}", fmt.Sprintf("%d", cfg.GzipCompLevel),
|
||||||
|
"{{OpenRestyResolverDirective}}", nginxResolverDirectivePlaceholder,
|
||||||
"{{OpenRestyCacheBlock}}", renderOpenRestyCacheTemplateBlock(cfg),
|
"{{OpenRestyCacheBlock}}", renderOpenRestyCacheTemplateBlock(cfg),
|
||||||
"{{OpenRestyRouteConfigInclude}}", nginxRouteConfigPlaceholder,
|
"{{OpenRestyRouteConfigInclude}}", nginxRouteConfigPlaceholder,
|
||||||
)
|
)
|
||||||
@@ -750,7 +753,7 @@ func nextVersionNumber(now time.Time) (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func renderHTTPProxyServer(domain string, originURL string, originHost string, customHeaders []ProxyRouteCustomHeaderInput) string {
|
func renderHTTPProxyServer(domain string, originURL string, originHost string, customHeaders []ProxyRouteCustomHeaderInput) string {
|
||||||
return fmt.Sprintf("server {\n listen 80;\n server_name %s;\n%s\n location / {\n%s proxy_pass %s;\n }\n}\n\n", domain, renderExactHostGuard(domain), renderProxyHeaderBlock(originURL, originHost, customHeaders), originURL)
|
return fmt.Sprintf("server {\n listen 80;\n server_name %s;\n%s\n location / {\n%s%s }\n}\n\n", domain, renderExactHostGuard(domain), renderProxyHeaderBlock(originURL, originHost, customHeaders), renderProxyPassBlock(originURL))
|
||||||
}
|
}
|
||||||
|
|
||||||
func renderHTTPRedirectServer(domain string) string {
|
func renderHTTPRedirectServer(domain string) string {
|
||||||
@@ -760,7 +763,7 @@ func renderHTTPRedirectServer(domain string) string {
|
|||||||
func renderHTTPSServer(domain string, originURL string, originHost string, certificateID uint, customHeaders []ProxyRouteCustomHeaderInput) string {
|
func renderHTTPSServer(domain string, originURL string, originHost string, certificateID uint, customHeaders []ProxyRouteCustomHeaderInput) string {
|
||||||
certPath := fmt.Sprintf("%s/%s", nginxCertDirPlaceholder, certificateCertFileName(certificateID))
|
certPath := fmt.Sprintf("%s/%s", nginxCertDirPlaceholder, certificateCertFileName(certificateID))
|
||||||
keyPath := fmt.Sprintf("%s/%s", nginxCertDirPlaceholder, certificateKeyFileName(certificateID))
|
keyPath := fmt.Sprintf("%s/%s", nginxCertDirPlaceholder, certificateKeyFileName(certificateID))
|
||||||
return fmt.Sprintf("server {\n listen 443 ssl;\n server_name %s;\n ssl_certificate %s;\n ssl_certificate_key %s;\n%s\n location / {\n%s proxy_pass %s;\n }\n}\n\n", domain, certPath, keyPath, renderExactHostGuard(domain), renderProxyHeaderBlock(originURL, originHost, customHeaders), originURL)
|
return fmt.Sprintf("server {\n listen 443 ssl;\n server_name %s;\n ssl_certificate %s;\n ssl_certificate_key %s;\n%s\n location / {\n%s%s }\n}\n\n", domain, certPath, keyPath, renderExactHostGuard(domain), renderProxyHeaderBlock(originURL, originHost, customHeaders), renderProxyPassBlock(originURL))
|
||||||
}
|
}
|
||||||
|
|
||||||
func renderExactHostGuard(domain string) string {
|
func renderExactHostGuard(domain string) string {
|
||||||
@@ -795,6 +798,33 @@ func renderProxyHeaderBlock(originURL string, originHost string, customHeaders [
|
|||||||
return builder.String()
|
return builder.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func renderProxyPassBlock(originURL string) string {
|
||||||
|
parsed, err := url.Parse(originURL)
|
||||||
|
if err != nil || parsed.Host == "" || parsed.Scheme == "" {
|
||||||
|
return fmt.Sprintf(" proxy_pass %s;\n", originURL)
|
||||||
|
}
|
||||||
|
upstreamURL := fmt.Sprintf("%s://%s", parsed.Scheme, parsed.Host)
|
||||||
|
basePath := strings.TrimRight(parsed.EscapedPath(), "/")
|
||||||
|
if basePath == "" || basePath == "." {
|
||||||
|
basePath = ""
|
||||||
|
}
|
||||||
|
if parsed.RawQuery != "" {
|
||||||
|
if basePath == "" {
|
||||||
|
basePath = "/"
|
||||||
|
}
|
||||||
|
basePath += "?" + parsed.RawQuery
|
||||||
|
}
|
||||||
|
var builder strings.Builder
|
||||||
|
builder.WriteString(fmt.Sprintf(" set $openflare_upstream %s;\n", quoteNginxStringLiteral(upstreamURL)))
|
||||||
|
if basePath != "" {
|
||||||
|
builder.WriteString(fmt.Sprintf(" set $openflare_upstream_base_path %s;\n", quoteNginxStringLiteral(basePath)))
|
||||||
|
builder.WriteString(" proxy_pass $openflare_upstream$openflare_upstream_base_path$request_uri;\n")
|
||||||
|
return builder.String()
|
||||||
|
}
|
||||||
|
builder.WriteString(" proxy_pass $openflare_upstream$request_uri;\n")
|
||||||
|
return builder.String()
|
||||||
|
}
|
||||||
|
|
||||||
func resolveUpstreamServerName(originURL string, originHost string) string {
|
func resolveUpstreamServerName(originURL string, originHost string) string {
|
||||||
parsed, err := url.Parse(originURL)
|
parsed, err := url.Parse(originURL)
|
||||||
if err != nil || !strings.EqualFold(parsed.Scheme, "https") {
|
if err != nil || !strings.EqualFold(parsed.Scheme, "https") {
|
||||||
@@ -811,6 +841,10 @@ func resolveUpstreamServerName(originURL string, originHost string) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func quoteNginxHeaderValue(value string) string {
|
func quoteNginxHeaderValue(value string) string {
|
||||||
|
return quoteNginxStringLiteral(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func quoteNginxStringLiteral(value string) string {
|
||||||
escaped := strings.ReplaceAll(value, `\`, `\\`)
|
escaped := strings.ReplaceAll(value, `\`, `\\`)
|
||||||
escaped = strings.ReplaceAll(escaped, `"`, `\"`)
|
escaped = strings.ReplaceAll(escaped, `"`, `\"`)
|
||||||
return fmt.Sprintf(`"%s"`, escaped)
|
return fmt.Sprintf(`"%s"`, escaped)
|
||||||
|
|||||||
@@ -63,6 +63,9 @@ func TestCreateTLSCertificateAndRenderHTTPSConfig(t *testing.T) {
|
|||||||
if !strings.Contains(result.Version.MainConfig, "listen __OPENFLARE_OBSERVABILITY_LISTEN__;") {
|
if !strings.Contains(result.Version.MainConfig, "listen __OPENFLARE_OBSERVABILITY_LISTEN__;") {
|
||||||
t.Fatal("expected main config to include managed openresty observability listen placeholder")
|
t.Fatal("expected main config to include managed openresty observability listen placeholder")
|
||||||
}
|
}
|
||||||
|
if !strings.Contains(result.Version.MainConfig, "__OPENFLARE_RESOLVER_DIRECTIVE__") {
|
||||||
|
t.Fatal("expected main config to include managed resolver directive placeholder")
|
||||||
|
}
|
||||||
if strings.Contains(result.Version.MainConfig, "allow 127.0.0.1;") {
|
if strings.Contains(result.Version.MainConfig, "allow 127.0.0.1;") {
|
||||||
t.Fatal("expected main config to avoid hard-coded allow rules on observability server")
|
t.Fatal("expected main config to avoid hard-coded allow rules on observability server")
|
||||||
}
|
}
|
||||||
@@ -138,6 +141,12 @@ func TestPublishConfigVersionRendersCustomHeaders(t *testing.T) {
|
|||||||
if !strings.Contains(result.Version.RenderedConfig, "proxy_set_header Connection $http_connection;") {
|
if !strings.Contains(result.Version.RenderedConfig, "proxy_set_header Connection $http_connection;") {
|
||||||
t.Fatal("expected rendered config to forward websocket connection header")
|
t.Fatal("expected rendered config to forward websocket connection header")
|
||||||
}
|
}
|
||||||
|
if !strings.Contains(result.Version.RenderedConfig, `set $openflare_upstream "https://origin.internal";`) {
|
||||||
|
t.Fatal("expected rendered config to defer upstream resolution via variable proxy_pass")
|
||||||
|
}
|
||||||
|
if !strings.Contains(result.Version.RenderedConfig, "proxy_pass $openflare_upstream$request_uri;") {
|
||||||
|
t.Fatal("expected rendered config to proxy via runtime-resolved upstream variable")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPublishConfigVersionOverridesOriginHostHeader(t *testing.T) {
|
func TestPublishConfigVersionOverridesOriginHostHeader(t *testing.T) {
|
||||||
@@ -166,6 +175,9 @@ func TestPublishConfigVersionOverridesOriginHostHeader(t *testing.T) {
|
|||||||
if !strings.Contains(result.Version.RenderedConfig, `proxy_ssl_name "git.arctel.net";`) {
|
if !strings.Contains(result.Version.RenderedConfig, `proxy_ssl_name "git.arctel.net";`) {
|
||||||
t.Fatal("expected rendered config to set proxy ssl name from origin host override")
|
t.Fatal("expected rendered config to set proxy ssl name from origin host override")
|
||||||
}
|
}
|
||||||
|
if !strings.Contains(result.Version.RenderedConfig, `set $openflare_upstream "https://git.arctel.net";`) {
|
||||||
|
t.Fatal("expected rendered config to avoid resolving https upstream during config load")
|
||||||
|
}
|
||||||
if !strings.Contains(result.Version.SnapshotJSON, `"origin_host":"git.arctel.net"`) {
|
if !strings.Contains(result.Version.SnapshotJSON, `"origin_host":"git.arctel.net"`) {
|
||||||
t.Fatal("expected snapshot to include origin_host override")
|
t.Fatal("expected snapshot to include origin_host override")
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user