|
4 | 4 | package main
|
5 | 5 |
|
6 | 6 | import (
|
| 7 | + "crypto/rand" |
7 | 8 | "fmt"
|
| 9 | + "log" |
| 10 | + "math/big" |
8 | 11 | "net/http"
|
| 12 | + "os" |
| 13 | + "strconv" |
9 | 14 | "time"
|
10 | 15 | )
|
11 | 16 |
|
| 17 | +// getRandomNumber generates a random integer between min and max (inclusive). |
| 18 | +func getRandomNumber(min, max int) (int, error) { |
| 19 | + if min > max { |
| 20 | + return 0, fmt.Errorf("min (%d) cannot be greater than max (%d)", min, max) |
| 21 | + } |
| 22 | + |
| 23 | + // Calculate the range size. |
| 24 | + rangeSize := big.NewInt(int64(max - min + 1)) |
| 25 | + |
| 26 | + // Generate a random number n, where 0 <= n < rangeSize. |
| 27 | + n, err := rand.Int(rand.Reader, rangeSize) |
| 28 | + if err != nil { |
| 29 | + // Return an error if random number generation fails |
| 30 | + return 0, fmt.Errorf("failed to generate random number: %w", err) |
| 31 | + } |
| 32 | + |
| 33 | + // Convert the big.Int result back to a regular int. |
| 34 | + // Add min to shift the range from [0, rangeSize) to [min, max]. |
| 35 | + // n.Int64() is safe here because rangeSize fits in int64 if max-min+1 does. |
| 36 | + result := int(n.Int64()) + min |
| 37 | + return result, nil |
| 38 | +} |
| 39 | + |
| 40 | +// rollDiceHandler handles requests to the /rolldice endpoint. |
| 41 | +func rollDiceHandler(w http.ResponseWriter, r *http.Request) { |
| 42 | + // Ensure we only handle GET requests for this endpoint |
| 43 | + if r.Method != http.MethodGet { |
| 44 | + http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) |
| 45 | + return |
| 46 | + } |
| 47 | + |
| 48 | + // Generate a random number between 1 and 6 |
| 49 | + diceRoll, err := getRandomNumber(1, 6) |
| 50 | + if err != nil { |
| 51 | + // Log the error and return an internal server error if generation failed |
| 52 | + log.Printf("Error generating random number: %v", err) |
| 53 | + http.Error(w, "Internal Server Error", http.StatusInternalServerError) |
| 54 | + return |
| 55 | + } |
| 56 | + |
| 57 | + // Convert the integer result to a string |
| 58 | + responseString := strconv.Itoa(diceRoll) |
| 59 | + |
| 60 | + // Set the content type header to text/plain |
| 61 | + w.Header().Set("Content-Type", "text/plain") |
| 62 | + |
| 63 | + // Write the string response back to the client |
| 64 | + _, writeErr := fmt.Fprint(w, responseString) |
| 65 | + if writeErr != nil { |
| 66 | + // Log the error if writing the response failed (response may be partially sent) |
| 67 | + log.Printf("Error writing response: %v", writeErr) |
| 68 | + // Attempt to send an error, though headers might already be sent |
| 69 | + // http.Error(w, "Internal Server Error", http.StatusInternalServerError) // Avoid sending second error |
| 70 | + } |
| 71 | + log.Printf("Rolled a %d for request from %s", diceRoll, r.RemoteAddr) // Optional: Log the roll |
| 72 | +} |
| 73 | + |
12 | 74 | func main() {
|
13 |
| - http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { |
14 |
| - fmt.Println("Hi") |
15 |
| - }) |
| 75 | + // Get the port from the environment variable, default to "8095" if not set |
| 76 | + port := os.Getenv("PORT") |
| 77 | + if port == "" { |
| 78 | + port = "8095" |
| 79 | + } |
| 80 | + |
| 81 | + // Register the handler function for the "/rolldice" path with the DefaultServeMux. |
| 82 | + http.HandleFunc("/rolldice", rollDiceHandler) |
| 83 | + |
| 84 | + // Construct the server address string (e.g., ":8095") |
| 85 | + serverAddr := ":" + port |
16 | 86 |
|
| 87 | + // Configure the HTTP server with timeouts |
17 | 88 | server := &http.Server{
|
18 |
| - Addr: ":8080", |
19 |
| - ReadHeaderTimeout: 3 * time.Second, |
| 89 | + Addr: serverAddr, |
| 90 | + // Use DefaultServeMux by leaving Handler nil, which includes our /rolldice handler |
| 91 | + Handler: nil, |
| 92 | + ReadTimeout: 10 * time.Second, // Max time to read entire request, including body |
| 93 | + WriteTimeout: 10 * time.Second, // Max time to write response |
| 94 | + IdleTimeout: 60 * time.Second, // Max time for connections using TCP Keep-Alive |
20 | 95 | }
|
21 | 96 |
|
| 97 | + // Print a message indicating the server is starting |
| 98 | + log.Printf("Listening for requests on http://localhost:%s", port) |
| 99 | + |
| 100 | + // Start the configured HTTP server. |
| 101 | + // This ListenAndServe is now called on our configured server instance. |
22 | 102 | err := server.ListenAndServe()
|
23 |
| - if err != nil { |
24 |
| - panic(err) |
| 103 | + if err != nil && err != http.ErrServerClosed { |
| 104 | + // Log any fatal errors encountered during server startup, ignore ErrServerClosed |
| 105 | + log.Fatalf("Server failed to start or unexpectedly closed: %v", err) |
| 106 | + } else if err == http.ErrServerClosed { |
| 107 | + log.Println("Server shut down gracefully") |
25 | 108 | }
|
26 | 109 | }
|
0 commit comments