Compare commits

..

7 Commits

Author SHA1 Message Date
dcarrillo 7e2f3c397a Remove the 'Go Report Card' badge, as the service has been discontinued 2026-07-22 13:34:09 +02:00
dcarrillo 6284359df5 Revise Docker image size information
Updated Docker image size description in README.
2026-07-22 13:03:59 +02:00
dcarrillo cd6d5db918 chore: Upgrade integration tests dependencies (#59) 2026-07-22 12:55:28 +02:00
dcarrillo 04d6927cf5 chore: Update README.md 2026-07-22 12:36:00 +02:00
dcarrillo 49a63ef071 feat: Create multiarch artifacts (#58) 2026-07-22 12:33:57 +02:00
dcarrillo a36a5504fa chore: Add AGENTS.md 2026-07-21 18:40:56 +02:00
dcarrillo 2e32a20f60 refactor: dependency injection instead of package globals (#57)
* refactor: export Settings type, Setup returns value

* refactor: resolver.Setup takes explicit Settings struct

* refactor: GetHeadersWithoutTrustedHeaders takes explicit header params

* refactor: server constructors take narrow config

* refactor: Router struct with handler methods, remove geoSvc global

* refactor: wire DI through main, remove setting.App references

* refactor: remove App global, use returned Settings

* refactor: update router tests for DI

* chore: fix lint issues — rename ServerTimeouts to Timeouts, fix shadowed variables
2026-07-21 18:37:21 +02:00
30 changed files with 603 additions and 452 deletions
+92 -16
View File
@@ -27,17 +27,26 @@ jobs:
- name: ${{ matrix.make }} - name: ${{ matrix.make }}
run: make ${{ matrix.make }} run: make ${{ matrix.make }}
deploy: build-binaries:
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: tests needs: tests
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags') if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags')
strategy: strategy:
matrix: matrix:
goosarch: [linux-amd64] include:
- goos: linux
goarch: amd64
- goos: linux
goarch: arm64
- goos: darwin
goarch: amd64
- goos: darwin
goarch: arm64
- goos: windows
goarch: amd64
steps: steps:
- uses: actions/checkout@v6 - uses: actions/checkout@v6
with:
fetch-depth: 0
- name: install go - name: install go
uses: actions/setup-go@v6 uses: actions/setup-go@v6
with: with:
@@ -47,27 +56,94 @@ jobs:
- name: Set env - name: Set env
run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
- name: Sign in to dockerhub
run: echo "${{ secrets.DOCKERHUB_TOKEN }}" | docker login -u ${{ secrets.DOCKERHUB_USERNAME }} --password-stdin
- name: Deploy image
run: make docker-push VERSION=$RELEASE_VERSION
- name: Build - name: Build
run: make build VERSION=$RELEASE_VERSION run: make build VERSION="$RELEASE_VERSION" GOOS=${{ matrix.goos }} GOARCH=${{ matrix.goarch }}
- name: Prepare release - name: Package
run: |
BASE="whatismyip-$RELEASE_VERSION-${{ matrix.goos }}-${{ matrix.goarch }}"
if [ "${{ matrix.goos }}" = "windows" ]; then
cp whatismyip "${BASE}.exe"
zip "${BASE}.zip" "${BASE}.exe" LICENSE README.md
sha256sum "${BASE}.zip" > "${BASE}.zip.sha256"
else
tar zcvf "${BASE}.tar.gz" whatismyip LICENSE README.md
sha256sum "${BASE}.tar.gz" > "${BASE}.tar.gz.sha256"
fi
- name: Upload artifact
uses: actions/upload-artifact@v6
with:
name: binary-${{ matrix.goos }}-${{ matrix.goarch }}
path: |
whatismyip-${{ env.RELEASE_VERSION }}-${{ matrix.goos }}-${{ matrix.goarch }}.*
if-no-files-found: error
publish-docker:
runs-on: ubuntu-latest
needs: tests
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags')
steps:
- uses: actions/checkout@v6
- name: Set env
run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
- name: Set up QEMU
uses: docker/setup-qemu-action@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4
- name: Sign in to Docker Hub
uses: docker/login-action@v4
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v6
with:
context: .
platforms: linux/amd64,linux/arm64
build-args: ARG_VERSION=${{ env.RELEASE_VERSION }}
tags: |
${{ secrets.DOCKERHUB_USERNAME }}/whatismyip:${{ env.RELEASE_VERSION }}
${{ secrets.DOCKERHUB_USERNAME }}/whatismyip:latest
push: true
cache-from: type=gha
cache-to: type=gha,mode=max
release:
runs-on: ubuntu-latest
needs: [build-binaries, publish-docker]
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags')
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Set env
run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
- name: Download all artifacts
uses: actions/download-artifact@v6
with:
pattern: binary-*
merge-multiple: true
- name: Generate changelog
run: | run: |
git log $(git describe HEAD~ --tags --abbrev=0)..HEAD --pretty='format:%h - %s <%an>' --no-merges > changelog.txt git log $(git describe HEAD~ --tags --abbrev=0)..HEAD --pretty='format:%h - %s <%an>' --no-merges > changelog.txt
tar zcvf whatismyip-$RELEASE_VERSION-${{matrix.goosarch}}.tar.gz whatismyip LICENSE README.md
sha256sum whatismyip-$RELEASE_VERSION-${{matrix.goosarch}}.tar.gz > whatismyip-$RELEASE_VERSION-${{matrix.goosarch}}.tar.gz.sha256
- name: Release - name: Release
uses: softprops/action-gh-release@v3 uses: softprops/action-gh-release@v3
with: with:
body_path: changelog.txt body_path: changelog.txt
files: | files: |
whatismyip-${{env.RELEASE_VERSION}}-${{matrix.goosarch}}.tar.gz whatismyip-${{ env.RELEASE_VERSION }}-*.tar.gz
whatismyip-${{env.RELEASE_VERSION}}-${{matrix.goosarch}}.tar.gz.sha256 whatismyip-${{ env.RELEASE_VERSION }}-*.tar.gz.sha256
whatismyip-${{ env.RELEASE_VERSION }}-*.zip
whatismyip-${{ env.RELEASE_VERSION }}-*.zip.sha256
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+1
View File
@@ -1 +1,2 @@
whatismyip whatismyip
dist/
+68
View File
@@ -0,0 +1,68 @@
# whatismyip
Single-binary Go service: HTTP/TLS/QUIC server that returns the client's IP, geolocation, ASN, headers, TCP port scan, and DNS discovery. Uses [gin](https://github.com/gin-gonic/gin) + [httprouter](https://github.com/julienschmidt/httprouter). Requires Go >= 1.25.
## Commands
```sh
make build # CGO_ENABLED=0 -> ./whatismyip (GOOS/GOARCH for cross-compile)
make build-all # cross-compile all 5 platforms into dist/
make unit-test # go test -count=1 -race -short -cover ./...
make integration-test # requires Docker; runs integration-tests/ module
make test # unit-test + integration-test
make lint # gofmt -l + golangci-lint + shadow (auto-installs tools)
make docker-build # local single-arch build (root Dockerfile)
make docker-run # builds dev image + runs with test DBs, ports :8080 :8081 :9100
```
**Order and quirks**: `lint -> unit-test -> integration-test`. Integration tests use testcontainers-go (needs Docker). They are a separate Go module with its own `go.mod` — run from the `integration-tests/` directory. They use `test/Dockerfile` (distinct from the root `Dockerfile`).
## Architecture
```
cmd/whatismyip.go # entrypoint: parses flags, wires servers, runs
├── server/server.go # Manager: Start/Stop/SIGHUP-reload for all server types
├── server/tcp.go # plain HTTP
├── server/tls.go # TLS/HTTP2
├── server/quic.go # HTTP/3 (requires TLS server)
├── server/dns.go # micro-DNS server for discovery
├── server/prometheus.go # separate metrics endpoint
├── router/setup.go # Gin route registration
│ ├── router/generic.go # /, /client-port, /all, /json
│ ├── router/geo.go # /geo/*, /asn/*
│ ├── router/headers.go # /headers, /:header
│ ├── router/dns.go # DNS discovery HTTP handler
│ ├── router/port_scanner.go # /scan/tcp/:port
│ └── router/templates.go # embedded HTML template
├── service/geo.go # GeoIP lookup service (MaxMind MMDB)
├── service/port_scanner.go
├── resolver/setup.go # authoritative micro-DNS server (miekg/dns)
├── internal/setting/ # flag parsing + resolver YAML config
├── internal/httputils/ # header filtering and formatting
├── internal/metrics/ # Prometheus counters/histograms (opt-in)
├── internal/validator/uuid/
├── internal/core/version.go
└── models/geo.go # GeoDB wrapper around oschwald/maxminddb-golang
```
## Key facts
- **DNS discovery**: enabled via `-resolver test/resolver.yml`. The resolver is an authoritative DNS server answering for a subdomain. Clients prove their DNS provider by resolving `<uuid>.dns.<domain>`.
- **Geo**: both `-geoip2-city` and `-geoip2-asn` required together (or omitted). Uses test DBs in `test/`.
- **Trusted headers**: `-trusted-header X-Real-IP` for proxy mode. When set without `-trusted-port-header`, client port shows "unknown".
- **SIGHUP**: reloads GeoIP databases and restarts all servers without full stop.
- **HTTP/3**: requires `-tls-bind`; reuses its port for UDP.
- **Prometheus**: separate process/port via `-metrics-bind`. Metrics prefixed `whatismyip_*`.
- **Port scan**: enabled by default; disable with `-disable-scan`.
- **Embedded template**: `router/templates.go` has the default `home` template. `-template` overrides.
- **Version**: injected at build via `-ldflags="-X 'github.com/dcarrillo/whatismyip/internal/core.Version=${VERSION}'"`.
## Testing quirks
- Integration tests need Docker (testcontainers-go) and will take longer. They run against a real containerized build.
- Unit tests in `internal/setting/`, `internal/httputils/`, `internal/metrics/`, `internal/validator/uuid/`, `models/`, `router/`, and `service/` use test DBs from `test/`.
- The router test reads `test/` resources by relative path — run from repo root.
## Lint
Uses `golangci-lint` v2 config (`.golangci.yaml`) with strict `revive` rules and `goimports` as formatter. One exception: `internal/metrics/` gets a pass on `var-naming` (package name conflicts with stdlib `metrics`). Also runs `shadow` and `gofmt -l -d`.
+9 -18
View File
@@ -1,29 +1,20 @@
FROM golang:1.25-alpine AS builder FROM --platform=$BUILDPLATFORM golang:1.25-alpine AS builder
ARG ARG_VERSION ARG ARG_VERSION
ARG TARGETOS
ARG TARGETARCH
ENV VERSION=$ARG_VERSION ENV VERSION=$ARG_VERSION
WORKDIR /app WORKDIR /app
COPY go.mod . COPY go.mod go.sum ./
COPY go.sum .
RUN --mount=type=cache,target=/go/pkg/mod/ go mod download -x RUN --mount=type=cache,target=/go/pkg/mod/ go mod download -x
COPY . . COPY . .
FROM builder AS build-dev-app RUN --mount=type=cache,target=/go/pkg/mod/ apk --no-cache add make ca-certificates \
# hadolint ignore=DL3018
RUN --mount=type=cache,target=/go/pkg/mod/ apk --no-cache add make && make build
FROM builder AS build-prod-app
# hadolint ignore=DL3018
RUN apk --no-cache update && apk add --no-cache ca-certificates make upx \
&& update-ca-certificates \ && update-ca-certificates \
&& make build \ && GOOS=$TARGETOS GOARCH=$TARGETARCH make build
&& upx --best --lzma whatismyip
FROM scratch AS dev FROM scratch
COPY --from=build-dev-app /app/whatismyip /usr/bin/ COPY --from=builder /app/whatismyip /usr/bin/
ENTRYPOINT ["whatismyip"] COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
FROM scratch AS prod
COPY --from=build-prod-app /app/whatismyip /usr/bin/
ENTRYPOINT ["whatismyip"] ENTRYPOINT ["whatismyip"]
+13 -16
View File
@@ -1,6 +1,8 @@
GOPATH ?= $(shell go env GOPATH) GOPATH ?= $(shell go env GOPATH)
VERSION ?= devel-$(shell git rev-parse --short HEAD) VERSION ?= devel-$(shell git rev-parse --short HEAD)
DOCKER_URL ?= dcarrillo/whatismyip DOCKER_URL ?= dcarrillo/whatismyip
GOOS ?= $(shell go env GOOS)
GOARCH ?= $(shell go env GOARCH)
.PHONY: test .PHONY: test
test: unit-test integration-test test: unit-test integration-test
@@ -32,25 +34,20 @@ lint: install-tools
shadow ./... shadow ./...
build: build:
CGO_ENABLED=0 go build -ldflags="-s -w -X 'github.com/dcarrillo/whatismyip/internal/core.Version=${VERSION}'" -o whatismyip ./cmd CGO_ENABLED=0 GOOS=$(GOOS) GOARCH=$(GOARCH) go build -ldflags="-s -w -X 'github.com/dcarrillo/whatismyip/internal/core.Version=${VERSION}'" -o whatismyip ./cmd
docker-build-dev: build-all:
docker build --target=dev --build-arg=ARG_VERSION="${VERSION}" --tag ${DOCKER_URL}:${VERSION} . @mkdir -p dist
GOOS=linux GOARCH=amd64 $(MAKE) build && mv whatismyip dist/whatismyip-linux-amd64
GOOS=linux GOARCH=arm64 $(MAKE) build && mv whatismyip dist/whatismyip-linux-arm64
GOOS=darwin GOARCH=amd64 $(MAKE) build && mv whatismyip dist/whatismyip-darwin-amd64
GOOS=darwin GOARCH=arm64 $(MAKE) build && mv whatismyip dist/whatismyip-darwin-arm64
GOOS=windows GOARCH=amd64 $(MAKE) build && mv whatismyip dist/whatismyip-windows-amd64.exe
docker-build-prod: docker-build:
docker build --target=prod --build-arg=ARG_VERSION="${VERSION}" --tag ${DOCKER_URL}:${VERSION} . docker build --build-arg=ARG_VERSION="${VERSION}" --tag ${DOCKER_URL}:${VERSION} .
docker-push: docker-build-prod docker-run: docker-build
ifneq (,$(findstring devel-,$(VERSION)))
@echo "VERSION is set to ${VERSION}, I can't push devel builds"
exit 1
else
docker push ${DOCKER_URL}:${VERSION}
docker tag ${DOCKER_URL}:${VERSION} ${DOCKER_URL}:latest
docker push ${DOCKER_URL}:latest
endif
docker-run: docker-build-dev
docker run --tty --interactive --rm \ docker run --tty --interactive --rm \
--publish 8080:8080/tcp \ --publish 8080:8080/tcp \
--publish 8081:8081/tcp \ --publish 8081:8081/tcp \
+1 -2
View File
@@ -2,7 +2,6 @@
[![CI](https://github.com/dcarrillo/whatismyip/workflows/CI/badge.svg)](https://github.com/dcarrillo/whatismyip/actions) [![CI](https://github.com/dcarrillo/whatismyip/workflows/CI/badge.svg)](https://github.com/dcarrillo/whatismyip/actions)
[![CodeQL](https://github.com/dcarrillo/whatismyip/actions/workflows/codeql-analysis.yml/badge.svg)](https://github.com/dcarrillo/whatismyip/actions/workflows/codeql-analysis.yml) [![CodeQL](https://github.com/dcarrillo/whatismyip/actions/workflows/codeql-analysis.yml/badge.svg)](https://github.com/dcarrillo/whatismyip/actions/workflows/codeql-analysis.yml)
[![Go Report Card](https://goreportcard.com/badge/github.com/dcarrillo/whatismyip)](https://goreportcard.com/report/github.com/dcarrillo/whatismyip)
[![GitHub release](https://img.shields.io/github/release/dcarrillo/whatismyip.svg)](https://github.com/dcarrillo/whatismyip/releases/) [![GitHub release](https://img.shields.io/github/release/dcarrillo/whatismyip.svg)](https://github.com/dcarrillo/whatismyip/releases/)
[![License Apache 2.0](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](./LICENSE) [![License Apache 2.0](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](./LICENSE)
@@ -202,7 +201,7 @@ Download the latest version from [github](https://github.com/dcarrillo/whatismyi
## Docker ## Docker
An ultra-light (~6MB) image is available on [docker hub](https://hub.docker.com/r/dcarrillo/whatismyip). Since version `2.1.2`, the binary is compressed using [upx](https://github.com/upx/upx). A very lightweight (~9MB) image is available on [docker hub](https://hub.docker.com/r/dcarrillo/whatismyip). ~Since version `2.1.2`, the binary is compressed using [upx](https://github.com/upx/upx).~
### Run a container locally using test databases ### Run a container locally using test databases
+49 -34
View File
@@ -15,17 +15,17 @@ import (
"github.com/dcarrillo/whatismyip/internal/metrics" "github.com/dcarrillo/whatismyip/internal/metrics"
"github.com/dcarrillo/whatismyip/internal/setting" "github.com/dcarrillo/whatismyip/internal/setting"
"github.com/dcarrillo/whatismyip/resolver" "github.com/dcarrillo/whatismyip/resolver"
"github.com/dcarrillo/whatismyip/router"
"github.com/dcarrillo/whatismyip/server" "github.com/dcarrillo/whatismyip/server"
"github.com/dcarrillo/whatismyip/service" "github.com/dcarrillo/whatismyip/service"
"github.com/gin-contrib/secure" "github.com/gin-contrib/secure"
"github.com/patrickmn/go-cache" "github.com/patrickmn/go-cache"
"github.com/dcarrillo/whatismyip/router"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
) )
func main() { func main() {
o, err := setting.Setup(os.Args[1:]) cfg, o, err := setting.Setup(os.Args[1:])
if err != nil { if err != nil {
if errors.Is(err, flag.ErrHelp) || errors.Is(err, setting.ErrVersion) { if errors.Is(err, flag.ErrHelp) || errors.Is(err, setting.ErrVersion) {
fmt.Print(o) fmt.Print(o)
@@ -35,33 +35,44 @@ func main() {
os.Exit(1) os.Exit(1)
} }
servers := []server.Server{}
engine := setupEngine()
if setting.App.Resolver.Domain != "" {
store := cache.New(1*time.Minute, 10*time.Minute)
var dnsEngine *resolver.Resolver
if dnsEngine, err = resolver.Setup(store); err != nil {
log.Fatalf("Invalid resolver configuration: %s", err)
}
nameServer := server.NewDNSServer(context.Background(), dnsEngine.Handler())
servers = append(servers, nameServer)
engine.Use(router.GetDNSDiscoveryHandler(store, setting.App.Resolver.Domain, setting.App.Resolver.RedirectPort))
}
var geoSvc *service.Geo var geoSvc *service.Geo
if setting.App.GeodbPath.City != "" || setting.App.GeodbPath.ASN != "" { if cfg.GeodbPath.City != "" || cfg.GeodbPath.ASN != "" {
if geoSvc, err = service.NewGeo(context.Background(), setting.App.GeodbPath.City, setting.App.GeodbPath.ASN); err != nil { if geoSvc, err = service.NewGeo(context.Background(), cfg.GeodbPath.City, cfg.GeodbPath.ASN); err != nil {
log.Fatalf("Failed to load geo databases: %s", err) log.Fatalf("Failed to load geo databases: %s", err)
} }
} }
router.SetupTemplate(engine) servers := []server.Server{}
router.Setup(engine, geoSvc) engine := setupEngine(cfg)
servers = slices.Concat(servers, setupHTTPServers(context.Background(), engine.Handler()))
if setting.App.PrometheusAddress != "" { if cfg.Resolver.Domain != "" {
prometheusServer := server.NewPrometheusServer(context.Background()) store := cache.New(1*time.Minute, 10*time.Minute)
var dnsEngine *resolver.Resolver
if dnsEngine, err = resolver.Setup(store, resolver.Settings{
Domain: cfg.Resolver.Domain,
ResourceRecords: cfg.Resolver.ResourceRecords,
RedirectPort: cfg.Resolver.RedirectPort,
IPv4: cfg.Resolver.Ipv4,
IPv6: cfg.Resolver.Ipv6,
}); err != nil {
log.Fatalf("Invalid resolver configuration: %s", err)
}
nameServer := server.NewDNSServer(context.Background(), dnsEngine.Handler())
servers = append(servers, nameServer)
engine.Use(router.GetDNSDiscoveryHandler(store, geoSvc, cfg.Resolver.Domain, cfg.Resolver.RedirectPort))
}
rt := router.NewRouter(geoSvc, cfg.TrustedHeader, cfg.TrustedPortHeader, cfg.TemplatePath, cfg.DisableTCPScan)
router.SetupTemplate(engine, cfg.TemplatePath)
router.Setup(engine, rt)
servers = slices.Concat(servers, setupHTTPServers(context.Background(), engine.Handler(), cfg))
if cfg.PrometheusAddress != "" {
prometheusServer := server.NewPrometheusServer(context.Background(), cfg.PrometheusAddress,
server.Timeouts{
ReadTimeout: cfg.Server.ReadTimeout,
WriteTimeout: cfg.Server.WriteTimeout,
})
servers = append(servers, prometheusServer) servers = append(servers, prometheusServer)
} }
@@ -69,18 +80,18 @@ func main() {
whatismyip.Run() whatismyip.Run()
} }
func setupEngine() *gin.Engine { func setupEngine(cfg setting.Settings) *gin.Engine {
gin.DisableConsoleColor() gin.DisableConsoleColor()
if os.Getenv(gin.EnvGinMode) == "" { if os.Getenv(gin.EnvGinMode) == "" {
gin.SetMode(gin.ReleaseMode) gin.SetMode(gin.ReleaseMode)
} }
engine := gin.New() engine := gin.New()
engine.Use(gin.LoggerWithFormatter(httputils.GetLogFormatter), gin.Recovery()) engine.Use(gin.LoggerWithFormatter(httputils.GetLogFormatter), gin.Recovery())
if setting.App.PrometheusAddress != "" { if cfg.PrometheusAddress != "" {
metrics.Enable() metrics.Enable()
engine.Use(metrics.GinMiddleware()) engine.Use(metrics.GinMiddleware())
} }
if setting.App.EnableSecureHeaders { if cfg.EnableSecureHeaders {
engine.Use(secure.New(secure.Config{ engine.Use(secure.New(secure.Config{
BrowserXssFilter: true, BrowserXssFilter: true,
ContentTypeNosniff: true, ContentTypeNosniff: true,
@@ -88,24 +99,28 @@ func setupEngine() *gin.Engine {
})) }))
} }
_ = engine.SetTrustedProxies(nil) _ = engine.SetTrustedProxies(nil)
engine.TrustedPlatform = setting.App.TrustedHeader engine.TrustedPlatform = cfg.TrustedHeader
return engine return engine
} }
func setupHTTPServers(ctx context.Context, handler http.Handler) []server.Server { func setupHTTPServers(ctx context.Context, handler http.Handler, cfg setting.Settings) []server.Server {
var servers []server.Server var servers []server.Server
timeouts := server.Timeouts{
ReadTimeout: cfg.Server.ReadTimeout,
WriteTimeout: cfg.Server.WriteTimeout,
}
if setting.App.BindAddress != "" { if cfg.BindAddress != "" {
tcpServer := server.NewTCPServer(ctx, handler) tcpServer := server.NewTCPServer(ctx, handler, cfg.BindAddress, timeouts)
servers = append(servers, tcpServer) servers = append(servers, tcpServer)
} }
if setting.App.TLSAddress != "" { if cfg.TLSAddress != "" {
tlsServer := server.NewTLSServer(ctx, handler) tlsServer := server.NewTLSServer(ctx, handler, cfg.TLSAddress, cfg.TLSCrtPath, cfg.TLSKeyPath, timeouts)
servers = append(servers, tlsServer) servers = append(servers, tlsServer)
if setting.App.EnableHTTP3 { if cfg.EnableHTTP3 {
quicServer := server.NewQuicServer(ctx, tlsServer) quicServer := server.NewQuicServer(ctx, tlsServer, cfg.TLSAddress, cfg.TLSCrtPath, cfg.TLSKeyPath)
servers = append(servers, quicServer) servers = append(servers, quicServer)
} }
} }
+2 -2
View File
@@ -9,7 +9,7 @@ require (
github.com/miekg/dns v1.1.72 github.com/miekg/dns v1.1.72
github.com/oschwald/maxminddb-golang v1.13.1 github.com/oschwald/maxminddb-golang v1.13.1
github.com/patrickmn/go-cache v2.1.0+incompatible github.com/patrickmn/go-cache v2.1.0+incompatible
github.com/prometheus/client_golang v1.23.2 github.com/prometheus/client_golang v1.24.0
github.com/quic-go/quic-go v0.60.0 github.com/quic-go/quic-go v0.60.0
github.com/stretchr/testify v1.11.1 github.com/stretchr/testify v1.11.1
gopkg.in/yaml.v3 v3.0.1 gopkg.in/yaml.v3 v3.0.1
@@ -41,7 +41,7 @@ require (
github.com/pelletier/go-toml/v2 v2.4.3 // indirect github.com/pelletier/go-toml/v2 v2.4.3 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_model v0.6.2 // indirect github.com/prometheus/client_model v0.6.2 // indirect
github.com/prometheus/common v0.70.0 // indirect github.com/prometheus/common v0.70.1 // indirect
github.com/prometheus/procfs v0.21.1 // indirect github.com/prometheus/procfs v0.21.1 // indirect
github.com/quic-go/qpack v0.6.0 // indirect github.com/quic-go/qpack v0.6.0 // indirect
github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/rogpeppe/go-internal v1.14.1 // indirect
+6 -6
View File
@@ -40,8 +40,8 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= github.com/klauspost/compress v1.19.0 h1:sXLILfc9jV2QYWkzFOPWStmcUVH2RHEB1JCdY2oVvCQ=
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= github.com/klauspost/compress v1.19.0/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ=
github.com/klauspost/cpuid/v2 v2.4.0 h1:S6Hrbc7+ywsr0r+RLapfGBHfyefhCTwEh3A0tV913Dw= github.com/klauspost/cpuid/v2 v2.4.0 h1:S6Hrbc7+ywsr0r+RLapfGBHfyefhCTwEh3A0tV913Dw=
github.com/klauspost/cpuid/v2 v2.4.0/go.mod h1:19jmZ9mjzoF//ddRSUsv0zfBTJWh3QJh9FNxZTMrGxU= github.com/klauspost/cpuid/v2 v2.4.0/go.mod h1:19jmZ9mjzoF//ddRSUsv0zfBTJWh3QJh9FNxZTMrGxU=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
@@ -71,12 +71,12 @@ github.com/pelletier/go-toml/v2 v2.4.3 h1:GTRvJQutkOSftxIFD5xw9aepkYNuPWmVJpffdD
github.com/pelletier/go-toml/v2 v2.4.3/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= github.com/pelletier/go-toml/v2 v2.4.3/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= github.com/prometheus/client_golang v1.24.0 h1:5XStIklKuAtJSNpdD3s8XJj/Yv78IQmE1kbNk87JrAI=
github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg= github.com/prometheus/client_golang v1.24.0/go.mod h1:QcsNdotprC2nS4BTM2ucbcqxd2CeXTEa9jW7zHO9iDE=
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
github.com/prometheus/common v0.70.0 h1:bcpru3tWPVnxGnETLgOV5jbp/JRXgYEyv65CuBLAMMI= github.com/prometheus/common v0.70.1 h1:1HvjP4D5oL3t8RsPlwxA9onvvStjtIHYE5XuuwOi/PY=
github.com/prometheus/common v0.70.0/go.mod h1:S/SFasQmgGiYH6C81LKCtYa8QACgthGg5zxL2udV7SY= github.com/prometheus/common v0.70.1/go.mod h1:VdFUQDMZK3VLkurFUVhia6uys/0suUp86TJz5qbJRhc=
github.com/prometheus/procfs v0.21.1 h1:GljZCt+zSTS+NZq88cyQ1LjZ+RCHp3uVuabBWA5+OJI= github.com/prometheus/procfs v0.21.1 h1:GljZCt+zSTS+NZq88cyQ1LjZ+RCHp3uVuabBWA5+OJI=
github.com/prometheus/procfs v0.21.1/go.mod h1:aB55Cww9pdSJVHk0hUf0inxWyyjPogFIjmHKYgMKmtY= github.com/prometheus/procfs v0.21.1/go.mod h1:aB55Cww9pdSJVHk0hUf0inxWyyjPogFIjmHKYgMKmtY=
github.com/quic-go/go-ossfuzz-seeds v0.1.0 h1:APacT+iIaNF6fd8AGEiN3bT/Jtkd2jz4v4TzM7MFjy0= github.com/quic-go/go-ossfuzz-seeds v0.1.0 h1:APacT+iIaNF6fd8AGEiN3bT/Jtkd2jz4v4TzM7MFjy0=
+26 -27
View File
@@ -6,31 +6,34 @@ replace github.com/dcarrillo/whatismyip => ..
require ( require (
github.com/dcarrillo/whatismyip v0.0.0-00010101000000-000000000000 github.com/dcarrillo/whatismyip v0.0.0-00010101000000-000000000000
github.com/docker/docker v28.0.4+incompatible github.com/moby/moby/api v1.54.2
github.com/moby/moby/client v0.4.0
github.com/quic-go/quic-go v0.60.0 github.com/quic-go/quic-go v0.60.0
github.com/stretchr/testify v1.11.1 github.com/stretchr/testify v1.11.1
github.com/testcontainers/testcontainers-go v0.36.0 github.com/testcontainers/testcontainers-go v0.43.0
) )
require ( require (
dario.cat/mergo v1.0.1 // indirect dario.cat/mergo v1.0.2 // indirect
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect
github.com/Microsoft/go-winio v0.6.2 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/beorn7/perks v1.0.1 // indirect github.com/beorn7/perks v1.0.1 // indirect
github.com/bytedance/gopkg v0.1.4 // indirect github.com/bytedance/gopkg v0.1.4 // indirect
github.com/bytedance/sonic v1.15.2 // indirect github.com/bytedance/sonic v1.15.2 // indirect
github.com/bytedance/sonic/loader v0.5.1 // indirect github.com/bytedance/sonic/loader v0.5.1 // indirect
github.com/cenkalti/backoff/v4 v4.2.1 // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/cloudwego/base64x v0.1.7 // indirect github.com/cloudwego/base64x v0.1.7 // indirect
github.com/containerd/errdefs v1.0.0 // indirect
github.com/containerd/errdefs/pkg v0.3.0 // indirect
github.com/containerd/log v0.1.0 // indirect github.com/containerd/log v0.1.0 // indirect
github.com/containerd/platforms v0.2.1 // indirect github.com/containerd/platforms v0.2.1 // indirect
github.com/cpuguy83/dockercfg v0.3.2 // indirect github.com/cpuguy83/dockercfg v0.3.2 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect
github.com/distribution/reference v0.6.0 // indirect github.com/distribution/reference v0.6.0 // indirect
github.com/docker/go-connections v0.5.0 // indirect github.com/docker/go-connections v0.6.0 // indirect
github.com/docker/go-units v0.5.0 // indirect github.com/docker/go-units v0.5.0 // indirect
github.com/ebitengine/purego v0.8.2 // indirect github.com/ebitengine/purego v0.10.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/gabriel-vasile/mimetype v1.4.13 // indirect github.com/gabriel-vasile/mimetype v1.4.13 // indirect
github.com/gin-contrib/sse v1.1.1 // indirect github.com/gin-contrib/sse v1.1.1 // indirect
@@ -43,60 +46,56 @@ require (
github.com/go-playground/validator/v10 v10.30.3 // indirect github.com/go-playground/validator/v10 v10.30.3 // indirect
github.com/goccy/go-json v0.10.6 // indirect github.com/goccy/go-json v0.10.6 // indirect
github.com/goccy/go-yaml v1.19.2 // indirect github.com/goccy/go-yaml v1.19.2 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/google/uuid v1.6.0 // indirect github.com/google/uuid v1.6.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/compress v1.18.0 // indirect github.com/klauspost/compress v1.19.0 // indirect
github.com/klauspost/cpuid/v2 v2.4.0 // indirect github.com/klauspost/cpuid/v2 v2.4.0 // indirect
github.com/leodido/go-urn v1.4.0 // indirect github.com/leodido/go-urn v1.4.0 // indirect
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
github.com/magiconair/properties v1.8.9 // indirect github.com/magiconair/properties v1.8.10 // indirect
github.com/mattn/go-isatty v0.0.23 // indirect github.com/mattn/go-isatty v0.0.23 // indirect
github.com/moby/docker-image-spec v1.3.1 // indirect github.com/moby/docker-image-spec v1.3.1 // indirect
github.com/moby/patternmatcher v0.6.0 // indirect github.com/moby/go-archive v0.2.0 // indirect
github.com/moby/sys/sequential v0.5.0 // indirect github.com/moby/patternmatcher v0.6.1 // indirect
github.com/moby/sys/user v0.1.0 // indirect github.com/moby/sys/sequential v0.6.0 // indirect
github.com/moby/sys/user v0.4.0 // indirect
github.com/moby/sys/userns v0.1.0 // indirect github.com/moby/sys/userns v0.1.0 // indirect
github.com/moby/term v0.5.0 // indirect github.com/moby/term v0.5.2 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/morikuni/aec v1.0.0 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.1.1 // indirect github.com/opencontainers/image-spec v1.1.1 // indirect
github.com/oschwald/maxminddb-golang v1.13.1 // indirect github.com/oschwald/maxminddb-golang v1.13.1 // indirect
github.com/patrickmn/go-cache v2.1.0+incompatible // indirect github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
github.com/pelletier/go-toml/v2 v2.4.3 // indirect github.com/pelletier/go-toml/v2 v2.4.3 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
github.com/prometheus/client_golang v1.23.2 // indirect github.com/prometheus/client_golang v1.24.0 // indirect
github.com/prometheus/client_model v0.6.2 // indirect github.com/prometheus/client_model v0.6.2 // indirect
github.com/prometheus/common v0.70.0 // indirect github.com/prometheus/common v0.70.1 // indirect
github.com/prometheus/procfs v0.21.1 // indirect github.com/prometheus/procfs v0.21.1 // indirect
github.com/quic-go/qpack v0.6.0 // indirect github.com/quic-go/qpack v0.6.0 // indirect
github.com/shirou/gopsutil/v4 v4.25.1 // indirect github.com/shirou/gopsutil/v4 v4.26.5 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect github.com/sirupsen/logrus v1.9.4 // indirect
github.com/tklauser/go-sysconf v0.3.12 // indirect github.com/tklauser/go-sysconf v0.3.16 // indirect
github.com/tklauser/numcpus v0.6.1 // indirect github.com/tklauser/numcpus v0.11.0 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.3.1 // indirect github.com/ugorji/go/codec v1.3.1 // indirect
github.com/yusufpapurcu/wmi v1.2.4 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect
go.mongodb.org/mongo-driver/v2 v2.8.0 // indirect go.mongodb.org/mongo-driver/v2 v2.8.0 // indirect
go.opentelemetry.io/auto/sdk v1.2.1 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 // indirect
go.opentelemetry.io/otel v1.44.0 // indirect go.opentelemetry.io/otel v1.44.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.44.0 // indirect
go.opentelemetry.io/otel/metric v1.44.0 // indirect go.opentelemetry.io/otel/metric v1.44.0 // indirect
go.opentelemetry.io/otel/sdk v1.44.0 // indirect go.opentelemetry.io/otel/sdk v1.44.0 // indirect
go.opentelemetry.io/otel/sdk/metric v1.44.0 // indirect
go.opentelemetry.io/otel/trace v1.44.0 // indirect go.opentelemetry.io/otel/trace v1.44.0 // indirect
go.opentelemetry.io/proto/otlp v1.10.0 // indirect
golang.org/x/arch v0.29.0 // indirect golang.org/x/arch v0.29.0 // indirect
golang.org/x/crypto v0.54.0 // indirect golang.org/x/crypto v0.54.0 // indirect
golang.org/x/net v0.57.0 // indirect golang.org/x/net v0.57.0 // indirect
golang.org/x/sys v0.47.0 // indirect golang.org/x/sys v0.47.0 // indirect
golang.org/x/text v0.40.0 // indirect golang.org/x/text v0.40.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260715232425-e75dac1f907d // indirect
google.golang.org/protobuf v1.36.11 // indirect google.golang.org/protobuf v1.36.11 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect
) )
+62 -103
View File
@@ -1,9 +1,9 @@
dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s= dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8=
dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA=
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU= github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 h1:He8afgbRMd7mFxO99hRNu+6tazq8nFF9lIwo9JFroBk=
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8=
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg=
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
@@ -14,33 +14,35 @@ github.com/bytedance/sonic v1.15.2 h1:90H+rcF/FwLXwfB1cudOLq/je83n683Utf4Cbp0xHC
github.com/bytedance/sonic v1.15.2/go.mod h1:mT2NbXunuaEbnZ+mRIX/vYqKISmgEuHFDI4UzmKx2SA= github.com/bytedance/sonic v1.15.2/go.mod h1:mT2NbXunuaEbnZ+mRIX/vYqKISmgEuHFDI4UzmKx2SA=
github.com/bytedance/sonic/loader v0.5.1 h1:Ygpfa9zwRCCKSlrp5bBP/b/Xzc3VxsAW+5NIYXrOOpI= github.com/bytedance/sonic/loader v0.5.1 h1:Ygpfa9zwRCCKSlrp5bBP/b/Xzc3VxsAW+5NIYXrOOpI=
github.com/bytedance/sonic/loader v0.5.1/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo= github.com/bytedance/sonic/loader v0.5.1/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo=
github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cloudwego/base64x v0.1.7 h1:NppS+Fgzg5ovhn4NkUXaDT3x9jldgH5ToMCqzBSi2zI= github.com/cloudwego/base64x v0.1.7 h1:NppS+Fgzg5ovhn4NkUXaDT3x9jldgH5ToMCqzBSi2zI=
github.com/cloudwego/base64x v0.1.7/go.mod h1:Cu1PV9zfrSf7ET2tIbWbbEy7jO7HHJ13q4X2SQ8aWYg= github.com/cloudwego/base64x v0.1.7/go.mod h1:Cu1PV9zfrSf7ET2tIbWbbEy7jO7HHJ13q4X2SQ8aWYg=
github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI=
github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M=
github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE=
github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk=
github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A= github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A=
github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw= github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw=
github.com/cpuguy83/dockercfg v0.3.2 h1:DlJTyZGBDlXqUZ2Dk2Q3xHs/FtnooJJVaad2S9GKorA= github.com/cpuguy83/dockercfg v0.3.2 h1:DlJTyZGBDlXqUZ2Dk2Q3xHs/FtnooJJVaad2S9GKorA=
github.com/cpuguy83/dockercfg v0.3.2/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc= github.com/cpuguy83/dockercfg v0.3.2/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc=
github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s=
github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
github.com/docker/docker v28.0.4+incompatible h1:JNNkBctYKurkw6FrHfKqY0nKIDf5nrbxjVBtS+cdcok= github.com/docker/go-connections v0.6.0 h1:LlMG9azAe1TqfR7sO+NJttz1gy6KO7VJBh+pMmjSD94=
github.com/docker/docker v28.0.4+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.6.0/go.mod h1:AahvXYshr6JgfUJGdDCs2b5EZG/vmaMAntpSFH5BFKE=
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/ebitengine/purego v0.8.2 h1:jPPGWs2sZ1UgOSgD2bClL0MJIqu58nOmIcBuXr62z1I= github.com/ebitengine/purego v0.10.0 h1:QIw4xfpWT6GWTzaW5XEKy3HXoqrJGx1ijYHzTF0/ISU=
github.com/ebitengine/purego v0.8.2/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/ebitengine/purego v0.10.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/gabriel-vasile/mimetype v1.4.13 h1:46nXokslUBsAJE/wMsp5gtO500a4F3Nkz9Ufpk2AcUM= github.com/gabriel-vasile/mimetype v1.4.13 h1:46nXokslUBsAJE/wMsp5gtO500a4F3Nkz9Ufpk2AcUM=
@@ -68,22 +70,16 @@ github.com/goccy/go-json v0.10.6 h1:p8HrPJzOakx/mn/bQtjgNjdTcN+/S6FcG2CTtQOrHVU=
github.com/goccy/go-json v0.10.6/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= github.com/goccy/go-json v0.10.6/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/goccy/go-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM= github.com/goccy/go-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM=
github.com/goccy/go-yaml v1.19.2/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= github.com/goccy/go-yaml v1.19.2/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 h1:HWRh5R2+9EifMyIHV7ZV+MIZqgz+PMpZ14Jynv3O2Zs=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0/go.mod h1:JfhWUomR1baixubs02l85lZYYOm7LV6om4ceouMv45c=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/klauspost/compress v1.19.0 h1:sXLILfc9jV2QYWkzFOPWStmcUVH2RHEB1JCdY2oVvCQ=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.19.0/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ=
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
github.com/klauspost/cpuid/v2 v2.4.0 h1:S6Hrbc7+ywsr0r+RLapfGBHfyefhCTwEh3A0tV913Dw= github.com/klauspost/cpuid/v2 v2.4.0 h1:S6Hrbc7+ywsr0r+RLapfGBHfyefhCTwEh3A0tV913Dw=
github.com/klauspost/cpuid/v2 v2.4.0/go.mod h1:19jmZ9mjzoF//ddRSUsv0zfBTJWh3QJh9FNxZTMrGxU= github.com/klauspost/cpuid/v2 v2.4.0/go.mod h1:19jmZ9mjzoF//ddRSUsv0zfBTJWh3QJh9FNxZTMrGxU=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
@@ -96,29 +92,33 @@ github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
github.com/magiconair/properties v1.8.9 h1:nWcCbLq1N2v/cpNsy5WvQ37Fb+YElfq20WJ/a8RkpQM= github.com/magiconair/properties v1.8.10 h1:s31yESBquKXCV9a/ScB3ESkOjUYYv+X0rg8SYxI99mE=
github.com/magiconair/properties v1.8.9/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/magiconair/properties v1.8.10/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
github.com/mattn/go-isatty v0.0.23 h1:cYwCQTQf3HB6xUC+BtyCLZNr7IzbOmoZbmssVNzSyiQ= github.com/mattn/go-isatty v0.0.23 h1:cYwCQTQf3HB6xUC+BtyCLZNr7IzbOmoZbmssVNzSyiQ=
github.com/mattn/go-isatty v0.0.23/go.mod h1:nMCL3Zebbrt45jsMDgnfIwz6ydEQApk5oEI3HqDio6A= github.com/mattn/go-isatty v0.0.23/go.mod h1:nMCL3Zebbrt45jsMDgnfIwz6ydEQApk5oEI3HqDio6A=
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk= github.com/moby/go-archive v0.2.0 h1:zg5QDUM2mi0JIM9fdQZWC7U8+2ZfixfTYoHL7rWUcP8=
github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= github.com/moby/go-archive v0.2.0/go.mod h1:mNeivT14o8xU+5q1YnNrkQVpK+dnNe/K6fHqnTg4qPU=
github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc= github.com/moby/moby/api v1.54.2 h1:wiat9QAhnDQjA7wk1kh/TqHz2I1uUA7M7t9SAl/JNXg=
github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo= github.com/moby/moby/api v1.54.2/go.mod h1:+RQ6wluLwtYaTd1WnPLykIDPekkuyD/ROWQClE83pzs=
github.com/moby/sys/user v0.1.0 h1:WmZ93f5Ux6het5iituh9x2zAG7NFY9Aqi49jjE1PaQg= github.com/moby/moby/client v0.4.0 h1:S+2XegzHQrrvTCvF6s5HFzcrywWQmuVnhOXe2kiWjIw=
github.com/moby/sys/user v0.1.0/go.mod h1:fKJhFOnsCN6xZ5gSfbM6zaHGgDJMrqt9/reuj4T7MmU= github.com/moby/moby/client v0.4.0/go.mod h1:QWPbvWchQbxBNdaLSpoKpCdf5E+WxFAgNHogCWDoa7g=
github.com/moby/patternmatcher v0.6.1 h1:qlhtafmr6kgMIJjKJMDmMWq7WLkKIo23hsrpR3x084U=
github.com/moby/patternmatcher v0.6.1/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc=
github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU=
github.com/moby/sys/sequential v0.6.0/go.mod h1:uyv8EUTrca5PnDsdMGXhZe6CCe8U/UiTWd+lL+7b/Ko=
github.com/moby/sys/user v0.4.0 h1:jhcMKit7SA80hivmFJcbB1vqmw//wU61Zdui2eQXuMs=
github.com/moby/sys/user v0.4.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs=
github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g= github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g=
github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28= github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28=
github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= github.com/moby/term v0.5.2 h1:6qk3FJAFDs6i/q3W/pQ97SX192qKfZgGjCQqfCJkgzQ=
github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= github.com/moby/term v0.5.2/go.mod h1:d3djjFCrjnB+fl8NJux+EJzu0msscUP+f8it8hPkFLc=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
@@ -131,18 +131,16 @@ github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaR
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
github.com/pelletier/go-toml/v2 v2.4.3 h1:GTRvJQutkOSftxIFD5xw9aepkYNuPWmVJpffdDPYVpY= github.com/pelletier/go-toml/v2 v2.4.3 h1:GTRvJQutkOSftxIFD5xw9aepkYNuPWmVJpffdDPYVpY=
github.com/pelletier/go-toml/v2 v2.4.3/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= github.com/pelletier/go-toml/v2 v2.4.3/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU=
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= github.com/prometheus/client_golang v1.24.0 h1:5XStIklKuAtJSNpdD3s8XJj/Yv78IQmE1kbNk87JrAI=
github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg= github.com/prometheus/client_golang v1.24.0/go.mod h1:QcsNdotprC2nS4BTM2ucbcqxd2CeXTEa9jW7zHO9iDE=
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
github.com/prometheus/common v0.70.0 h1:bcpru3tWPVnxGnETLgOV5jbp/JRXgYEyv65CuBLAMMI= github.com/prometheus/common v0.70.1 h1:1HvjP4D5oL3t8RsPlwxA9onvvStjtIHYE5XuuwOi/PY=
github.com/prometheus/common v0.70.0/go.mod h1:S/SFasQmgGiYH6C81LKCtYa8QACgthGg5zxL2udV7SY= github.com/prometheus/common v0.70.1/go.mod h1:VdFUQDMZK3VLkurFUVhia6uys/0suUp86TJz5qbJRhc=
github.com/prometheus/procfs v0.21.1 h1:GljZCt+zSTS+NZq88cyQ1LjZ+RCHp3uVuabBWA5+OJI= github.com/prometheus/procfs v0.21.1 h1:GljZCt+zSTS+NZq88cyQ1LjZ+RCHp3uVuabBWA5+OJI=
github.com/prometheus/procfs v0.21.1/go.mod h1:aB55Cww9pdSJVHk0hUf0inxWyyjPogFIjmHKYgMKmtY= github.com/prometheus/procfs v0.21.1/go.mod h1:aB55Cww9pdSJVHk0hUf0inxWyyjPogFIjmHKYgMKmtY=
github.com/quic-go/go-ossfuzz-seeds v0.1.0 h1:APacT+iIaNF6fd8AGEiN3bT/Jtkd2jz4v4TzM7MFjy0= github.com/quic-go/go-ossfuzz-seeds v0.1.0 h1:APacT+iIaNF6fd8AGEiN3bT/Jtkd2jz4v4TzM7MFjy0=
@@ -153,57 +151,51 @@ github.com/quic-go/quic-go v0.60.0 h1:xcQioE8OM66UQLeUMHltK1CCcOu3JbVB4JAQdDQSB+
github.com/quic-go/quic-go v0.60.0/go.mod h1:wpKpjmPpftl30sL6pFh7REVpjbcCVy4zt2vDyK1TuJk= github.com/quic-go/quic-go v0.60.0/go.mod h1:wpKpjmPpftl30sL6pFh7REVpjbcCVy4zt2vDyK1TuJk=
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/shirou/gopsutil/v4 v4.25.1 h1:QSWkTc+fu9LTAWfkZwZ6j8MSUk4A2LV7rbH0ZqmLjXs= github.com/shirou/gopsutil/v4 v4.26.5 h1:RPcBXkpz7kOj9PqGFQOlBPZHsyaPvPVQc098y9RmCNM=
github.com/shirou/gopsutil/v4 v4.25.1/go.mod h1:RoUCUpndaJFtT+2zsZzzmhvbfGoDCJ7nFXKJf8GqJbI= github.com/shirou/gopsutil/v4 v4.26.5/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 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.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.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= 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.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
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.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.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= 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.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 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/testcontainers/testcontainers-go v0.36.0 h1:YpffyLuHtdp5EUsI5mT4sRw8GZhO/5ozyDT1xWGXt00= github.com/testcontainers/testcontainers-go v0.43.0 h1:oEQx5MW2DGd9z3AeEQfB2lPM0eLs7ztyaGRu75bFo5A=
github.com/testcontainers/testcontainers-go v0.36.0/go.mod h1:yk73GVJ0KUZIHUtFna6MO7QS144qYpoY8lEEtU9Hed0= github.com/testcontainers/testcontainers-go v0.43.0/go.mod h1:+VxkT2NQnKOZPKi6praMuMKYHYyOGXr0XSBSlSMCzFo=
github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA=
github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI=
github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw=
github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= github.com/tklauser/numcpus v0.11.0/go.mod h1:z+LwcLq54uWZTX0u/bGobaV34u6V7KNlTZejzM6/3MQ=
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= 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/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
github.com/ugorji/go/codec v1.3.1 h1:waO7eEiFDwidsBN6agj1vJQ4AG7lh2yqXyOXqhgQuyY= 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/ugorji/go/codec v1.3.1/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
go.mongodb.org/mongo-driver/v2 v2.8.0 h1:CxWDGQYY8QQwNjAl/aq2sfWakdnWZynnqJ9F4DhHbP8= go.mongodb.org/mongo-driver/v2 v2.8.0 h1:CxWDGQYY8QQwNjAl/aq2sfWakdnWZynnqJ9F4DhHbP8=
go.mongodb.org/mongo-driver/v2 v2.8.0/go.mod h1:yOI9kBsufol30iFsl1slpdq1I0eHPzybRWdyYUs8K/0= go.mongodb.org/mongo-driver/v2 v2.8.0/go.mod h1:yOI9kBsufol30iFsl1slpdq1I0eHPzybRWdyYUs8K/0=
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 h1:sbiXRNDSWJOTobXh5HyQKjq6wUC5tNybqjIqDpAY4CU=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0/go.mod h1:69uWxva0WgAA/4bu2Yy70SLDBwZXuQ6PbBpbsa5iZrQ=
go.opentelemetry.io/otel v1.44.0 h1:JjwHmHpA4iZ3wBxluu2fbbE7j4kqlE8jXyAyPXH7HqU= go.opentelemetry.io/otel v1.44.0 h1:JjwHmHpA4iZ3wBxluu2fbbE7j4kqlE8jXyAyPXH7HqU=
go.opentelemetry.io/otel v1.44.0/go.mod h1:BMgjTHL9WPRlRjL2oZCBTL4whCGtXch2H4BhOPIAyYc= go.opentelemetry.io/otel v1.44.0/go.mod h1:BMgjTHL9WPRlRjL2oZCBTL4whCGtXch2H4BhOPIAyYc=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.44.0 h1:4YsVu3B8+3qtWYYrsUYgn0OG78pN0rnNPRGX4SbokQI=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.44.0/go.mod h1:+wnlSn0mD1ADVMe3v9Z/WIaiz6q6gL2J/ejaAmdmv80=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0 h1:IeMeyr1aBvBiPVYihXIaeIZba6b8E1bYp7lbdxK8CQg=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0/go.mod h1:oVdCUtjq9MK9BlS7TtucsQwUcXcymNiEDjgDD2jMtZU=
go.opentelemetry.io/otel/metric v1.44.0 h1:1w0gILTcHdr3YI+ixLyjemwrVnsMURbTZFrSYCdDdmc= go.opentelemetry.io/otel/metric v1.44.0 h1:1w0gILTcHdr3YI+ixLyjemwrVnsMURbTZFrSYCdDdmc=
go.opentelemetry.io/otel/metric v1.44.0/go.mod h1:8O7hanEPBNgEMmybD3s2VBKcgWOCsA6tzHBPODAiquo= go.opentelemetry.io/otel/metric v1.44.0/go.mod h1:8O7hanEPBNgEMmybD3s2VBKcgWOCsA6tzHBPODAiquo=
go.opentelemetry.io/otel/sdk v1.44.0 h1:nHYwb9lK+fJPU/dnT6s7W7Z8itMWyqrnVfbheVYrZ58= go.opentelemetry.io/otel/sdk v1.44.0 h1:nHYwb9lK+fJPU/dnT6s7W7Z8itMWyqrnVfbheVYrZ58=
go.opentelemetry.io/otel/sdk v1.44.0/go.mod h1:Osuydd3Se74nqjAKxid74N5eC+jfEqfTegHRnq58oK0= go.opentelemetry.io/otel/sdk v1.44.0/go.mod h1:Osuydd3Se74nqjAKxid74N5eC+jfEqfTegHRnq58oK0=
go.opentelemetry.io/otel/sdk/metric v1.44.0 h1:3LlKgI+VjbVsjNRFZJZAJ30WjXC5VkNRks6si09iEfI=
go.opentelemetry.io/otel/sdk/metric v1.44.0/go.mod h1:5B5pMARnXxKhltooO4xUuCBorl65a4EpnTalObqOigA=
go.opentelemetry.io/otel/trace v1.44.0 h1:jxF5CsGYCe74MCRx2X4g7WsY/VBKRqqpNvXlX/6gtIk= go.opentelemetry.io/otel/trace v1.44.0 h1:jxF5CsGYCe74MCRx2X4g7WsY/VBKRqqpNvXlX/6gtIk=
go.opentelemetry.io/otel/trace v1.44.0/go.mod h1:oLl1jrMQAVo6v3GAggN+1VH9VIz9iUSvW53sW1Q8PIE= go.opentelemetry.io/otel/trace v1.44.0/go.mod h1:oLl1jrMQAVo6v3GAggN+1VH9VIz9iUSvW53sW1Q8PIE=
go.opentelemetry.io/proto/otlp v1.10.0 h1:IQRWgT5srOCYfiWnpqUYz9CVmbO8bFmKcwYxpuCSL2g=
go.opentelemetry.io/proto/otlp v1.10.0/go.mod h1:/CV4QoCR/S9yaPj8utp3lvQPoqMtxXdzn7ozvvozVqk=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y= go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y=
@@ -212,55 +204,20 @@ go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ=
go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ=
golang.org/x/arch v0.29.0 h1:8sSET5wB0+exBm0FGmOtdHMqjlRdV2DRD3/IV6OZgho= golang.org/x/arch v0.29.0 h1:8sSET5wB0+exBm0FGmOtdHMqjlRdV2DRD3/IV6OZgho=
golang.org/x/arch v0.29.0/go.mod h1:0X+GdSIP+kL5wPmpK7sdkEVTt2XoYP0cSjQSbZBwOi8= golang.org/x/arch v0.29.0/go.mod h1:0X+GdSIP+kL5wPmpK7sdkEVTt2XoYP0cSjQSbZBwOi8=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.54.0 h1:YLIA59K4fiNzHzjnZt2tUJQjQtUWfWbeHBqKtk3eScw= golang.org/x/crypto v0.54.0 h1:YLIA59K4fiNzHzjnZt2tUJQjQtUWfWbeHBqKtk3eScw=
golang.org/x/crypto v0.54.0/go.mod h1:KWL8ny2AZdGR2cWmzeHrp2azQPGogOv+HeQaVEXC2dk= golang.org/x/crypto v0.54.0/go.mod h1:KWL8ny2AZdGR2cWmzeHrp2azQPGogOv+HeQaVEXC2dk=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.57.0 h1:K5+3DljvIuDG9/Jv9rvyMywYNFCQ9RSUY6OOTTkT+tE= golang.org/x/net v0.57.0 h1:K5+3DljvIuDG9/Jv9rvyMywYNFCQ9RSUY6OOTTkT+tE=
golang.org/x/net v0.57.0/go.mod h1:KpXc8iv+r3XplLAG/f7Jsf9RPszJzdR0f58q9vGOuEU= golang.org/x/net v0.57.0/go.mod h1:KpXc8iv+r3XplLAG/f7Jsf9RPszJzdR0f58q9vGOuEU=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs=
golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/term v0.45.0 h1:NwWyBmoJCbfTHpxrWoZ9C6/VxOf7ic219I8xZZFdrf0= golang.org/x/term v0.45.0 h1:NwWyBmoJCbfTHpxrWoZ9C6/VxOf7ic219I8xZZFdrf0=
golang.org/x/term v0.45.0/go.mod h1:9aqxs0blBcrm/n0L9QW0aRVD+ktan8ssZromtqJC43w= golang.org/x/term v0.45.0/go.mod h1:9aqxs0blBcrm/n0L9QW0aRVD+ktan8ssZromtqJC43w=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.40.0 h1:Ub2Z6/xjgF1WrYQz2nuITOEegKFtiIy+rieRJ5lHZKs= golang.org/x/text v0.40.0 h1:Ub2Z6/xjgF1WrYQz2nuITOEegKFtiIy+rieRJ5lHZKs=
golang.org/x/text v0.40.0/go.mod h1:hpnzDAfGV753zIKo+wk3u1bVKCGPbrnF7+7LBF/UHVY= golang.org/x/text v0.40.0/go.mod h1:hpnzDAfGV753zIKo+wk3u1bVKCGPbrnF7+7LBF/UHVY=
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 h1:vVKdlvoWBphwdxWKrFZEuM0kGgGLxUOYcY4U/2Vjg44=
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57 h1:JLQynH/LBHfCTSbDWl+py8C+Rg/k1OVH3xfcaiANuF0=
google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:kSJwQxqmFXeo79zOmbrALdflXQeAYcUbgS7PbpMknCY=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260715232425-e75dac1f907d h1:Jkpk39hlTZOIp3RbfvNX9R8Hv+Sw0X89nlU/xFOErsc=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260715232425-e75dac1f907d/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
google.golang.org/grpc v1.79.2 h1:fRMD94s2tITpyJGtBBn7MkMseNpOZU8ZxgC3MMBaXRU=
google.golang.org/grpc v1.79.2/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
@@ -269,5 +226,7 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EV
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU= gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q=
gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA=
pgregory.net/rapid v1.2.0 h1:keKAYRcjm+e1F0oAuU5F5+YPAWcyxNNRK2wud503Gnk=
pgregory.net/rapid v1.2.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04=
+25 -9
View File
@@ -13,7 +13,9 @@ import (
validator "github.com/dcarrillo/whatismyip/internal/validator/uuid" validator "github.com/dcarrillo/whatismyip/internal/validator/uuid"
"github.com/dcarrillo/whatismyip/router" "github.com/dcarrillo/whatismyip/router"
"github.com/docker/docker/api/types" "github.com/moby/moby/api/types/container"
"github.com/moby/moby/api/types/network"
"github.com/moby/moby/client"
"github.com/quic-go/quic-go/http3" "github.com/quic-go/quic-go/http3"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
@@ -90,16 +92,25 @@ func TestContainerIntegration(t *testing.T) {
Dockerfile: "./test/Dockerfile", Dockerfile: "./test/Dockerfile",
PrintBuildLog: true, PrintBuildLog: true,
KeepImage: false, KeepImage: false,
BuildOptionsModifier: func(buildOptions *types.ImageBuildOptions) { BuildOptionsModifier: func(buildOptions *client.ImageBuildOptions) {
buildOptions.Target = "test" buildOptions.Target = "test"
}, },
}, },
ExposedPorts: []string{ ExposedPorts: []string{
"8000:8000", "8000/tcp",
"8001:8001", "8001/tcp",
"8001:8001/udp", "8001/udp",
"9100:9100", "9100/tcp",
"53531:53/udp", "53/udp",
},
HostConfigModifier: func(hostConfig *container.HostConfig) {
hostConfig.PortBindings = network.PortMap{
network.MustParsePort("8000/tcp"): []network.PortBinding{{HostPort: "8000"}},
network.MustParsePort("8001/tcp"): []network.PortBinding{{HostPort: "8001"}},
network.MustParsePort("8001/udp"): []network.PortBinding{{HostPort: "8001"}},
network.MustParsePort("9100/tcp"): []network.PortBinding{{HostPort: "9100"}},
network.MustParsePort("53/udp"): []network.PortBinding{{HostPort: "53531"}},
}
}, },
Cmd: []string{ Cmd: []string{
"-geoip2-city", "/GeoIP2-City-Test.mmdb", "-geoip2-city", "/GeoIP2-City-Test.mmdb",
@@ -274,12 +285,17 @@ func TestContainerIntegrationDisableScan(t *testing.T) {
Dockerfile: "./test/Dockerfile", Dockerfile: "./test/Dockerfile",
PrintBuildLog: true, PrintBuildLog: true,
KeepImage: false, KeepImage: false,
BuildOptionsModifier: func(buildOptions *types.ImageBuildOptions) { BuildOptionsModifier: func(buildOptions *client.ImageBuildOptions) {
buildOptions.Target = "test" buildOptions.Target = "test"
}, },
}, },
ExposedPorts: []string{ ExposedPorts: []string{
"8000:8000", "8000/tcp",
},
HostConfigModifier: func(hostConfig *container.HostConfig) {
hostConfig.PortBindings = network.PortMap{
network.MustParsePort("8000/tcp"): []network.PortBinding{{HostPort: "8000"}},
}
}, },
Cmd: []string{ Cmd: []string{
"-geoip2-city", "/GeoIP2-City-Test.mmdb", "-geoip2-city", "/GeoIP2-City-Test.mmdb",
+2 -3
View File
@@ -7,7 +7,6 @@ import (
"sort" "sort"
"strings" "strings"
"github.com/dcarrillo/whatismyip/internal/setting"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
) )
@@ -30,10 +29,10 @@ func HeadersToSortedString(headers http.Header) string {
} }
// GetHeadersWithoutTrustedHeaders returns a copy of the request headers with the trusted headers removed // GetHeadersWithoutTrustedHeaders returns a copy of the request headers with the trusted headers removed
func GetHeadersWithoutTrustedHeaders(ctx *gin.Context) http.Header { func GetHeadersWithoutTrustedHeaders(ctx *gin.Context, trustedHeader, trustedPortHeader string) http.Header {
h := ctx.Request.Header.Clone() h := ctx.Request.Header.Clone()
for _, k := range []string{setting.App.TrustedHeader, setting.App.TrustedPortHeader} { for _, k := range []string{trustedHeader, trustedPortHeader} {
delete(h, textproto.CanonicalMIMEHeaderKey(k)) delete(h, textproto.CanonicalMIMEHeaderKey(k))
} }
+39 -42
View File
@@ -29,7 +29,7 @@ type resolver struct {
Ipv6 []string `yaml:"ipv6,omitempty"` Ipv6 []string `yaml:"ipv6,omitempty"`
} }
type settings struct { type Settings struct {
GeodbPath geodbConf GeodbPath geodbConf
TemplatePath string TemplatePath string
BindAddress string BindAddress string
@@ -51,75 +51,72 @@ const defaultAddress = ":8080"
var ErrVersion = errors.New("setting: version requested") var ErrVersion = errors.New("setting: version requested")
var App = settings{ func Setup(args []string) (cfg Settings, output string, err error) {
// hard-coded for the time being
Server: serverSettings{
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
},
}
func Setup(args []string) (output string, err error) {
flags := flag.NewFlagSet("whatismyip", flag.ContinueOnError) flags := flag.NewFlagSet("whatismyip", flag.ContinueOnError)
var buf bytes.Buffer var buf bytes.Buffer
var resolverConf string var resolverConf string
flags.SetOutput(&buf) flags.SetOutput(&buf)
flags.StringVar(&App.GeodbPath.City, "geoip2-city", "", "Path to GeoIP2 city database. Enables geo information (--geoip2-asn becomes mandatory)") cfg.Server = serverSettings{
flags.StringVar(&App.GeodbPath.ASN, "geoip2-asn", "", "Path to GeoIP2 ASN database. Enables ASN information. (--geoip2-city becomes mandatory)") ReadTimeout: 10 * time.Second,
flags.StringVar(&App.TemplatePath, "template", "", "Path to the template file") WriteTimeout: 10 * time.Second,
}
flags.StringVar(&cfg.GeodbPath.City, "geoip2-city", "", "Path to GeoIP2 city database. Enables geo information (--geoip2-asn becomes mandatory)")
flags.StringVar(&cfg.GeodbPath.ASN, "geoip2-asn", "", "Path to GeoIP2 ASN database. Enables ASN information. (--geoip2-city becomes mandatory)")
flags.StringVar(&cfg.TemplatePath, "template", "", "Path to the template file")
flags.StringVar( flags.StringVar(
&resolverConf, &resolverConf,
"resolver", "resolver",
"", "",
"Path to the resolver configuration. It actually enables the resolver for DNS client discovery.") "Path to the resolver configuration. It actually enables the resolver for DNS client discovery.")
flags.StringVar( flags.StringVar(
&App.BindAddress, &cfg.BindAddress,
"bind", "bind",
defaultAddress, defaultAddress,
"Listening address (see https://pkg.go.dev/net?#Listen)", "Listening address (see https://pkg.go.dev/net?#Listen)",
) )
flags.StringVar( flags.StringVar(
&App.TLSAddress, &cfg.TLSAddress,
"tls-bind", "tls-bind",
"", "",
"Listening address for TLS (see https://pkg.go.dev/net?#Listen)", "Listening address for TLS (see https://pkg.go.dev/net?#Listen)",
) )
flags.StringVar(&App.TLSCrtPath, "tls-crt", "", "When using TLS, path to certificate file") flags.StringVar(&cfg.TLSCrtPath, "tls-crt", "", "When using TLS, path to certificate file")
flags.StringVar(&App.TLSKeyPath, "tls-key", "", "When using TLS, path to private key file") flags.StringVar(&cfg.TLSKeyPath, "tls-key", "", "When using TLS, path to private key file")
flags.StringVar( flags.StringVar(
&App.PrometheusAddress, &cfg.PrometheusAddress,
"metrics-bind", "metrics-bind",
"", "",
"Listening address for Prometheus metrics endpoint (see https://pkg.go.dev/net?#Listen). It enables the metrics available at the given address/port via the /metrics endpoint.", "Listening address for Prometheus metrics endpoint (see https://pkg.go.dev/net?#Listen). It enables the metrics available at the given address/port via the /metrics endpoint.",
) )
flags.StringVar( flags.StringVar(
&App.TrustedHeader, &cfg.TrustedHeader,
"trusted-header", "trusted-header",
"", "",
"Trusted request header for remote IP (e.g. X-Real-IP). When using this feature if -trusted-port-header is not set the client port is shown as 'unknown'", "Trusted request header for remote IP (e.g. X-Real-IP). When using this feature if -trusted-port-header is not set the client port is shown as 'unknown'",
) )
flags.StringVar( flags.StringVar(
&App.TrustedPortHeader, &cfg.TrustedPortHeader,
"trusted-port-header", "trusted-port-header",
"", "",
"Trusted request header for remote client port (e.g. X-Real-Port). When this parameter is set -trusted-header becomes mandatory", "Trusted request header for remote client port (e.g. X-Real-Port). When this parameter is set -trusted-header becomes mandatory",
) )
flags.BoolVar(&App.version, "version", false, "Output version information and exit") flags.BoolVar(&cfg.version, "version", false, "Output version information and exit")
flags.BoolVar( flags.BoolVar(
&App.EnableSecureHeaders, &cfg.EnableSecureHeaders,
"enable-secure-headers", "enable-secure-headers",
false, false,
"Add sane security-related headers to every response", "Add sane security-related headers to every response",
) )
flags.BoolVar( flags.BoolVar(
&App.EnableHTTP3, &cfg.EnableHTTP3,
"enable-http3", "enable-http3",
false, false,
"Enable HTTP/3 protocol. HTTP/3 requires --tls-bind set, as HTTP/3 starts as a TLS connection that then gets upgraded to UDP. The UDP port is the same as the one used for the TLS server.", "Enable HTTP/3 protocol. HTTP/3 requires --tls-bind set, as HTTP/3 starts as a TLS connection that then gets upgraded to UDP. The UDP port is the same as the one used for the TLS server.",
) )
flags.BoolVar( flags.BoolVar(
&App.DisableTCPScan, &cfg.DisableTCPScan,
"disable-scan", "disable-scan",
false, false,
"Disable TCP port scanning functionality", "Disable TCP port scanning functionality",
@@ -127,48 +124,48 @@ func Setup(args []string) (output string, err error) {
err = flags.Parse(args) err = flags.Parse(args)
if err != nil { if err != nil {
return buf.String(), err return cfg, buf.String(), err
} }
if App.version { if cfg.version {
return fmt.Sprintf("whatismyip version %s", core.Version), ErrVersion return cfg, fmt.Sprintf("whatismyip version %s", core.Version), ErrVersion
} }
if (App.GeodbPath.City != "" && App.GeodbPath.ASN == "") || (App.GeodbPath.City == "" && App.GeodbPath.ASN != "") { if (cfg.GeodbPath.City != "" && cfg.GeodbPath.ASN == "") || (cfg.GeodbPath.City == "" && cfg.GeodbPath.ASN != "") {
return "", errors.New("both --geoip2-city and --geoip2-asn are mandatory to enable geo information") return cfg, "", errors.New("both --geoip2-city and --geoip2-asn are mandatory to enable geo information")
} }
if App.TrustedPortHeader != "" && App.TrustedHeader == "" { if cfg.TrustedPortHeader != "" && cfg.TrustedHeader == "" {
return "", errors.New("trusted-header is mandatory when trusted-port-header is set") return cfg, "", errors.New("trusted-header is mandatory when trusted-port-header is set")
} }
if (App.TLSAddress != "") && (App.TLSCrtPath == "" || App.TLSKeyPath == "") { if (cfg.TLSAddress != "") && (cfg.TLSCrtPath == "" || cfg.TLSKeyPath == "") {
return "", errors.New("in order to use TLS, the -tls-crt and -tls-key flags are mandatory") return cfg, "", errors.New("in order to use TLS, the -tls-crt and -tls-key flags are mandatory")
} }
if App.EnableHTTP3 && App.TLSAddress == "" { if cfg.EnableHTTP3 && cfg.TLSAddress == "" {
return "", errors.New("in order to use HTTP3, the -tls-bind is mandatory") return cfg, "", errors.New("in order to use HTTP3, the -tls-bind is mandatory")
} }
if App.TemplatePath != "" { if cfg.TemplatePath != "" {
info, err := os.Stat(App.TemplatePath) info, err := os.Stat(cfg.TemplatePath)
if err != nil { if err != nil {
return "", fmt.Errorf("template path: %w", err) return cfg, "", fmt.Errorf("template path: %w", err)
} }
if info.IsDir() { if info.IsDir() {
return "", fmt.Errorf("%s must be a file", App.TemplatePath) return cfg, "", fmt.Errorf("%s must be a file", cfg.TemplatePath)
} }
} }
if resolverConf != "" { if resolverConf != "" {
var err error var err error
App.Resolver, err = readYAML(resolverConf) cfg.Resolver, err = readYAML(resolverConf)
if err != nil { if err != nil {
return "", fmt.Errorf("reading resolver configuration: %w", err) return cfg, "", fmt.Errorf("reading resolver configuration: %w", err)
} }
} }
return buf.String(), nil return cfg, buf.String(), nil
} }
func readYAML(path string) (resolver resolver, err error) { func readYAML(path string) (resolver resolver, err error) {
+13 -13
View File
@@ -55,7 +55,7 @@ func TestParseMandatoryFlags(t *testing.T) {
for _, tt := range mandatoryFlags { for _, tt := range mandatoryFlags {
t.Run(strings.Join(tt.args, " "), func(t *testing.T) { t.Run(strings.Join(tt.args, " "), func(t *testing.T) {
_, err := Setup(tt.args) _, _, err := Setup(tt.args)
require.Error(t, err) require.Error(t, err)
assert.Contains(t, err.Error(), "mandatory") assert.Contains(t, err.Error(), "mandatory")
}) })
@@ -65,11 +65,11 @@ func TestParseMandatoryFlags(t *testing.T) {
func TestParseFlags(t *testing.T) { func TestParseFlags(t *testing.T) {
flags := []struct { flags := []struct {
args []string args []string
conf settings conf Settings
}{ }{
{ {
[]string{}, []string{},
settings{ Settings{
BindAddress: ":8080", BindAddress: ":8080",
Server: serverSettings{ Server: serverSettings{
ReadTimeout: 10 * time.Second, ReadTimeout: 10 * time.Second,
@@ -79,7 +79,7 @@ func TestParseFlags(t *testing.T) {
}, },
{ {
[]string{"-disable-scan"}, []string{"-disable-scan"},
settings{ Settings{
BindAddress: ":8080", BindAddress: ":8080",
Server: serverSettings{ Server: serverSettings{
ReadTimeout: 10 * time.Second, ReadTimeout: 10 * time.Second,
@@ -90,7 +90,7 @@ func TestParseFlags(t *testing.T) {
}, },
{ {
[]string{"-bind", ":8001", "-geoip2-city", "/city-path", "-geoip2-asn", "/asn-path"}, []string{"-bind", ":8001", "-geoip2-city", "/city-path", "-geoip2-asn", "/asn-path"},
settings{ Settings{
GeodbPath: geodbConf{ GeodbPath: geodbConf{
City: "/city-path", City: "/city-path",
ASN: "/asn-path", ASN: "/asn-path",
@@ -107,7 +107,7 @@ func TestParseFlags(t *testing.T) {
"-geoip2-city", "/city-path", "-geoip2-asn", "/asn-path", "-tls-bind", ":9000", "-geoip2-city", "/city-path", "-geoip2-asn", "/asn-path", "-tls-bind", ":9000",
"-tls-crt", "/crt-path", "-tls-key", "/key-path", "-tls-crt", "/crt-path", "-tls-key", "/key-path",
}, },
settings{ Settings{
GeodbPath: geodbConf{ GeodbPath: geodbConf{
City: "/city-path", City: "/city-path",
ASN: "/asn-path", ASN: "/asn-path",
@@ -127,7 +127,7 @@ func TestParseFlags(t *testing.T) {
"-geoip2-city", "/city-path", "-geoip2-asn", "/asn-path", "-geoip2-city", "/city-path", "-geoip2-asn", "/asn-path",
"-trusted-header", "header", "-trusted-port-header", "port-header", "-trusted-header", "header", "-trusted-port-header", "port-header",
}, },
settings{ Settings{
GeodbPath: geodbConf{ GeodbPath: geodbConf{
City: "/city-path", City: "/city-path",
ASN: "/asn-path", ASN: "/asn-path",
@@ -146,7 +146,7 @@ func TestParseFlags(t *testing.T) {
"-geoip2-city", "/city-path", "-geoip2-asn", "/asn-path", "-geoip2-city", "/city-path", "-geoip2-asn", "/asn-path",
"-trusted-header", "header", "-enable-secure-headers", "-trusted-header", "header", "-enable-secure-headers",
}, },
settings{ Settings{
GeodbPath: geodbConf{ GeodbPath: geodbConf{
City: "/city-path", City: "/city-path",
ASN: "/asn-path", ASN: "/asn-path",
@@ -164,9 +164,9 @@ func TestParseFlags(t *testing.T) {
for _, tt := range flags { for _, tt := range flags {
t.Run(strings.Join(tt.args, " "), func(t *testing.T) { t.Run(strings.Join(tt.args, " "), func(t *testing.T) {
_, err := Setup(tt.args) cfg, _, err := Setup(tt.args)
require.Nil(t, err) require.Nil(t, err)
assert.True(t, reflect.DeepEqual(App, tt.conf)) assert.True(t, reflect.DeepEqual(cfg, tt.conf))
}) })
} }
} }
@@ -176,7 +176,7 @@ func TestParseFlagsUsage(t *testing.T) {
for _, arg := range usageArgs { for _, arg := range usageArgs {
t.Run(arg, func(t *testing.T) { t.Run(arg, func(t *testing.T) {
output, err := Setup([]string{arg}) _, output, err := Setup([]string{arg})
assert.ErrorIs(t, err, flag.ErrHelp) assert.ErrorIs(t, err, flag.ErrHelp)
assert.Contains(t, output, "Usage of") assert.Contains(t, output, "Usage of")
}) })
@@ -184,7 +184,7 @@ func TestParseFlagsUsage(t *testing.T) {
} }
func TestParseFlagVersion(t *testing.T) { func TestParseFlagVersion(t *testing.T) {
output, err := Setup([]string{"-version"}) _, output, err := Setup([]string{"-version"})
assert.ErrorIs(t, err, ErrVersion) assert.ErrorIs(t, err, ErrVersion)
assert.Contains(t, output, "whatismyip version") assert.Contains(t, output, "whatismyip version")
} }
@@ -209,7 +209,7 @@ func TestParseFlagTemplate(t *testing.T) {
for _, tc := range testCases { for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) { t.Run(tc.name, func(t *testing.T) {
_, err := Setup(tc.flags) _, _, err := Setup(tc.flags)
require.Error(t, err) require.Error(t, err)
assert.Contains(t, err.Error(), tc.errMsg) assert.Contains(t, err.Error(), tc.errMsg)
}) })
+14 -7
View File
@@ -7,12 +7,19 @@ import (
"strings" "strings"
"github.com/dcarrillo/whatismyip/internal/metrics" "github.com/dcarrillo/whatismyip/internal/metrics"
"github.com/dcarrillo/whatismyip/internal/setting"
"github.com/dcarrillo/whatismyip/internal/validator/uuid" "github.com/dcarrillo/whatismyip/internal/validator/uuid"
"github.com/miekg/dns" "github.com/miekg/dns"
"github.com/patrickmn/go-cache" "github.com/patrickmn/go-cache"
) )
type Settings struct {
Domain string
ResourceRecords []string
RedirectPort string
IPv4 []string
IPv6 []string
}
type Resolver struct { type Resolver struct {
handler *dns.ServeMux handler *dns.ServeMux
store *cache.Cache store *cache.Cache
@@ -29,11 +36,11 @@ func ensureDotSuffix(s string) string {
return s return s
} }
func Setup(store *cache.Cache) (*Resolver, error) { func Setup(store *cache.Cache, cfg Settings) (*Resolver, error) {
domain := ensureDotSuffix(setting.App.Resolver.Domain) domain := ensureDotSuffix(cfg.Domain)
rr := make([]dns.RR, 0, len(setting.App.Resolver.ResourceRecords)) rr := make([]dns.RR, 0, len(cfg.ResourceRecords))
for _, res := range setting.App.Resolver.ResourceRecords { for _, res := range cfg.ResourceRecords {
record, err := dns.NewRR(domain + " " + res) record, err := dns.NewRR(domain + " " + res)
if err != nil { if err != nil {
return nil, fmt.Errorf("parsing resource record %q: %w", res, err) return nil, fmt.Errorf("parsing resource record %q: %w", res, err)
@@ -41,11 +48,11 @@ func Setup(store *cache.Cache) (*Resolver, error) {
rr = append(rr, record) rr = append(rr, record)
} }
ipv4, err := parseIPs(setting.App.Resolver.Ipv4) ipv4, err := parseIPs(cfg.IPv4)
if err != nil { if err != nil {
return nil, fmt.Errorf("parsing ipv4 addresses: %w", err) return nil, fmt.Errorf("parsing ipv4 addresses: %w", err)
} }
ipv6, err := parseIPs(setting.App.Resolver.Ipv6) ipv6, err := parseIPs(cfg.IPv6)
if err != nil { if err != nil {
return nil, fmt.Errorf("parsing ipv6 addresses: %w", err) return nil, fmt.Errorf("parsing ipv6 addresses: %w", err)
} }
+4 -3
View File
@@ -7,6 +7,7 @@ import (
"strings" "strings"
validator "github.com/dcarrillo/whatismyip/internal/validator/uuid" validator "github.com/dcarrillo/whatismyip/internal/validator/uuid"
"github.com/dcarrillo/whatismyip/service"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/patrickmn/go-cache" "github.com/patrickmn/go-cache"
@@ -27,7 +28,7 @@ type dnsData struct {
// TODO // TODO
// Implement a proper vhost manager instead of using a middleware // Implement a proper vhost manager instead of using a middleware
func GetDNSDiscoveryHandler(store *cache.Cache, domain string, redirectPort string) gin.HandlerFunc { func GetDNSDiscoveryHandler(store *cache.Cache, geoSvc *service.Geo, domain string, redirectPort string) gin.HandlerFunc {
return func(ctx *gin.Context) { return func(ctx *gin.Context) {
host := hostWithoutPort(ctx.Request.Host) host := hostWithoutPort(ctx.Request.Host)
if host != domain && !strings.HasSuffix(host, "."+domain) { if host != domain && !strings.HasSuffix(host, "."+domain) {
@@ -41,7 +42,7 @@ func GetDNSDiscoveryHandler(store *cache.Cache, domain string, redirectPort stri
return return
} }
handleDNS(ctx, store) handleDNS(ctx, store, geoSvc)
ctx.Abort() ctx.Abort()
} }
} }
@@ -53,7 +54,7 @@ func hostWithoutPort(host string) string {
return host return host
} }
func handleDNS(ctx *gin.Context, store *cache.Cache) { func handleDNS(ctx *gin.Context, store *cache.Cache, geoSvc *service.Geo) {
d := strings.Split(ctx.Request.Host, ".")[0] d := strings.Split(ctx.Request.Host, ".")[0]
if !validator.IsValid(d) { if !validator.IsValid(d) {
ctx.String(http.StatusNotFound, http.StatusText(http.StatusNotFound)) ctx.String(http.StatusNotFound, http.StatusText(http.StatusNotFound))
+3 -3
View File
@@ -16,7 +16,7 @@ import (
func TestGetDNSDiscoveryHandler(t *testing.T) { func TestGetDNSDiscoveryHandler(t *testing.T) {
store := cache.New(cache.NoExpiration, cache.NoExpiration) store := cache.New(cache.NoExpiration, cache.NoExpiration)
handler := GetDNSDiscoveryHandler(store, domain, "") handler := GetDNSDiscoveryHandler(store, rt.geo, domain, "")
t.Run("calls next if host does not have domain suffix", func(t *testing.T) { t.Run("calls next if host does not have domain suffix", func(t *testing.T) {
req, _ := http.NewRequest("GET", "/", nil) req, _ := http.NewRequest("GET", "/", nil)
@@ -106,7 +106,7 @@ func TestHandleDNS(t *testing.T) {
w := httptest.NewRecorder() w := httptest.NewRecorder()
c, _ := gin.CreateTestContext(w) c, _ := gin.CreateTestContext(w)
c.Request = req c.Request = req
handleDNS(c, store) handleDNS(c, store, rt.geo)
assert.Equal(t, http.StatusNotFound, w.Code) assert.Equal(t, http.StatusNotFound, w.Code)
}) })
} }
@@ -144,7 +144,7 @@ func TestAcceptDNSRequest(t *testing.T) {
c.Request = req c.Request = req
store.Set(u, testIP.ipv4, cache.DefaultExpiration) store.Set(u, testIP.ipv4, cache.DefaultExpiration)
handleDNS(c, store) handleDNS(c, store, rt.geo)
assert.Equal(t, http.StatusOK, w.Code) assert.Equal(t, http.StatusOK, w.Code)
assert.Equal(t, tt.want, w.Body.String()) assert.Equal(t, tt.want, w.Body.String())
+25 -26
View File
@@ -6,7 +6,6 @@ import (
"path/filepath" "path/filepath"
"github.com/dcarrillo/whatismyip/internal/httputils" "github.com/dcarrillo/whatismyip/internal/httputils"
"github.com/dcarrillo/whatismyip/internal/setting"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
) )
@@ -31,31 +30,31 @@ type JSONResponse struct {
GeoResponse GeoResponse
} }
func getRoot(ctx *gin.Context) { func (rt *Router) getRoot(ctx *gin.Context) {
switch ctx.NegotiateFormat(gin.MIMEPlain, gin.MIMEHTML, gin.MIMEJSON) { switch ctx.NegotiateFormat(gin.MIMEPlain, gin.MIMEHTML, gin.MIMEJSON) {
case gin.MIMEHTML: case gin.MIMEHTML:
name := "home" name := "home"
if setting.App.TemplatePath != "" { if rt.templatePath != "" {
name = filepath.Base(setting.App.TemplatePath) name = filepath.Base(rt.templatePath)
} }
ctx.HTML(http.StatusOK, name, jsonOutput(ctx)) ctx.HTML(http.StatusOK, name, rt.jsonOutput(ctx))
case gin.MIMEJSON: case gin.MIMEJSON:
getJSON(ctx) rt.getJSON(ctx)
default: default:
ctx.String(http.StatusOK, ctx.ClientIP()+"\n") ctx.String(http.StatusOK, ctx.ClientIP()+"\n")
} }
} }
func getClientPort(ctx *gin.Context) string { func (rt *Router) getClientPort(ctx *gin.Context) string {
var port string var port string
if setting.App.TrustedPortHeader == "" { if rt.trustedPortHeader == "" {
if setting.App.TrustedHeader != "" { if rt.trustedHeader != "" {
port = "unknown" port = "unknown"
} else { } else {
_, port, _ = net.SplitHostPort(ctx.Request.RemoteAddr) _, port, _ = net.SplitHostPort(ctx.Request.RemoteAddr)
} }
} else { } else {
port = ctx.GetHeader(setting.App.TrustedPortHeader) port = ctx.GetHeader(rt.trustedPortHeader)
if port == "" { if port == "" {
port = "unknown" port = "unknown"
} }
@@ -64,37 +63,37 @@ func getClientPort(ctx *gin.Context) string {
return port return port
} }
func getClientPortAsString(ctx *gin.Context) { func (rt *Router) getClientPortAsString(ctx *gin.Context) {
ctx.String(http.StatusOK, getClientPort(ctx)+"\n") ctx.String(http.StatusOK, rt.getClientPort(ctx)+"\n")
} }
func getAllAsString(ctx *gin.Context) { func (rt *Router) getAllAsString(ctx *gin.Context) {
ip := net.ParseIP(ctx.ClientIP()) ip := net.ParseIP(ctx.ClientIP())
output := "IP: " + ip.String() + "\n" output := "IP: " + ip.String() + "\n"
output += "Client Port: " + getClientPort(ctx) + "\n" output += "Client Port: " + rt.getClientPort(ctx) + "\n"
if geoSvc != nil { if rt.geo != nil {
if cityRecord := geoSvc.LookUpCity(ip); cityRecord != nil { if cityRecord := rt.geo.LookUpCity(ip); cityRecord != nil {
output += geoCityRecordToString(cityRecord) + "\n" output += geoCityRecordToString(cityRecord) + "\n"
} }
if asnRecord := geoSvc.LookUpASN(ip); asnRecord != nil { if asnRecord := rt.geo.LookUpASN(ip); asnRecord != nil {
output += geoASNRecordToString(asnRecord) + "\n" output += geoASNRecordToString(asnRecord) + "\n"
} }
} }
h := httputils.GetHeadersWithoutTrustedHeaders(ctx) h := httputils.GetHeadersWithoutTrustedHeaders(ctx, rt.trustedHeader, rt.trustedPortHeader)
h.Set("Host", ctx.Request.Host) h.Set("Host", ctx.Request.Host)
output += httputils.HeadersToSortedString(h) output += httputils.HeadersToSortedString(h)
ctx.String(http.StatusOK, output) ctx.String(http.StatusOK, output)
} }
func getJSON(ctx *gin.Context) { func (rt *Router) getJSON(ctx *gin.Context) {
ctx.JSON(http.StatusOK, jsonOutput(ctx)) ctx.JSON(http.StatusOK, rt.jsonOutput(ctx))
} }
func jsonOutput(ctx *gin.Context) JSONResponse { func (rt *Router) jsonOutput(ctx *gin.Context) JSONResponse {
ip := net.ParseIP(ctx.ClientIP()) ip := net.ParseIP(ctx.ClientIP())
var version byte = 4 var version byte = 4
@@ -103,8 +102,8 @@ func jsonOutput(ctx *gin.Context) JSONResponse {
} }
geoResp := GeoResponse{} geoResp := GeoResponse{}
if geoSvc != nil { if rt.geo != nil {
if cityRecord := geoSvc.LookUpCity(ip); cityRecord != nil { if cityRecord := rt.geo.LookUpCity(ip); cityRecord != nil {
geoResp.Country = cityRecord.Country.Names["en"] geoResp.Country = cityRecord.Country.Names["en"]
geoResp.CountryCode = cityRecord.Country.ISOCode geoResp.CountryCode = cityRecord.Country.ISOCode
geoResp.City = cityRecord.City.Names["en"] geoResp.City = cityRecord.City.Names["en"]
@@ -113,7 +112,7 @@ func jsonOutput(ctx *gin.Context) JSONResponse {
geoResp.PostalCode = cityRecord.Postal.Code geoResp.PostalCode = cityRecord.Postal.Code
geoResp.TimeZone = cityRecord.Location.TimeZone geoResp.TimeZone = cityRecord.Location.TimeZone
} }
if asnRecord := geoSvc.LookUpASN(ip); asnRecord != nil { if asnRecord := rt.geo.LookUpASN(ip); asnRecord != nil {
geoResp.ASN = asnRecord.AutonomousSystemNumber geoResp.ASN = asnRecord.AutonomousSystemNumber
geoResp.ASNOrganization = asnRecord.AutonomousSystemOrganization geoResp.ASNOrganization = asnRecord.AutonomousSystemOrganization
} }
@@ -122,9 +121,9 @@ func jsonOutput(ctx *gin.Context) JSONResponse {
return JSONResponse{ return JSONResponse{
IP: ip.String(), IP: ip.String(),
IPVersion: version, IPVersion: version,
ClientPort: getClientPort(ctx), ClientPort: rt.getClientPort(ctx),
Host: ctx.Request.Host, Host: ctx.Request.Host,
Headers: httputils.GetHeadersWithoutTrustedHeaders(ctx), Headers: httputils.GetHeadersWithoutTrustedHeaders(ctx, rt.trustedHeader, rt.trustedPortHeader),
GeoResponse: geoResp, GeoResponse: geoResp,
} }
} }
+29 -41
View File
@@ -1,12 +1,14 @@
package router package router
import ( import (
"context"
"net" "net"
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
"testing" "testing"
"github.com/dcarrillo/whatismyip/internal/setting" "github.com/dcarrillo/whatismyip/service"
"github.com/gin-gonic/gin"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
@@ -99,8 +101,9 @@ func TestHost(t *testing.T) {
func TestClientPort(t *testing.T) { func TestClientPort(t *testing.T) {
type args struct { type args struct {
params []string trustedHeader string
headers map[string][]string trustedPortHeader string
headers map[string][]string
} }
tests := []struct { tests := []struct {
name string name string
@@ -114,35 +117,23 @@ func TestClientPort(t *testing.T) {
{ {
name: "Trusted header only set", name: "Trusted header only set",
args: args{ args: args{
params: []string{ trustedHeader: trustedHeader,
"-geoip2-city", "city",
"-geoip2-asn", "asn",
"-trusted-header", trustedHeader,
},
}, },
expected: "unknown\n", expected: "unknown\n",
}, },
{ {
name: "Trusted and port header set but not included in headers", name: "Trusted and port header set but not included in headers",
args: args{ args: args{
params: []string{ trustedHeader: trustedHeader,
"-geoip2-city", "city", trustedPortHeader: trustedPortHeader,
"-geoip2-asn", "asn",
"-trusted-header", trustedHeader,
"-trusted-port-header", trustedPortHeader,
},
}, },
expected: "unknown\n", expected: "unknown\n",
}, },
{ {
name: "Trusted and port header set and included in headers", name: "Trusted and port header set and included in headers",
args: args{ args: args{
params: []string{ trustedHeader: trustedHeader,
"-geoip2-city", "city", trustedPortHeader: trustedPortHeader,
"-geoip2-asn", "asn",
"-trusted-header", trustedHeader,
"-trusted-port-header", trustedPortHeader,
},
headers: map[string][]string{ headers: map[string][]string{
trustedHeader: {testIP.ipv4}, trustedHeader: {testIP.ipv4},
trustedPortHeader: {"1001"}, trustedPortHeader: {"1001"},
@@ -153,19 +144,22 @@ func TestClientPort(t *testing.T) {
} }
for _, tt := range tests { for _, tt := range tests {
_, _ = setting.Setup(tt.args.params)
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
engine := gin.Default()
engine.TrustedPlatform = tt.args.trustedHeader
r := NewRouter(nil, tt.args.trustedHeader, tt.args.trustedPortHeader, "", false)
Setup(engine, r)
req, _ := http.NewRequest("GET", "/client-port", nil) req, _ := http.NewRequest("GET", "/client-port", nil)
req.RemoteAddr = net.JoinHostPort(testIP.ipv4, "1000") req.RemoteAddr = net.JoinHostPort(testIP.ipv4, "1000")
req.Header = tt.args.headers req.Header = tt.args.headers
w := httptest.NewRecorder() w := httptest.NewRecorder()
app.ServeHTTP(w, req) engine.ServeHTTP(w, req)
assert.Equal(t, 200, w.Code) assert.Equal(t, 200, w.Code)
assert.Equal(t, contentType.text, w.Header().Get("Content-Type")) assert.Equal(t, contentType.text, w.Header().Get("Content-Type"))
assert.Equal(t, tt.expected, w.Body.String()) assert.Equal(t, tt.expected, w.Body.String())
t.Log(w.Header())
}) })
} }
} }
@@ -181,14 +175,11 @@ func TestNotFound(t *testing.T) {
} }
func TestJSON(t *testing.T) { func TestJSON(t *testing.T) {
_, _ = setting.Setup( svc, _ := service.NewGeo(context.Background(), "../test/GeoIP2-City-Test.mmdb", "../test/GeoLite2-ASN-Test.mmdb")
[]string{ engine := gin.Default()
"-geoip2-city", "city", engine.TrustedPlatform = trustedHeader
"-geoip2-asn", "asn", r := NewRouter(svc, trustedHeader, trustedPortHeader, "", false)
"-trusted-header", trustedHeader, Setup(engine, r)
"-trusted-port-header", trustedPortHeader,
},
)
type args struct { type args struct {
ip string ip string
@@ -222,7 +213,7 @@ func TestJSON(t *testing.T) {
req.Header.Set(trustedPortHeader, "1001") req.Header.Set(trustedPortHeader, "1001")
w := httptest.NewRecorder() w := httptest.NewRecorder()
app.ServeHTTP(w, req) engine.ServeHTTP(w, req)
assert.Equal(t, 200, w.Code) assert.Equal(t, 200, w.Code)
assert.Equal(t, contentType.json, w.Header().Get("Content-Type")) assert.Equal(t, contentType.json, w.Header().Get("Content-Type"))
@@ -248,14 +239,11 @@ ASN Organization:
Header1: one Header1: one
Host: test Host: test
` `
_, _ = setting.Setup( svc, _ := service.NewGeo(context.Background(), "../test/GeoIP2-City-Test.mmdb", "../test/GeoLite2-ASN-Test.mmdb")
[]string{ engine := gin.Default()
"-geoip2-city", "city", engine.TrustedPlatform = trustedHeader
"-geoip2-asn", "asn", r := NewRouter(svc, trustedHeader, trustedPortHeader, "", false)
"-trusted-header", trustedHeader, Setup(engine, r)
"-trusted-port-header", trustedPortHeader,
},
)
req, _ := http.NewRequest("GET", "/all", nil) req, _ := http.NewRequest("GET", "/all", nil)
req.RemoteAddr = net.JoinHostPort(testIP.ipv4, "1000") req.RemoteAddr = net.JoinHostPort(testIP.ipv4, "1000")
@@ -265,7 +253,7 @@ Host: test
req.Header.Set("Header1", "one") req.Header.Set("Header1", "one")
w := httptest.NewRecorder() w := httptest.NewRecorder()
app.ServeHTTP(w, req) engine.ServeHTTP(w, req)
assert.Equal(t, 200, w.Code) assert.Equal(t, 200, w.Code)
assert.Equal(t, contentType.text, w.Header().Get("Content-Type")) assert.Equal(t, contentType.text, w.Header().Get("Content-Type"))
+6 -6
View File
@@ -81,13 +81,13 @@ var asnOutput = map[string]asnDataFormatter{
}, },
} }
func getGeoAsString(ctx *gin.Context) { func (rt *Router) getGeoAsString(ctx *gin.Context) {
if geoSvc == nil { if rt.geo == nil {
ctx.String(http.StatusNotFound, http.StatusText(http.StatusNotFound)) ctx.String(http.StatusNotFound, http.StatusText(http.StatusNotFound))
return return
} }
record := geoSvc.LookUpCity(net.ParseIP(ctx.ClientIP())) record := rt.geo.LookUpCity(net.ParseIP(ctx.ClientIP()))
if record == nil { if record == nil {
ctx.String(http.StatusNotFound, http.StatusText(http.StatusNotFound)) ctx.String(http.StatusNotFound, http.StatusText(http.StatusNotFound))
return return
@@ -107,13 +107,13 @@ func getGeoAsString(ctx *gin.Context) {
ctx.String(http.StatusOK, g.format(record)) ctx.String(http.StatusOK, g.format(record))
} }
func getASNAsString(ctx *gin.Context) { func (rt *Router) getASNAsString(ctx *gin.Context) {
if geoSvc == nil { if rt.geo == nil {
ctx.String(http.StatusNotFound, http.StatusText(http.StatusNotFound)) ctx.String(http.StatusNotFound, http.StatusText(http.StatusNotFound))
return return
} }
record := geoSvc.LookUpASN(net.ParseIP(ctx.ClientIP())) record := rt.geo.LookUpASN(net.ParseIP(ctx.ClientIP()))
if record == nil { if record == nil {
ctx.String(http.StatusNotFound, http.StatusText(http.StatusNotFound)) ctx.String(http.StatusNotFound, http.StatusText(http.StatusNotFound))
return return
+4 -5
View File
@@ -9,15 +9,14 @@ import (
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
) )
func getHeadersAsSortedString(ctx *gin.Context) { func (rt *Router) getHeadersAsSortedString(ctx *gin.Context) {
h := httputils.GetHeadersWithoutTrustedHeaders(ctx) h := httputils.GetHeadersWithoutTrustedHeaders(ctx, rt.trustedHeader, rt.trustedPortHeader)
h.Set("Host", ctx.Request.Host) h.Set("Host", ctx.Request.Host)
ctx.String(http.StatusOK, httputils.HeadersToSortedString(h)) ctx.String(http.StatusOK, httputils.HeadersToSortedString(h))
} }
func getHeaderAsString(ctx *gin.Context) { func (rt *Router) getHeaderAsString(ctx *gin.Context) {
headers := httputils.GetHeadersWithoutTrustedHeaders(ctx) headers := httputils.GetHeadersWithoutTrustedHeaders(ctx, rt.trustedHeader, rt.trustedPortHeader)
h := ctx.Params.ByName("header") h := ctx.Params.ByName("header")
if v := headers.Get(ctx.Params.ByName("header")); v != "" { if v := headers.Get(ctx.Params.ByName("header")); v != "" {
+9 -8
View File
@@ -1,11 +1,13 @@
package router package router
import ( import (
"context"
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
"testing" "testing"
"github.com/dcarrillo/whatismyip/internal/setting" "github.com/dcarrillo/whatismyip/service"
"github.com/gin-gonic/gin"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
@@ -27,12 +29,11 @@ Header2: value22
Header3: value3 Header3: value3
Host: Host:
` `
_, _ = setting.Setup([]string{ svc, _ := service.NewGeo(context.Background(), "../test/GeoIP2-City-Test.mmdb", "../test/GeoLite2-ASN-Test.mmdb")
"-geoip2-city", "city", engine := gin.Default()
"-geoip2-asn", "asn", engine.TrustedPlatform = trustedHeader
"-trusted-header", trustedHeader, r := NewRouter(svc, trustedHeader, trustedPortHeader, "", false)
"-trusted-port-header", trustedPortHeader, Setup(engine, r)
})
req, _ := http.NewRequest("GET", "/headers", nil) req, _ := http.NewRequest("GET", "/headers", nil)
req.Header = map[string][]string{ req.Header = map[string][]string{
"Header1": {"value1"}, "Header1": {"value1"},
@@ -43,7 +44,7 @@ Host:
req.Header.Set(trustedPortHeader, "1025") req.Header.Set(trustedPortHeader, "1025")
w := httptest.NewRecorder() w := httptest.NewRecorder()
app.ServeHTTP(w, req) engine.ServeHTTP(w, req)
assert.Equal(t, 200, w.Code) assert.Equal(t, 200, w.Code)
assert.Equal(t, contentType.text, w.Header().Get("Content-Type")) assert.Equal(t, contentType.text, w.Header().Get("Content-Type"))
+1 -1
View File
@@ -17,7 +17,7 @@ type JSONScanResponse struct {
Reason string `json:"reason"` Reason string `json:"reason"`
} }
func scanTCPPort(ctx *gin.Context) { func (rt *Router) scanTCPPort(ctx *gin.Context) {
port, err := strconv.Atoi(ctx.Params.ByName("port")) port, err := strconv.Atoi(ctx.Params.ByName("port"))
if err == nil && (port < 1 || port > 65535) { if err == nil && (port < 1 || port > 65535) {
err = fmt.Errorf("%d is not a valid port number", port) err = fmt.Errorf("%d is not a valid port number", port)
+34 -20
View File
@@ -4,35 +4,49 @@ import (
"html/template" "html/template"
"log" "log"
"github.com/dcarrillo/whatismyip/internal/setting"
"github.com/dcarrillo/whatismyip/service" "github.com/dcarrillo/whatismyip/service"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
) )
var geoSvc *service.Geo type Router struct {
geo *service.Geo
trustedHeader string
trustedPortHeader string
templatePath string
disableScan bool
}
func SetupTemplate(r *gin.Engine) { func NewRouter(geo *service.Geo, trustedHeader, trustedPortHeader, templatePath string, disableScan bool) *Router {
if setting.App.TemplatePath == "" { return &Router{
geo: geo,
trustedHeader: trustedHeader,
trustedPortHeader: trustedPortHeader,
templatePath: templatePath,
disableScan: disableScan,
}
}
func SetupTemplate(r *gin.Engine, templatePath string) {
if templatePath == "" {
r.SetHTMLTemplate(template.Must(template.New("home").Parse(home))) r.SetHTMLTemplate(template.Must(template.New("home").Parse(home)))
} else { } else {
log.Printf("Template %s has been loaded", setting.App.TemplatePath) log.Printf("Template %s has been loaded", templatePath)
r.LoadHTMLFiles(setting.App.TemplatePath) r.LoadHTMLFiles(templatePath)
} }
} }
func Setup(r *gin.Engine, geo *service.Geo) { func Setup(r *gin.Engine, rt *Router) {
geoSvc = geo r.GET("/", rt.getRoot)
r.GET("/", getRoot) if !rt.disableScan {
if !setting.App.DisableTCPScan { r.GET("/scan/tcp/:port", rt.scanTCPPort)
r.GET("/scan/tcp/:port", scanTCPPort)
} }
r.GET("/client-port", getClientPortAsString) r.GET("/client-port", rt.getClientPortAsString)
r.GET("/geo", getGeoAsString) r.GET("/geo", rt.getGeoAsString)
r.GET("/geo/:field", getGeoAsString) r.GET("/geo/:field", rt.getGeoAsString)
r.GET("/asn", getASNAsString) r.GET("/asn", rt.getASNAsString)
r.GET("/asn/:field", getASNAsString) r.GET("/asn/:field", rt.getASNAsString)
r.GET("/headers", getHeadersAsSortedString) r.GET("/headers", rt.getHeadersAsSortedString)
r.GET("/all", getAllAsString) r.GET("/all", rt.getAllAsString)
r.GET("/json", getJSON) r.GET("/json", rt.getJSON)
r.GET("/:header", getHeaderAsString) r.GET("/:header", rt.getHeaderAsString)
} }
+4 -1
View File
@@ -47,11 +47,14 @@ const (
domain = "dns.example.com" domain = "dns.example.com"
) )
var rt *Router
func TestMain(m *testing.M) { func TestMain(m *testing.M) {
app = gin.Default() app = gin.Default()
app.TrustedPlatform = trustedHeader app.TrustedPlatform = trustedHeader
svc, _ := service.NewGeo(context.Background(), "../test/GeoIP2-City-Test.mmdb", "../test/GeoLite2-ASN-Test.mmdb") svc, _ := service.NewGeo(context.Background(), "../test/GeoIP2-City-Test.mmdb", "../test/GeoLite2-ASN-Test.mmdb")
Setup(app, svc) rt = NewRouter(svc, trustedHeader, "", "", false)
Setup(app, rt)
os.Exit(m.Run()) os.Exit(m.Run())
} }
+12 -9
View File
@@ -6,18 +6,21 @@ import (
"log" "log"
"net/http" "net/http"
"github.com/dcarrillo/whatismyip/internal/setting"
"github.com/prometheus/client_golang/prometheus/promhttp" "github.com/prometheus/client_golang/prometheus/promhttp"
) )
type Prometheus struct { type Prometheus struct {
server *http.Server server *http.Server
ctx context.Context ctx context.Context
addr string
timeouts Timeouts
} }
func NewPrometheusServer(ctx context.Context) *Prometheus { func NewPrometheusServer(ctx context.Context, addr string, timeouts Timeouts) *Prometheus {
return &Prometheus{ return &Prometheus{
ctx: ctx, ctx: ctx,
addr: addr,
timeouts: timeouts,
} }
} }
@@ -26,13 +29,13 @@ func (p *Prometheus) Start() {
mux.Handle("/metrics", promhttp.Handler()) mux.Handle("/metrics", promhttp.Handler())
p.server = &http.Server{ p.server = &http.Server{
Addr: setting.App.PrometheusAddress, Addr: p.addr,
Handler: mux, Handler: mux,
ReadTimeout: setting.App.Server.ReadTimeout, ReadTimeout: p.timeouts.ReadTimeout,
WriteTimeout: setting.App.Server.WriteTimeout, WriteTimeout: p.timeouts.WriteTimeout,
} }
log.Printf("Starting Prometheus server listening on %s", setting.App.PrometheusAddress) log.Printf("Starting Prometheus server listening on %s", p.addr)
go func() { go func() {
if err := p.server.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) { if err := p.server.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
log.Fatal(err) log.Fatal(err)
+10 -5
View File
@@ -6,7 +6,6 @@ import (
"log" "log"
"net/http" "net/http"
"github.com/dcarrillo/whatismyip/internal/setting"
"github.com/quic-go/quic-go/http3" "github.com/quic-go/quic-go/http3"
) )
@@ -14,18 +13,24 @@ type Quic struct {
server *http3.Server server *http3.Server
tlsServer *TLS tlsServer *TLS
ctx context.Context ctx context.Context
addr string
crtPath string
keyPath string
} }
func NewQuicServer(ctx context.Context, tlsServer *TLS) *Quic { func NewQuicServer(ctx context.Context, tlsServer *TLS, addr, crt, key string) *Quic {
return &Quic{ return &Quic{
tlsServer: tlsServer, tlsServer: tlsServer,
ctx: ctx, ctx: ctx,
addr: addr,
crtPath: crt,
keyPath: key,
} }
} }
func (q *Quic) Start() { func (q *Quic) Start() {
q.server = &http3.Server{ q.server = &http3.Server{
Addr: setting.App.TLSAddress, Addr: q.addr,
Handler: q.tlsServer.server.Handler, Handler: q.tlsServer.server.Handler,
} }
@@ -38,9 +43,9 @@ func (q *Quic) Start() {
parentHandler.ServeHTTP(rw, req) parentHandler.ServeHTTP(rw, req)
}) })
log.Printf("Starting QUIC server listening on %s (udp)", setting.App.TLSAddress) log.Printf("Starting QUIC server listening on %s (udp)", q.addr)
go func() { go func() {
if err := q.server.ListenAndServeTLS(setting.App.TLSCrtPath, setting.App.TLSKeyPath); err != nil && if err := q.server.ListenAndServeTLS(q.crtPath, q.keyPath); err != nil &&
!errors.Is(err, http.ErrServerClosed) { !errors.Is(err, http.ErrServerClosed) {
log.Fatal(err) log.Fatal(err)
} }
+21 -13
View File
@@ -5,32 +5,40 @@ import (
"errors" "errors"
"log" "log"
"net/http" "net/http"
"time"
"github.com/dcarrillo/whatismyip/internal/setting"
) )
type TCP struct { type Timeouts struct {
server *http.Server ReadTimeout time.Duration
handler http.Handler WriteTimeout time.Duration
ctx context.Context
} }
func NewTCPServer(ctx context.Context, handler http.Handler) *TCP { type TCP struct {
server *http.Server
handler http.Handler
ctx context.Context
addr string
timeouts Timeouts
}
func NewTCPServer(ctx context.Context, handler http.Handler, addr string, timeouts Timeouts) *TCP {
return &TCP{ return &TCP{
handler: handler, handler: handler,
ctx: ctx, ctx: ctx,
addr: addr,
timeouts: timeouts,
} }
} }
func (t *TCP) Start() { func (t *TCP) Start() {
t.server = &http.Server{ t.server = &http.Server{
Addr: setting.App.BindAddress, Addr: t.addr,
Handler: t.handler, Handler: t.handler,
ReadTimeout: setting.App.Server.ReadTimeout, ReadTimeout: t.timeouts.ReadTimeout,
WriteTimeout: setting.App.Server.WriteTimeout, WriteTimeout: t.timeouts.WriteTimeout,
} }
log.Printf("Starting TCP server listening on %s", setting.App.BindAddress) log.Printf("Starting TCP server listening on %s", t.addr)
go func() { go func() {
if err := t.server.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) { if err := t.server.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
log.Fatal(err) log.Fatal(err)
+19 -13
View File
@@ -6,37 +6,43 @@ import (
"errors" "errors"
"log" "log"
"net/http" "net/http"
"github.com/dcarrillo/whatismyip/internal/setting"
) )
type TLS struct { type TLS struct {
server *http.Server server *http.Server
handler http.Handler handler http.Handler
ctx context.Context ctx context.Context
addr string
crtPath string
keyPath string
timeouts Timeouts
} }
func NewTLSServer(ctx context.Context, handler http.Handler) *TLS { func NewTLSServer(ctx context.Context, handler http.Handler, addr, crt, key string, timeouts Timeouts) *TLS {
return &TLS{ return &TLS{
handler: handler, handler: handler,
ctx: ctx, ctx: ctx,
addr: addr,
crtPath: crt,
keyPath: key,
timeouts: timeouts,
} }
} }
func (t *TLS) Start() { func (t *TLS) Start() {
t.server = &http.Server{ t.server = &http.Server{
Addr: setting.App.TLSAddress, Addr: t.addr,
Handler: t.handler, Handler: t.handler,
ReadTimeout: setting.App.Server.ReadTimeout, ReadTimeout: t.timeouts.ReadTimeout,
WriteTimeout: setting.App.Server.WriteTimeout, WriteTimeout: t.timeouts.WriteTimeout,
TLSConfig: &tls.Config{ TLSConfig: &tls.Config{
MinVersion: tls.VersionTLS12, MinVersion: tls.VersionTLS12,
}, },
} }
log.Printf("Starting TLS server listening on %s", setting.App.TLSAddress) log.Printf("Starting TLS server listening on %s", t.addr)
go func() { go func() {
if err := t.server.ListenAndServeTLS(setting.App.TLSCrtPath, setting.App.TLSKeyPath); err != nil && if err := t.server.ListenAndServeTLS(t.crtPath, t.keyPath); err != nil &&
!errors.Is(err, http.ErrServerClosed) { !errors.Is(err, http.ErrServerClosed) {
log.Fatal(err) log.Fatal(err)
} }