21225: Skip clipboard test in firefox
[arvados.git] / services / keepstore / router_test.go
index f4bcdd4ae4df19ba8baccb87e7775282957b7fcb..215033b48ed9f00054b1da713a890b96691f4610 100644 (file)
@@ -78,22 +78,26 @@ func (s *routerSuite) TestBlockRead_Token(c *C) {
        resp := call(router, "GET", "http://example/"+locSigned, "", nil, nil)
        c.Check(resp.Code, Equals, http.StatusUnauthorized)
        c.Check(resp.Body.String(), Matches, "no token provided in Authorization header\n")
+       checkCORSHeaders(c, resp.Header())
 
        // Different token => invalid signature
        resp = call(router, "GET", "http://example/"+locSigned, "badtoken", nil, nil)
        c.Check(resp.Code, Equals, http.StatusBadRequest)
        c.Check(resp.Body.String(), Equals, "invalid signature\n")
+       checkCORSHeaders(c, resp.Header())
 
        // Correct token
        resp = call(router, "GET", "http://example/"+locSigned, arvadostest.ActiveTokenV2, nil, nil)
        c.Check(resp.Code, Equals, http.StatusOK)
        c.Check(resp.Body.String(), Equals, "foo")
+       checkCORSHeaders(c, resp.Header())
 
        // HEAD
        resp = call(router, "HEAD", "http://example/"+locSigned, arvadostest.ActiveTokenV2, nil, nil)
        c.Check(resp.Code, Equals, http.StatusOK)
        c.Check(resp.Result().ContentLength, Equals, int64(3))
        c.Check(resp.Body.String(), Equals, "")
+       checkCORSHeaders(c, resp.Header())
 }
 
 // As a special case we allow HEAD requests that only provide a hash
@@ -165,13 +169,16 @@ func (s *routerSuite) TestBlockRead_ChecksumMismatch(c *C) {
                }
                c.Check(resp.Body.Len(), Not(Equals), len(gooddata))
                c.Check(resp.Result().ContentLength, Equals, int64(len(gooddata)))
+               checkCORSHeaders(c, resp.Header())
 
                resp = call(router, "HEAD", "http://example/"+locSigned, arvadostest.ActiveTokenV2, nil, nil)
                c.Check(resp.Code, Equals, http.StatusBadGateway)
+               checkCORSHeaders(c, resp.Header())
 
                hashSigned := router.keepstore.signLocator(arvadostest.ActiveTokenV2, hash)
                resp = call(router, "HEAD", "http://example/"+hashSigned, arvadostest.ActiveTokenV2, nil, nil)
                c.Check(resp.Code, Equals, http.StatusBadGateway)
+               checkCORSHeaders(c, resp.Header())
        }
 }
 
