chore(iot): лимит тела запроса + запрет неизвестных полей + таймауты сервера (Этап 5)
- decodeJSON: http.MaxBytesReader (64 КиБ) + DisallowUnknownFields; используется в Create/Update устройства. Ловит опечатки в полях (важно для Terraform). - http.Server: добавлены ReadTimeout=15с, WriteTimeout=60с, IdleTimeout=120с (был только ReadHeaderTimeout).
This commit is contained in:
@@ -104,6 +104,11 @@ func run() int {
|
|||||||
Addr: ":" + cfg.APIPort,
|
Addr: ":" + cfg.APIPort,
|
||||||
Handler: router,
|
Handler: router,
|
||||||
ReadHeaderTimeout: 10 * time.Second,
|
ReadHeaderTimeout: 10 * time.Second,
|
||||||
|
ReadTimeout: 15 * time.Second,
|
||||||
|
// WriteTimeout с запасом: admin-stats/telemetry отвечают через SQS/PG
|
||||||
|
// (внутренние таймауты до 15с). IdleTimeout — для keep-alive платформы.
|
||||||
|
WriteTimeout: 60 * time.Second,
|
||||||
|
IdleTimeout: 120 * time.Second,
|
||||||
}
|
}
|
||||||
|
|
||||||
// bridge + consumer — в фоне.
|
// bridge + consumer — в фоне.
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ package handler
|
|||||||
import (
|
import (
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
"errors"
|
||||||
"net/http"
|
"net/http"
|
||||||
"regexp"
|
"regexp"
|
||||||
@@ -143,7 +142,7 @@ func (h *Handler) CreateIoTDevice(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var req iotDeviceCreateRequest
|
var req iotDeviceCreateRequest
|
||||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
if err := decodeJSON(w, r, &req); err != nil {
|
||||||
writeJSON(w, http.StatusBadRequest, errResp("invalid JSON: "+err.Error()))
|
writeJSON(w, http.StatusBadRequest, errResp("invalid JSON: "+err.Error()))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -299,7 +298,7 @@ func (h *Handler) UpdateIoTDevice(w http.ResponseWriter, r *http.Request) {
|
|||||||
name := pathVar(r, "name")
|
name := pathVar(r, "name")
|
||||||
|
|
||||||
var req iotDeviceUpdateRequest
|
var req iotDeviceUpdateRequest
|
||||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
if err := decodeJSON(w, r, &req); err != nil {
|
||||||
writeJSON(w, http.StatusBadRequest, errResp("invalid JSON: "+err.Error()))
|
writeJSON(w, http.StatusBadRequest, errResp("invalid JSON: "+err.Error()))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,6 +38,21 @@ func writeJSON(w http.ResponseWriter, status int, v any) {
|
|||||||
_ = json.NewEncoder(w).Encode(v)
|
_ = json.NewEncoder(w).Encode(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// maxRequestBodyBytes — максимум размера тела запроса (устройства/метаданные
|
||||||
|
// небольшие; 64 КиБ с запасом).
|
||||||
|
const maxRequestBodyBytes = 64 * 1024
|
||||||
|
|
||||||
|
// decodeJSON читает JSON-тело с лимитом размера и запретом неизвестных полей
|
||||||
|
// (ревью 2026-10-10). MaxBytesReader защищает от раздутых тел;
|
||||||
|
// DisallowUnknownFields ловит опечатки в полях — критично для Terraform (лучше
|
||||||
|
// 400 с понятной ошибкой, чем молча проигнорированное поле).
|
||||||
|
func decodeJSON(w http.ResponseWriter, r *http.Request, v any) error {
|
||||||
|
r.Body = http.MaxBytesReader(w, r.Body, maxRequestBodyBytes)
|
||||||
|
dec := json.NewDecoder(r.Body)
|
||||||
|
dec.DisallowUnknownFields()
|
||||||
|
return dec.Decode(v)
|
||||||
|
}
|
||||||
|
|
||||||
// errResp возвращает структуру ошибки для JSON.
|
// errResp возвращает структуру ошибки для JSON.
|
||||||
func errResp(msg string) map[string]string {
|
func errResp(msg string) map[string]string {
|
||||||
return map[string]string{"error": msg}
|
return map[string]string{"error": msg}
|
||||||
|
|||||||
Reference in New Issue
Block a user