diff --git a/pkg/api/handlers/compat/auth.go b/pkg/api/handlers/compat/auth.go index a6d2f02ecb..67416573f4 100644 --- a/pkg/api/handlers/compat/auth.go +++ b/pkg/api/handlers/compat/auth.go @@ -3,9 +3,7 @@ package compat import ( - "encoding/json" "errors" - "fmt" "io" "net/http" "strings" @@ -22,9 +20,8 @@ import ( func Auth(w http.ResponseWriter, r *http.Request) { var authConfig registry.AuthConfig - err := json.NewDecoder(r.Body).Decode(&authConfig) - if err != nil { - utils.Error(w, http.StatusInternalServerError, fmt.Errorf("failed to parse request: %w", err)) + if err := utils.ReadJSONFromBody(r, &authConfig); err != nil { + utils.Error(w, http.StatusBadRequest, err) return } diff --git a/pkg/api/handlers/compat/containers.go b/pkg/api/handlers/compat/containers.go index 759591ed4f..01d07d9172 100644 --- a/pkg/api/handlers/compat/containers.go +++ b/pkg/api/handlers/compat/containers.go @@ -766,8 +766,8 @@ func UpdateContainer(w http.ResponseWriter, r *http.Request) { } options := new(container.UpdateConfig) - if err := json.NewDecoder(r.Body).Decode(options); err != nil { - utils.Error(w, http.StatusInternalServerError, fmt.Errorf("decoding request body: %w", err)) + if err := utils.ReadJSONFromBody(r, &options); err != nil { + utils.Error(w, http.StatusBadRequest, err) return } diff --git a/pkg/api/handlers/compat/containers_create.go b/pkg/api/handlers/compat/containers_create.go index f181b8c6db..1e59592653 100644 --- a/pkg/api/handlers/compat/containers_create.go +++ b/pkg/api/handlers/compat/containers_create.go @@ -48,8 +48,8 @@ func CreateContainer(w http.ResponseWriter, r *http.Request) { // compatible configuration body := handlers.CreateContainerConfig{} - if err := json.NewDecoder(r.Body).Decode(&body); err != nil { - utils.Error(w, http.StatusInternalServerError, fmt.Errorf("decode(): %w", err)) + if err := utils.ReadJSONFromBody(r, &body); err != nil { + utils.Error(w, http.StatusBadRequest, err) return } diff --git a/pkg/api/handlers/compat/exec.go b/pkg/api/handlers/compat/exec.go index 61241e2fe7..a3b26e362a 100644 --- a/pkg/api/handlers/compat/exec.go +++ b/pkg/api/handlers/compat/exec.go @@ -3,7 +3,6 @@ package compat import ( - "encoding/json" "errors" "fmt" "net/http" @@ -28,8 +27,8 @@ func ExecCreateHandler(w http.ResponseWriter, r *http.Request) { runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) input := new(handlers.ExecCreateConfig) - if err := json.NewDecoder(r.Body).Decode(&input); err != nil { - utils.InternalServerError(w, fmt.Errorf("decoding request body as JSON: %w", err)) + if err := utils.ReadJSONFromBody(r, &input); err != nil { + utils.Error(w, http.StatusBadRequest, err) return } @@ -142,11 +141,11 @@ func ExecStartHandler(w http.ResponseWriter, r *http.Request) { // TODO: We should read/support Tty from here. bodyParams := new(handlers.ExecStartConfig) - - if err := json.NewDecoder(r.Body).Decode(&bodyParams); err != nil { - utils.Error(w, http.StatusBadRequest, fmt.Errorf("failed to decode parameters for %s: %w", r.URL.String(), err)) + if err := utils.ReadJSONFromBody(r, &bodyParams); err != nil { + utils.Error(w, http.StatusBadRequest, err) return } + // TODO: Verify TTY setting against what inspect session was made with sessionCtr, err := runtime.GetExecSessionContainer(sessionID) @@ -219,9 +218,8 @@ func ExecRemoveHandler(w http.ResponseWriter, r *http.Request) { sessionID := mux.Vars(r)["id"] bodyParams := new(handlers.ExecRemoveConfig) - - if err := json.NewDecoder(r.Body).Decode(&bodyParams); err != nil { - utils.Error(w, http.StatusBadRequest, fmt.Errorf("failed to decode parameters for %s: %w", r.URL.String(), err)) + if err := utils.ReadJSONFromBody(r, &bodyParams); err != nil { + utils.Error(w, http.StatusBadRequest, err) return } diff --git a/pkg/api/handlers/compat/networks.go b/pkg/api/handlers/compat/networks.go index a072426a06..7288a4153b 100644 --- a/pkg/api/handlers/compat/networks.go +++ b/pkg/api/handlers/compat/networks.go @@ -3,7 +3,6 @@ package compat import ( - "encoding/json" "errors" "fmt" "net" @@ -210,8 +209,9 @@ func CreateNetwork(w http.ResponseWriter, r *http.Request) { responseWarning string ) runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) - if err := json.NewDecoder(r.Body).Decode(&networkCreate); err != nil { - utils.Error(w, http.StatusInternalServerError, fmt.Errorf("Decode(): %w", err)) + + if err := utils.ReadJSONFromBody(r, &networkCreate); err != nil { + utils.Error(w, http.StatusBadRequest, err) return } @@ -376,8 +376,8 @@ func Connect(w http.ResponseWriter, r *http.Request) { runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) var netConnect dockerNetwork.ConnectRequest - if err := json.NewDecoder(r.Body).Decode(&netConnect); err != nil { - utils.Error(w, http.StatusInternalServerError, fmt.Errorf("Decode(): %w", err)) + if err := utils.ReadJSONFromBody(r, &netConnect); err != nil { + utils.Error(w, http.StatusBadRequest, err) return } @@ -441,8 +441,8 @@ func Disconnect(w http.ResponseWriter, r *http.Request) { runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) var netDisconnect dockerNetwork.DisconnectRequest - if err := json.NewDecoder(r.Body).Decode(&netDisconnect); err != nil { - utils.Error(w, http.StatusInternalServerError, fmt.Errorf("Decode(): %w", err)) + if err := utils.ReadJSONFromBody(r, &netDisconnect); err != nil { + utils.Error(w, http.StatusBadRequest, err) return } diff --git a/pkg/api/handlers/compat/secrets.go b/pkg/api/handlers/compat/secrets.go index 3bec29079e..4c31e1fbb0 100644 --- a/pkg/api/handlers/compat/secrets.go +++ b/pkg/api/handlers/compat/secrets.go @@ -5,7 +5,6 @@ package compat import ( "bytes" "encoding/base64" - "encoding/json" "errors" "fmt" "net/http" @@ -123,8 +122,8 @@ func CreateSecret(w http.ResponseWriter, r *http.Request) { Labels map[string]string `schema:"labels"` }{} - if err := json.NewDecoder(r.Body).Decode(&createParams); err != nil { - utils.Error(w, http.StatusInternalServerError, fmt.Errorf("Decode(): %w", err)) + if err := utils.ReadJSONFromBody(r, &createParams); err != nil { + utils.Error(w, http.StatusBadRequest, err) return } diff --git a/pkg/api/handlers/compat/volumes.go b/pkg/api/handlers/compat/volumes.go index 9f65afdbf3..b996b304d6 100644 --- a/pkg/api/handlers/compat/volumes.go +++ b/pkg/api/handlers/compat/volumes.go @@ -4,7 +4,6 @@ package compat import ( "bytes" - "encoding/json" "errors" "fmt" "net/http" @@ -108,8 +107,8 @@ func CreateVolume(w http.ResponseWriter, r *http.Request) { } // decode params from body input := client.VolumeCreateOptions{} - if err := json.NewDecoder(r.Body).Decode(&input); err != nil { - utils.Error(w, http.StatusInternalServerError, fmt.Errorf("Decode(): %w", err)) + if err := utils.ReadJSONFromBody(r, &input); err != nil { + utils.Error(w, http.StatusBadRequest, err) return } diff --git a/pkg/api/handlers/libpod/containers.go b/pkg/api/handlers/libpod/containers.go index 70def7b205..cb55704bc3 100644 --- a/pkg/api/handlers/libpod/containers.go +++ b/pkg/api/handlers/libpod/containers.go @@ -496,8 +496,8 @@ func UpdateContainer(w http.ResponseWriter, r *http.Request) { } wire := updateEntitiesWire{} - if err := json.NewDecoder(r.Body).Decode(&wire); err != nil { - utils.InternalServerError(w, fmt.Errorf("decode(): %w", err)) + if err := utils.ReadJSONFromBody(r, &wire); err != nil { + utils.Error(w, http.StatusBadRequest, err) return } diff --git a/pkg/api/handlers/libpod/containers_create.go b/pkg/api/handlers/libpod/containers_create.go index a657644424..f6f2c6bec9 100644 --- a/pkg/api/handlers/libpod/containers_create.go +++ b/pkg/api/handlers/libpod/containers_create.go @@ -3,7 +3,6 @@ package libpod import ( - "encoding/json" "errors" "fmt" "net/http" @@ -62,8 +61,8 @@ func CreateContainer(w http.ResponseWriter, r *http.Request) { }, } - if err := json.NewDecoder(r.Body).Decode(&wire); err != nil { - utils.Error(w, http.StatusInternalServerError, fmt.Errorf("decode(): %w", err)) + if err := utils.ReadJSONFromBody(r, &wire); err != nil { + utils.Error(w, http.StatusBadRequest, err) return } diff --git a/pkg/api/handlers/libpod/manifests.go b/pkg/api/handlers/libpod/manifests.go index f2cbc3da9d..ea79944136 100644 --- a/pkg/api/handlers/libpod/manifests.go +++ b/pkg/api/handlers/libpod/manifests.go @@ -212,8 +212,8 @@ func ManifestAddV3(w http.ResponseWriter, r *http.Request) { entities.ManifestAddOptions TLSVerify bool `schema:"tlsVerify"` }{} - if err := json.NewDecoder(r.Body).Decode(&query); err != nil { - utils.Error(w, http.StatusInternalServerError, fmt.Errorf("decoding AddV3 query: %w", err)) + if err := utils.ReadJSONFromBody(r, &query); err != nil { + utils.Error(w, http.StatusBadRequest, err) return } @@ -495,8 +495,8 @@ func ManifestModify(w http.ResponseWriter, r *http.Request) { if err != nil { multireader = nil // not multipart - request is just encoded JSON, nothing else - if err := json.NewDecoder(r.Body).Decode(body); err != nil { - utils.Error(w, http.StatusInternalServerError, fmt.Errorf("decoding modify request: %w", err)) + if err := utils.ReadJSONFromBody(r, &body); err != nil { + utils.Error(w, http.StatusBadRequest, err) return } } else { diff --git a/pkg/api/handlers/libpod/networks.go b/pkg/api/handlers/libpod/networks.go index 4ce841c774..d21cb8ad38 100644 --- a/pkg/api/handlers/libpod/networks.go +++ b/pkg/api/handlers/libpod/networks.go @@ -3,7 +3,6 @@ package libpod import ( - "encoding/json" "errors" "fmt" "net/http" @@ -27,8 +26,8 @@ func CreateNetwork(w http.ResponseWriter, r *http.Request) { runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) network := types.Network{} - if err := json.NewDecoder(r.Body).Decode(&network); err != nil { - utils.Error(w, http.StatusInternalServerError, fmt.Errorf("failed to decode request JSON payload: %w", err)) + if err := utils.ReadJSONFromBody(r, &network); err != nil { + utils.Error(w, http.StatusBadRequest, err) return } @@ -59,8 +58,8 @@ func UpdateNetwork(w http.ResponseWriter, r *http.Request) { ic := abi.ContainerEngine{Libpod: runtime} networkUpdateOptions := entities.NetworkUpdateOptions{} - if err := json.NewDecoder(r.Body).Decode(&networkUpdateOptions); err != nil { - utils.Error(w, http.StatusBadRequest, fmt.Errorf("failed to decode request JSON payload: %w", err)) + if err := utils.ReadJSONFromBody(r, &networkUpdateOptions); err != nil { + utils.Error(w, http.StatusBadRequest, err) return } @@ -174,8 +173,8 @@ func Connect(w http.ResponseWriter, r *http.Request) { runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) var netConnect entities.NetworkConnectOptions - if err := json.NewDecoder(r.Body).Decode(&netConnect); err != nil { - utils.Error(w, http.StatusInternalServerError, fmt.Errorf("failed to decode request JSON payload: %w", err)) + if err := utils.ReadJSONFromBody(r, &netConnect); err != nil { + utils.Error(w, http.StatusBadRequest, err) return } name := utils.GetName(r) diff --git a/pkg/api/handlers/libpod/pods.go b/pkg/api/handlers/libpod/pods.go index 997e998353..062112828a 100644 --- a/pkg/api/handlers/libpod/pods.go +++ b/pkg/api/handlers/libpod/pods.go @@ -35,8 +35,8 @@ func PodCreate(w http.ResponseWriter, r *http.Request) { err error ) psg := specgen.PodSpecGenerator{InfraContainerSpec: &specgen.SpecGenerator{}} - if err = json.NewDecoder(r.Body).Decode(&psg); err != nil { - utils.Error(w, http.StatusInternalServerError, fmt.Errorf("%v: %w", failedToDecodeSpecgen, err)) + if err := utils.ReadJSONFromBody(r, &psg); err != nil { + utils.Error(w, http.StatusBadRequest, err) return } diff --git a/pkg/api/handlers/libpod/volumes.go b/pkg/api/handlers/libpod/volumes.go index b8b122a77b..2b3f0f4a29 100644 --- a/pkg/api/handlers/libpod/volumes.go +++ b/pkg/api/handlers/libpod/volumes.go @@ -3,7 +3,6 @@ package libpod import ( - "encoding/json" "errors" "fmt" "maps" @@ -39,9 +38,8 @@ func CreateVolume(w http.ResponseWriter, r *http.Request) { } input := entities.VolumeCreateOptions{} - // decode params from body - if err := json.NewDecoder(r.Body).Decode(&input); err != nil { - utils.Error(w, http.StatusInternalServerError, fmt.Errorf("Decode(): %w", err)) + if err := utils.ReadJSONFromBody(r, &input); err != nil { + utils.Error(w, http.StatusBadRequest, err) return } diff --git a/pkg/api/handlers/utils/handler.go b/pkg/api/handlers/utils/handler.go index a9fa011a99..cd74e59949 100644 --- a/pkg/api/handlers/utils/handler.go +++ b/pkg/api/handlers/utils/handler.go @@ -152,6 +152,19 @@ func MarshalErrorSliceJSONIsEmpty(ptr unsafe.Pointer) bool { return len(*((*[]error)(ptr))) == 0 } +// ReadJSONFromBody reads JSON from a request body into the given struct. +// If the body is not provided/is empty, this returns immediately with no modification to the given struct. +// This means that defaults will NOT be overwritten in this case. +func ReadJSONFromBody(r *http.Request, unmarshalTo any) error { + if r.Body == nil || r.ContentLength == 0 { + return nil + } + if err := json.NewDecoder(r.Body).Decode(unmarshalTo); err != nil { + return fmt.Errorf("decoding request body as JSON: %w", err) + } + return nil +} + // WriteJSON writes an interface value encoded as JSON to w func WriteJSON(w http.ResponseWriter, code int, value any) { // FIXME: we don't need to write the header in all/some circumstances. diff --git a/test/apiv2/35-networks.at b/test/apiv2/35-networks.at index 976c2a4045..0090e8af38 100644 --- a/test/apiv2/35-networks.at +++ b/test/apiv2/35-networks.at @@ -46,10 +46,10 @@ t POST libpod/networks/create name="network4" \ .labels.zaq=val # test for empty mask -t POST libpod/networks/create subnets='[{"subnet":"10.10.134.0"}]' 500 \ +t POST libpod/networks/create subnets='[{"subnet":"10.10.134.0"}]' 400 \ .cause~'.*invalid CIDR address: 10.10.134.0' # test for invalid mask -t POST libpod/networks/create subnets='[{"subnet":"10.10.134.0/65"}]' 500 \ +t POST libpod/networks/create subnets='[{"subnet":"10.10.134.0/65"}]' 400 \ .cause~'.*invalid CIDR address: 10.10.134.0/65' # network list