github.com/icyphox/x@v0.0.355-0.20220311094250-029bd783e8b8/decoderx/http.go (about)

     1  package decoderx
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"crypto/sha256"
     7  	"encoding/json"
     8  	"fmt"
     9  	"io"
    10  	"io/ioutil"
    11  	"net/http"
    12  	"net/url"
    13  	"strconv"
    14  	"strings"
    15  
    16  	"github.com/pkg/errors"
    17  	"github.com/tidwall/gjson"
    18  	"github.com/tidwall/sjson"
    19  
    20  	"github.com/ory/jsonschema/v3"
    21  
    22  	"github.com/ory/herodot"
    23  
    24  	"github.com/ory/x/httpx"
    25  	"github.com/ory/x/jsonschemax"
    26  	"github.com/ory/x/stringslice"
    27  )
    28  
    29  type (
    30  	// HTTP decodes json and form-data from HTTP Request Bodies.
    31  	HTTP struct{}
    32  
    33  	httpDecoderOptions struct {
    34  		keepRequestBody           bool
    35  		allowedContentTypes       []string
    36  		allowedHTTPMethods        []string
    37  		jsonSchemaRef             string
    38  		jsonSchemaCompiler        *jsonschema.Compiler
    39  		jsonSchemaValidate        bool
    40  		maxCircularReferenceDepth uint8
    41  		handleParseErrors         parseErrorStrategy
    42  		expectJSONFlattened       bool
    43  		queryAndBody              bool
    44  	}
    45  
    46  	// HTTPDecoderOption configures the HTTP decoder.
    47  	HTTPDecoderOption func(*httpDecoderOptions)
    48  
    49  	parseErrorStrategy uint8
    50  )
    51  
    52  const (
    53  	httpContentTypeMultipartForm  = "multipart/form-data"
    54  	httpContentTypeURLEncodedForm = "application/x-www-form-urlencoded"
    55  	httpContentTypeJSON           = "application/json"
    56  )
    57  
    58  const (
    59  	// ParseErrorIgnoreConversionErrors will ignore any errors caused by strconv.Parse* and use the
    60  	// raw form field value, which is a string, when such a parse error occurs.
    61  	//
    62  	// If the JSON Schema defines `{"ratio": {"type": "number"}}` but `ratio=foobar` then field
    63  	// `ratio` will be handled as a string. If the destination struct is a `json.RawMessage`, then
    64  	// the output will be `{"ratio": "foobar"}`.
    65  	ParseErrorIgnoreConversionErrors parseErrorStrategy = iota + 1
    66  
    67  	// ParseErrorUseEmptyValueOnConversionErrors will ignore any parse errors caused by strconv.Parse* and use the
    68  	// default value of the type to be casted, e.g. float64(0), string("").
    69  	//
    70  	// If the JSON Schema defines `{"ratio": {"type": "number"}}` but `ratio=foobar` then field
    71  	// `ratio` will receive the default value for the primitive type (here `0.0` for `number`).
    72  	// If the destination struct is a `json.RawMessage`, then the output will be `{"ratio": 0.0}`.
    73  	ParseErrorUseEmptyValueOnConversionErrors
    74  
    75  	// ParseErrorReturnOnConversionErrors will abort and return with an error if strconv.Parse* returns
    76  	// an error.
    77  	//
    78  	// If the JSON Schema defines `{"ratio": {"type": "number"}}` but `ratio=foobar` the parser aborts
    79  	// and returns an error, here: `strconv.ParseFloat: parsing "foobar"`.
    80  	ParseErrorReturnOnConversionErrors
    81  )
    82  
    83  var errKeyNotFound = errors.New("key not found")
    84  
    85  // HTTPFormDecoder configures the HTTP decoder to only accept form-data
    86  // (application/x-www-form-urlencoded, multipart/form-data)
    87  func HTTPFormDecoder() HTTPDecoderOption {
    88  	return func(o *httpDecoderOptions) {
    89  		o.allowedContentTypes = []string{httpContentTypeMultipartForm, httpContentTypeURLEncodedForm}
    90  	}
    91  }
    92  
    93  // HTTPJSONDecoder configures the HTTP decoder to only accept form-data
    94  // (application/json).
    95  func HTTPJSONDecoder() HTTPDecoderOption {
    96  	return func(o *httpDecoderOptions) {
    97  		o.allowedContentTypes = []string{httpContentTypeJSON}
    98  	}
    99  }
   100  
   101  // HTTPKeepRequestBody configures the HTTP decoder to allow other
   102  // HTTP request body readers to read the body as well by keeping
   103  // the data in memory.
   104  func HTTPKeepRequestBody(keep bool) HTTPDecoderOption {
   105  	return func(o *httpDecoderOptions) {
   106  		o.keepRequestBody = keep
   107  	}
   108  }
   109  
   110  // HTTPDecoderSetValidatePayloads sets if payloads should be validated or not.
   111  func HTTPDecoderSetValidatePayloads(validate bool) HTTPDecoderOption {
   112  	return func(o *httpDecoderOptions) {
   113  		o.jsonSchemaValidate = validate
   114  		o.keepRequestBody = true
   115  	}
   116  }
   117  
   118  // HTTPDecoderJSONFollowsFormFormat if set tells the decoder that JSON follows the same conventions
   119  // as the form decoder, meaning `{"foo.bar": "..."}` is translated to `{"foo": {"bar": "..."}}`.
   120  func HTTPDecoderJSONFollowsFormFormat() HTTPDecoderOption {
   121  	return func(o *httpDecoderOptions) {
   122  		o.expectJSONFlattened = true
   123  		o.keepRequestBody = true
   124  	}
   125  }
   126  
   127  // HTTPDecoderAllowedMethods sets the allowed HTTP methods. Defaults are POST, PUT, PATCH.
   128  func HTTPDecoderAllowedMethods(method ...string) HTTPDecoderOption {
   129  	return func(o *httpDecoderOptions) {
   130  		o.allowedHTTPMethods = method
   131  	}
   132  }
   133  
   134  // HTTPDecoderUseQueryAndBody will check both the HTTP body and the HTTP query params when decoding.
   135  // Only relevant for non-GET operations.
   136  func HTTPDecoderUseQueryAndBody() HTTPDecoderOption {
   137  	return func(o *httpDecoderOptions) {
   138  		o.queryAndBody = true
   139  	}
   140  }
   141  
   142  // HTTPDecoderSetIgnoreParseErrorsStrategy sets a strategy for dealing with strconv.Parse* errors:
   143  //
   144  // - decoderx.ParseErrorIgnoreConversionErrors will ignore any parse errors caused by strconv.Parse* and use the
   145  // raw form field value, which is a string, when such a parse error occurs. (default)
   146  // - decoderx.ParseErrorUseEmptyValueOnConversionErrors will ignore any parse errors caused by strconv.Parse* and use the
   147  // default value of the type to be casted, e.g. float64(0), string("").
   148  // - decoderx.ParseErrorReturnOnConversionErrors will abort and return with an error if strconv.Parse* returns
   149  // an error.
   150  func HTTPDecoderSetIgnoreParseErrorsStrategy(strategy parseErrorStrategy) HTTPDecoderOption {
   151  	return func(o *httpDecoderOptions) {
   152  		o.handleParseErrors = strategy
   153  	}
   154  }
   155  
   156  // HTTPDecoderSetMaxCircularReferenceDepth sets the maximum recursive reference resolution depth.
   157  func HTTPDecoderSetMaxCircularReferenceDepth(depth uint8) HTTPDecoderOption {
   158  	return func(o *httpDecoderOptions) {
   159  		o.maxCircularReferenceDepth = depth
   160  	}
   161  }
   162  
   163  // HTTPJSONSchemaCompiler sets a JSON schema to be used for validation and type assertion of
   164  // incoming requests.
   165  func HTTPJSONSchemaCompiler(ref string, compiler *jsonschema.Compiler) HTTPDecoderOption {
   166  	return func(o *httpDecoderOptions) {
   167  		if compiler == nil {
   168  			compiler = jsonschema.NewCompiler()
   169  		}
   170  		compiler.ExtractAnnotations = true
   171  		o.jsonSchemaCompiler = compiler
   172  		o.jsonSchemaRef = ref
   173  		o.jsonSchemaValidate = true
   174  	}
   175  }
   176  
   177  // HTTPRawJSONSchemaCompiler uses a JSON Schema Compiler with the provided JSON Schema in raw byte form.
   178  func HTTPRawJSONSchemaCompiler(raw []byte) (HTTPDecoderOption, error) {
   179  	compiler := jsonschema.NewCompiler()
   180  	id := fmt.Sprintf("%x.json", sha256.Sum256(raw))
   181  	if err := compiler.AddResource(id, bytes.NewReader(raw)); err != nil {
   182  		return nil, err
   183  	}
   184  	compiler.ExtractAnnotations = true
   185  
   186  	return func(o *httpDecoderOptions) {
   187  		o.jsonSchemaCompiler = compiler
   188  		o.jsonSchemaRef = id
   189  		o.jsonSchemaValidate = true
   190  	}, nil
   191  }
   192  
   193  // MustHTTPRawJSONSchemaCompiler uses HTTPRawJSONSchemaCompiler and panics on error.
   194  func MustHTTPRawJSONSchemaCompiler(raw []byte) HTTPDecoderOption {
   195  	f, err := HTTPRawJSONSchemaCompiler(raw)
   196  	if err != nil {
   197  		panic(err)
   198  	}
   199  	return f
   200  }
   201  
   202  func newHTTPDecoderOptions(fs []HTTPDecoderOption) *httpDecoderOptions {
   203  	o := &httpDecoderOptions{
   204  		allowedContentTypes: []string{
   205  			httpContentTypeMultipartForm, httpContentTypeURLEncodedForm, httpContentTypeJSON,
   206  		},
   207  		allowedHTTPMethods:        []string{"POST", "PUT", "PATCH"},
   208  		maxCircularReferenceDepth: 5,
   209  		handleParseErrors:         ParseErrorIgnoreConversionErrors,
   210  	}
   211  
   212  	for _, f := range fs {
   213  		f(o)
   214  	}
   215  
   216  	return o
   217  }
   218  
   219  // NewHTTP creates a new HTTP decoder.
   220  func NewHTTP() *HTTP {
   221  	return new(HTTP)
   222  }
   223  
   224  func (t *HTTP) validateRequest(r *http.Request, c *httpDecoderOptions) error {
   225  	method := strings.ToUpper(r.Method)
   226  
   227  	if !stringslice.Has(c.allowedHTTPMethods, method) {
   228  		return errors.WithStack(herodot.ErrBadRequest.WithReasonf(`Unable to decode body because HTTP Request Method was "%s" but only %v are supported.`, method, c.allowedHTTPMethods))
   229  	}
   230  
   231  	if method != "GET" {
   232  		if r.ContentLength == 0 && method != "GET" {
   233  			return errors.WithStack(herodot.ErrBadRequest.WithReasonf(`Unable to decode HTTP Request Body because its HTTP Header "Content-Length" is zero.`))
   234  		}
   235  
   236  		if !httpx.HasContentType(r, c.allowedContentTypes...) {
   237  			return errors.WithStack(herodot.ErrBadRequest.WithReasonf(`HTTP %s Request used unknown HTTP Header "Content-Type: %s", only %v are supported.`, method, r.Header.Get("Content-Type"), c.allowedContentTypes))
   238  		}
   239  	}
   240  
   241  	return nil
   242  }
   243  
   244  func (t *HTTP) validatePayload(ctx context.Context, raw json.RawMessage, c *httpDecoderOptions) error {
   245  	if !c.jsonSchemaValidate {
   246  		return nil
   247  	}
   248  
   249  	if c.jsonSchemaCompiler == nil {
   250  		return errors.WithStack(herodot.ErrInternalServerError.WithReasonf("JSON Schema Validation is required but no compiler was provided."))
   251  	}
   252  
   253  	schema, err := c.jsonSchemaCompiler.Compile(ctx, c.jsonSchemaRef)
   254  	if err != nil {
   255  		return errors.WithStack(herodot.ErrInternalServerError.WithReasonf("Unable to load JSON Schema from location: %s", c.jsonSchemaRef).WithDebug(err.Error()))
   256  	}
   257  
   258  	if err := schema.Validate(bytes.NewBuffer(raw)); err != nil {
   259  		if _, ok := err.(*jsonschema.ValidationError); ok {
   260  			return errors.WithStack(err)
   261  		}
   262  		return errors.WithStack(herodot.ErrInternalServerError.WithReasonf("Unable to process JSON Schema and input: %s", err).WithDebug(err.Error()))
   263  	}
   264  
   265  	return nil
   266  }
   267  
   268  // Decode takes a HTTP Request Body and decodes it into destination.
   269  func (t *HTTP) Decode(r *http.Request, destination interface{}, opts ...HTTPDecoderOption) error {
   270  	c := newHTTPDecoderOptions(opts)
   271  	if err := t.validateRequest(r, c); err != nil {
   272  		return err
   273  	}
   274  
   275  	if r.Method == "GET" {
   276  		return t.decodeForm(r, destination, c)
   277  	} else if httpx.HasContentType(r, httpContentTypeJSON) {
   278  		if c.expectJSONFlattened {
   279  			return t.decodeJSONForm(r, destination, c)
   280  		}
   281  		return t.decodeJSON(r, destination, c, false)
   282  	} else if httpx.HasContentType(r, httpContentTypeMultipartForm, httpContentTypeURLEncodedForm) {
   283  		return t.decodeForm(r, destination, c)
   284  	}
   285  
   286  	return errors.WithStack(herodot.ErrInternalServerError.WithReasonf("Unable to determine decoder for content type: %s", r.Header.Get("Content-Type")))
   287  }
   288  
   289  func (t *HTTP) requestBody(r *http.Request, o *httpDecoderOptions) (reader io.ReadCloser, err error) {
   290  	if strings.ToUpper(r.Method) == "GET" {
   291  		return ioutil.NopCloser(bytes.NewBufferString(r.URL.Query().Encode())), nil
   292  	}
   293  
   294  	if !o.keepRequestBody {
   295  		return r.Body, nil
   296  	}
   297  
   298  	bodyBytes, err := ioutil.ReadAll(r.Body)
   299  	if err != nil {
   300  		return nil, errors.Wrapf(err, "unable to read body")
   301  	}
   302  
   303  	_ = r.Body.Close() //  must close
   304  	r.Body = ioutil.NopCloser(bytes.NewBuffer(bodyBytes))
   305  
   306  	return ioutil.NopCloser(bytes.NewBuffer(bodyBytes)), nil
   307  }
   308  
   309  func (t *HTTP) decodeJSONForm(r *http.Request, destination interface{}, o *httpDecoderOptions) error {
   310  	if o.jsonSchemaCompiler == nil {
   311  		return errors.WithStack(herodot.ErrInternalServerError.WithReasonf("Unable to decode HTTP Form Body because no validation schema was provided. This is a code bug."))
   312  	}
   313  
   314  	paths, err := jsonschemax.ListPathsWithRecursion(r.Context(), o.jsonSchemaRef, o.jsonSchemaCompiler, o.maxCircularReferenceDepth)
   315  	if err != nil {
   316  		return errors.WithStack(herodot.ErrInternalServerError.WithTrace(err).WithReasonf("Unable to prepare JSON Schema for HTTP Post Body Form parsing: %s", err).WithDebugf("%+v", err))
   317  	}
   318  
   319  	reader, err := t.requestBody(r, o)
   320  	if err != nil {
   321  		return err
   322  	}
   323  
   324  	var interim json.RawMessage
   325  	if err := json.NewDecoder(reader).Decode(&interim); err != nil {
   326  		return err
   327  	}
   328  
   329  	parsed := gjson.ParseBytes(interim)
   330  	if !parsed.IsObject() {
   331  		return errors.WithStack(herodot.ErrBadRequest.WithReasonf("Expected JSON sent in request body to be an object but got: %s", parsed.Type.String()))
   332  	}
   333  
   334  	values := url.Values{}
   335  	var notJSONForm bool
   336  	parsed.ForEach(func(k, v gjson.Result) bool {
   337  		if v.IsArray() || v.IsObject() {
   338  			notJSONForm = true
   339  			return false
   340  		}
   341  		values.Set(k.String(), v.String())
   342  		return true
   343  	})
   344  
   345  	if notJSONForm {
   346  		return t.decodeJSON(r, destination, o, true)
   347  	}
   348  
   349  	if o.queryAndBody {
   350  		_ = r.ParseForm()
   351  		for k := range r.Form {
   352  			values.Set(k, r.Form.Get(k))
   353  		}
   354  	}
   355  
   356  	raw, err := t.decodeURLValues(values, paths, o)
   357  	if err != nil {
   358  		return err
   359  	}
   360  
   361  	if err := json.Unmarshal(raw, destination); err != nil {
   362  		return errors.WithStack(err)
   363  	}
   364  
   365  	return t.validatePayload(r.Context(), raw, o)
   366  }
   367  
   368  func (t *HTTP) decodeForm(r *http.Request, destination interface{}, o *httpDecoderOptions) error {
   369  	if o.jsonSchemaCompiler == nil {
   370  		return errors.WithStack(herodot.ErrInternalServerError.WithReasonf("Unable to decode HTTP Form Body because no validation schema was provided. This is a code bug."))
   371  	}
   372  
   373  	reader, err := t.requestBody(r, o)
   374  	if err != nil {
   375  		return err
   376  	}
   377  
   378  	defer func() {
   379  		r.Body = reader
   380  	}()
   381  
   382  	if err := r.ParseForm(); err != nil {
   383  		return errors.WithStack(herodot.ErrBadRequest.WithReasonf("Unable to decode HTTP %s form body: %s", strings.ToUpper(r.Method), err).WithDebug(err.Error()))
   384  	}
   385  
   386  	paths, err := jsonschemax.ListPathsWithRecursion(r.Context(), o.jsonSchemaRef, o.jsonSchemaCompiler, o.maxCircularReferenceDepth)
   387  	if err != nil {
   388  		return errors.WithStack(herodot.ErrInternalServerError.WithTrace(err).WithReasonf("Unable to prepare JSON Schema for HTTP Post Body Form parsing: %s", err).WithDebugf("%+v", err))
   389  	}
   390  
   391  	values := r.PostForm
   392  	if r.Method == "GET" || o.queryAndBody {
   393  		values = r.Form
   394  	}
   395  
   396  	raw, err := t.decodeURLValues(values, paths, o)
   397  	if err != nil && !errors.Is(err, errKeyNotFound) {
   398  		return err
   399  	}
   400  
   401  	if err := json.NewDecoder(bytes.NewReader(raw)).Decode(destination); err != nil {
   402  		return errors.WithStack(herodot.ErrBadRequest.WithReasonf("Unable to decode JSON payload: %s", err))
   403  	}
   404  
   405  	return t.validatePayload(r.Context(), raw, o)
   406  }
   407  
   408  func (t *HTTP) decodeURLValues(values url.Values, paths []jsonschemax.Path, o *httpDecoderOptions) (json.RawMessage, error) {
   409  	raw := json.RawMessage(`{}`)
   410  	for key := range values {
   411  		for _, path := range paths {
   412  			if key == path.Name {
   413  				var err error
   414  				switch path.Type.(type) {
   415  				case []string:
   416  					raw, err = sjson.SetBytes(raw, path.Name, values[key])
   417  				case []float64:
   418  					for k, v := range values[key] {
   419  						if f, err := strconv.ParseFloat(v, 64); err != nil {
   420  							switch o.handleParseErrors {
   421  							case ParseErrorIgnoreConversionErrors:
   422  								raw, err = sjson.SetBytes(raw, path.Name+"."+strconv.Itoa(k), v)
   423  							case ParseErrorUseEmptyValueOnConversionErrors:
   424  								raw, err = sjson.SetBytes(raw, path.Name+"."+strconv.Itoa(k), f)
   425  							case ParseErrorReturnOnConversionErrors:
   426  								return nil, errors.WithStack(herodot.ErrBadRequest.WithReasonf("Expected value to be a number.").
   427  									WithDetail("parse_error", err.Error()).
   428  									WithDetail("name", key).
   429  									WithDetailf("index", "%d", k).
   430  									WithDetail("value", v))
   431  							}
   432  						} else {
   433  							raw, err = sjson.SetBytes(raw, path.Name+"."+strconv.Itoa(k), f)
   434  						}
   435  					}
   436  				case []bool:
   437  					for k, v := range values[key] {
   438  						if f, err := strconv.ParseBool(v); err != nil {
   439  							switch o.handleParseErrors {
   440  							case ParseErrorIgnoreConversionErrors:
   441  								raw, err = sjson.SetBytes(raw, path.Name+"."+strconv.Itoa(k), v)
   442  							case ParseErrorUseEmptyValueOnConversionErrors:
   443  								raw, err = sjson.SetBytes(raw, path.Name+"."+strconv.Itoa(k), f)
   444  							case ParseErrorReturnOnConversionErrors:
   445  								return nil, errors.WithStack(herodot.ErrBadRequest.WithReasonf("Expected value to be a boolean.").
   446  									WithDetail("parse_error", err.Error()).
   447  									WithDetail("name", key).
   448  									WithDetailf("index", "%d", k).
   449  									WithDetail("value", v))
   450  							}
   451  						} else {
   452  							raw, err = sjson.SetBytes(raw, path.Name+"."+strconv.Itoa(k), f)
   453  						}
   454  					}
   455  				case []interface{}:
   456  					raw, err = sjson.SetBytes(raw, path.Name, values[key])
   457  				case bool:
   458  					v := values[key][len(values[key])-1]
   459  					if len(v) == 0 {
   460  						if !path.Required {
   461  							continue
   462  						}
   463  						v = "false"
   464  					}
   465  
   466  					if f, err := strconv.ParseBool(v); err != nil {
   467  						switch o.handleParseErrors {
   468  						case ParseErrorIgnoreConversionErrors:
   469  							raw, err = sjson.SetBytes(raw, path.Name, v)
   470  						case ParseErrorUseEmptyValueOnConversionErrors:
   471  							raw, err = sjson.SetBytes(raw, path.Name, f)
   472  						case ParseErrorReturnOnConversionErrors:
   473  							return nil, errors.WithStack(herodot.ErrBadRequest.WithReasonf("Expected value to be a boolean.").
   474  								WithDetail("parse_error", err.Error()).
   475  								WithDetail("name", key).
   476  								WithDetail("value", values.Get(key)))
   477  						}
   478  					} else {
   479  						raw, err = sjson.SetBytes(raw, path.Name, f)
   480  					}
   481  				case float64:
   482  					v := values.Get(key)
   483  					if len(v) == 0 {
   484  						if !path.Required {
   485  							continue
   486  						}
   487  						v = "0.0"
   488  					}
   489  
   490  					if f, err := strconv.ParseFloat(v, 64); err != nil {
   491  						switch o.handleParseErrors {
   492  						case ParseErrorIgnoreConversionErrors:
   493  							raw, err = sjson.SetBytes(raw, path.Name, v)
   494  						case ParseErrorUseEmptyValueOnConversionErrors:
   495  							raw, err = sjson.SetBytes(raw, path.Name, f)
   496  						case ParseErrorReturnOnConversionErrors:
   497  							return nil, errors.WithStack(herodot.ErrBadRequest.WithReasonf("Expected value to be a number.").
   498  								WithDetail("parse_error", err.Error()).
   499  								WithDetail("name", key).
   500  								WithDetail("value", values.Get(key)))
   501  						}
   502  					} else {
   503  						raw, err = sjson.SetBytes(raw, path.Name, f)
   504  					}
   505  				case string:
   506  					v := values.Get(key)
   507  					if len(v) == 0 {
   508  						continue
   509  					}
   510  
   511  					raw, err = sjson.SetBytes(raw, path.Name, v)
   512  				case map[string]interface{}:
   513  					v := values.Get(key)
   514  					if len(v) == 0 && !path.Required {
   515  						continue
   516  					}
   517  
   518  					raw, err = sjson.SetBytes(raw, path.Name, v)
   519  				case []map[string]interface{}:
   520  					raw, err = sjson.SetBytes(raw, path.Name, values[key])
   521  				}
   522  
   523  				if err != nil {
   524  					return nil, errors.WithStack(herodot.ErrBadRequest.WithReasonf("Unable to type assert values from HTTP Post Body: %s", err))
   525  				}
   526  				break
   527  			}
   528  		}
   529  	}
   530  
   531  	for _, path := range paths {
   532  		if path.TypeHint != jsonschemax.JSON {
   533  			continue
   534  		}
   535  
   536  		if !gjson.GetBytes(raw, path.Name).Exists() {
   537  			var err error
   538  			raw, err = sjson.SetRawBytes(raw, path.Name, []byte(`{}`))
   539  			if err != nil {
   540  				return nil, errors.WithStack(err)
   541  			}
   542  		}
   543  	}
   544  
   545  	return raw, nil
   546  }
   547  
   548  func (t *HTTP) decodeJSON(r *http.Request, destination interface{}, o *httpDecoderOptions, isRetry bool) error {
   549  	reader, err := t.requestBody(r, o)
   550  	if err != nil {
   551  		return err
   552  	}
   553  
   554  	raw, err := ioutil.ReadAll(reader)
   555  	if err != nil {
   556  		return errors.WithStack(herodot.ErrBadRequest.WithReasonf("Unable to read HTTP POST body: %s", err))
   557  	}
   558  
   559  	dc := json.NewDecoder(bytes.NewReader(raw))
   560  	if !isRetry {
   561  		dc.DisallowUnknownFields()
   562  	}
   563  	if err := json.NewDecoder(bytes.NewReader(raw)).Decode(destination); err != nil {
   564  		return errors.WithStack(herodot.ErrBadRequest.WithReasonf("Unable to decode JSON payload: %s", err))
   565  	}
   566  
   567  	if err := t.validatePayload(r.Context(), raw, o); err != nil {
   568  		if o.expectJSONFlattened && strings.Contains(err.Error(), "json: unknown field") && !isRetry {
   569  			return t.decodeJSONForm(r, destination, o)
   570  		}
   571  		return err
   572  	}
   573  
   574  	return nil
   575  }