Merge branch '21666-provision-test-improvement'
[arvados.git] / lib / controller / router / request.go
index 39b4c5100608ebf6a14b250ec26185886aed3ad3..68fffa0681333dc1dfead29eefc45c528424abd4 100644 (file)
@@ -55,15 +55,31 @@ func guessAndParse(k, v string) (interface{}, error) {
        // foo=["bar","baz"]?
 }
 
-// Parse req as an Arvados V1 API request and return the request
-// parameters.
+// Return a map of incoming HTTP request parameters. Also load
+// parameters into opts, unless opts is nil.
 //
 // If the request has a parameter whose name is attrsKey (e.g.,
 // "collection"), it is renamed to "attrs".
-func (rtr *router) loadRequestParams(req *http.Request, attrsKey string) (map[string]interface{}, error) {
+func (rtr *router) loadRequestParams(req *http.Request, attrsKey string, opts interface{}) (map[string]interface{}, error) {
+       // Here we call ParseForm and ParseMultipartForm explicitly
+       // (even though ParseMultipartForm calls ParseForm if
+       // necessary) to ensure we catch errors encountered in
+       // ParseForm. In the non-multipart-form case,
+       // ParseMultipartForm returns ErrNotMultipart and hides the
+       // ParseForm error.
        err := req.ParseForm()
+       if err == nil {
+               err = req.ParseMultipartForm(int64(rtr.config.MaxRequestSize))
+               if err == http.ErrNotMultipart {
+                       err = nil
+               }
+       }
        if err != nil {
-               return nil, httpError(http.StatusBadRequest, err)
+               if err.Error() == "http: request body too large" {
+                       return nil, httpError(http.StatusRequestEntityTooLarge, err)
+               } else {
+                       return nil, httpError(http.StatusBadRequest, err)
+               }
        }
        params := map[string]interface{}{}
 
@@ -137,6 +153,24 @@ func (rtr *router) loadRequestParams(req *http.Request, attrsKey string) (map[st
                }
        }
 
+       if opts != nil {
+               // Load all path, query, and form params into opts.
+               err = rtr.transcode(params, opts)
+               if err != nil {
+                       return nil, fmt.Errorf("transcode: %w", err)
+               }
+
+               // Special case: if opts has Method or Header fields, load the
+               // request method/header.
+               err = rtr.transcode(struct {
+                       Method string
+                       Header http.Header
+               }{req.Method, req.Header}, opts)
+               if err != nil {
+                       return nil, fmt.Errorf("transcode: %w", err)
+               }
+       }
+
        return params, nil
 }
 
@@ -169,6 +203,10 @@ var boolParams = map[string]bool{
        "include_old_versions":    true,
        "redirect_to_new_user":    true,
        "send_notification_email": true,
+       "bypass_federation":       true,
+       "recursive":               true,
+       "exclude_home_project":    true,
+       "no_forward":              true,
 }
 
 func stringToBool(s string) bool {