http.ResponseController:
•Позволяет переопределять ваши общесерверные таймауты/дедлайны чтения и записи новыми для каждого отдельного запроса.
•Шаблон использования интерфейсов http.Flusher и http.Hijacker стал более понятным и менее сложным. Нам больше не нужны никакие утверждения типов!
•Он делает проще и безопаснее создание и использование пользовательских реализаций http.ResponseWriter.
Таймауты для отдельных запросов
http.Server Go имеет настройки ReadTimeout и WriteTimeout, которые вы можете использовать для автоматического закрытия HTTP-соединения, если время, затраченное на чтение запроса или запись ответа, превышает какое-либо фиксированное значение. Эти настройки являются общесерверными и применяются ко всем запросам, независимо от обработчика или URL.
С появлением http.ResponseController можно использовать методы SetReadDeadline() и SetWriteDeadline(), чтобы ослабить или, наоборот, ужесточить эти настройки для каждого конкретного запроса в зависимости от ваших потребностей. Например:
func exampleHandler(w http.ResponseWriter, r *http.Request) {
rc := http.NewResponseController(w)
// Установим таймаут записи в 5 секунд.
err := rc.SetWriteDeadline(time.Now().Add(5 * time.Second))
if err != nil {
// Обработка ошибки
}
// Делаем здесь что-нибудь...
// Записываем ответ как обычно
w.Write([]byte("Done!"))
}
Это особенно полезно для приложений, содержащих небольшое количество обработчиков, которым требуются более длительные таймауты, чем всем остальным, для таких вещей, как обработка загрузки файла или выполнение длительной операции.
Несколько деталей, о которых стоит упомянуть:
Если вы установите очень короткий общесерверный таймаут, и этот таймаут будет достигнут до того, как вы вызовете SetWriteDeadline() или SetReadDeadline(), то они не возымеют никакого эффекта. Общесерверный таймаут в этом случае побеждает.
Если базовый http.ResponseWriter не поддерживает установку таймаутов для отдельных запросов, то вызов SetWriteDeadline() или SetReadDeadline() вернет ошибку http.ErrNotSupported.
Теперь можено отменять общесерверный таймаут для отдельных запросов, передав обнуленную структур time.Time в SetWriteDeadlin() или SetReadDeadline(). Например:
rc := http.NewResponseController(w)
err := rc.SetWriteDeadline(time.Time{})
if err != nil {
// Обработка ошибки
}
Интерфейсы Flusher и Hijacker
Тип http.ResponseController также делает более удобным использование «опциональных» интерфейсов http.Flusher и http.Hijacker. Например, до Go 1.20, чтобы отправить данные ответа клиенту, вы могли использовать кода следующего вида:
func exampleHandler(w http.ResponseWriter, r *http.Request) {
f, ok := w.(http.Flusher)
if !ok {
// Обработка ошибки
}
for i := 0; i < 5; i++ {
fmt.Fprintf(w, "Write %d\n", i)
f.Flush()
time.Sleep(time.Second)
}
}
Теперь можно сделать это так:
func exampleHandler(w http.ResponseWriter, r *http.Request) {
rc := http.NewResponseController(w)
for i := 0; i < 5; i++ {
fmt.Fprintf(w, "Write %d\n", i)
err := rc.Flush()
if err != nil {
// Обработка ошибки
}
time.Sleep(time.Second)
}
}
Шаб
лонный код перехвата (hijacking) соединения аналогичен:
func (app *application) home(w http.ResponseWriter, r *http.Request) {
rc := http.NewResponseController(w)
conn, bufrw, err := rc.Hijack()
if err != nil {
// Обработка ошибки
}
defer conn.Close()
// Делаем здесь что-нибудь...
}
Опять же, если ваш базовый
http.ResponseWriter не поддерживает flush или перехват соединения, то вызов Flush() или Hijack() в http.ResponseController также вернет ошибку http.ErrNotSupported.
Теперь также проще и безопаснее создавать и использовать пользовательские реализации http.ResponseWriter, которые поддерживают flush и перехват соединения.
Приведите пример с кодом реализации своей http.ResponseWriter в комментариях 👇
@golang_interview