Compare commits

...

23 Commits

Author SHA1 Message Date
yusing
77e486f4fe refactor(route): ensure validation and start only starts once, and lock error before finishing 2025-10-31 18:10:09 +08:00
yusing
3ccaba3163 fix(validation): prioritize pointer method for custom validation in serialization 2025-10-31 18:06:41 +08:00
yusing
705923960c feat(fileserver): add rules support for fileservers 2025-10-31 17:32:37 +08:00
yusing
ca737c8979 fix(modify-html): re-enable modifying HTML with chunked encoding 2025-10-31 17:30:23 +08:00
yusing
b6b5d4dbd7 fix(auth): handle nil defaultAuth to prevent nil panic before auth intializes 2025-10-31 17:15:03 +08:00
yusing
b2919fbaf6 feat(rules): supress some errors in rule execution 2025-10-31 17:13:09 +08:00
yusing
722c40d103 chore(examples): update example configurations with comments for certificate paths and lite variant 2025-10-30 11:45:06 +08:00
yusing
860d9c71b6 fix(pool,io): overlap memory on buffer splitting; hook in HookReadCloser should run after Close 2025-10-29 22:48:28 +08:00
yusing
e354d901c4 fix(monitor): safer approach to avoid nil panic in edge cases 2025-10-29 00:19:17 +08:00
yusing
921a8fb935 fix(monitor): handle missing container state in Docker health check 2025-10-28 23:49:59 +08:00
yusing
975354cdc1 chore(compose): comment out user for lite variant in example configuration 2025-10-28 23:15:54 +08:00
yusing
7d38bfd2d2 build: drop old image name support 2025-10-28 22:05:35 +08:00
yusing
5506cafa26 fix(rules): pages not loading correct for lite webui variant 2025-10-28 22:00:54 +08:00
yusing
9fd5bff81a fix(oidc): fix Webui OIDC loop 2025-10-28 21:54:46 +08:00
yusing
38041ca5b8 fix(pool): handle buffer capacity check in GetSized
- Added a check to ensure the buffer's capacity is sufficient before reusing it.
- Included a FIXME comment to address an unexpected condition in buffer allocation.
2025-10-28 21:48:27 +08:00
yusing
61be88c1d3 chore: upgrade dependencies 2025-10-28 21:40:23 +08:00
yusing
cb4dcb962e fix(http): nil panic in goutils http/intercept.go 2025-10-28 21:37:19 +08:00
yusing
1797a222cd fix(middlewares): correctly bypass middlewares with response rules 2025-10-28 20:44:46 +08:00
yusing
098fb7e62d fix(compose): update rootless compose example 2025-10-28 17:03:20 +08:00
yusing
d4dfec8293 refactor(http): proper ResponseWriter and headers handling across files 2025-10-28 14:43:10 +08:00
yusing
f29b69ff3b refactor(rules): remove Flush method and replace with http.NewResponseController in ResponseModifier 2025-10-27 17:46:23 +08:00
yusing
5e00e1c437 fix(middleware): correct and simplify HTML modification / buffer management logic, correct Accept-Encoding header 2025-10-27 15:08:29 +08:00
yusing
39c8cc2820 fix(auth): nil panic by handling in TryRefreshToken 2025-10-27 14:25:05 +08:00
25 changed files with 298 additions and 120 deletions

View File

@@ -10,7 +10,6 @@ jobs:
uses: ./.github/workflows/docker-image.yml
with:
image_name: ${{ github.repository_owner }}/godoxy
old_image_name: ${{ github.repository_owner }}/go-proxy
tag: latest
target: main
build-prod-agent:

View File

@@ -9,9 +9,6 @@ on:
image_name:
required: true
type: string
old_image_name:
required: false
type: string
target:
required: true
type: string
@@ -156,17 +153,6 @@ jobs:
docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
$(printf '${{ env.REGISTRY }}/${{ inputs.image_name }}@sha256:%s ' *)
- name: Old image name
if: inputs.old_image_name != ''
run: |
docker buildx imagetools create -t ${{ env.REGISTRY }}/${{ inputs.old_image_name }}:${{ steps.meta.outputs.version }}\
${{ env.REGISTRY }}/${{ inputs.image_name }}:${{ steps.meta.outputs.version }}
- name: Inspect image
run: |
docker buildx imagetools inspect ${{ env.REGISTRY }}/${{ inputs.image_name }}:${{ steps.meta.outputs.version }}
- name: Inspect image (old)
if: inputs.old_image_name != ''
run: |
docker buildx imagetools inspect ${{ env.REGISTRY }}/${{ inputs.old_image_name }}:${{ steps.meta.outputs.version }}

View File

