examples/production-usage/main.go¶
:material-github: View on GitHub · 227 lines · live source, included at build time
package main
import (
"context"
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"os"
"os/signal"
"runtime"
"sync/atomic"
"syscall"
"time"
"github.com/alikatgh/safeheaders-go/jsmn-go"
)
const (
// Server configuration
ServerPort = ":8080"
ReadTimeout = 10 * time.Second
WriteTimeout = 10 * time.Second
IdleTimeout = 60 * time.Second
MaxHeaderSize = 1 << 20 // 1MB
// Request limits
MaxRequestSize = 10 * 1024 * 1024 // 10MB
ParseTimeout = 30 * time.Second
)
func main() {
fmt.Println("SafeHeaders-Go Production Example")
fmt.Println("==================================")
fmt.Println()
// Configure runtime
runtime.GOMAXPROCS(runtime.NumCPU())
// Create HTTP server
srv := &http.Server{
Addr: ServerPort,
Handler: setupRoutes(),
ReadTimeout: ReadTimeout,
WriteTimeout: WriteTimeout,
IdleTimeout: IdleTimeout,
MaxHeaderBytes: MaxHeaderSize,
}
// Start server in goroutine
go func() {
log.Printf("🚀 Server starting on %s", ServerPort)
log.Printf(" Max CPUs: %d", runtime.NumCPU())
log.Printf(" Max request size: %d MB", MaxRequestSize/(1024*1024))
log.Println()
log.Println("Endpoints:")
log.Println(" POST /api/parse - Parse JSON")
log.Println(" GET /health - Health check")
log.Println(" GET /stats - Server statistics")
log.Println()
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
log.Fatalf("Server failed: %v", err)
}
}()
// Wait for interrupt signal
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<-quit
log.Println("\n🛑 Shutting down server...")
// Graceful shutdown
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
if err := srv.Shutdown(ctx); err != nil {
log.Fatalf("Server forced to shutdown: %v", err)
}
log.Println("✅ Server exited gracefully")
}
func setupRoutes() http.Handler {
mux := http.NewServeMux()
// API endpoints
mux.HandleFunc("/api/parse", withMiddleware(handleParse))
mux.HandleFunc("/health", withMiddleware(handleHealth))
mux.HandleFunc("/stats", withMiddleware(handleStats))
return mux
}
// Middleware chain
func withMiddleware(next http.HandlerFunc) http.HandlerFunc {
return logging(rateLimit(validateRequest(next)))
}
// Request validation middleware
func validateRequest(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// Limit request body size
r.Body = http.MaxBytesReader(w, r.Body, MaxRequestSize)
next(w, r)
}
}
// Rate limiting middleware (simple example)
var requestCount int64
func rateLimit(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
atomic.AddInt64(&requestCount, 1) // handlers run one goroutine per request
// In production, use a proper rate limiter
next(w, r)
}
}
// Logging middleware
func logging(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
log.Printf("→ %s %s", r.Method, r.URL.Path)
next(w, r)
log.Printf("← %s %s (took %v)", r.Method, r.URL.Path, time.Since(start))
}
}
// Handle JSON parsing requests
func handleParse(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
// Read the full body. r.Body was wrapped in MaxBytesReader (10 MB cap), so
// io.ReadAll won't over-read; a single Read can short-read multi-segment
// bodies and silently truncate them.
body, err := io.ReadAll(r.Body)
if err != nil {
http.Error(w, fmt.Sprintf("Failed to read body: %v", err), http.StatusBadRequest)
return
}
// Validate input
if len(body) == 0 {
http.Error(w, "Empty request body", http.StatusBadRequest)
return
}
// Parse JSON with timeout
ctx, cancel := context.WithTimeout(r.Context(), ParseTimeout)
defer cancel()
start := time.Now()
tokens, err := jsmngo.ParseParallelWithContext(ctx, body)
duration := time.Since(start)
if err != nil {
log.Printf("❌ Parse failed: %v", err)
http.Error(w, fmt.Sprintf("Parse failed: %v", err), http.StatusBadRequest)
return
}
// Build response
response := map[string]interface{}{
"status": "success",
"tokenCount": len(tokens),
"inputSize": len(body),
"parseTime": duration.String(),
"timestamp": time.Now().Format(time.RFC3339),
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(response)
log.Printf("✅ Parsed %d tokens from %d bytes in %v", len(tokens), len(body), duration)
}
// Health check endpoint
func handleHealth(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
var m runtime.MemStats
runtime.ReadMemStats(&m)
health := map[string]interface{}{
"status": "healthy",
"timestamp": time.Now().Format(time.RFC3339),
"memory": map[string]interface{}{
"allocMB": m.Alloc / 1024 / 1024,
"totalMB": m.TotalAlloc / 1024 / 1024,
"sysMB": m.Sys / 1024 / 1024,
"numGC": m.NumGC,
"goroutines": runtime.NumGoroutine(),
},
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(health)
}
// Statistics endpoint
func handleStats(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
stats := map[string]interface{}{
"requestCount": atomic.LoadInt64(&requestCount),
"cpus": runtime.NumCPU(),
"goVersion": runtime.Version(),
"timestamp": time.Now().Format(time.RFC3339),
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(stats)
}