Compare commits
9 Commits
2025021901
...
2025030301
Author | SHA1 | Date | |
---|---|---|---|
83dfcba4ae | |||
504a809c16 | |||
f9ff71f62a | |||
629c095bbe | |||
9b34fd29c0 | |||
3cfa6c6116 | |||
27634d016b | |||
ab852a6520 | |||
18bdc4f54e |
@ -3,22 +3,25 @@ name: build container
|
|||||||
run-name: build container on ${{ gitea.actor }}
|
run-name: build container on ${{ gitea.actor }}
|
||||||
on: [push]
|
on: [push]
|
||||||
|
|
||||||
env:
|
|
||||||
GOPROXY: ${{ vars.GOPROXY }}
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build-container:
|
build-container:
|
||||||
runs-on: bookworm
|
runs-on: docker
|
||||||
steps:
|
steps:
|
||||||
- name: Setup apt mirror
|
- name: Setup apt mirror
|
||||||
run: sed -i "s,deb.debian.org,${{ vars.DEBIAN_MIRROR }},g" ${{ vars.DEBIAN_APT_SOURCES }}
|
run: sed -i "s,deb.debian.org,${{ vars.DEBIAN_MIRROR }},g" ${{ vars.DEBIAN_APT_SOURCES }}
|
||||||
if: ${{ vars.DEBIAN_MIRROR && vars.DEBIAN_APT_SOURCES }}
|
if: ${{ vars.DEBIAN_MIRROR && vars.DEBIAN_APT_SOURCES }}
|
||||||
- name: Setup debian environment
|
- name: Setup debian environment
|
||||||
run: apt update && apt install -y podman podman-compose nodejs
|
run: apt update && apt install -y podman podman-compose nodejs
|
||||||
|
- name: Setup cache for podman
|
||||||
|
uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
/var/lib/containers
|
||||||
|
key: podman-storage
|
||||||
- name: Check out repository code
|
- name: Check out repository code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
- name: Build container
|
- name: Build container
|
||||||
run: REGISTRY=${{ github.server_url}}; REGISTRY=${REGISTRY##https://}; REGISTRY=${REGISTRY##http://}; podman build -t $REGISTRY/${{ github.repository }} .
|
run: REGISTRY=${{ github.server_url}}; REGISTRY=${REGISTRY##https://}; REGISTRY=${REGISTRY##http://}; podman build --build-arg GOPROXY=${{ vars.GOPROXY }} -t $REGISTRY/${{ github.repository }} .
|
||||||
- name: Login to Container Registry
|
- name: Login to Container Registry
|
||||||
run: echo "${{ secrets.ACTION_PACKAGE_WRITE_TOKEN }}" | podman login ${{ github.server_url }} -u ${{ github.repository_owner }} --password-stdin
|
run: echo "${{ secrets.ACTION_PACKAGE_WRITE_TOKEN }}" | podman login ${{ github.server_url }} -u ${{ github.repository_owner }} --password-stdin
|
||||||
- name: Push Container Image
|
- name: Push Container Image
|
||||||
|
@ -7,22 +7,33 @@ on:
|
|||||||
- go.mod
|
- go.mod
|
||||||
- go.lock
|
- go.lock
|
||||||
- go.work
|
- go.work
|
||||||
|
- .gitea/workflows/go-test.yaml
|
||||||
env:
|
env:
|
||||||
GOPROXY: ${{ vars.GOPROXY }}
|
GOPROXY: ${{ vars.GOPROXY }}
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
test:
|
test:
|
||||||
runs-on: bookworm
|
runs-on: docker
|
||||||
steps:
|
steps:
|
||||||
- name: Setup apt mirror
|
- name: Setup apt mirror
|
||||||
run: sed -i "s,deb.debian.org,${{ vars.DEBIAN_MIRROR }},g" ${{ vars.DEBIAN_APT_SOURCES }}
|
run: sed -i "s,deb.debian.org,${{ vars.DEBIAN_MIRROR }},g" ${{ vars.DEBIAN_APT_SOURCES }}
|
||||||
if: ${{ vars.DEBIAN_MIRROR && vars.DEBIAN_APT_SOURCES }}
|
if: ${{ vars.DEBIAN_MIRROR && vars.DEBIAN_APT_SOURCES }}
|
||||||
- name: Setup Debian environment
|
- name: Setup Debian environment
|
||||||
run: apt update && apt install -y nodejs
|
run: apt update && apt install -y nodejs
|
||||||
|
- name: Setup cache for golang toolchain
|
||||||
|
uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
/opt/hostedtoolcache/go
|
||||||
|
/root/go/pkg/mod
|
||||||
|
/root/.cache/go-build
|
||||||
|
key: ${{ runner.os }}-golang
|
||||||
- name: Setup Golang
|
- name: Setup Golang
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: 1.24.0
|
go-version: 1.24.0
|
||||||
|
cache-dependency-path: |
|
||||||
|
go.sum
|
||||||
- name: Check out repository code
|
- name: Check out repository code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -2,3 +2,6 @@
|
|||||||
|
|
||||||
data
|
data
|
||||||
__debug*
|
__debug*
|
||||||
|
|
||||||
|
.env
|
||||||
|
.envrc
|
10
Dockerfile
10
Dockerfile
@ -1,10 +1,12 @@
|
|||||||
FROM docker.io/library/golang:1.23-alpine
|
FROM docker.io/library/golang:1.24-alpine
|
||||||
ARG GOPROXY
|
ARG GOPROXY=direct
|
||||||
|
ARG TAGS=""
|
||||||
|
ARG ALPINE_MIRROR=https://mirrors.ustc.edu.cn
|
||||||
WORKDIR /src
|
WORKDIR /src
|
||||||
COPY go.mod go.sum ./
|
COPY go.mod go.sum ./
|
||||||
RUN apk add git && env GOPROXY=${GOPROXY:-direct} go mod download
|
RUN sed -i "s,https://dl-cdn.alpinelinux.org,${ALPINE_MIRROR}," /etc/apk/repositories; apk add git && env GOPROXY=${GOPROXY:-direct} go mod download
|
||||||
ADD . /src
|
ADD . /src
|
||||||
RUN go build -o /cache-proxy ./cmd/proxy
|
RUN go build -o /cache-proxy -tags "${TAGS}" ./cmd/proxy
|
||||||
|
|
||||||
FROM docker.io/library/alpine:3.21 AS runtime
|
FROM docker.io/library/alpine:3.21 AS runtime
|
||||||
COPY --from=0 /cache-proxy /bin/cache-proxy
|
COPY --from=0 /cache-proxy /bin/cache-proxy
|
||||||
|
17
cmd/proxy/debug.go
Normal file
17
cmd/proxy/debug.go
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
//go:build pprof
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
flag.BoolVar(&pprofEnabled, "pprof", true, "")
|
||||||
|
|
||||||
|
if v, ok := os.LookupEnv("SENTRY_DSN"); ok {
|
||||||
|
sentrydsn = v
|
||||||
|
}
|
||||||
|
flag.StringVar(&sentrydsn, "sentry", sentrydsn, "sentry dsn to report errors")
|
||||||
|
}
|
@ -4,6 +4,7 @@ import (
|
|||||||
"flag"
|
"flag"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/http/pprof"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"time"
|
"time"
|
||||||
@ -22,6 +23,7 @@ var (
|
|||||||
configFilePath = "config.yaml"
|
configFilePath = "config.yaml"
|
||||||
logLevel = "info"
|
logLevel = "info"
|
||||||
sentrydsn = ""
|
sentrydsn = ""
|
||||||
|
pprofEnabled = false
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@ -31,12 +33,8 @@ func init() {
|
|||||||
if v, ok := os.LookupEnv("LOG_LEVEL"); ok {
|
if v, ok := os.LookupEnv("LOG_LEVEL"); ok {
|
||||||
logLevel = v
|
logLevel = v
|
||||||
}
|
}
|
||||||
if v, ok := os.LookupEnv("SENTRY_DSN"); ok {
|
|
||||||
sentrydsn = v
|
|
||||||
}
|
|
||||||
flag.StringVar(&configFilePath, "config", configFilePath, "path to config file")
|
flag.StringVar(&configFilePath, "config", configFilePath, "path to config file")
|
||||||
flag.StringVar(&logLevel, "log-level", logLevel, "log level. (trace, debug, info, warn, error)")
|
flag.StringVar(&logLevel, "log-level", logLevel, "log level. (trace, debug, info, warn, error)")
|
||||||
flag.StringVar(&sentrydsn, "sentry", sentrydsn, "sentry dsn to report errors")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func configFromFile(path string) (*cacheproxy.Config, error) {
|
func configFromFile(path string) (*cacheproxy.Config, error) {
|
||||||
@ -57,9 +55,9 @@ func configFromFile(path string) (*cacheproxy.Config, error) {
|
|||||||
Local: &cacheproxy.LocalStorage{
|
Local: &cacheproxy.LocalStorage{
|
||||||
Path: "./data",
|
Path: "./data",
|
||||||
TemporaryFilePattern: "temp.*",
|
TemporaryFilePattern: "temp.*",
|
||||||
},
|
|
||||||
Accel: cacheproxy.Accel{
|
Accel: cacheproxy.Accel{
|
||||||
ResponseWithHeaders: []string{"X-Sendfile", "X-Accel-Redirect"},
|
RespondWithHeaders: []string{"X-Sendfile", "X-Accel-Redirect"},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Misc: cacheproxy.MiscConfig{
|
Misc: cacheproxy.MiscConfig{
|
||||||
@ -124,6 +122,14 @@ func main() {
|
|||||||
|
|
||||||
mux.HandleFunc("GET /", server.HandleRequestWithCache)
|
mux.HandleFunc("GET /", server.HandleRequestWithCache)
|
||||||
|
|
||||||
|
if pprofEnabled {
|
||||||
|
mux.HandleFunc("GET /debug/pprof/", pprof.Index)
|
||||||
|
mux.HandleFunc("GET /debug/pprof/cmdline", pprof.Cmdline)
|
||||||
|
mux.HandleFunc("GET /debug/pprof/profile", pprof.Profile)
|
||||||
|
mux.HandleFunc("GET /debug/pprof/symbol", pprof.Symbol)
|
||||||
|
mux.HandleFunc("GET /debug/pprof/trace", pprof.Trace)
|
||||||
|
}
|
||||||
|
|
||||||
slog.With("addr", ":8881").Info("serving app")
|
slog.With("addr", ":8881").Info("serving app")
|
||||||
if err := http.ListenAndServe(":8881", middleware.Use(mux,
|
if err := http.ListenAndServe(":8881", middleware.Use(mux,
|
||||||
recover.Recover(),
|
recover.Recover(),
|
||||||
|
20
compose.release.yaml
Normal file
20
compose.release.yaml
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
services:
|
||||||
|
cache-proxy:
|
||||||
|
image: ${REPOSITORY:-git.jeffthecoder.xyz}/${OWNER:-public}/cache-proxy:${VERSION:-latest}
|
||||||
|
build:
|
||||||
|
args:
|
||||||
|
GOPROXY: ${GOPROXY:-direct}
|
||||||
|
ALPINE_MIRROR: ${ALPINE_MIRROR:-https://mirrors.ustc.edu.cn}
|
||||||
|
|
||||||
|
restart: always
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
- ./config.yaml:/config.yaml:ro
|
||||||
|
- ./data:/data
|
||||||
|
ports:
|
||||||
|
- 8881:8881
|
||||||
|
environment:
|
||||||
|
- LOG_LEVEL=info
|
||||||
|
env_file:
|
||||||
|
- path: .env
|
||||||
|
required: false
|
13
compose.yaml
13
compose.yaml
@ -1,11 +1,12 @@
|
|||||||
services:
|
services:
|
||||||
cache-proxy:
|
cache-proxy:
|
||||||
image: 100.64.0.2:3000/guochao/cache-proxy
|
image: ${REPOSITORY:-git.jeffthecoder.xyz}/${OWNER:-public}/cache-proxy:${VERSION:-latest}
|
||||||
build:
|
build:
|
||||||
args:
|
args:
|
||||||
GOPROXY: ${GOPROXY:-direct}
|
GOPROXY: ${GOPROXY:-direct}
|
||||||
|
ALPINE_MIRROR: ${ALPINE_MIRROR:-https://mirrors.ustc.edu.cn}
|
||||||
|
TAGS: pprof
|
||||||
|
|
||||||
pull_policy: always
|
|
||||||
restart: always
|
restart: always
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
@ -14,8 +15,8 @@ services:
|
|||||||
ports:
|
ports:
|
||||||
- 8881:8881
|
- 8881:8881
|
||||||
environment:
|
environment:
|
||||||
- HTTPS_PROXY=http://100.64.0.23:7890
|
- SENTRY_DSN=${SENTRY_DSN}
|
||||||
- HTTP_PROXY=http://100.64.0.23:7890
|
|
||||||
- ALL_PROXY=http://100.64.0.23:7890
|
|
||||||
- SENTRY_DSN=http://3b7336602c77427d96318074cd86ee04@100.64.0.2:8000/2
|
|
||||||
- LOG_LEVEL=debug
|
- LOG_LEVEL=debug
|
||||||
|
env_file:
|
||||||
|
- path: .env
|
||||||
|
required: false
|
@ -36,17 +36,18 @@ func (upstream Upstream) GetPath(orig string) (string, bool, error) {
|
|||||||
type LocalStorage struct {
|
type LocalStorage struct {
|
||||||
Path string `yaml:"path"`
|
Path string `yaml:"path"`
|
||||||
TemporaryFilePattern string `yaml:"temporary-file-pattern"`
|
TemporaryFilePattern string `yaml:"temporary-file-pattern"`
|
||||||
|
|
||||||
|
Accel Accel `yaml:"accel"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Accel struct {
|
type Accel struct {
|
||||||
EnableByHeader string `yaml:"enable-by-header"`
|
EnableByHeader string `yaml:"enable-by-header"`
|
||||||
ResponseWithHeaders []string `yaml:"response-with-headers"`
|
RespondWithHeaders []string `yaml:"respond-with-headers"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Storage struct {
|
type Storage struct {
|
||||||
Type string `yaml:"type"`
|
Type string `yaml:"type"`
|
||||||
Local *LocalStorage `yaml:"local"`
|
Local *LocalStorage `yaml:"local"`
|
||||||
Accel Accel `yaml:"accel"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type CachePolicyOnPath struct {
|
type CachePolicyOnPath struct {
|
||||||
|
29
config.yaml
29
config.yaml
@ -1,7 +1,7 @@
|
|||||||
upstream:
|
upstream:
|
||||||
- server: https://mirrors.aliyun.com
|
- server: https://mirrors.aliyun.com
|
||||||
match:
|
match:
|
||||||
match: /(debian|ubuntu|ubuntu-releases|alpine|archlinux|kali|manjaro|msys2|almalinux|rocky|centos|centos-stream|centos-vault|fedora|epel|gnu)/(.*)
|
match: /(debian|ubuntu|ubuntu-releases|alpine|archlinux|kali|manjaro|msys2|almalinux|rocky|centos|centos-stream|centos-vault|fedora|epel|gnu|pypi)/(.*)
|
||||||
replace: '/$1/$2'
|
replace: '/$1/$2'
|
||||||
- server: https://mirrors.tencent.com
|
- server: https://mirrors.tencent.com
|
||||||
match:
|
match:
|
||||||
@ -9,7 +9,7 @@ upstream:
|
|||||||
replace: '/$1/$2'
|
replace: '/$1/$2'
|
||||||
- server: https://mirrors.ustc.edu.cn
|
- server: https://mirrors.ustc.edu.cn
|
||||||
match:
|
match:
|
||||||
match: /(debian|ubuntu|ubuntu-releases|alpine|archlinux|kali|manjaro|msys2|almalinux|rocky|centos|centos-stream|centos-vault|fedora|epel|elrepo|remi|rpmfusion|tailscale|gnu|rust-static)/(.*)
|
match: /(debian|ubuntu|ubuntu-releases|alpine|archlinux|kali|manjaro|msys2|almalinux|rocky|centos|centos-stream|centos-vault|fedora|epel|elrepo|remi|rpmfusion|tailscale|gnu|rust-static|pypi)/(.*)
|
||||||
replace: '/$1/$2'
|
replace: '/$1/$2'
|
||||||
- server: https://packages.microsoft.com/repos/code
|
- server: https://packages.microsoft.com/repos/code
|
||||||
match:
|
match:
|
||||||
@ -88,21 +88,21 @@ upstream:
|
|||||||
allowed-redirect: "^https?://cf-builds.garudalinux.org/.*/chaotic-aur/.*$"
|
allowed-redirect: "^https?://cf-builds.garudalinux.org/.*/chaotic-aur/.*$"
|
||||||
|
|
||||||
misc:
|
misc:
|
||||||
first-chunk-bytes: 31457280 # 1024*1024*30, all upstreams with 200 response will wait for the first chunks to select a upstream by bandwidth, instead of by latency. defaults to 50M
|
first-chunk-bytes: 31457280 ## 1024*1024*30, all upstreams with 200 response will wait for the first chunks to select a upstream by bandwidth, instead of by latency. defaults to 50M
|
||||||
# chunk-bytes: 1048576 # 1024*1024
|
# chunk-bytes: 1048576 ## 1024*1024
|
||||||
|
|
||||||
cache:
|
cache:
|
||||||
timeout: 1h
|
timeout: 1h
|
||||||
policies:
|
policies:
|
||||||
- match: '.*\.(rpm|deb|apk|iso)$' # rpm/deb/apk/iso won't change, create only
|
- match: '.*\.(rpm|deb|apk|iso)$' ## rpm/deb/apk/iso won't change, create only
|
||||||
refresh-after: never
|
refresh-after: never
|
||||||
- match: '^/.*-security/.*' # mostly ubuntu/debian security
|
- match: '^/.*-security/.*' ## mostly ubuntu/debian security
|
||||||
refresh-after: 1h
|
refresh-after: 1h
|
||||||
- match: '^/archlinux-localaur/.*' # archlinux local repo
|
- match: '^/archlinux-localaur/.*' ## archlinux local repo
|
||||||
refresh-after: never
|
refresh-after: never
|
||||||
- match: '^/(archlinux.*|chaotic-aur)/*.tar.*' # archlinux packages
|
- match: '^/(archlinux.*|chaotic-aur)/*.tar.*' ## archlinux packages
|
||||||
refresh-after: never
|
refresh-after: never
|
||||||
- match: '/chaotic-aur/.*\.db$' # archlinux chaotic-aur database
|
- match: '/chaotic-aur/.*\.db$' ## archlinux chaotic-aur database
|
||||||
refresh-after: 24h
|
refresh-after: 24h
|
||||||
- match: '/centos/7'
|
- match: '/centos/7'
|
||||||
refresh-after: never
|
refresh-after: never
|
||||||
@ -110,12 +110,12 @@ cache:
|
|||||||
refresh-after: never
|
refresh-after: never
|
||||||
|
|
||||||
storage:
|
storage:
|
||||||
type: local # ignored
|
type: local ## ignored for now
|
||||||
local:
|
local:
|
||||||
path: ./data # defaults to ./data
|
path: ./data ## defaults to ./data
|
||||||
|
|
||||||
accel:
|
accel:
|
||||||
# example nginx config:
|
## example nginx config:
|
||||||
## location /i {
|
## location /i {
|
||||||
## internal;
|
## internal;
|
||||||
## alias /path/to/data;
|
## alias /path/to/data;
|
||||||
@ -125,5 +125,8 @@ storage:
|
|||||||
## proxy_set_header X-Accel-Path /i;
|
## proxy_set_header X-Accel-Path /i;
|
||||||
## }
|
## }
|
||||||
##
|
##
|
||||||
|
## then cache proxy will respond to backend a header to indicate sendfile(join(X-Accel-Path, relpath))
|
||||||
# enable-by-header: "X-Accel-Path"
|
# enable-by-header: "X-Accel-Path"
|
||||||
|
|
||||||
|
## respond with different headers to
|
||||||
|
# respond-with-headers: [X-Sendfile, X-Accel-Redirect]
|
||||||
|
30
server.go
30
server.go
@ -7,6 +7,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
@ -106,7 +107,7 @@ type Chunk struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (server *Server) serveFile(w http.ResponseWriter, r *http.Request, path string) {
|
func (server *Server) serveFile(w http.ResponseWriter, r *http.Request, path string) {
|
||||||
if location := r.Header.Get(server.Storage.Accel.EnableByHeader); server.Storage.Accel.EnableByHeader != "" && location != "" {
|
if location := r.Header.Get(server.Storage.Local.Accel.EnableByHeader); server.Storage.Local.Accel.EnableByHeader != "" && location != "" {
|
||||||
relPath, err := filepath.Rel(server.Storage.Local.Path, path)
|
relPath, err := filepath.Rel(server.Storage.Local.Path, path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||||
@ -114,7 +115,7 @@ func (server *Server) serveFile(w http.ResponseWriter, r *http.Request, path str
|
|||||||
}
|
}
|
||||||
accelPath := filepath.Join(location, relPath)
|
accelPath := filepath.Join(location, relPath)
|
||||||
|
|
||||||
for _, headerKey := range server.Storage.Accel.ResponseWithHeaders {
|
for _, headerKey := range server.Storage.Local.Accel.RespondWithHeaders {
|
||||||
w.Header().Set(headerKey, accelPath)
|
w.Header().Set(headerKey, accelPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -332,7 +333,30 @@ func (server *Server) streamOnline(w http.ResponseWriter, r *http.Request, mtime
|
|||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
slog.With("error", err, "upstreamIdx", selectedIdx).Error("something happened during download. will not cache this response")
|
logger := slog.With("upstreamIdx", selectedIdx)
|
||||||
|
logger.Error("something happened during download. will not cache this response. setting lingering to reset the connection.")
|
||||||
|
hijacker, ok := w.(http.Hijacker)
|
||||||
|
if !ok {
|
||||||
|
logger.Warn("response writer is not a hijacker. failed to set lingering")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
conn, _, err := hijacker.Hijack()
|
||||||
|
if err != nil {
|
||||||
|
logger.With("error", err).Warn("hijack failed. failed to set lingering")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
|
tcpConn, ok := conn.(*net.TCPConn)
|
||||||
|
if !ok {
|
||||||
|
logger.With("error", err).Warn("connection is not a *net.TCPConn. failed to set lingering")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := tcpConn.SetLinger(0); err != nil {
|
||||||
|
logger.With("error", err).Warn("failed to set lingering")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
logger.Debug("connection set to linger. it will be reset once the conn.Close is called")
|
||||||
}
|
}
|
||||||
go func() {
|
go func() {
|
||||||
defer func() {
|
defer func() {
|
||||||
|
Reference in New Issue
Block a user