@mytechtoday/augment-extensions 1.4.0 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/augment-extensions/coding-standards/c/CHANGELOG.md +55 -0
- package/augment-extensions/coding-standards/c/LICENSE +22 -0
- package/augment-extensions/coding-standards/c/README.md +167 -0
- package/augment-extensions/coding-standards/c/config/defaults.json +26 -0
- package/augment-extensions/coding-standards/c/config/examples/embedded.yaml +25 -0
- package/augment-extensions/coding-standards/c/config/examples/systems.json +31 -0
- package/augment-extensions/coding-standards/c/config/schema.json +244 -0
- package/augment-extensions/coding-standards/c/docs/API.md +613 -0
- package/augment-extensions/coding-standards/c/docs/CONFIGURATION.md +259 -0
- package/augment-extensions/coding-standards/c/docs/USER_GUIDE.md +567 -0
- package/augment-extensions/coding-standards/c/examples/drivers/Makefile +33 -0
- package/augment-extensions/coding-standards/c/examples/drivers/README.md +192 -0
- package/augment-extensions/coding-standards/c/examples/drivers/dma-example.c +224 -0
- package/augment-extensions/coding-standards/c/examples/drivers/example.dts +64 -0
- package/augment-extensions/coding-standards/c/examples/drivers/platform-driver.c +174 -0
- package/augment-extensions/coding-standards/c/examples/embedded/README.md +167 -0
- package/augment-extensions/coding-standards/c/examples/embedded/gpio-control.c +172 -0
- package/augment-extensions/coding-standards/c/examples/embedded/timer-isr.c +198 -0
- package/augment-extensions/coding-standards/c/examples/embedded/uart-communication.c +212 -0
- package/augment-extensions/coding-standards/c/examples/kernel/Makefile +82 -0
- package/augment-extensions/coding-standards/c/examples/kernel/README.md +168 -0
- package/augment-extensions/coding-standards/c/examples/kernel/char-device.c +198 -0
- package/augment-extensions/coding-standards/c/examples/kernel/proc-file.c +131 -0
- package/augment-extensions/coding-standards/c/examples/kernel/simple-module.c +111 -0
- package/augment-extensions/coding-standards/c/examples/legacy/Makefile +62 -0
- package/augment-extensions/coding-standards/c/examples/legacy/README.md +255 -0
- package/augment-extensions/coding-standards/c/examples/legacy/c89-to-c11-migration.c +268 -0
- package/augment-extensions/coding-standards/c/examples/legacy/compatibility-layer.c +239 -0
- package/augment-extensions/coding-standards/c/examples/networking/Makefile +35 -0
- package/augment-extensions/coding-standards/c/examples/networking/README.md +207 -0
- package/augment-extensions/coding-standards/c/examples/networking/protocol-parser.c +270 -0
- package/augment-extensions/coding-standards/c/examples/networking/tcp-server.c +197 -0
- package/augment-extensions/coding-standards/c/examples/networking/udp-multicast.c +220 -0
- package/augment-extensions/coding-standards/c/examples/realtime/Makefile +53 -0
- package/augment-extensions/coding-standards/c/examples/realtime/README.md +199 -0
- package/augment-extensions/coding-standards/c/examples/realtime/deadline-monitoring.c +260 -0
- package/augment-extensions/coding-standards/c/examples/realtime/priority-scheduling.c +258 -0
- package/augment-extensions/coding-standards/c/examples/systems/Makefile +34 -0
- package/augment-extensions/coding-standards/c/examples/systems/README.md +123 -0
- package/augment-extensions/coding-standards/c/examples/systems/ipc-pipes.c +181 -0
- package/augment-extensions/coding-standards/c/examples/systems/process-management.c +153 -0
- package/augment-extensions/coding-standards/c/examples/systems/signal-handling.c +162 -0
- package/augment-extensions/coding-standards/c/module.json +149 -0
- package/augment-extensions/coding-standards/c/rules/categories/drivers.md +635 -0
- package/augment-extensions/coding-standards/c/rules/categories/embedded.md +510 -0
- package/augment-extensions/coding-standards/c/rules/categories/kernel.md +653 -0
- package/augment-extensions/coding-standards/c/rules/categories/legacy.md +526 -0
- package/augment-extensions/coding-standards/c/rules/categories/networking.md +735 -0
- package/augment-extensions/coding-standards/c/rules/categories/realtime.md +631 -0
- package/augment-extensions/coding-standards/c/rules/categories/systems.md +586 -0
- package/augment-extensions/coding-standards/c/rules/universal/const-correctness.md +275 -0
- package/augment-extensions/coding-standards/c/rules/universal/documentation.md +251 -0
- package/augment-extensions/coding-standards/c/rules/universal/error-handling.md +250 -0
- package/augment-extensions/coding-standards/c/rules/universal/header-guards.md +254 -0
- package/augment-extensions/coding-standards/c/rules/universal/memory-safety.md +233 -0
- package/augment-extensions/coding-standards/c/rules/universal/naming.md +146 -0
- package/augment-extensions/coding-standards/c/src/conflict-detector.ts +461 -0
- package/augment-extensions/coding-standards/c/src/prompt-generator.ts +307 -0
- package/augment-extensions/coding-standards/c/src/rule-evaluator.ts +307 -0
- package/augment-extensions/coding-standards/c/src/rule-override.ts +427 -0
- package/augment-extensions/coding-standards/c/src/template-engine.ts +217 -0
- package/augment-extensions/coding-standards/c/templates/prompts/drivers.txt +191 -0
- package/augment-extensions/coding-standards/c/templates/prompts/embedded.txt +164 -0
- package/augment-extensions/coding-standards/c/templates/prompts/kernel.txt +175 -0
- package/augment-extensions/coding-standards/c/templates/prompts/legacy.txt +280 -0
- package/augment-extensions/coding-standards/c/templates/prompts/networking.txt +259 -0
- package/augment-extensions/coding-standards/c/templates/prompts/realtime.txt +219 -0
- package/augment-extensions/coding-standards/c/templates/prompts/systems.txt +147 -0
- package/augment-extensions/coding-standards/c/tests/integration/category-specific.test.ts +356 -0
- package/augment-extensions/coding-standards/c/tests/integration/end-to-end-workflow.test.ts +377 -0
- package/augment-extensions/coding-standards/c/tests/performance/benchmarks.test.ts +407 -0
- package/augment-extensions/coding-standards/c/tests/unit/config-manager.test.ts +345 -0
- package/augment-extensions/coding-standards/c/tests/unit/conflict-detector.test.ts +294 -0
- package/augment-extensions/coding-standards/c/tests/unit/prompt-generator.test.ts +174 -0
- package/augment-extensions/coding-standards/c/tests/unit/registry.test.ts +313 -0
- package/augment-extensions/coding-standards/c/tests/unit/rule-evaluator.test.ts +318 -0
- package/augment-extensions/coding-standards/c/tests/unit/rule-override.test.ts +326 -0
- package/augment-extensions/coding-standards/c/tests/unit/template-engine.test.ts +314 -0
- package/augment-extensions/coding-standards/go/CHARACTER-COUNT-REPORT.md +135 -0
- package/augment-extensions/coding-standards/go/PHASE1-COMPLETION.md +146 -0
- package/augment-extensions/coding-standards/go/PHASE4-COMPLETION.md +184 -0
- package/augment-extensions/coding-standards/go/README.md +200 -0
- package/augment-extensions/coding-standards/go/VALIDATION-CHECKLIST.md +154 -0
- package/augment-extensions/coding-standards/go/config/examples/example-cli.json +15 -0
- package/augment-extensions/coding-standards/go/config/examples/example-microservices.json +21 -0
- package/augment-extensions/coding-standards/go/config/examples/example-multi-category.yaml +24 -0
- package/augment-extensions/coding-standards/go/config/examples/example-web.json +15 -0
- package/augment-extensions/coding-standards/go/config/schema.json +110 -0
- package/augment-extensions/coding-standards/go/docs/CATEGORIES.md +221 -0
- package/augment-extensions/coding-standards/go/docs/CONFIGURATION.md +198 -0
- package/augment-extensions/coding-standards/go/docs/TROUBLESHOOTING.md +285 -0
- package/augment-extensions/coding-standards/go/examples/cli/cobra-app.go +287 -0
- package/augment-extensions/coding-standards/go/examples/cloud-native-app.go +217 -0
- package/augment-extensions/coding-standards/go/examples/devops-tool.go +250 -0
- package/augment-extensions/coding-standards/go/examples/distributed-system.go +247 -0
- package/augment-extensions/coding-standards/go/examples/microservices/grpc-service.go +253 -0
- package/augment-extensions/coding-standards/go/examples/rest-api.go +270 -0
- package/augment-extensions/coding-standards/go/examples/web/http-server.go +224 -0
- package/augment-extensions/coding-standards/go/module.json +139 -0
- package/augment-extensions/coding-standards/go/rules/categories/api-development/api-versioning.md +149 -0
- package/augment-extensions/coding-standards/go/rules/categories/api-development/rate-limiting.md +209 -0
- package/augment-extensions/coding-standards/go/rules/categories/api-development/rest-api-design.md +183 -0
- package/augment-extensions/coding-standards/go/rules/categories/cloud-native/cloud-config.md +193 -0
- package/augment-extensions/coding-standards/go/rules/categories/cloud-native/health-checks.md +231 -0
- package/augment-extensions/coding-standards/go/rules/categories/cloud-native/kubernetes.md +180 -0
- package/augment-extensions/coding-standards/go/rules/categories/devops-tooling/automation.md +179 -0
- package/augment-extensions/coding-standards/go/rules/categories/devops-tooling/ci-cd-integration.md +147 -0
- package/augment-extensions/coding-standards/go/rules/categories/devops-tooling/infrastructure-as-code.md +231 -0
- package/augment-extensions/coding-standards/go/rules/categories/distributed-systems/caching.md +150 -0
- package/augment-extensions/coding-standards/go/rules/categories/distributed-systems/consensus.md +187 -0
- package/augment-extensions/coding-standards/go/rules/categories/distributed-systems/event-sourcing.md +246 -0
- package/augment-extensions/coding-standards/go/rules/cli/command-parsing.md +264 -0
- package/augment-extensions/coding-standards/go/rules/cli/configuration.md +268 -0
- package/augment-extensions/coding-standards/go/rules/cli/cross-platform.md +324 -0
- package/augment-extensions/coding-standards/go/rules/microservices/distributed-tracing.md +253 -0
- package/augment-extensions/coding-standards/go/rules/microservices/grpc.md +257 -0
- package/augment-extensions/coding-standards/go/rules/microservices/metrics.md +278 -0
- package/augment-extensions/coding-standards/go/rules/microservices/service-discovery.md +249 -0
- package/augment-extensions/coding-standards/go/rules/universal/code-organization.md +221 -0
- package/augment-extensions/coding-standards/go/rules/universal/documentation.md +269 -0
- package/augment-extensions/coding-standards/go/rules/universal/performance.md +323 -0
- package/augment-extensions/coding-standards/go/rules/universal/testing.md +162 -0
- package/augment-extensions/coding-standards/go/rules/web/graceful-shutdown.md +249 -0
- package/augment-extensions/coding-standards/go/rules/web/http-handlers.md +164 -0
- package/augment-extensions/coding-standards/go/rules/web/middleware.md +234 -0
- package/augment-extensions/coding-standards/go/rules/web/routing.md +251 -0
- package/augment-extensions/coding-standards/go/templates/prompts/api.md +160 -0
- package/augment-extensions/coding-standards/go/templates/prompts/cli.md +225 -0
- package/augment-extensions/coding-standards/go/templates/prompts/cloud-native.md +121 -0
- package/augment-extensions/coding-standards/go/templates/prompts/devops.md +146 -0
- package/augment-extensions/coding-standards/go/templates/prompts/distributed.md +133 -0
- package/augment-extensions/coding-standards/go/templates/prompts/microservices.md +225 -0
- package/augment-extensions/coding-standards/go/templates/prompts/web.md +181 -0
- package/augment-extensions/coding-standards/go/tests/integration/module-integration.test.ts +164 -0
- package/augment-extensions/coding-standards/go/tests/unit/category-selection.test.ts +147 -0
- package/augment-extensions/coding-standards/go/tests/unit/module-structure.test.ts +154 -0
- package/augment-extensions/coding-standards/go/tests/validate-character-count.ps1 +13 -0
- package/augment-extensions/coding-standards/go/tests/validate-examples.ps1 +148 -0
- package/augment-extensions/coding-standards/go/tests/validate-examples.sh +135 -0
- package/cli/dist/analysis/ast-parser.d.ts +47 -0
- package/cli/dist/analysis/ast-parser.d.ts.map +1 -0
- package/cli/dist/analysis/ast-parser.js +161 -0
- package/cli/dist/analysis/ast-parser.js.map +1 -0
- package/cli/dist/analysis/complexity-analyzer.d.ts +27 -0
- package/cli/dist/analysis/complexity-analyzer.d.ts.map +1 -0
- package/cli/dist/analysis/complexity-analyzer.js +189 -0
- package/cli/dist/analysis/complexity-analyzer.js.map +1 -0
- package/cli/dist/analysis/dependency-analyzer.d.ts +23 -0
- package/cli/dist/analysis/dependency-analyzer.d.ts.map +1 -0
- package/cli/dist/analysis/dependency-analyzer.js +237 -0
- package/cli/dist/analysis/dependency-analyzer.js.map +1 -0
- package/cli/dist/analysis/index.d.ts +9 -0
- package/cli/dist/analysis/index.d.ts.map +1 -0
- package/cli/dist/analysis/index.js +25 -0
- package/cli/dist/analysis/index.js.map +1 -0
- package/cli/dist/analysis/security-scanner.d.ts +11 -0
- package/cli/dist/analysis/security-scanner.d.ts.map +1 -0
- package/cli/dist/analysis/security-scanner.js +294 -0
- package/cli/dist/analysis/security-scanner.js.map +1 -0
- package/cli/dist/analysis/types.d.ts +151 -0
- package/cli/dist/analysis/types.d.ts.map +1 -0
- package/cli/dist/analysis/types.js +6 -0
- package/cli/dist/analysis/types.js.map +1 -0
- package/cli/dist/cli.js +24 -0
- package/cli/dist/cli.js.map +1 -1
- package/cli/dist/commands/code-analysis.d.ts +11 -0
- package/cli/dist/commands/code-analysis.d.ts.map +1 -0
- package/cli/dist/commands/code-analysis.js +412 -0
- package/cli/dist/commands/code-analysis.js.map +1 -0
- package/modules.md +99 -3
- package/package.json +14 -2
- package/cli/dist/commands/agent.d.ts +0 -37
- package/cli/dist/commands/agent.d.ts.map +0 -1
- package/cli/dist/commands/agent.js +0 -222
- package/cli/dist/commands/agent.js.map +0 -1
- package/cli/dist/commands/beads.d.ts +0 -64
- package/cli/dist/commands/beads.d.ts.map +0 -1
- package/cli/dist/commands/beads.js +0 -377
- package/cli/dist/commands/beads.js.map +0 -1
- package/cli/dist/commands/change.d.ts +0 -54
- package/cli/dist/commands/change.d.ts.map +0 -1
- package/cli/dist/commands/change.js +0 -243
- package/cli/dist/commands/change.js.map +0 -1
- package/cli/dist/commands/clean.d.ts +0 -15
- package/cli/dist/commands/clean.d.ts.map +0 -1
- package/cli/dist/commands/clean.js +0 -63
- package/cli/dist/commands/clean.js.map +0 -1
- package/cli/dist/commands/clone.d.ts +0 -15
- package/cli/dist/commands/clone.d.ts.map +0 -1
- package/cli/dist/commands/clone.js +0 -49
- package/cli/dist/commands/clone.js.map +0 -1
- package/cli/dist/commands/config.d.ts +0 -33
- package/cli/dist/commands/config.d.ts.map +0 -1
- package/cli/dist/commands/config.js +0 -166
- package/cli/dist/commands/config.js.map +0 -1
- package/cli/dist/commands/context.d.ts +0 -38
- package/cli/dist/commands/context.d.ts.map +0 -1
- package/cli/dist/commands/context.js +0 -205
- package/cli/dist/commands/context.js.map +0 -1
- package/cli/dist/commands/create.d.ts +0 -18
- package/cli/dist/commands/create.d.ts.map +0 -1
- package/cli/dist/commands/create.js +0 -178
- package/cli/dist/commands/create.js.map +0 -1
- package/cli/dist/commands/diff.d.ts +0 -19
- package/cli/dist/commands/diff.d.ts.map +0 -1
- package/cli/dist/commands/diff.js +0 -104
- package/cli/dist/commands/diff.js.map +0 -1
- package/cli/dist/commands/doctor.d.ts +0 -14
- package/cli/dist/commands/doctor.d.ts.map +0 -1
- package/cli/dist/commands/doctor.js +0 -62
- package/cli/dist/commands/doctor.js.map +0 -1
- package/cli/dist/commands/export.d.ts +0 -28
- package/cli/dist/commands/export.d.ts.map +0 -1
- package/cli/dist/commands/export.js +0 -135
- package/cli/dist/commands/export.js.map +0 -1
- package/cli/dist/commands/import.d.ts +0 -23
- package/cli/dist/commands/import.d.ts.map +0 -1
- package/cli/dist/commands/import.js +0 -118
- package/cli/dist/commands/import.js.map +0 -1
- package/cli/dist/commands/prompt.d.ts +0 -45
- package/cli/dist/commands/prompt.d.ts.map +0 -1
- package/cli/dist/commands/prompt.js +0 -223
- package/cli/dist/commands/prompt.js.map +0 -1
- package/cli/dist/commands/spec.d.ts +0 -57
- package/cli/dist/commands/spec.d.ts.map +0 -1
- package/cli/dist/commands/spec.js +0 -279
- package/cli/dist/commands/spec.js.map +0 -1
- package/cli/dist/commands/stats.d.ts +0 -18
- package/cli/dist/commands/stats.d.ts.map +0 -1
- package/cli/dist/commands/stats.js +0 -85
- package/cli/dist/commands/stats.js.map +0 -1
- package/cli/dist/commands/task.d.ts +0 -65
- package/cli/dist/commands/task.d.ts.map +0 -1
- package/cli/dist/commands/task.js +0 -282
- package/cli/dist/commands/task.js.map +0 -1
- package/cli/dist/commands/template.d.ts +0 -17
- package/cli/dist/commands/template.d.ts.map +0 -1
- package/cli/dist/commands/template.js +0 -55
- package/cli/dist/commands/template.js.map +0 -1
- package/cli/dist/utils/agent-config.d.ts +0 -129
- package/cli/dist/utils/agent-config.d.ts.map +0 -1
- package/cli/dist/utils/agent-config.js +0 -297
- package/cli/dist/utils/agent-config.js.map +0 -1
- package/cli/dist/utils/beads-graph.d.ts +0 -17
- package/cli/dist/utils/beads-graph.d.ts.map +0 -1
- package/cli/dist/utils/beads-graph.js +0 -150
- package/cli/dist/utils/beads-graph.js.map +0 -1
- package/cli/dist/utils/beads-integration.d.ts +0 -112
- package/cli/dist/utils/beads-integration.d.ts.map +0 -1
- package/cli/dist/utils/beads-integration.js +0 -312
- package/cli/dist/utils/beads-integration.js.map +0 -1
- package/cli/dist/utils/beads-reporter.d.ts +0 -17
- package/cli/dist/utils/beads-reporter.d.ts.map +0 -1
- package/cli/dist/utils/beads-reporter.js +0 -160
- package/cli/dist/utils/beads-reporter.js.map +0 -1
- package/cli/dist/utils/cache-manager.d.ts +0 -55
- package/cli/dist/utils/cache-manager.d.ts.map +0 -1
- package/cli/dist/utils/cache-manager.js +0 -150
- package/cli/dist/utils/cache-manager.js.map +0 -1
- package/cli/dist/utils/change-manager.d.ts +0 -70
- package/cli/dist/utils/change-manager.d.ts.map +0 -1
- package/cli/dist/utils/change-manager.js +0 -412
- package/cli/dist/utils/change-manager.js.map +0 -1
- package/cli/dist/utils/config-manager-enhanced.d.ts +0 -66
- package/cli/dist/utils/config-manager-enhanced.d.ts.map +0 -1
- package/cli/dist/utils/config-manager-enhanced.js +0 -77
- package/cli/dist/utils/config-manager-enhanced.js.map +0 -1
- package/cli/dist/utils/context-manager.d.ts +0 -96
- package/cli/dist/utils/context-manager.d.ts.map +0 -1
- package/cli/dist/utils/context-manager.js +0 -258
- package/cli/dist/utils/context-manager.js.map +0 -1
- package/cli/dist/utils/diff-engine.d.ts +0 -78
- package/cli/dist/utils/diff-engine.d.ts.map +0 -1
- package/cli/dist/utils/diff-engine.js +0 -233
- package/cli/dist/utils/diff-engine.js.map +0 -1
- package/cli/dist/utils/export-system.d.ts +0 -101
- package/cli/dist/utils/export-system.d.ts.map +0 -1
- package/cli/dist/utils/export-system.js +0 -289
- package/cli/dist/utils/export-system.js.map +0 -1
- package/cli/dist/utils/health-checker.d.ts +0 -66
- package/cli/dist/utils/health-checker.d.ts.map +0 -1
- package/cli/dist/utils/health-checker.js +0 -285
- package/cli/dist/utils/health-checker.js.map +0 -1
- package/cli/dist/utils/import-system.d.ts +0 -74
- package/cli/dist/utils/import-system.d.ts.map +0 -1
- package/cli/dist/utils/import-system.js +0 -317
- package/cli/dist/utils/import-system.js.map +0 -1
- package/cli/dist/utils/module-cloner.d.ts +0 -40
- package/cli/dist/utils/module-cloner.d.ts.map +0 -1
- package/cli/dist/utils/module-cloner.js +0 -136
- package/cli/dist/utils/module-cloner.js.map +0 -1
- package/cli/dist/utils/prompt-manager.d.ts +0 -90
- package/cli/dist/utils/prompt-manager.d.ts.map +0 -1
- package/cli/dist/utils/prompt-manager.js +0 -302
- package/cli/dist/utils/prompt-manager.js.map +0 -1
- package/cli/dist/utils/spec-manager.d.ts +0 -65
- package/cli/dist/utils/spec-manager.d.ts.map +0 -1
- package/cli/dist/utils/spec-manager.js +0 -329
- package/cli/dist/utils/spec-manager.js.map +0 -1
- package/cli/dist/utils/stats-collector.d.ts +0 -74
- package/cli/dist/utils/stats-collector.d.ts.map +0 -1
- package/cli/dist/utils/stats-collector.js +0 -164
- package/cli/dist/utils/stats-collector.js.map +0 -1
- package/cli/dist/utils/template-engine.d.ts +0 -47
- package/cli/dist/utils/template-engine.d.ts.map +0 -1
- package/cli/dist/utils/template-engine.js +0 -204
- package/cli/dist/utils/template-engine.js.map +0 -1
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
# Go Web Services - Graceful Shutdown
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
Graceful shutdown ensures that a web service stops accepting new requests while completing in-flight requests before terminating. This document defines best practices for implementing graceful shutdown in Go web services.
|
|
6
|
+
|
|
7
|
+
## Core Principles
|
|
8
|
+
|
|
9
|
+
1. **Signal Handling**: Listen for OS signals (SIGINT, SIGTERM)
|
|
10
|
+
2. **Connection Draining**: Complete in-flight requests before shutdown
|
|
11
|
+
3. **Timeout Management**: Set reasonable shutdown timeouts
|
|
12
|
+
4. **Resource Cleanup**: Close database connections, file handles, etc.
|
|
13
|
+
5. **Health Check Updates**: Mark service as unhealthy during shutdown
|
|
14
|
+
|
|
15
|
+
## Rules
|
|
16
|
+
|
|
17
|
+
### GOL.3.1.4.1: Implement Signal-Based Shutdown
|
|
18
|
+
|
|
19
|
+
**Rule**: Always implement graceful shutdown using OS signal handling.
|
|
20
|
+
|
|
21
|
+
**Severity**: ERROR
|
|
22
|
+
|
|
23
|
+
**Rationale**: Prevents data loss and ensures clean termination in production environments.
|
|
24
|
+
|
|
25
|
+
**✅ Good**:
|
|
26
|
+
```go
|
|
27
|
+
func main() {
|
|
28
|
+
// Create server
|
|
29
|
+
srv := &http.Server{
|
|
30
|
+
Addr: ":8080",
|
|
31
|
+
Handler: setupRoutes(),
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Start server in goroutine
|
|
35
|
+
go func() {
|
|
36
|
+
log.Println("Server starting on :8080")
|
|
37
|
+
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
|
38
|
+
log.Fatalf("Server failed: %v", err)
|
|
39
|
+
}
|
|
40
|
+
}()
|
|
41
|
+
|
|
42
|
+
// Wait for interrupt signal
|
|
43
|
+
quit := make(chan os.Signal, 1)
|
|
44
|
+
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
|
|
45
|
+
<-quit
|
|
46
|
+
|
|
47
|
+
log.Println("Server shutting down...")
|
|
48
|
+
|
|
49
|
+
// Create shutdown context with timeout
|
|
50
|
+
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
|
51
|
+
defer cancel()
|
|
52
|
+
|
|
53
|
+
// Attempt graceful shutdown
|
|
54
|
+
if err := srv.Shutdown(ctx); err != nil {
|
|
55
|
+
log.Fatalf("Server forced to shutdown: %v", err)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
log.Println("Server exited")
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
**❌ Bad**:
|
|
63
|
+
```go
|
|
64
|
+
func main() {
|
|
65
|
+
// No graceful shutdown - server terminates immediately on Ctrl+C
|
|
66
|
+
http.ListenAndServe(":8080", setupRoutes())
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### GOL.3.1.4.2: Set Appropriate Shutdown Timeout
|
|
71
|
+
|
|
72
|
+
**Rule**: Configure shutdown timeout based on expected request duration.
|
|
73
|
+
|
|
74
|
+
**Severity**: WARNING
|
|
75
|
+
|
|
76
|
+
**Rationale**: Too short causes request failures; too long delays deployment.
|
|
77
|
+
|
|
78
|
+
**✅ Good**:
|
|
79
|
+
```go
|
|
80
|
+
const (
|
|
81
|
+
shutdownTimeout = 30 * time.Second // For typical web services
|
|
82
|
+
// shutdownTimeout = 5 * time.Minute // For long-running operations
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
func gracefulShutdown(srv *http.Server) error {
|
|
86
|
+
quit := make(chan os.Signal, 1)
|
|
87
|
+
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
|
|
88
|
+
<-quit
|
|
89
|
+
|
|
90
|
+
log.Println("Shutdown signal received")
|
|
91
|
+
|
|
92
|
+
ctx, cancel := context.WithTimeout(context.Background(), shutdownTimeout)
|
|
93
|
+
defer cancel()
|
|
94
|
+
|
|
95
|
+
return srv.Shutdown(ctx)
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### GOL.3.1.4.3: Clean Up Resources During Shutdown
|
|
100
|
+
|
|
101
|
+
**Rule**: Close all resources (database connections, file handles) during shutdown.
|
|
102
|
+
|
|
103
|
+
**Severity**: ERROR
|
|
104
|
+
|
|
105
|
+
**Rationale**: Prevents resource leaks and ensures data consistency.
|
|
106
|
+
|
|
107
|
+
**✅ Good**:
|
|
108
|
+
```go
|
|
109
|
+
type Server struct {
|
|
110
|
+
http *http.Server
|
|
111
|
+
db *sql.DB
|
|
112
|
+
cache *redis.Client
|
|
113
|
+
logger *slog.Logger
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
func (s *Server) Shutdown(ctx context.Context) error {
|
|
117
|
+
s.logger.Info("Starting graceful shutdown")
|
|
118
|
+
|
|
119
|
+
// Stop accepting new requests
|
|
120
|
+
if err := s.http.Shutdown(ctx); err != nil {
|
|
121
|
+
s.logger.Error("HTTP server shutdown error", "error", err)
|
|
122
|
+
return err
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Close database connections
|
|
126
|
+
if err := s.db.Close(); err != nil {
|
|
127
|
+
s.logger.Error("Database close error", "error", err)
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Close cache connections
|
|
131
|
+
if err := s.cache.Close(); err != nil {
|
|
132
|
+
s.logger.Error("Cache close error", "error", err)
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
s.logger.Info("Shutdown complete")
|
|
136
|
+
return nil
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
func main() {
|
|
140
|
+
srv := NewServer()
|
|
141
|
+
|
|
142
|
+
// Start server
|
|
143
|
+
go func() {
|
|
144
|
+
if err := srv.http.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
|
145
|
+
log.Fatal(err)
|
|
146
|
+
}
|
|
147
|
+
}()
|
|
148
|
+
|
|
149
|
+
// Wait for signal
|
|
150
|
+
quit := make(chan os.Signal, 1)
|
|
151
|
+
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
|
|
152
|
+
<-quit
|
|
153
|
+
|
|
154
|
+
// Graceful shutdown
|
|
155
|
+
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
|
156
|
+
defer cancel()
|
|
157
|
+
|
|
158
|
+
if err := srv.Shutdown(ctx); err != nil {
|
|
159
|
+
log.Fatal(err)
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### GOL.3.1.4.4: Update Health Checks During Shutdown
|
|
165
|
+
|
|
166
|
+
**Rule**: Mark service as unhealthy when shutdown begins.
|
|
167
|
+
|
|
168
|
+
**Severity**: WARNING
|
|
169
|
+
|
|
170
|
+
**Rationale**: Prevents load balancers from sending new requests during shutdown.
|
|
171
|
+
|
|
172
|
+
**✅ Good**:
|
|
173
|
+
```go
|
|
174
|
+
type HealthChecker struct {
|
|
175
|
+
mu sync.RWMutex
|
|
176
|
+
healthy bool
|
|
177
|
+
shutdown bool
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
func (h *HealthChecker) SetHealthy(healthy bool) {
|
|
181
|
+
h.mu.Lock()
|
|
182
|
+
defer h.mu.Unlock()
|
|
183
|
+
h.healthy = healthy
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
func (h *HealthChecker) SetShutdown() {
|
|
187
|
+
h.mu.Lock()
|
|
188
|
+
defer h.mu.Unlock()
|
|
189
|
+
h.shutdown = true
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
func (h *HealthChecker) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|
193
|
+
h.mu.RLock()
|
|
194
|
+
defer h.mu.RUnlock()
|
|
195
|
+
|
|
196
|
+
if h.shutdown {
|
|
197
|
+
w.WriteHeader(http.StatusServiceUnavailable)
|
|
198
|
+
json.NewEncoder(w).Encode(map[string]string{
|
|
199
|
+
"status": "shutting_down",
|
|
200
|
+
})
|
|
201
|
+
return
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
if !h.healthy {
|
|
205
|
+
w.WriteHeader(http.StatusServiceUnavailable)
|
|
206
|
+
json.NewEncoder(w).Encode(map[string]string{
|
|
207
|
+
"status": "unhealthy",
|
|
208
|
+
})
|
|
209
|
+
return
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
w.WriteHeader(http.StatusOK)
|
|
213
|
+
json.NewEncoder(w).Encode(map[string]string{
|
|
214
|
+
"status": "healthy",
|
|
215
|
+
})
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
func main() {
|
|
219
|
+
health := &HealthChecker{healthy: true}
|
|
220
|
+
|
|
221
|
+
http.Handle("/health", health)
|
|
222
|
+
http.Handle("/api/", apiHandler)
|
|
223
|
+
|
|
224
|
+
srv := &http.Server{Addr: ":8080"}
|
|
225
|
+
|
|
226
|
+
go srv.ListenAndServe()
|
|
227
|
+
|
|
228
|
+
quit := make(chan os.Signal, 1)
|
|
229
|
+
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
|
|
230
|
+
<-quit
|
|
231
|
+
|
|
232
|
+
// Mark as shutting down
|
|
233
|
+
health.SetShutdown()
|
|
234
|
+
|
|
235
|
+
// Wait for load balancer to detect unhealthy status
|
|
236
|
+
time.Sleep(5 * time.Second)
|
|
237
|
+
|
|
238
|
+
// Proceed with shutdown
|
|
239
|
+
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
|
240
|
+
defer cancel()
|
|
241
|
+
srv.Shutdown(ctx)
|
|
242
|
+
}
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
## References
|
|
246
|
+
|
|
247
|
+
- [Go HTTP Server Shutdown](https://pkg.go.dev/net/http#Server.Shutdown)
|
|
248
|
+
- [Graceful Shutdown Patterns](https://medium.com/honestbee-tw-engineer/gracefully-shutdown-in-go-http-server-5f5e6b83da5a)
|
|
249
|
+
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
# Go Web Services - HTTP Handlers
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
HTTP handlers are the core of web services in Go. This document defines best practices for implementing HTTP handlers that are robust, maintainable, and idiomatic.
|
|
6
|
+
|
|
7
|
+
## Core Principles
|
|
8
|
+
|
|
9
|
+
1. **Context Usage**: Always use `context.Context` for request-scoped values and cancellation
|
|
10
|
+
2. **Error Handling**: Return errors explicitly, use `http.Error` for client errors
|
|
11
|
+
3. **Status Codes**: Use appropriate HTTP status codes (2xx, 4xx, 5xx)
|
|
12
|
+
4. **Request Validation**: Validate all inputs before processing
|
|
13
|
+
5. **Response Formatting**: Use consistent response formats (JSON, XML, etc.)
|
|
14
|
+
|
|
15
|
+
## Rules
|
|
16
|
+
|
|
17
|
+
### GOL.3.1.1: Use Standard Handler Interfaces
|
|
18
|
+
|
|
19
|
+
**Rule**: Implement handlers using `http.Handler` or `http.HandlerFunc` interfaces.
|
|
20
|
+
|
|
21
|
+
**Severity**: ERROR
|
|
22
|
+
|
|
23
|
+
**Rationale**: Standard interfaces ensure compatibility with middleware and routing libraries.
|
|
24
|
+
|
|
25
|
+
**✅ Good**:
|
|
26
|
+
```go
|
|
27
|
+
// Using http.HandlerFunc
|
|
28
|
+
func handleUser(w http.ResponseWriter, r *http.Request) {
|
|
29
|
+
ctx := r.Context()
|
|
30
|
+
userID := r.URL.Query().Get("id")
|
|
31
|
+
|
|
32
|
+
user, err := getUserByID(ctx, userID)
|
|
33
|
+
if err != nil {
|
|
34
|
+
http.Error(w, "User not found", http.StatusNotFound)
|
|
35
|
+
return
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
json.NewEncoder(w).Encode(user)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Using http.Handler
|
|
42
|
+
type UserHandler struct {
|
|
43
|
+
db *sql.DB
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
func (h *UserHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|
47
|
+
// Handler implementation
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
**❌ Bad**:
|
|
52
|
+
```go
|
|
53
|
+
// Non-standard signature
|
|
54
|
+
func handleUser(userID string) (User, error) {
|
|
55
|
+
// Cannot be used directly with http.ServeMux
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### GOL.3.1.2: Always Use Context for Request-Scoped Values
|
|
60
|
+
|
|
61
|
+
**Rule**: Use `r.Context()` for request-scoped values, timeouts, and cancellation.
|
|
62
|
+
|
|
63
|
+
**Severity**: ERROR
|
|
64
|
+
|
|
65
|
+
**Rationale**: Context enables proper timeout handling, cancellation propagation, and request tracing.
|
|
66
|
+
|
|
67
|
+
**✅ Good**:
|
|
68
|
+
```go
|
|
69
|
+
func handleCreateUser(w http.ResponseWriter, r *http.Request) {
|
|
70
|
+
ctx := r.Context()
|
|
71
|
+
|
|
72
|
+
// Add timeout to context
|
|
73
|
+
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
|
|
74
|
+
defer cancel()
|
|
75
|
+
|
|
76
|
+
var user User
|
|
77
|
+
if err := json.NewDecoder(r.Body).Decode(&user); err != nil {
|
|
78
|
+
http.Error(w, "Invalid request body", http.StatusBadRequest)
|
|
79
|
+
return
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Pass context to database operations
|
|
83
|
+
if err := createUser(ctx, &user); err != nil {
|
|
84
|
+
if errors.Is(err, context.DeadlineExceeded) {
|
|
85
|
+
http.Error(w, "Request timeout", http.StatusRequestTimeout)
|
|
86
|
+
return
|
|
87
|
+
}
|
|
88
|
+
http.Error(w, "Internal server error", http.StatusInternalServerError)
|
|
89
|
+
return
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
w.WriteHeader(http.StatusCreated)
|
|
93
|
+
json.NewEncoder(w).Encode(user)
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
**❌ Bad**:
|
|
98
|
+
```go
|
|
99
|
+
func handleCreateUser(w http.ResponseWriter, r *http.Request) {
|
|
100
|
+
// Not using context - no timeout or cancellation support
|
|
101
|
+
var user User
|
|
102
|
+
json.NewDecoder(r.Body).Decode(&user)
|
|
103
|
+
createUser(&user) // No context passed
|
|
104
|
+
json.NewEncoder(w).Encode(user)
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### GOL.3.1.3: Use Appropriate HTTP Status Codes
|
|
109
|
+
|
|
110
|
+
**Rule**: Return correct HTTP status codes for different scenarios.
|
|
111
|
+
|
|
112
|
+
**Severity**: WARNING
|
|
113
|
+
|
|
114
|
+
**Rationale**: Proper status codes enable clients to handle responses correctly.
|
|
115
|
+
|
|
116
|
+
**Status Code Guidelines**:
|
|
117
|
+
- `200 OK`: Successful GET, PUT, PATCH
|
|
118
|
+
- `201 Created`: Successful POST creating a resource
|
|
119
|
+
- `204 No Content`: Successful DELETE
|
|
120
|
+
- `400 Bad Request`: Invalid client input
|
|
121
|
+
- `401 Unauthorized`: Authentication required
|
|
122
|
+
- `403 Forbidden`: Authenticated but not authorized
|
|
123
|
+
- `404 Not Found`: Resource doesn't exist
|
|
124
|
+
- `409 Conflict`: Resource conflict (e.g., duplicate)
|
|
125
|
+
- `422 Unprocessable Entity`: Validation errors
|
|
126
|
+
- `500 Internal Server Error`: Server-side errors
|
|
127
|
+
- `503 Service Unavailable`: Temporary unavailability
|
|
128
|
+
|
|
129
|
+
**✅ Good**:
|
|
130
|
+
```go
|
|
131
|
+
func handleUpdateUser(w http.ResponseWriter, r *http.Request) {
|
|
132
|
+
ctx := r.Context()
|
|
133
|
+
userID := chi.URLParam(r, "id")
|
|
134
|
+
|
|
135
|
+
var updates UserUpdate
|
|
136
|
+
if err := json.NewDecoder(r.Body).Decode(&updates); err != nil {
|
|
137
|
+
http.Error(w, "Invalid JSON", http.StatusBadRequest)
|
|
138
|
+
return
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
user, err := updateUser(ctx, userID, updates)
|
|
142
|
+
if err != nil {
|
|
143
|
+
switch {
|
|
144
|
+
case errors.Is(err, ErrUserNotFound):
|
|
145
|
+
http.Error(w, "User not found", http.StatusNotFound)
|
|
146
|
+
case errors.Is(err, ErrValidation):
|
|
147
|
+
http.Error(w, err.Error(), http.StatusUnprocessableEntity)
|
|
148
|
+
default:
|
|
149
|
+
http.Error(w, "Internal error", http.StatusInternalServerError)
|
|
150
|
+
}
|
|
151
|
+
return
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
w.WriteHeader(http.StatusOK)
|
|
155
|
+
json.NewEncoder(w).Encode(user)
|
|
156
|
+
}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
## References
|
|
160
|
+
|
|
161
|
+
- [Effective Go - Web Servers](https://golang.org/doc/effective_go#web_server)
|
|
162
|
+
- [Go net/http package](https://pkg.go.dev/net/http)
|
|
163
|
+
- [HTTP Status Codes](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status)
|
|
164
|
+
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
# Go Web Services - Middleware Patterns
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
Middleware provides a powerful way to add cross-cutting concerns to HTTP handlers. This document defines best practices for implementing and using middleware in Go web services.
|
|
6
|
+
|
|
7
|
+
## Core Principles
|
|
8
|
+
|
|
9
|
+
1. **Composability**: Middleware should be composable and chainable
|
|
10
|
+
2. **Single Responsibility**: Each middleware should do one thing well
|
|
11
|
+
3. **Context Propagation**: Use context to pass values between middleware
|
|
12
|
+
4. **Error Handling**: Handle errors gracefully and consistently
|
|
13
|
+
5. **Performance**: Minimize overhead in hot paths
|
|
14
|
+
|
|
15
|
+
## Rules
|
|
16
|
+
|
|
17
|
+
### GOL.3.1.2.1: Use Standard Middleware Pattern
|
|
18
|
+
|
|
19
|
+
**Rule**: Implement middleware using the standard `func(http.Handler) http.Handler` pattern.
|
|
20
|
+
|
|
21
|
+
**Severity**: ERROR
|
|
22
|
+
|
|
23
|
+
**Rationale**: Standard pattern ensures compatibility with all routing libraries and middleware chains.
|
|
24
|
+
|
|
25
|
+
**✅ Good**:
|
|
26
|
+
```go
|
|
27
|
+
// Standard middleware signature
|
|
28
|
+
func loggingMiddleware(next http.Handler) http.Handler {
|
|
29
|
+
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
30
|
+
start := time.Now()
|
|
31
|
+
|
|
32
|
+
// Call next handler
|
|
33
|
+
next.ServeHTTP(w, r)
|
|
34
|
+
|
|
35
|
+
// Log after handler completes
|
|
36
|
+
log.Printf("%s %s %v", r.Method, r.URL.Path, time.Since(start))
|
|
37
|
+
})
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Usage
|
|
41
|
+
http.Handle("/api/", loggingMiddleware(apiHandler))
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
**❌ Bad**:
|
|
45
|
+
```go
|
|
46
|
+
// Non-standard signature - not composable
|
|
47
|
+
func loggingMiddleware(w http.ResponseWriter, r *http.Request, handler http.Handler) {
|
|
48
|
+
start := time.Now()
|
|
49
|
+
handler.ServeHTTP(w, r)
|
|
50
|
+
log.Printf("%s %s %v", r.Method, r.URL.Path, time.Since(start))
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### GOL.3.1.2.2: Authentication Middleware
|
|
55
|
+
|
|
56
|
+
**Rule**: Implement authentication middleware that validates credentials and adds user context.
|
|
57
|
+
|
|
58
|
+
**Severity**: ERROR
|
|
59
|
+
|
|
60
|
+
**Rationale**: Centralized authentication ensures consistent security across all protected endpoints.
|
|
61
|
+
|
|
62
|
+
**✅ Good**:
|
|
63
|
+
```go
|
|
64
|
+
type contextKey string
|
|
65
|
+
|
|
66
|
+
const userContextKey contextKey = "user"
|
|
67
|
+
|
|
68
|
+
func authMiddleware(next http.Handler) http.Handler {
|
|
69
|
+
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
70
|
+
token := r.Header.Get("Authorization")
|
|
71
|
+
if token == "" {
|
|
72
|
+
http.Error(w, "Unauthorized", http.StatusUnauthorized)
|
|
73
|
+
return
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Validate token
|
|
77
|
+
user, err := validateToken(r.Context(), token)
|
|
78
|
+
if err != nil {
|
|
79
|
+
http.Error(w, "Invalid token", http.StatusUnauthorized)
|
|
80
|
+
return
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Add user to context
|
|
84
|
+
ctx := context.WithValue(r.Context(), userContextKey, user)
|
|
85
|
+
next.ServeHTTP(w, r.WithContext(ctx))
|
|
86
|
+
})
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Helper to extract user from context
|
|
90
|
+
func getUserFromContext(ctx context.Context) (*User, bool) {
|
|
91
|
+
user, ok := ctx.Value(userContextKey).(*User)
|
|
92
|
+
return user, ok
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### GOL.3.1.2.3: Request Logging Middleware
|
|
97
|
+
|
|
98
|
+
**Rule**: Log all requests with method, path, status code, duration, and request ID.
|
|
99
|
+
|
|
100
|
+
**Severity**: WARNING
|
|
101
|
+
|
|
102
|
+
**Rationale**: Comprehensive logging enables debugging, monitoring, and audit trails.
|
|
103
|
+
|
|
104
|
+
**✅ Good**:
|
|
105
|
+
```go
|
|
106
|
+
type responseWriter struct {
|
|
107
|
+
http.ResponseWriter
|
|
108
|
+
statusCode int
|
|
109
|
+
written int64
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
func (rw *responseWriter) WriteHeader(code int) {
|
|
113
|
+
rw.statusCode = code
|
|
114
|
+
rw.ResponseWriter.WriteHeader(code)
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
func (rw *responseWriter) Write(b []byte) (int, error) {
|
|
118
|
+
n, err := rw.ResponseWriter.Write(b)
|
|
119
|
+
rw.written += int64(n)
|
|
120
|
+
return n, err
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
func requestLoggingMiddleware(logger *slog.Logger) func(http.Handler) http.Handler {
|
|
124
|
+
return func(next http.Handler) http.Handler {
|
|
125
|
+
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
126
|
+
start := time.Now()
|
|
127
|
+
requestID := uuid.New().String()
|
|
128
|
+
|
|
129
|
+
// Add request ID to context
|
|
130
|
+
ctx := context.WithValue(r.Context(), "request_id", requestID)
|
|
131
|
+
|
|
132
|
+
// Wrap response writer to capture status code
|
|
133
|
+
rw := &responseWriter{
|
|
134
|
+
ResponseWriter: w,
|
|
135
|
+
statusCode: http.StatusOK,
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Call next handler
|
|
139
|
+
next.ServeHTTP(rw, r.WithContext(ctx))
|
|
140
|
+
|
|
141
|
+
// Log request details
|
|
142
|
+
logger.Info("request completed",
|
|
143
|
+
"request_id", requestID,
|
|
144
|
+
"method", r.Method,
|
|
145
|
+
"path", r.URL.Path,
|
|
146
|
+
"status", rw.statusCode,
|
|
147
|
+
"duration_ms", time.Since(start).Milliseconds(),
|
|
148
|
+
"bytes_written", rw.written,
|
|
149
|
+
"remote_addr", r.RemoteAddr,
|
|
150
|
+
)
|
|
151
|
+
})
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### GOL.3.1.2.4: Panic Recovery Middleware
|
|
157
|
+
|
|
158
|
+
**Rule**: Always include panic recovery middleware to prevent server crashes.
|
|
159
|
+
|
|
160
|
+
**Severity**: ERROR
|
|
161
|
+
|
|
162
|
+
**Rationale**: Unhandled panics crash the entire server; recovery middleware ensures graceful error handling.
|
|
163
|
+
|
|
164
|
+
**✅ Good**:
|
|
165
|
+
```go
|
|
166
|
+
func recoveryMiddleware(logger *slog.Logger) func(http.Handler) http.Handler {
|
|
167
|
+
return func(next http.Handler) http.Handler {
|
|
168
|
+
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
169
|
+
defer func() {
|
|
170
|
+
if err := recover(); err != nil {
|
|
171
|
+
// Log panic with stack trace
|
|
172
|
+
logger.Error("panic recovered",
|
|
173
|
+
"error", err,
|
|
174
|
+
"path", r.URL.Path,
|
|
175
|
+
"stack", string(debug.Stack()),
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
// Return 500 to client
|
|
179
|
+
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
|
|
180
|
+
}
|
|
181
|
+
}()
|
|
182
|
+
|
|
183
|
+
next.ServeHTTP(w, r)
|
|
184
|
+
})
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### GOL.3.1.2.5: Metrics Middleware
|
|
190
|
+
|
|
191
|
+
**Rule**: Instrument HTTP handlers with Prometheus metrics for monitoring.
|
|
192
|
+
|
|
193
|
+
**Severity**: WARNING
|
|
194
|
+
|
|
195
|
+
**✅ Good**:
|
|
196
|
+
```go
|
|
197
|
+
var (
|
|
198
|
+
httpRequestsTotal = promauto.NewCounterVec(
|
|
199
|
+
prometheus.CounterOpts{
|
|
200
|
+
Name: "http_requests_total",
|
|
201
|
+
Help: "Total number of HTTP requests",
|
|
202
|
+
},
|
|
203
|
+
[]string{"method", "path", "status"},
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
httpRequestDuration = promauto.NewHistogramVec(
|
|
207
|
+
prometheus.HistogramOpts{
|
|
208
|
+
Name: "http_request_duration_seconds",
|
|
209
|
+
Help: "HTTP request duration in seconds",
|
|
210
|
+
Buckets: prometheus.DefBuckets,
|
|
211
|
+
},
|
|
212
|
+
[]string{"method", "path"},
|
|
213
|
+
)
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
func metricsMiddleware(next http.Handler) http.Handler {
|
|
217
|
+
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
218
|
+
start := time.Now()
|
|
219
|
+
rw := &responseWriter{ResponseWriter: w, statusCode: http.StatusOK}
|
|
220
|
+
|
|
221
|
+
next.ServeHTTP(rw, r)
|
|
222
|
+
|
|
223
|
+
duration := time.Since(start).Seconds()
|
|
224
|
+
httpRequestsTotal.WithLabelValues(r.Method, r.URL.Path, strconv.Itoa(rw.statusCode)).Inc()
|
|
225
|
+
httpRequestDuration.WithLabelValues(r.Method, r.URL.Path).Observe(duration)
|
|
226
|
+
})
|
|
227
|
+
}
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
## References
|
|
231
|
+
|
|
232
|
+
- [Go Middleware Patterns](https://www.alexedwards.net/blog/making-and-using-middleware)
|
|
233
|
+
- [Effective Go - Web Servers](https://golang.org/doc/effective_go#web_server)
|
|
234
|
+
|