WebSocket 把 proxy_http_version 1.1 + Upgrade/Connection 移进 location / 内部
CI / verify (push) Canceled after 0s
CI / verify (push) Canceled after 0s
This commit is contained in:
@@ -81,6 +81,16 @@ func TestNginxRouteSharedTemplateSemantics(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
denyIdx := strings.Index(out, "deny 8.8.8.8;")
|
denyIdx := strings.Index(out, "deny 8.8.8.8;")
|
||||||
|
// WebSocket 头必须落在 location 内部:location 自定义 proxy_set_header 后,
|
||||||
|
// server 级的那组不会被继承,写在 server 块里等于没生效(升级请求会一直挂起)
|
||||||
|
locationIdx := strings.Index(out, "location / {")
|
||||||
|
upgradeIdx := strings.Index(out, "proxy_set_header Upgrade $http_upgrade;")
|
||||||
|
if locationIdx < 0 || upgradeIdx < 0 || upgradeIdx < locationIdx {
|
||||||
|
t.Fatalf("Upgrade 头必须写在 location 内:\n%s", out)
|
||||||
|
}
|
||||||
|
if !strings.Contains(out, "proxy_read_timeout 3600s;") {
|
||||||
|
t.Fatalf("WebSocket 未配读超时时应给长连接默认超时:\n%s", out)
|
||||||
|
}
|
||||||
allowIdx := strings.Index(out, "allow 192.168.1.0/24;")
|
allowIdx := strings.Index(out, "allow 192.168.1.0/24;")
|
||||||
denyAllIdx := strings.Index(out, "deny all;")
|
denyAllIdx := strings.Index(out, "deny all;")
|
||||||
if denyIdx < 0 || allowIdx < 0 || denyAllIdx < 0 || !(denyIdx < allowIdx && allowIdx < denyAllIdx) {
|
if denyIdx < 0 || allowIdx < 0 || denyAllIdx < 0 || !(denyIdx < allowIdx && allowIdx < denyAllIdx) {
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ import (
|
|||||||
"github.com/havline/havline/internal/sysinfo"
|
"github.com/havline/havline/internal/sysinfo"
|
||||||
)
|
)
|
||||||
|
|
||||||
const AgentVersion = "0.15.3"
|
const AgentVersion = "0.15.4"
|
||||||
|
|
||||||
// Config 是 havline-agent 的运行配置。agent 只在 VPS 本地管理自己的 Nginx 片段、证书和 frps 配置。
|
// Config 是 havline-agent 的运行配置。agent 只在 VPS 本地管理自己的 Nginx 片段、证书和 frps 配置。
|
||||||
type Config struct {
|
type Config struct {
|
||||||
@@ -1656,10 +1656,6 @@ func nginxRoute(req routeRequest, certDir string) string {
|
|||||||
if req.ConnLimitMax > 0 {
|
if req.ConnLimitMax > 0 {
|
||||||
b.WriteString(" limit_conn_status 503;\n")
|
b.WriteString(" limit_conn_status 503;\n")
|
||||||
}
|
}
|
||||||
if req.WebSocket {
|
|
||||||
b.WriteString(" proxy_http_version 1.1;\n")
|
|
||||||
b.WriteString(nginxtmpl.UpgradeHeaders(" ", "\"upgrade\""))
|
|
||||||
}
|
|
||||||
// IP 访问控制:白名单(allow + deny all)与黑名单(deny)可同时生效;
|
// IP 访问控制:白名单(allow + deny all)与黑名单(deny)可同时生效;
|
||||||
// 顺序由共享模板统一:deny → 仅大陆规则 → allow → deny all。
|
// 顺序由共享模板统一:deny → 仅大陆规则 → allow → deny all。
|
||||||
chinaDeny := ""
|
chinaDeny := ""
|
||||||
@@ -1690,7 +1686,17 @@ func nginxRoute(req routeRequest, certDir string) string {
|
|||||||
Burst: req.RateLimitBurst,
|
Burst: req.RateLimitBurst,
|
||||||
Conn: req.ConnLimitMax,
|
Conn: req.ConnLimitMax,
|
||||||
}, " ")
|
}, " ")
|
||||||
fmt.Fprintf(&b, " location / {\n%s%s%s proxy_pass http://%s;\n proxy_set_header Host $host;\n proxy_set_header X-Real-IP $remote_addr;\n proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n proxy_http_version 1.1;\n }\n}\n", aclText, authLine, limitText, req.Upstream)
|
// WebSocket 必须写在 location 里:location 一旦自定义了 proxy_set_header,
|
||||||
|
// server 级的那组不会被继承——写在 server 块等于没写,表现就是升级请求一直挂起。
|
||||||
|
wsText := ""
|
||||||
|
if req.WebSocket {
|
||||||
|
wsText = nginxtmpl.UpgradeHeaders(" ", "\"upgrade\"")
|
||||||
|
// 规则没显式配 proxy_read_timeout 时给长连接留足空闲时间,避免 60 秒被 Nginx 掐断
|
||||||
|
if strings.TrimSpace(req.ProxyReadTimeout) == "" {
|
||||||
|
wsText += " proxy_read_timeout 3600s;\n"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fmt.Fprintf(&b, " location / {\n%s%s%s proxy_pass http://%s;\n proxy_set_header Host $host;\n proxy_set_header X-Real-IP $remote_addr;\n proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n proxy_http_version 1.1;\n%s }\n}\n", aclText, authLine, limitText, req.Upstream, wsText)
|
||||||
return b.String()
|
return b.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
@@ -4,14 +4,21 @@ Manual OCI layout pusher for registries whose token realm is misconfigured.
|
|||||||
This script is an emergency fallback. It reads Docker's OCI layout output,
|
This script is an emergency fallback. It reads Docker's OCI layout output,
|
||||||
obtains a Bearer token over HTTPS from the Git credential store, and uploads
|
obtains a Bearer token over HTTPS from the Git credential store, and uploads
|
||||||
the blobs and image manifest directly through the Registry HTTP API.
|
the blobs and image manifest directly through the Registry HTTP API.
|
||||||
|
|
||||||
|
默认面向 GitHub Container Registry(ghcr.io)。如果要用回自建 Gitea registry:
|
||||||
|
-Registry jayhub.top -Repository mrlee/havline -CredentialHost jayhub.top `
|
||||||
|
-TokenPath /v2/token -TokenService container_registry
|
||||||
#>
|
#>
|
||||||
param(
|
param(
|
||||||
[Parameter(Mandatory = $true)][string]$LayoutDir,
|
[Parameter(Mandatory = $true)][string]$LayoutDir,
|
||||||
[Parameter(Mandatory = $true)][string]$Platform,
|
[Parameter(Mandatory = $true)][string]$Platform,
|
||||||
[Parameter(Mandatory = $true)][string]$Tag,
|
[Parameter(Mandatory = $true)][string]$Tag,
|
||||||
[string]$Registry = "jayhub.top",
|
[string]$Registry = "ghcr.io",
|
||||||
[string]$Repository = "mrlee/havline",
|
[string]$Repository = "mrlee233/havline",
|
||||||
[string]$CredentialHost = "jayhub.top"
|
[string]$CredentialHost = "ghcr.io",
|
||||||
|
# GHCR: /token?service=ghcr.io;Gitea: /v2/token?service=container_registry
|
||||||
|
[string]$TokenPath = "/token",
|
||||||
|
[string]$TokenService = "ghcr.io"
|
||||||
)
|
)
|
||||||
|
|
||||||
$ErrorActionPreference = "Stop"
|
$ErrorActionPreference = "Stop"
|
||||||
@@ -33,7 +40,7 @@ function Get-RegistryBasicAuth {
|
|||||||
|
|
||||||
function Get-BearerToken([string]$Scope) {
|
function Get-BearerToken([string]$Scope) {
|
||||||
$basic = Get-RegistryBasicAuth
|
$basic = Get-RegistryBasicAuth
|
||||||
$url = "https://$Registry/v2/token?service=container_registry&scope=$Scope"
|
$url = "https://$Registry$TokenPath?service=$TokenService&scope=$Scope"
|
||||||
$response = Invoke-RestMethod -Uri $url -Headers @{ Authorization = $basic }
|
$response = Invoke-RestMethod -Uri $url -Headers @{ Authorization = $basic }
|
||||||
if (-not $response.token) { throw "Registry did not return a token" }
|
if (-not $response.token) { throw "Registry did not return a token" }
|
||||||
return $response.token
|
return $response.token
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
#
|
#
|
||||||
# 可选环境变量:
|
# 可选环境变量:
|
||||||
# COMPOSE_FILE=docker-compose.hub.yml # 要用的编排文件
|
# COMPOSE_FILE=docker-compose.hub.yml # 要用的编排文件
|
||||||
# REPO=http://.../mrlee/Havline.git # 源码仓库
|
# REPO=https://github.com/mrlee233/havline.git # 源码仓库
|
||||||
# BRANCH=main # 分支
|
# BRANCH=main # 分支
|
||||||
#
|
#
|
||||||
# 它做四件事:
|
# 它做四件事:
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
# 4) 打印「回滚到上一个 tag」的命令
|
# 4) 打印「回滚到上一个 tag」的命令
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
REPO="${REPO:-https://jayhub.top/mrlee/Havline.git}"
|
REPO="${REPO:-https://github.com/mrlee233/havline.git}"
|
||||||
BRANCH="${BRANCH:-main}"
|
BRANCH="${BRANCH:-main}"
|
||||||
COMPOSE_FILE="${COMPOSE_FILE:-docker-compose.hub.yml}"
|
COMPOSE_FILE="${COMPOSE_FILE:-docker-compose.hub.yml}"
|
||||||
APP_CONTAINER="${APP_CONTAINER:-havline}"
|
APP_CONTAINER="${APP_CONTAINER:-havline}"
|
||||||
|
|||||||
Reference in New Issue
Block a user