-
Notifications
You must be signed in to change notification settings - Fork 0
audit: comprehensive hygiene, bug fixes, and new features #19
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 17 commits
Commits
Show all changes
19 commits
Select commit
Hold shift + click to select a range
fc722b3
fix(sendRequest): handle nil GetBody, close response on retry, and co…
99222ee
test: use closed localhost port for deterministic network-error test
a51743d
docs: add copilot instructions and docker-only + atomic commits guidance
ef542d1
refactor: pre-read request body when GetBody missing; improve retry d…
2d68217
fix(gosec): guard uint64<->int64 conversions for MAX_BODY_SIZE displa…
022aa68
fix(gosec): remove unused math import
98b1b33
fix(gosec): guard uint64->int64 conversion for MAX_BODY_SIZE
0a7c369
fix(gosec): validate target URLs and annotate jitter/client.Do for gosec
cee4a3a
fix(gosec): suppress SSRF warning at request creation, use http.Serve…
ec50d4e
fix(gosec): avoid log injection and handle encoder/close errors
4517c31
fix(gosec): avoid log injection and handle Close/Encode errors
f0f36b0
fix: remove deprecated rand.Seed, min func shadow, net.Error.Temporary()
36373ca
perf/fix: cache startup config; record bodySize for all body paths; r…
508ae88
feat: X-Request-ID correlation; latency_seconds encoding; truncation …
376d87c
fix: implement -healthcheck flag; move flag handling before server setup
1c4b628
test: adapt tests for cached config vars; restore maxRetries global s…
3c1c06f
ci: enforce lint/vet gate; add gosec scan to binary-release workflow
3cc5924
fix: address Copilot review comments
d050b23
chore: merge main (Dependabot action bumps)
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,147 @@ | ||
| # Copilot instructions — FanOut | ||
|
|
||
| This file gives targeted, repository-specific guidance for future Copilot sessions working on FanOut (Go, single-binary HTTP fan-out service). | ||
|
|
||
| --- | ||
|
|
||
| ## Build, test, and lint commands | ||
|
|
||
| Build (release): | ||
|
|
||
| go build -trimpath -ldflags="-w -s" -o fanout | ||
|
|
||
| Build (debug): | ||
|
|
||
| go build -tags=debug -o fanout-debug | ||
|
|
||
| Run locally (echo mode for development): | ||
|
|
||
| TARGETS=localonly go run fanout.go | ||
|
|
||
| Run with production targets: | ||
|
|
||
| TARGETS="https://a.example/,https://b.example/" PORT=8080 go run fanout.go | ||
|
|
||
| Run the full test suite (with race detector): | ||
|
|
||
| go test -v -race ./... | ||
|
|
||
| Run a single test (exact name): | ||
|
|
||
| go test -run '^TestSendRequest$' . | ||
|
|
||
| Run a single test with race and verbose output: | ||
|
|
||
| go test -run '^TestSendRequest$' -v -race . | ||
|
|
||
| Formatting / vet: | ||
|
|
||
| gofmt -w . | ||
| go vet ./... | ||
|
|
||
| Security scan (used by README/CI): | ||
|
|
||
| gosec ./... | ||
|
|
||
| Docker (local): | ||
|
|
||
| docker build -t fanout:dev . | ||
|
|
||
| Multi-arch build (CI / release): | ||
|
|
||
| docker buildx build --platform linux/amd64,linux/arm64 -t yourorg/fanout:latest . | ||
|
|
||
| CI workflows: | ||
|
|
||
| - .github/workflows/docker-image.yml | ||
| - .github/workflows/binary-release.yml | ||
|
|
||
| --- | ||
|
|
||
| ## High-level architecture (big picture) | ||
|
|
||
| - Entrypoint: `fanout.go` — sets up HTTP handlers and environment-based configuration in `init()`. | ||
|
|
||
| - Endpoints: | ||
| - `ENDPOINT_PATH` (default `/fanout`) — main fan-out endpoint. | ||
| - `/health` — simple health check. | ||
| - `/version` — binary/version metadata. | ||
| - `/metrics` — Prometheus handler (enabled when `METRICS_ENABLED=true`). | ||
|
|
||
| - Modes: | ||
| - Echo mode: `TARGETS=localonly` — inbound requests are echoed back by `echoHandler`. | ||
| - Multiplex mode: `TARGETS` contains comma-separated targets; `multiplex` spawns one goroutine per target. | ||
|
|
||
| - Dispatcher & concurrency: | ||
| - `multiplex` launches a goroutine per configured target; responses are collected via a buffered channel and WaitGroup. Response order is not guaranteed. | ||
|
|
||
| - Request forwarding (`sendRequest`): | ||
| - Re-creates the original request per target, clones headers via `cloneHeaders` (sensitive headers are logged), and sets Content-Length appropriately. | ||
| - Implements retries for network errors and server (5xx) responses using exponential backoff + jitter. | ||
| - Adds `X-Retry-Count` on retry attempts. | ||
|
|
||
| - Logging & metrics: | ||
| - Asynchronous logger: `logQueue` is a buffered channel, format controlled by `LOG_FORMAT` (json/text) and `LOG_LEVEL`. | ||
| - Prometheus metrics (prefixed `fanout_`) are recorded when `METRICS_ENABLED=true`. | ||
|
|
||
| --- | ||
|
|
||
| ## Key repository conventions and gotchas | ||
|
|
||
| - Configuration is environment-driven and read in `init()`; changing env vars requires restarting the process. | ||
|
|
||
| - Body handling / GetBody semantics: | ||
| - The code uses a pre-read body optimization: when available, `preReadBody` is used for the first attempt; subsequent attempts call `getBody()`. | ||
| - Tests use `httptest.NewRequest` which provides `GetBody`; when writing tests or mock requests, ensure `GetBody` is present or provide a pre-read body. | ||
|
|
||
| - Retry behavior: | ||
| - Controlled via `MAX_RETRIES` (default: 3). | ||
| - Network errors are detected by substring matching in `isRetryableError` (e.g., "connection refused", "timeout", "deadline exceeded", "connection reset", "no such host"). | ||
| - 5xx responses trigger retries up to the configured limit. | ||
|
|
||
| - Sensitive headers: | ||
| - Configured via `SENSITIVE_HEADERS` (default `Authorization,Cookie`). `cloneHeaders` will log a warning when those are detected. | ||
|
|
||
| - Metrics naming and labels: | ||
| - Prometheus metrics use fixed names (e.g., `fanout_requests_total`, `fanout_target_requests_total`). Avoid renaming these without updating monitoring. | ||
|
|
||
| - Concurrency expectations: | ||
| - `multiplex` returns responses as they arrive. Do not rely on responses being in the same order as `TARGETS` unless ordering is explicitly implemented. | ||
|
|
||
| - Logging behavior: | ||
| - Log entries are queued to `logQueue`; if the queue is full, entries may be dropped or logged directly when errors occur. | ||
|
|
||
| - Versioning variables: | ||
| - `Version`, `GitCommit`, and `BuildTime` are populated at build time (defaults: dev/unknown). CI/release workflows set these. | ||
|
|
||
| --- | ||
|
|
||
| ## Where to look (short pointers) | ||
|
|
||
| - Core: `fanout.go` (single-file service implementation) | ||
| - Unit tests: `fanout_test.go` | ||
| - Container: `Dockerfile`, `compose.yml` | ||
| - CI: `.github/workflows/*` | ||
|
|
||
| --- | ||
|
|
||
| ## Repository workflow preferences | ||
|
|
||
| - Docker-only execution: All development, builds, tests and linters should be executed inside Docker containers, not on the host machine. This includes local runs, single-test runs, formatting, vetting, and CI-parity commands. Examples: | ||
|
|
||
| # Run full test suite inside official Go container | ||
| docker run --rm -v $(pwd):/src -w /src golang:1.24 go test -v -race ./... | ||
|
|
||
| # Run a single test inside Docker | ||
| docker run --rm -v $(pwd):/src -w /src golang:1.24 go test -run '^TestSendRequest$' -v -race . | ||
|
|
||
| # Build inside Docker | ||
| docker run --rm -v $(pwd):/src -w /src golang:1.24 go build -trimpath -ldflags="-w -s" -o fanout | ||
|
|
||
| Prefer running via docker-compose (compose.yml) or CI-style containers so host toolchains are not required. | ||
|
|
||
| - Atomic commits: Make small, atomic commits for every logical change. Each commit should be self-contained and reversible. Use a separate branch per feature/bugfix and keep commit messages focused on a single purpose. | ||
|
|
||
| --- | ||
|
|
||
| If something important is missing or you want additional coverage (examples, more test-run tips, or CI notes), ask and this file can be expanded. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -53,10 +53,18 @@ jobs: | |||||
| with: | ||||||
| go-version: '1.24' | ||||||
|
|
||||||
| - name: Run unit tests | ||||||
| - name: Static analysis (gofmt, go vet) | ||||||
| run: | | ||||||
| go mod download | ||||||
| go test -v ./... | ||||||
| docker run --rm -v ${{ github.workspace }}:/src -w /src golang:1.24 sh -c \ | ||||||
| 'GOFMT_OUT=$(gofmt -l .); [ -z "$GOFMT_OUT" ] || (echo "$GOFMT_OUT"; exit 1); go vet ./...' | ||||||
|
|
||||||
| - name: Security scan (gosec) | ||||||
| run: | | ||||||
| docker run --rm -v ${{ github.workspace }}:/src -w /src securego/gosec:latest gosec ./... | ||||||
|
||||||
| docker run --rm -v ${{ github.workspace }}:/src -w /src securego/gosec:latest gosec ./... | |
| docker run --rm -v ${{ github.workspace }}:/src -w /src securego/gosec:v2.22.2 gosec ./... |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The workflow pulls
securego/gosec:latest, which makes release gating non-deterministic and can break unexpectedly when upstream publishes a new image. Consider pinning to a specific gosec version tag or an image digest for reproducible release builds.