Ошибка http.ErrHandlerTimeout

Ошибка http.ErrHandlerTimeout возвращается функцией http.Server.ServeHTTP, когда обработчик запроса превышает максимально допустимое время выполнения. Эта ошибка возникает при использовании полей ReadTimeout, WriteTimeout или IdleTimeout структуры http.Server. Ошибка указывает на то, что клиентское соединение было закрыто из-за превышения таймаута.

Синтаксис

gohttp.ErrHandlerTimeout

Пример

Давайте создадим HTTP-сервер с таймаутом и обработчиком, который превышает этот таймаут:

gopackage main

import (
  "fmt"
  "net/http"
  "time"
)

func main() {
  mux := http.NewServeMux()
  mux.HandleFunc("/slow", func(w http.ResponseWriter, r *http.Request) {
    time.Sleep(2 * time.Second)
    fmt.Fprintf(w, "Slow response")
  })

  server := &http.Server{
    Addr:     ":8080",
    Handler:    mux,
    ReadTimeout:  1 * time.Second,
    WriteTimeout: 1 * time.Second,
  }

  err := server.ListenAndServe()
  if err != nil {
    fmt.Println(err)
  }
}

Результат выполнения кода при обращении к /slow:

gohttp: Handler timeout

Пример

Давайте проверим, является ли ошибка именно http.ErrHandlerTimeout и обработаем ее соответствующим образом:

gopackage main

import (
  "errors"
  "fmt"
  "net/http"
  "time"
)

func main() {
  mux := http.NewServeMux()
  mux.HandleFunc("/timeout", func(w http.ResponseWriter, r *http.Request) {
    time.Sleep(3 * time.Second)
    fmt.Fprintf(w, "Completed")
  })

  server := &http.Server{
    Addr:     ":8081",
    Handler:    mux,
    WriteTimeout: 1 * time.Second,
  }

  go server.ListenAndServe()
  time.Sleep(100 * time.Millisecond)

  resp, err := http.Get("http://localhost:8081/timeout")
  if err != nil {
    if errors.Is(err, http.ErrHandlerTimeout) {
      fmt.Println("Handler timeout detected")
    } else {
      fmt.Println("Other error:", err)
    }
    return
  }
  defer resp.Body.Close()
  fmt.Println("Response status:", resp.Status)
}

Результат выполнения кода:

go"Handler timeout detected"

Пример

Давайте создадим обработчик с контекстом, который проверяет превышение таймаута и возвращает соответствующий HTTP-статус:

gopackage main

import (
  "context"
  "fmt"
  "net/http"
  "time"
)

func main() {
  mux := http.NewServeMux()
  mux.HandleFunc("/context", func(w http.ResponseWriter, r *http.Request) {
    ctx := r.Context()
    select {
    case <-time.After(5 * time.Second):
      fmt.Fprintf(w, "Task completed")
    case <-ctx.Done():
      if ctx.Err() == context.DeadlineExceeded {
        http.Error(w, "Request timeout", http.StatusRequestTimeout)
      }
    }
  })

  server := &http.Server{
    Addr:     ":8082",
    Handler:    mux,
    WriteTimeout: 2 * time.Second,
  }

  go server.ListenAndServe()
  time.Sleep(100 * time.Millisecond)

  resp, err := http.Get("http://localhost:8082/context")
  if err != nil {
    fmt.Println("Error:", err)
    return
  }
  defer resp.Body.Close()
  fmt.Println("Response status:", resp.Status)
}

Результат выполнения кода:

go"Response status: 408 Request Timeout"

Смотрите также

  • ошибку http.ErrServerClosed,
    которая возникает при закрытии сервера
  • функцию http.Server.ListenAndServe,
    которая запускает HTTP-сервер с настройками
  • функцию http.TimeoutHandler,
    которая создает обработчик с таймаутом
  • функцию http.Server.Shutdown,
    которая корректно завершает работу сервера