Http Server Timeouts

  • Setting timeouts in server is important to conserve system resources and to protect from DDOS attack.
  • File descriptors are limited.
  • Malicious user can ioen many client connections, consuming all file descriptor. Server will not able to accept any new connection.

net/http Timeouts

  • There are four main timeouts exposed in http.server
    • Read Timeout: covers the time from when the connection is accepted, to when the request body is fully read.
    • Read Header Timeout: amount of time allowed to read request headers.
    • Write Timeout: covers the time from the end of the request header read to the end of the response write
    • Idle Timeout: maximum amount of time to wait for the next request when keep-alive is enabled. Image

Set Timeouts by explicity using a Server

srv := &http.Server{
    ReadTimeout: 1*time.Second,
    ReadHeaderTimeout: 1*time.Second,
    WriteTimeout: 1*time.Second,
    IdleTimeout: 30*time.Second,
    Handler: serveMux 
}
  • Set Connection timeouts when dealing with untrusted clients and networks.
  • Protect Server from client which are slow to read and write.

HTTP Handler Functions

  • Connection timeouts apply at network connection level.
  • HTTP Handler Functions are unaware of these timeouts, they run to completion, consuming resources.

http.TimeoutHandler()

    srv := http.Server{
        Addr:         "localhost:8000",
        WriteTimeout: 2 * time.Second,
        Handler:      http.TimeoutHandler(http.HandlerFunc(slowHandler), 1*time.Second, "Timeout!\n"),
    }
  • net/http package provides TimeoutHandler()
  • TimeoutHandler returns a Handler that run input handler with the given time limit.
  • If input handler runs for longer than its time limit, the handler sends the client a 503 Service Unavailable error and HTML error message.

Context Timeouts and Cancellation

  • Use Context timeouts and cancellation to propagate the cancellation signal down the call graph.
  • The Request type already has a context attached to it. ctx := req.Context()
  • Server cancels this context when
    • Client close the connection.
    • Timeout
    • ServeHTTp method returns. Image

Exercises

package main

import (
    "context"
    "database/sql"
    "errors"
    "fmt"
    "log"
    "net/http"
    "time"

    _ "github.com/lib/pq"
)

var db *sql.DB

func slowQuery(ctx context.Context) error {
    _, err := db.ExecContext(ctx, "SELECT pg_sleep(5)")
    return err
}

func slowHandler(w http.ResponseWriter, req *http.Request) {
    start := time.Now()
    err := slowQuery(req.Context())
    if err != nil {
        switch {
        case errors.Is(err, context.Canceled):
            log.Printf("Warning: %s\n", err.Error())
        default:
            log.Printf("Error: %s\n", err.Error())
        }
        return
    }
    fmt.Fprintln(w, "OK")
    fmt.Printf("slowHandler took: %v\n", time.Since(start))
}

func main() {
    var err error

    connstr := "host=localhost port=5432 user=alice password=pa$$word  dbname=wonderland sslmode=disable"

    db, err = sql.Open("postgres", connstr)
    if err != nil {
        log.Fatal(err)
    }

    ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
    defer cancel()

    if err = db.PingContext(ctx); err != nil {
        log.Fatal(err)
    }
    srv := http.Server{
        Addr:         "localhost:8000",
        WriteTimeout: 2 * time.Second,
        Handler:      http.TimeoutHandler(http.HandlerFunc(slowHandler), 1*time.Second, "Timeout!\n"),
    }

    if err := srv.ListenAndServe(); err != nil {
        fmt.Printf("Server failed: %s\n", err)
    }
}
© Kimi Tsai all right reserved.            Updated : 2023-07-12 09:04:54

results matching ""

    No results matching ""

    results matching ""

      No results matching ""