13497: Avoid misdirecting POST to GET when request has double slash.
authorTom Clegg <tclegg@veritasgenetics.com>
Thu, 12 Jul 2018 20:50:59 +0000 (16:50 -0400)
committerTom Clegg <tclegg@veritasgenetics.com>
Fri, 13 Jul 2018 14:54:51 +0000 (10:54 -0400)
Arvados-DCO-1.1-Signed-off-by: Tom Clegg <tclegg@veritasgenetics.com>

lib/controller/handler.go
services/keep-web/cache.go
services/keep-web/cadaver_test.go

index 30994130d5a3312de1a2485cc426076467cd1a55..a1a69a88e4ccd1bbb6d4882620581e91b3a03523 100644 (file)
@@ -30,6 +30,20 @@ type Handler struct {
 
 func (h *Handler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
        h.setupOnce.Do(h.setup)
+       if req.Method != "GET" && req.Method != "HEAD" {
+               // http.ServeMux returns 301 with a cleaned path if
+               // the incoming request has a double slash. Some
+               // clients (including the Go standard library) change
+               // the request method to GET when following a 301
+               // redirect if the original method was not HEAD
+               // (RFC7231 6.4.2 specifically allows this in the case
+               // of POST). Thus "POST //foo" gets misdirected to
+               // "GET /foo". To avoid this, eliminate double slashes
+               // before passing the request to ServeMux.
+               for strings.Contains(req.URL.Path, "//") {
+                       req.URL.Path = strings.Replace(req.URL.Path, "//", "/", -1)
+               }
+       }
        h.handlerStack.ServeHTTP(w, req)
 }
 
index 9ee99903c8d1e537d487a67d1c77d848fc93c807..59e8de3bc9f884dec899e22072c3afe684aceb1a 100644 (file)
@@ -99,7 +99,7 @@ func (c *cache) Update(client *arvados.Client, coll arvados.Collection, fs arvad
        }
        var updated arvados.Collection
        defer c.pdhs.Remove(coll.UUID)
-       err := client.RequestAndDecode(&updated, "PATCH", "/arvados/v1/collections/"+coll.UUID, client.UpdateBody(coll), nil)
+       err := client.RequestAndDecode(&updated, "PATCH", "arvados/v1/collections/"+coll.UUID, client.UpdateBody(coll), nil)
        if err == nil {
                c.collections.Add(client.AuthToken+"\000"+coll.PortableDataHash, &cachedCollection{
                        expire:     time.Now().Add(time.Duration(c.TTL)),
index 3814a459d53c46c8b92d7dc40d8fd8cd13ee6ae4..0e2f17c35b85df02b98df4d3e29a974d18deb17d 100644 (file)
@@ -74,7 +74,7 @@ func (s *IntegrationSuite) testCadaver(c *check.C, password string, pathFunc fun
        var newCollection arvados.Collection
        arv := arvados.NewClientFromEnv()
        arv.AuthToken = arvadostest.ActiveToken
-       err = arv.RequestAndDecode(&newCollection, "POST", "/arvados/v1/collections", bytes.NewBufferString(url.Values{"collection": {"{}"}}.Encode()), nil)
+       err = arv.RequestAndDecode(&newCollection, "POST", "arvados/v1/collections", bytes.NewBufferString(url.Values{"collection": {"{}"}}.Encode()), nil)
        c.Assert(err, check.IsNil)
 
        readPath, writePath, pdhPath := pathFunc(newCollection)