@@ -13,14 +13,14 @@ replace github.com/yusing/goutils => ../goutils
exclude github.com/containerd/nerdctl/mod/tigron v0.0.0
require (
github.com/bytedance/sonic v1.14.1
github.com/bytedance/sonic v1.14.2
github.com/gin-gonic/gin v1.11.0
github.com/gorilla/websocket v1.5.3
github.com/puzpuzpuz/xsync/v4 v4.2.0
github.com/rs/zerolog v1.34.0
github.com/stretchr/testify v1.11.1
github.com/valyala/fasthttp v1.68.0
github.com/yusing/godoxy v0.19.2
github.com/yusing/godoxy v0.20.2
github.com/yusing/godoxy/socketproxy v0.0.0-00010101000000-000000000000
github.com/yusing/goutils v0.7.0
)
@@ -32,7 +32,7 @@ require (
github.com/andybalholm/brotli v1.2.0 // indirect
github.com/andybalholm/cascadia v1.3.3 // indirect
github.com/bytedance/gopkg v0.1.3 // indirect
github.com/bytedance/sonic/loader v0.3.0 // indirect
github.com/bytedance/sonic/loader v0.4.0 // indirect
github.com/cenkalti/backoff/v5 v5.0.3 // indirect
github.com/cloudwego/base64x v0.1.6 // indirect
github.com/containerd/errdefs v1.0.0 // indirect
@@ -87,7 +87,7 @@ require (
github.com/tklauser/go-sysconf v0.3.15 // indirect
github.com/tklauser/numcpus v0.10.0 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.3.0 // indirect
github.com/ugorji/go/codec v1.3.1 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/vincent-petithory/dataurl v1.0.0 // indirect
github.com/yusing/ds v0.3.1 // indirect

View File

@@ -12,10 +12,10 @@ github.com/buger/goterm v1.0.4 h1:Z9YvGmOih81P0FbVtEYTFF6YsSgxSUKEhf/f9bTMXbY=
github.com/buger/goterm v1.0.4/go.mod h1:HiFWV3xnkolgrBV3mY8m0X0Pumt4zg4QhbdOzQtB8tE=
github.com/bytedance/gopkg v0.1.3 h1:TPBSwH8RsouGCBcMBktLt1AymVo2TVsBVCY4b6TnZ/M=
github.com/bytedance/gopkg v0.1.3/go.mod h1:576VvJ+eJgyCzdjS+c4+77QF3p7ubbtiKARP3TxducM=
github.com/bytedance/sonic v1.14.1 h1:FBMC0zVz5XUmE4z9wF4Jey0An5FueFvOsTKKKtwIl7w=
github.com/bytedance/sonic v1.14.1/go.mod h1:gi6uhQLMbTdeP0muCnrjHLeCUPyb70ujhnNlhOylAFc=
github.com/bytedance/sonic/loader v0.3.0 h1:dskwH8edlzNMctoruo8FPTJDF3vLtDT0sXZwvZJyqeA=
github.com/bytedance/sonic/loader v0.3.0/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI=
github.com/bytedance/sonic v1.14.2 h1:k1twIoe97C1DtYUo+fZQy865IuHia4PR5RPiuGPPIIE=
github.com/bytedance/sonic v1.14.2/go.mod h1:T80iDELeHiHKSc0C9tubFygiuXoGzrkjKzX2quAx980=
github.com/bytedance/sonic/loader v0.4.0 h1:olZ7lEqcxtZygCK9EKYKADnpQoYkRQxaeY2NYzevs+o=
github.com/bytedance/sonic/loader v0.4.0/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo=
github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM=
github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw=
github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M=
@@ -189,10 +189,12 @@ github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/tklauser/go-sysconf v0.3.15 h1:VE89k0criAymJ/Os65CSn1IXaol+1wrsFHEB8Ol49K4=
@@ -201,8 +203,8 @@ github.com/tklauser/numcpus v0.10.0 h1:18njr6LDBk1zuna922MgdjQuJFjrdppsZG60sHGfj
github.com/tklauser/numcpus v0.10.0/go.mod h1:BiTKazU708GQTYF4mB+cmlpT2Is1gLk7XVuEeem8LsQ=
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
github.com/ugorji/go/codec v1.3.0 h1:Qd2W2sQawAfG8XSvzwhBeoGq71zXOC/Q1E9y/wUcsUA=
github.com/ugorji/go/codec v1.3.0/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4=
github.com/ugorji/go/codec v1.3.1 h1:waO7eEiFDwidsBN6agj1vJQ4AG7lh2yqXyOXqhgQuyY=
github.com/ugorji/go/codec v1.3.1/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasthttp v1.68.0 h1:v12Nx16iepr8r9ySOwqI+5RBJ/DqTxhOy1HrHoDFnok=
@@ -335,8 +337,8 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
google.golang.org/genproto v0.0.0-20250908214217-97024824d090 h1:ywCL7vA2n3vVHyf+bx1ZV/knaTPRI8GIeKY0MEhEeOc=
google.golang.org/genproto/googleapis/api v0.0.0-20250929231259-57b25ae835d4 h1:8XJ4pajGwOlasW+L13MnEGA8W4115jJySQtVfS2/IBU=
google.golang.org/genproto/googleapis/api v0.0.0-20250929231259-57b25ae835d4/go.mod h1:NnuHhy+bxcg30o7FnVAZbXsPHUDQ9qKWAQKCD7VxFtk=
google.golang.org/genproto/googleapis/rpc v0.0.0-20251014184007-4626949a642f h1:1FTH6cpXFsENbPR5Bu8NQddPSaUUE6NA2XdZdDSAJK4=
google.golang.org/genproto/googleapis/rpc v0.0.0-20251014184007-4626949a642f/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk=
google.golang.org/genproto/googleapis/rpc v0.0.0-20251022142026-3a174f9686a8 h1:M1rk8KBnUsBDg1oPGHNCxG4vc1f49epmTO7xscSajMk=
google.golang.org/genproto/googleapis/rpc v0.0.0-20251022142026-3a174f9686a8/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk=
google.golang.org/grpc v1.76.0 h1:UnVkv1+uMLYXoIz6o7chp59WfQUYA2ex/BXQ9rHZu7A=
google.golang.org/grpc v1.76.0/go.mod h1:Ju12QI8M6iQJtbcsV+awF5a4hfJMLi4X0JLo94ULZ6c=
google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=

View File

@@ -22,9 +22,12 @@ services:
- ${SOCKET_PROXY_LISTEN_ADDR:-127.0.0.1:2375}:2375
frontend:
image: ghcr.io/yusing/godoxy-frontend:${TAG:-latest}
# lite variant
# image: ghcr.io/yusing/godoxy-frontend:${TAG:-latest}-lite
container_name: godoxy-frontend
restart: unless-stopped
env_file: .env
# comment out `user` for lite variant
user: ${GODOXY_UID:-1000}:${GODOXY_GID:-1000}
read_only: true
tmpfs:
@@ -73,10 +76,9 @@ services:
- ./error_pages:/app/error_pages:ro
- ./data:/app/data
# To use autocert, certs will be stored in "./certs".
# You can also use a docker volume to store it
# This path stores certs obtained from autocert and agent TLS client certs
- ./certs:/app/certs
# remove "./certs:/app/certs" and uncomment below to use existing certificate
# mount existing certificate
# - /path/to/certs/cert.crt:/app/certs/cert.crt
# - /path/to/certs/priv.key:/app/certs/priv.key

View File

@@ -4,6 +4,8 @@
# autocert:
# provider: local
# cert_path: /path/to/cert.crt # default: /app/certs/cert.crt
# key_path: /path/to/priv.key # default: /app/certs/priv.key
# 2. cloudflare
# autocert:

10
go.mod
View File

@@ -46,8 +46,8 @@ require (
github.com/spf13/afero v1.15.0
github.com/stretchr/testify v1.11.1
github.com/yusing/ds v0.3.1
github.com/yusing/godoxy/agent v0.0.0-20251025144347-1ec2872f3d4c
github.com/yusing/godoxy/internal/dnsproviders v0.0.0-20251025144347-1ec2872f3d4c
github.com/yusing/godoxy/agent v0.0.0-20251028124446-1797a222cd18
github.com/yusing/godoxy/internal/dnsproviders v0.0.0-20251028124446-1797a222cd18
github.com/yusing/goutils v0.7.0
)
@@ -136,7 +136,7 @@ require (
)
require (
github.com/bytedance/sonic v1.14.1
github.com/bytedance/sonic v1.14.2
github.com/shirou/gopsutil/v4 v4.25.9
github.com/valyala/fasthttp v1.68.0
github.com/yusing/gointernals v0.1.16
@@ -146,7 +146,7 @@ require (
github.com/akamai/AkamaiOPEN-edgegrid-golang/v11 v11.1.0 // indirect
github.com/andybalholm/brotli v1.2.0 // indirect
github.com/bytedance/gopkg v0.1.3 // indirect
github.com/bytedance/sonic/loader v0.3.0 // indirect
github.com/bytedance/sonic/loader v0.4.0 // indirect
github.com/cenkalti/backoff/v5 v5.0.3 // indirect
github.com/cloudwego/base64x v0.1.6 // indirect
github.com/containerd/errdefs v1.0.0 // indirect
@@ -174,7 +174,7 @@ require (
github.com/tklauser/go-sysconf v0.3.15 // indirect
github.com/tklauser/numcpus v0.10.0 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.3.0 // indirect
github.com/ugorji/go/codec v1.3.1 // indirect
github.com/ulikunitz/xz v0.5.14 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/vultr/govultr/v3 v3.24.0 // indirect

16
go.sum
View File

@@ -50,10 +50,10 @@ github.com/buger/goterm v1.0.4 h1:Z9YvGmOih81P0FbVtEYTFF6YsSgxSUKEhf/f9bTMXbY=
github.com/buger/goterm v1.0.4/go.mod h1:HiFWV3xnkolgrBV3mY8m0X0Pumt4zg4QhbdOzQtB8tE=
github.com/bytedance/gopkg v0.1.3 h1:TPBSwH8RsouGCBcMBktLt1AymVo2TVsBVCY4b6TnZ/M=
github.com/bytedance/gopkg v0.1.3/go.mod h1:576VvJ+eJgyCzdjS+c4+77QF3p7ubbtiKARP3TxducM=
github.com/bytedance/sonic v1.14.1 h1:FBMC0zVz5XUmE4z9wF4Jey0An5FueFvOsTKKKtwIl7w=
github.com/bytedance/sonic v1.14.1/go.mod h1:gi6uhQLMbTdeP0muCnrjHLeCUPyb70ujhnNlhOylAFc=
github.com/bytedance/sonic/loader v0.3.0 h1:dskwH8edlzNMctoruo8FPTJDF3vLtDT0sXZwvZJyqeA=
github.com/bytedance/sonic/loader v0.3.0/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI=
github.com/bytedance/sonic v1.14.2 h1:k1twIoe97C1DtYUo+fZQy865IuHia4PR5RPiuGPPIIE=
github.com/bytedance/sonic v1.14.2/go.mod h1:T80iDELeHiHKSc0C9tubFygiuXoGzrkjKzX2quAx980=
github.com/bytedance/sonic/loader v0.4.0 h1:olZ7lEqcxtZygCK9EKYKADnpQoYkRQxaeY2NYzevs+o=
github.com/bytedance/sonic/loader v0.4.0/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo=
github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM=
github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw=
github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M=
@@ -288,6 +288,7 @@ github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/objx v0.5.3 h1:jmXUvGomnU1o3W/V5h2VEradbpJDwGrzugQQvL0POH4=
github.com/stretchr/objx v0.5.3/go.mod h1:rDQraq+vQZU7Fde9LOZLr8Tax6zZvy4kuNKF+QYS+U0=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
@@ -295,7 +296,8 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/tklauser/go-sysconf v0.3.15 h1:VE89k0criAymJ/Os65CSn1IXaol+1wrsFHEB8Ol49K4=
@@ -304,8 +306,8 @@ github.com/tklauser/numcpus v0.10.0 h1:18njr6LDBk1zuna922MgdjQuJFjrdppsZG60sHGfj
github.com/tklauser/numcpus v0.10.0/go.mod h1:BiTKazU708GQTYF4mB+cmlpT2Is1gLk7XVuEeem8LsQ=
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
github.com/ugorji/go/codec v1.3.0 h1:Qd2W2sQawAfG8XSvzwhBeoGq71zXOC/Q1E9y/wUcsUA=
github.com/ugorji/go/codec v1.3.0/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4=
github.com/ugorji/go/codec v1.3.1 h1:waO7eEiFDwidsBN6agj1vJQ4AG7lh2yqXyOXqhgQuyY=
github.com/ugorji/go/codec v1.3.1/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4=
github.com/ulikunitz/xz v0.5.14 h1:uv/0Bq533iFdnMHZdRBTOlaNMdb1+ZxXIlHDZHIHcvg=
github.com/ulikunitz/xz v0.5.14/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=

Submodule goutils updated: c0955732e9...84457ea2e1

View File

@@ -51,6 +51,10 @@ func ProceedNext(w http.ResponseWriter, r *http.Request) {
}
func AuthCheckHandler(w http.ResponseWriter, r *http.Request) {
if defaultAuth == nil {
w.WriteHeader(http.StatusServiceUnavailable)
return
}
err := defaultAuth.CheckToken(r)
if err != nil {
defaultAuth.LoginHandler(w, r)
@@ -60,6 +64,10 @@ func AuthCheckHandler(w http.ResponseWriter, r *http.Request) {
}
func AuthOrProceed(w http.ResponseWriter, r *http.Request) (proceed bool) {
if defaultAuth == nil {
w.WriteHeader(http.StatusServiceUnavailable)
return false
}
err := defaultAuth.CheckToken(r)
if err != nil {
defaultAuth.LoginHandler(w, r)

View File

@@ -151,7 +151,11 @@ func (auth *OIDCProvider) TryRefreshToken(ctx context.Context, sessionJWT string
// verify the session cookie
claims, valid, err := auth.parseSessionJWT(sessionJWT)
if err != nil {
return nil, fmt.Errorf("session: %s - %w: %w", claims.SessionID, ErrInvalidSessionToken, err)
var sessionID sessionID
if claims != nil {
sessionID = claims.SessionID
}
return nil, fmt.Errorf("session: %s - %w: %w", sessionID, ErrInvalidSessionToken, err)
}
if !valid {
return nil, ErrInvalidSessionToken

View File

@@ -6,7 +6,7 @@ replace github.com/yusing/godoxy => ../..
require (
github.com/go-acme/lego/v4 v4.27.0
github.com/yusing/godoxy v0.19.2
github.com/yusing/godoxy v0.20.2
)
require (
@@ -23,8 +23,8 @@ require (
github.com/akamai/AkamaiOPEN-edgegrid-golang/v11 v11.1.0 // indirect
github.com/benbjohnson/clock v1.3.5 // indirect
github.com/bytedance/gopkg v0.1.3 // indirect
github.com/bytedance/sonic v1.14.1 // indirect
github.com/bytedance/sonic/loader v0.3.0 // indirect
github.com/bytedance/sonic v1.14.2 // indirect
github.com/bytedance/sonic/loader v0.4.0 // indirect
github.com/cenkalti/backoff/v5 v5.0.3 // indirect
github.com/cloudwego/base64x v0.1.6 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect

View File

@@ -36,10 +36,10 @@ github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz
github.com/benbjohnson/clock v1.3.5/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/bytedance/gopkg v0.1.3 h1:TPBSwH8RsouGCBcMBktLt1AymVo2TVsBVCY4b6TnZ/M=
github.com/bytedance/gopkg v0.1.3/go.mod h1:576VvJ+eJgyCzdjS+c4+77QF3p7ubbtiKARP3TxducM=
github.com/bytedance/sonic v1.14.1 h1:FBMC0zVz5XUmE4z9wF4Jey0An5FueFvOsTKKKtwIl7w=
github.com/bytedance/sonic v1.14.1/go.mod h1:gi6uhQLMbTdeP0muCnrjHLeCUPyb70ujhnNlhOylAFc=
github.com/bytedance/sonic/loader v0.3.0 h1:dskwH8edlzNMctoruo8FPTJDF3vLtDT0sXZwvZJyqeA=
github.com/bytedance/sonic/loader v0.3.0/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI=
github.com/bytedance/sonic v1.14.2 h1:k1twIoe97C1DtYUo+fZQy865IuHia4PR5RPiuGPPIIE=
github.com/bytedance/sonic v1.14.2/go.mod h1:T80iDELeHiHKSc0C9tubFygiuXoGzrkjKzX2quAx980=
github.com/bytedance/sonic/loader v0.4.0 h1:olZ7lEqcxtZygCK9EKYKADnpQoYkRQxaeY2NYzevs+o=
github.com/bytedance/sonic/loader v0.4.0/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo=
github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM=
github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw=
github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M=
@@ -165,13 +165,15 @@ github.com/sony/gobreaker v1.0.0/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJ
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/objx v0.5.3 h1:jmXUvGomnU1o3W/V5h2VEradbpJDwGrzugQQvL0POH4=
github.com/stretchr/objx v0.5.3/go.mod h1:rDQraq+vQZU7Fde9LOZLr8Tax6zZvy4kuNKF+QYS+U0=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=

View File

@@ -30,8 +30,8 @@ func (c *checkBypass) before(w http.ResponseWriter, r *http.Request) (proceedNex
return c.modReq.before(w, r)
}
func (c *checkBypass) modifyResponse(w http.ResponseWriter, resp *http.Response) error {
if c.modRes == nil || c.bypass.ShouldBypass(w, resp.Request) {
func (c *checkBypass) modifyResponse(resp *http.Response) error {
if c.modRes == nil || c.bypass.ShouldBypass(rules.ResponseAsRW(resp), resp.Request) {
return nil
}
return c.modRes.modifyResponse(resp)

View File

@@ -138,6 +138,82 @@ func TestReverseProxyBypass(t *testing.T) {
}
}
func TestBypassResponse(t *testing.T) {
t.Run("req_rules", func(t *testing.T) {
mr, err := ModifyResponse.New(map[string]any{
"bypass": []string{"path glob(/test/*) | path /api"},
"set_headers": map[string]string{
"Test-Header": "test-value",
},
})
expect.NoError(t, err)
tests := []struct {
name string
path string
expectBypass bool
}{
{"bypass", "/test/123", true},
{"bypass2", "/test/123/456", true},
{"bypass3", "/api", true},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
req := httptest.NewRequest("GET", "http://example.com"+test.path, nil)
resp := &http.Response{
StatusCode: http.StatusOK,
Body: io.NopCloser(strings.NewReader("test")),
Request: req,
Header: make(http.Header),
}
mErr := mr.ModifyResponse(resp)
expect.NoError(t, mErr)
if test.expectBypass {
expect.Equal(t, resp.Header.Get("Test-Header"), "")
} else {
expect.Equal(t, resp.Header.Get("Test-Header"), "test-value")
}
})
}
})
t.Run("res_rules", func(t *testing.T) {
mr, err := ModifyResponse.New(map[string]any{
"bypass": []string{"status 200"},
"set_headers": map[string]string{
"Test-Header": "test-value",
},
})
expect.NoError(t, err)
tests := []struct {
name string
statusCode int
expectBypass bool
}{
{"bypass", 200, true},
{"no_bypass", 201, false},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
resp := &http.Response{
StatusCode: test.statusCode,
Body: io.NopCloser(strings.NewReader("test")),
Header: make(http.Header),
}
mErr := mr.ModifyResponse(resp)
expect.NoError(t, mErr)
if test.expectBypass {
expect.Equal(t, resp.Header.Get("Test-Header"), "")
} else {
expect.Equal(t, resp.Header.Get("Test-Header"), "test-value")
}
})
}
})
}
func TestEntrypointBypassRoute(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("test"))

View File

@@ -15,20 +15,15 @@ import (
)
type modifyHTML struct {
Target string // css selector
HTML string // html to inject
Replace bool // replace the target element with the new html instead of appending it
bytesPool synk.UnsizedBytesPool
Target string // css selector
HTML string // html to inject
Replace bool // replace the target element with the new html instead of appending it
}
var ModifyHTML = NewMiddleware[modifyHTML]()
func (m *modifyHTML) setup() {
m.bytesPool = synk.GetUnsizedBytesPool()
}
func (m *modifyHTML) before(_ http.ResponseWriter, req *http.Request) bool {
req.Header.Set("Accept-Encoding", "")
req.Header.Set("Accept-Encoding", "identity")
return true
}
@@ -50,14 +45,26 @@ func (m *modifyHTML) modifyResponse(resp *http.Response) error {
return nil
}
// Skip modification for streaming/chunked responses to avoid blocking reads
// Unknown content length or any transfer encoding indicates streaming.
// if resp.ContentLength < 0 || len(resp.TransferEncoding) > 0 {
// log.Debug().Str("url", fullURL(resp.Request)).Strs("transfer-encoding", resp.TransferEncoding).Msg("skipping modification for streaming/chunked response")
// return nil
// }
// NOTE: do not put it in the defer, it will be used as resp.Body
content, release, err := httputils.ReadAllBody(resp)
resp.Body.Close()
if err != nil {
log.Err(err).Str("url", fullURL(resp.Request)).Msg("failed to read response body")
release(content)
// Fail open: do not abort the response. Return an empty body safely.
resp.ContentLength = 0
resp.Header.Set("Content-Length", "0")
resp.Header.Del("Transfer-Encoding")
resp.Header.Del("Trailer")
resp.Header.Del("Content-Encoding")
resp.Body = eofReader{}
return err
return nil
}
doc, err := goquery.NewDocumentFromReader(bytes.NewReader(content))
@@ -83,23 +90,27 @@ func (m *modifyHTML) modifyResponse(resp *http.Response) error {
ele.First().AppendHtml(m.HTML)
}
// should not use content (from sized pool) directly for bytes.Buffer
buf := m.bytesPool.GetBuffer()
buf.Write(content)
release(content)
pool := synk.GetUnsizedBytesPool()
buf := pool.GetBuffer()
err = buildHTML(doc, buf)
if err != nil {
pool.PutBuffer(buf)
log.Err(err).Str("url", fullURL(resp.Request)).Msg("failed to build html")
// invalid html, restore the original body
resp.Body = readerWithRelease(content, release)
return err
}
release(content)
resp.ContentLength = int64(buf.Len())
resp.Header.Set("Content-Length", strconv.Itoa(buf.Len()))
resp.Header.Del("Transfer-Encoding")
resp.Header.Del("Trailer")
resp.Header.Del("Content-Encoding")
resp.Header.Set("Content-Type", "text/html; charset=utf-8")
resp.Body = readerWithRelease(buf.Bytes(), func(_ []byte) {
m.bytesPool.PutBuffer(buf)
pool.PutBuffer(buf)
})
return nil
}

View File

@@ -45,10 +45,6 @@ var (
var fontCSSTemplate = template.Must(template.New("fontCSS").Parse(fontCSS))
func (m *themed) setup() {
m.m.setup()
}
func (m *themed) before(w http.ResponseWriter, req *http.Request) bool {
return m.m.before(w, req)
}

View File

@@ -86,6 +86,10 @@ func (s *FileServer) Start(parent task.Parent) gperr.Error {
}
}
if len(s.Rules) > 0 {
s.handler = s.Rules.BuildHandler(s.handler.ServeHTTP)
}
if s.UseHealthCheck() {
s.HealthMon = monitor.NewFileServerHealthMonitor(s.HealthCheck, s.Root)
if err := s.HealthMon.Start(s.task); err != nil {

View File

@@ -82,19 +82,39 @@ type (
impl types.Route
task *task.Task
isValidated bool
lastError gperr.Error
provider types.RouteProvider
// ensure err is read after validation or start
valErr lockedError
startErr lockedError
provider types.RouteProvider
agent *agent.AgentConfig
started chan struct{}
once sync.Once
started chan struct{}
onceStart sync.Once
onceValidate sync.Once
}
Routes map[string]*Route
Port = route.Port
)
type lockedError struct {
err gperr.Error
lock sync.Mutex
}
func (le *lockedError) Get() gperr.Error {
le.lock.Lock()
defer le.lock.Unlock()
return le.err
}
func (le *lockedError) Set(err gperr.Error) {
le.lock.Lock()
defer le.lock.Unlock()
le.err = err
}
const DefaultHost = "localhost"
func (r Routes) Contains(alias string) bool {
@@ -103,11 +123,13 @@ func (r Routes) Contains(alias string) bool {
}
func (r *Route) Validate() gperr.Error {
if r.isValidated {
return r.lastError
}
r.isValidated = true
r.onceValidate.Do(func() {
r.valErr.Set(r.validate())
})
return r.valErr.Get()
}
func (r *Route) validate() gperr.Error {
if r.Agent != "" {
if r.Container != nil {
return gperr.Errorf("specifying agent is not allowed for docker container routes")
@@ -250,7 +272,6 @@ func (r *Route) Validate() gperr.Error {
}
if errs.HasError() {
r.lastError = errs.Error()
return errs.Error()
}
@@ -266,7 +287,6 @@ func (r *Route) Validate() gperr.Error {
}
if err != nil {
r.lastError = err
return err
}
@@ -320,13 +340,10 @@ func (r *Route) Task() *task.Task {
}
func (r *Route) Start(parent task.Parent) gperr.Error {
if r.lastError != nil {
return r.lastError
}
r.once.Do(func() {
r.lastError = r.start(parent)
r.onceStart.Do(func() {
r.startErr.Set(r.start(parent))
})
return r.lastError
return r.startErr.Get()
}
func (r *Route) start(parent task.Parent) gperr.Error {
@@ -496,7 +513,7 @@ func (r *Route) IsZeroPort() bool {
}
func (r *Route) ShouldExclude() bool {
if r.lastError != nil {
if r.valErr.Get() != nil {
return true
}
if r.Excluded {
@@ -565,7 +582,7 @@ func (re ExcludedReason) MarshalJSON() ([]byte, error) {
// no need to unmarshal json because we don't store this
func (r *Route) findExcludedReason() ExcludedReason {
if r.lastError != nil {
if r.valErr.Get() != nil {
return ExcludedReasonError
}
if r.ExcludedReason != ExcludedReasonNone {

View File

@@ -5,7 +5,8 @@
on: |
!path regex("(_next/static|_next/image|favicon.ico).*")
!path glob("/api/v1/auth/*")
!path regex("[A-Za-z0-9_-]+\.(svg|png|jpg|jpeg|gif|ico|webp|woff2?|eot|ttf|otf)(\?.+)?")
!path glob("/auth/*")
!path regex("[A-Za-z0-9_-]+\.(svg|png|jpg|jpeg|gif|ico|webp|woff2?|eot|ttf|otf|txt)(\?.+)?")
!path /api/v1/version
do: require_auth
- name: proxy to backend

View File

@@ -4,10 +4,12 @@ import (
"bufio"
"bytes"
"errors"
"io"
"net"
"net/http"
"strconv"
"github.com/rs/zerolog/log"
gperr "github.com/yusing/goutils/errs"
"github.com/yusing/goutils/synk"
)
@@ -43,6 +45,29 @@ func unwrapResponseModifier(w http.ResponseWriter) *ResponseModifier {
}
}
type responseAsRW struct {
resp *http.Response
}
func (r responseAsRW) WriteHeader(code int) {
log.Error().Msg("write header after response has been created")
}
func (r responseAsRW) Write(b []byte) (int, error) {
return 0, io.ErrClosedPipe
}
func (r responseAsRW) Header() http.Header {
return r.resp.Header
}
func ResponseAsRW(resp *http.Response) *ResponseModifier {
return &ResponseModifier{
statusCode: resp.StatusCode,
w: responseAsRW{resp},
}
}
// GetInitResponseModifier returns the response modifier for the given response writer.
// If the response writer is already wrapped, it will return the wrapped response modifier.
// Otherwise, it will return a new response modifier.
@@ -144,15 +169,6 @@ func (rm *ResponseModifier) Hijack() (net.Conn, *bufio.ReadWriter, error) {
return nil, nil, errors.New("hijack not supported")
}
func (rm *ResponseModifier) Flush() error {
if flusher, ok := rm.w.(http.Flusher); ok {
flusher.Flush()
} else if errFlusher, ok := rm.w.(interface{ Flush() error }); ok {
return errFlusher.Flush()
}
return nil
}
// FlushRelease flushes the response modifier and releases the resources
// it returns the number of bytes written and the aggregated error
// if there is any error (rule errors or write error), it will be returned
@@ -167,6 +183,8 @@ func (rm *ResponseModifier) FlushRelease() (int, error) {
// }
contentLength := rm.ContentLength()
h.Set("Content-Length", strconv.Itoa(rm.ContentLength()))
h.Del("Transfer-Encoding")
h.Del("Trailer")
rm.w.WriteHeader(rm.StatusCode())
if contentLength > 0 {
@@ -175,7 +193,7 @@ func (rm *ResponseModifier) FlushRelease() (int, error) {
if werr != nil {
rm.errs.Addf("write error: %w", werr)
}
if err := rm.Flush(); err != nil {
if err := http.NewResponseController(rm.w).Flush(); err != nil {
rm.errs.Addf("flush error: %w", err)
}
}

View File

@@ -6,7 +6,11 @@ import (
"net/http"
"github.com/bytedance/sonic"
gperr "github.com/yusing/goutils/errs"
"github.com/quic-go/quic-go/http3"
"github.com/rs/zerolog/log"
"golang.org/x/net/http2"
_ "unsafe"
)
type (
@@ -89,6 +93,11 @@ func (rules Rules) BuildHandler(up http.HandlerFunc) http.HandlerFunc {
if defaultRule.IsResponseRule() {
return func(w http.ResponseWriter, r *http.Request) {
rm := NewResponseModifier(w)
defer func() {
if _, err := rm.FlushRelease(); err != nil {
logError(err, r)
}
}()
w = rm
up(w, r)
err := defaultRule.Do.exec.Handle(w, r)
@@ -99,6 +108,11 @@ func (rules Rules) BuildHandler(up http.HandlerFunc) http.HandlerFunc {
}
return func(w http.ResponseWriter, r *http.Request) {
rm := NewResponseModifier(w)
defer func() {
if _, err := rm.FlushRelease(); err != nil {
logError(err, r)
}
}()
w = rm
err := defaultRule.Do.exec.Handle(w, r)
if err == nil {
@@ -128,7 +142,7 @@ func (rules Rules) BuildHandler(up http.HandlerFunc) http.HandlerFunc {
rm := NewResponseModifier(w)
defer func() {
if _, err := rm.FlushRelease(); err != nil {
gperr.LogError("error executing rules", err)
logError(err, r)
}
}()
@@ -252,3 +266,31 @@ func (rule *Rule) Check(w http.ResponseWriter, r *http.Request) bool {
func (rule *Rule) Handle(w http.ResponseWriter, r *http.Request) error {
return rule.Do.exec.Handle(w, r)
}
//go:linkname errStreamClosed golang.org/x/net/http2.errStreamClosed
var errStreamClosed error
func logError(err error, r *http.Request) {
if errors.Is(err, errStreamClosed) {
return
}
var h2Err http2.StreamError
if errors.As(err, &h2Err) {
// ignore these errors
switch h2Err.Code {
case http2.ErrCodeStreamClosed:
return
}
}
var h3Err *http3.Error
if errors.As(err, &h3Err) {
// ignore these errors
switch h3Err.ErrorCode {
case
http3.ErrCodeNoError,
http3.ErrCodeRequestCanceled:
return
}
}
log.Err(err).Str("method", r.Method).Str("url", r.Host+r.URL.Path).Msg("error executing rules")
}

View File

@@ -41,12 +41,17 @@ func ValidateWithCustomValidator(v reflect.Value) gperr.Error {
} else {
vt := v.Type()
if vt.PkgPath() != "" { // not a builtin type
// prioritize pointer method
if v.CanAddr() {
vAddr := v.Addr()
if vAddr.Type().Implements(validatorType) {
return vAddr.Interface().(CustomValidator).Validate()
}
}
// fallback to value method
if vt.Implements(validatorType) {
return v.Interface().(CustomValidator).Validate()
}
if v.CanAddr() {
return validateWithValidator(v.Addr())
}
}
}
return nil

View File

@@ -49,10 +49,6 @@ func (mon *DockerHealthMonitor) Start(parent task.Parent) gperr.Error {
return nil
}
type inspectState struct {
State *container.State
}
func (mon *DockerHealthMonitor) interceptInspectResponse(resp *http.Response) (intercepted bool, err error) {
if resp.StatusCode != http.StatusOK {
return false, nil
@@ -64,12 +60,13 @@ func (mon *DockerHealthMonitor) interceptInspectResponse(resp *http.Response) (i
return false, err
}
var state inspectState
var state container.State
err = sonic.Unmarshal(body, &state)
release(body)
if err != nil {
return false, err
}
return true, httputils.NewRequestInterceptedError(resp, state)
}
@@ -86,13 +83,20 @@ func (mon *DockerHealthMonitor) CheckHealth() (types.HealthCheckResult, error) {
_, err := mon.client.ContainerInspect(ctx, mon.containerID)
var interceptedErr *httputils.RequestInterceptedError
if err != nil && !httputils.AsRequestInterceptedError(err, &interceptedErr) {
if !httputils.AsRequestInterceptedError(err, &interceptedErr) {
mon.numDockerFailures++
log.Debug().Err(err).Str("container_id", mon.containerID).Msg("docker health check failed, using fallback")
return mon.fallback.CheckHealth()
}
state := interceptedErr.Data.(inspectState).State
if interceptedErr == nil || interceptedErr.Data == nil { // should not happen
log.Debug().Msgf("intercepted error is nil or data is nil, container_id: %s", mon.containerID)
mon.numDockerFailures++
log.Debug().Err(err).Str("container_id", mon.containerID).Msg("docker health check failed, using fallback")
return mon.fallback.CheckHealth()
}
state := interceptedErr.Data.(container.State)
status := state.Status
switch status {
case "dead", "exited", "paused", "restarting", "removing":

View File

@@ -35,12 +35,9 @@ services:
depends_on:
- app
environment:
HOSTNAME: 0.0.0.0
PORT: 3000
GODOXY_API_ADDR: app:8888
labels:
proxy.aliases: ${GODOXY_FRONTEND_ALIASES:-godoxy}
proxy.#1.port: 3000
networks:
- godoxy
app: