mirror of
https://github.com/moby/moby.git
synced 2026-01-11 10:41:43 +00:00
The "param" field was only used to generate the error-message, and the produced error-message was missing a space so we may as well just inline it. Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
174 lines
4.9 KiB
Go
174 lines
4.9 KiB
Go
package httputils
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/distribution/reference"
|
|
"github.com/moby/moby/v2/errdefs"
|
|
"github.com/pkg/errors"
|
|
|
|
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
|
)
|
|
|
|
// BoolValue transforms a form value in different formats into a boolean type.
|
|
func BoolValue(r *http.Request, k string) bool {
|
|
switch strings.ToLower(strings.TrimSpace(r.FormValue(k))) {
|
|
case "", "0", "no", "false", "none":
|
|
return false
|
|
default:
|
|
return true
|
|
}
|
|
}
|
|
|
|
// BoolValueOrDefault returns the default bool passed if the query param is
|
|
// missing, otherwise it's just a proxy to boolValue above.
|
|
func BoolValueOrDefault(r *http.Request, k string, d bool) bool {
|
|
if _, ok := r.Form[k]; !ok {
|
|
return d
|
|
}
|
|
return BoolValue(r, k)
|
|
}
|
|
|
|
// Uint32Value parses a form value into an uint32 type. It returns an error
|
|
// if the field is not set, empty, incorrectly formatted, or out of range.
|
|
func Uint32Value(r *http.Request, field string) (uint32, error) {
|
|
// strconv.ParseUint returns an "strconv.ErrSyntax" for negative values,
|
|
// not an "out of range". Strip the prefix before parsing, and use it
|
|
// later to detect valid, but negative values.
|
|
v, isNeg := strings.CutPrefix(r.Form.Get(field), "-")
|
|
if v == "" || v[0] == '+' {
|
|
// Fast-path for invalid values.
|
|
return 0, strconv.ErrSyntax
|
|
}
|
|
|
|
i, err := strconv.ParseUint(v, 10, 32)
|
|
if err != nil {
|
|
// Unwrap to remove the 'strconv.ParseUint: parsing "some-invalid-value":' prefix.
|
|
return 0, errors.Unwrap(err)
|
|
}
|
|
if isNeg {
|
|
return 0, strconv.ErrRange
|
|
}
|
|
return uint32(i), nil
|
|
}
|
|
|
|
// Int64ValueOrZero parses a form value into an int64 type.
|
|
// It returns 0 if the parsing fails.
|
|
func Int64ValueOrZero(r *http.Request, k string) int64 {
|
|
val, err := Int64ValueOrDefault(r, k, 0)
|
|
if err != nil {
|
|
return 0
|
|
}
|
|
return val
|
|
}
|
|
|
|
// Int64ValueOrDefault parses a form value into an int64 type. If there is an
|
|
// error, returns the error. If there is no value returns the default value.
|
|
func Int64ValueOrDefault(r *http.Request, field string, def int64) (int64, error) {
|
|
if r.Form.Get(field) != "" {
|
|
value, err := strconv.ParseInt(r.Form.Get(field), 10, 64)
|
|
return value, err
|
|
}
|
|
return def, nil
|
|
}
|
|
|
|
// RepoTagReference parses form values "repo" and "tag" and returns a valid
|
|
// reference with repository and tag.
|
|
// If repo is empty, then a nil reference is returned.
|
|
// If no tag is given, then the default "latest" tag is set.
|
|
func RepoTagReference(repo, tag string) (reference.NamedTagged, error) {
|
|
if repo == "" {
|
|
return nil, nil
|
|
}
|
|
|
|
ref, err := reference.ParseNormalizedNamed(repo)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if _, isDigested := ref.(reference.Digested); isDigested {
|
|
return nil, errors.New("cannot import digest reference")
|
|
}
|
|
|
|
if tag != "" {
|
|
return reference.WithTag(ref, tag)
|
|
}
|
|
|
|
withDefaultTag := reference.TagNameOnly(ref)
|
|
|
|
namedTagged, ok := withDefaultTag.(reference.NamedTagged)
|
|
if !ok {
|
|
return nil, fmt.Errorf("unexpected reference: %q", ref.String())
|
|
}
|
|
|
|
return namedTagged, nil
|
|
}
|
|
|
|
// ArchiveOptions stores archive information for different operations.
|
|
type ArchiveOptions struct {
|
|
Name string
|
|
Path string
|
|
}
|
|
|
|
// ArchiveFormValues parses form values and turns them into ArchiveOptions.
|
|
// It fails if the archive name and path are not in the request.
|
|
func ArchiveFormValues(r *http.Request, vars map[string]string) (ArchiveOptions, error) {
|
|
if err := ParseForm(r); err != nil {
|
|
return ArchiveOptions{}, err
|
|
}
|
|
|
|
name := vars["name"]
|
|
if name == "" {
|
|
return ArchiveOptions{}, errdefs.InvalidParameter(errors.New("bad parameter: name cannot be empty"))
|
|
}
|
|
path := r.Form.Get("path")
|
|
if path == "" {
|
|
return ArchiveOptions{}, errdefs.InvalidParameter(errors.New("bad parameter: path cannot be empty"))
|
|
}
|
|
return ArchiveOptions{name, path}, nil
|
|
}
|
|
|
|
// DecodePlatform decodes the OCI platform JSON string into a Platform struct.
|
|
func DecodePlatform(platformJSON string) (*ocispec.Platform, error) {
|
|
var p ocispec.Platform
|
|
|
|
if err := json.Unmarshal([]byte(platformJSON), &p); err != nil {
|
|
return nil, errdefs.InvalidParameter(errors.Wrap(err, "failed to parse platform"))
|
|
}
|
|
|
|
hasAnyOptional := (p.Variant != "" || p.OSVersion != "" || len(p.OSFeatures) > 0)
|
|
|
|
if p.OS == "" && p.Architecture == "" && hasAnyOptional {
|
|
return nil, errdefs.InvalidParameter(errors.New("optional platform fields provided, but OS and Architecture are missing"))
|
|
}
|
|
|
|
if p.OS == "" || p.Architecture == "" {
|
|
return nil, errdefs.InvalidParameter(errors.New("both OS and Architecture must be provided"))
|
|
}
|
|
|
|
return &p, nil
|
|
}
|
|
|
|
// DecodePlatforms decodes the OCI platform JSON string into a Platform struct.
|
|
//
|
|
// Typically, the argument is a value of: r.Form["platform"]
|
|
func DecodePlatforms(platformJSONs []string) ([]ocispec.Platform, error) {
|
|
if len(platformJSONs) == 0 {
|
|
return nil, nil
|
|
}
|
|
|
|
var output []ocispec.Platform
|
|
for _, platform := range platformJSONs {
|
|
p, err := DecodePlatform(platform)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
output = append(output, *p)
|
|
}
|
|
return output, nil
|
|
}
|