package main import ( "encoding/json" "errors" "fmt" "net/http" "os" "sync" ) const visitsFile = "visits.json" // mu protects visits, and the file that holds it. var mu sync.Mutex var visits = 0 func loadVisits() (int, error) { data, err := os.ReadFile(visitsFile) if errors.Is(err, os.ErrNotExist) { return 0, nil } if err != nil { return 0, err } var loaded int err = json.Unmarshal(data, &loaded) if err != nil { return 0, fmt.Errorf("%s is not valid: %v", visitsFile, err) } return loaded, nil } func saveVisits(count int) error { data, err := json.Marshal(count) if err != nil { return err } temporary := visitsFile + ".tmp" err = os.WriteFile(temporary, data, 0644) if err != nil { return err } return os.Rename(temporary, visitsFile) } // nextVisit adds 1 and saves. If saving fails, the count stays as it was. func nextVisit() (int, error) { mu.Lock() defer mu.Unlock() err := saveVisits(visits + 1) if err != nil { return 0, err } visits = visits + 1 return visits, nil } func handleHome(w http.ResponseWriter, r *http.Request) { number, err := nextVisit() if err != nil { fmt.Println("Save error:", err) http.Error(w, "The visit could not be saved.", http.StatusInternalServerError) return } fmt.Fprintf(w, "You are visitor number %d.\n", number) } func main() { loaded, err := loadVisits() if err != nil { fmt.Println("Could not load the count:", err) fmt.Println("Fix or remove the file, then start the server again.") return } visits = loaded http.HandleFunc("GET /{$}", handleHome) fmt.Println("Server running at http://localhost:8080") fmt.Println("Press Ctrl+C to stop it.") err = http.ListenAndServe("localhost:8080", nil) if err != nil { fmt.Println("Server stopped:", err) } }