@@ -181,6 +188,7 @@ func (s *routerSuite) TestBlockWrite(c *C) {
 
        resp := call(router, "PUT", "http://example/"+fooHash, arvadostest.ActiveTokenV2, []byte("foo"), nil)
        c.Check(resp.Code, Equals, http.StatusOK)
+       checkCORSHeaders(c, resp.Header())
        locator := strings.TrimSpace(resp.Body.String())
 
        resp = call(router, "GET", "http://example/"+locator, arvadostest.ActiveTokenV2, nil, nil)
@@ -192,7 +200,7 @@ func (s *routerSuite) TestBlockWrite_Headers(c *C) {
        router, cancel := testRouter(c, s.cluster, nil)
        defer cancel()
 
-       resp := call(router, "PUT", "http://example/"+fooHash, arvadostest.ActiveTokenV2, []byte("foo"), http.Header{"X-Arvados-Replicas-Desired": []string{"2"}})
+       resp := call(router, "PUT", "http://example/"+fooHash, arvadostest.ActiveTokenV2, []byte("foo"), http.Header{"X-Keep-Desired-Replicas": []string{"2"}})
        c.Check(resp.Code, Equals, http.StatusOK)
        c.Check(resp.Header().Get("X-Keep-Replicas-Stored"), Equals, "1")
        c.Check(sortCommaSeparated(resp.Header().Get("X-Keep-Storage-Classes-Confirmed")), Equals, "testclass1=1")
@@ -268,7 +276,7 @@ func (s *routerSuite) TestBlockTrash(c *C) {
        resp := call(router, "DELETE", "http://example/"+fooHash+"+3", s.cluster.SystemRootToken, nil, nil)
        c.Check(resp.Code, Equals, http.StatusOK)
        c.Check(vol0.stubLog.String(), Matches, `(?ms).* trash .*`)
-       _, err = vol0.BlockRead(context.Background(), fooHash, io.Discard)
+       err = vol0.BlockRead(context.Background(), fooHash, brdiscard)
        c.Assert(err, Equals, os.ErrNotExist)
 }
 
@@ -281,12 +289,12 @@ func (s *routerSuite) TestBlockUntrash(c *C) {
        c.Assert(err, IsNil)
        err = vol0.BlockTrash(fooHash)
        c.Assert(err, IsNil)
-       _, err = vol0.BlockRead(context.Background(), fooHash, io.Discard)
+       err = vol0.BlockRead(context.Background(), fooHash, brdiscard)
        c.Assert(err, Equals, os.ErrNotExist)
        resp := call(router, "PUT", "http://example/untrash/"+fooHash+"+3", s.cluster.SystemRootToken, nil, nil)
        c.Check(resp.Code, Equals, http.StatusOK)
        c.Check(vol0.stubLog.String(), Matches, `(?ms).* untrash .*`)
-       _, err = vol0.BlockRead(context.Background(), fooHash, io.Discard)
+       err = vol0.BlockRead(context.Background(), fooHash, brdiscard)
        c.Check(err, IsNil)
 }
 
@@ -356,8 +364,8 @@ func (s *routerSuite) TestRequireAdminMgtToken(c *C) {
 func (s *routerSuite) TestVolumeErrorStatusCode(c *C) {
        router, cancel := testRouter(c, s.cluster, nil)
        defer cancel()
-       router.keepstore.mountsW[0].volume.(*stubVolume).blockRead = func(_ context.Context, hash string, w io.Writer) (int, error) {
-               return 0, httpserver.ErrorWithStatus(errors.New("test error"), http.StatusBadGateway)
+       router.keepstore.mountsW[0].volume.(*stubVolume).blockRead = func(_ context.Context, hash string, w io.WriterAt) error {
+               return httpserver.ErrorWithStatus(errors.New("test error"), http.StatusBadGateway)
        }
 
        // To test whether we fall back to volume 1 after volume 0
@@ -373,6 +381,13 @@ func (s *routerSuite) TestVolumeErrorStatusCode(c *C) {
        c.Check(resp.Code, Equals, http.StatusBadGateway)
        c.Check(resp.Body.String(), Equals, "test error\n")
 
+       router.keepstore.mountsW[0].volume.(*stubVolume).blockRead = func(_ context.Context, hash string, w io.WriterAt) error {
+               return errors.New("no http status provided")
+       }
+       resp = call(router, "GET", "http://example/"+locSigned, arvadostest.ActiveTokenV2, nil, nil)
+       c.Check(resp.Code, Equals, http.StatusInternalServerError)
+       c.Check(resp.Body.String(), Equals, "no http status provided\n")
+
        c.Assert(router.keepstore.mountsW[1].volume.BlockWrite(context.Background(), barHash, []byte("bar")), IsNil)
 
        // If the requested block is available on the second volume,
@@ -462,7 +477,6 @@ func (s *routerSuite) TestIndex(c *C) {
                c.Check(resp.Code, Equals, http.StatusOK)
                c.Check(strings.Split(resp.Body.String(), "\n"), HasLen, 5)
        }
-
 }
 
 // Check that the context passed to a volume method gets cancelled
@@ -472,10 +486,10 @@ func (s *routerSuite) TestCancelOnDisconnect(c *C) {
        defer cancel()
 
        unblock := make(chan struct{})
-       router.keepstore.mountsW[0].volume.(*stubVolume).blockRead = func(ctx context.Context, hash string, w io.Writer) (int, error) {
+       router.keepstore.mountsW[0].volume.(*stubVolume).blockRead = func(ctx context.Context, hash string, w io.WriterAt) error {
                <-unblock
                c.Check(ctx.Err(), NotNil)
-               return 0, ctx.Err()
+               return ctx.Err()
        }
        go func() {
                time.Sleep(time.Second / 10)
@@ -493,6 +507,19 @@ func (s *routerSuite) TestCancelOnDisconnect(c *C) {
        c.Check(resp.Code, Equals, 499)
 }
 
+func (s *routerSuite) TestCORSPreflight(c *C) {
+       router, cancel := testRouter(c, s.cluster, nil)
+       defer cancel()
+
+       for _, path := range []string{"/", "/whatever", "/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+123"} {
+               c.Logf("=== %s", path)
+               resp := call(router, http.MethodOptions, "http://example"+path, arvadostest.ActiveTokenV2, nil, nil)
+               c.Check(resp.Code, Equals, http.StatusOK)
+               c.Check(resp.Body.String(), Equals, "")
+               checkCORSHeaders(c, resp.Header())
+       }
+}
+
 func call(handler http.Handler, method, path, tok string, body []byte, hdr http.Header) *httptest.ResponseRecorder {
        resp := httptest.NewRecorder()
        req, err := http.NewRequest(method, path, bytes.NewReader(body))
@@ -508,3 +535,10 @@ func call(handler http.Handler, method, path, tok string, body []byte, hdr http.
        handler.ServeHTTP(resp, req)
        return resp
 }
+
+func checkCORSHeaders(c *C, h http.Header) {
+       c.Check(h.Get("Access-Control-Allow-Methods"), Equals, "GET, HEAD, PUT, OPTIONS")
+       c.Check(h.Get("Access-Control-Allow-Origin"), Equals, "*")
+       c.Check(h.Get("Access-Control-Allow-Headers"), Equals, "Authorization, Content-Length, Content-Type, X-Keep-Desired-Replicas, X-Keep-Signature, X-Keep-Storage-Classes")
+       c.Check(h.Get("Access-Control-Expose-Headers"), Equals, "X-Keep-Locator, X-Keep-Replicas-Stored, X-Keep-Storage-Classes-Confirmed")
+}