// Tests for Keep HTTP handlers:
//
-// GetBlockHandler
-// PutBlockHandler
-// IndexHandler
+// - GetBlockHandler
+// - PutBlockHandler
+// - IndexHandler
//
// The HTTP handlers are responsible for enforcing permission policy,
// so these tests must exercise all possible permission permutations.
-package main
+package keepstore
import (
"bytes"
// - permissions on, unauthenticated request, signed locator
// - permissions on, authenticated request, expired locator
// - permissions on, authenticated request, signed locator, transient error from backend
-//
func (s *HandlerSuite) TestGetHandler(c *check.C) {
c.Assert(s.handler.setup(context.Background(), s.cluster, "", prometheus.NewRegistry(), testServiceURL), check.IsNil)
// - no server key
// - with server key, authenticated request, unsigned locator
// - with server key, unauthenticated request, unsigned locator
-//
func (s *HandlerSuite) TestPutHandler(c *check.C) {
c.Assert(s.handler.setup(context.Background(), s.cluster, "", prometheus.NewRegistry(), testServiceURL), check.IsNil)
//
// The only /index requests that should succeed are those issued by the
// superuser. They should pass regardless of the value of BlobSigning.
-//
func (s *HandlerSuite) TestIndexHandler(c *check.C) {
c.Assert(s.handler.setup(context.Background(), s.cluster, "", prometheus.NewRegistry(), testServiceURL), check.IsNil)
//
// Cases tested:
//
-// With no token and with a non-data-manager token:
-// * Delete existing block
-// (test for 403 Forbidden, confirm block not deleted)
-//
-// With data manager token:
+// With no token and with a non-data-manager token:
+// * Delete existing block
+// (test for 403 Forbidden, confirm block not deleted)
//
-// * Delete existing block
-// (test for 200 OK, response counts, confirm block deleted)
+// With data manager token:
//
-// * Delete nonexistent block
-// (test for 200 OK, response counts)
+// * Delete existing block
+// (test for 200 OK, response counts, confirm block deleted)
//
-// TODO(twp):
+// * Delete nonexistent block
+// (test for 200 OK, response counts)
//
-// * Delete block on read-only and read-write volume
-// (test for 200 OK, response with copies_deleted=1,
-// copies_failed=1, confirm block deleted only on r/w volume)
+// TODO(twp):
//
-// * Delete block on read-only volume only
-// (test for 200 OK, response with copies_deleted=0, copies_failed=1,
-// confirm block not deleted)
+// * Delete block on read-only and read-write volume
+// (test for 200 OK, response with copies_deleted=1,
+// copies_failed=1, confirm block deleted only on r/w volume)
//
+// * Delete block on read-only volume only
+// (test for 200 OK, response with copies_deleted=0, copies_failed=1,
+// confirm block not deleted)
func (s *HandlerSuite) TestDeleteHandler(c *check.C) {
c.Assert(s.handler.setup(context.Background(), s.cluster, "", prometheus.NewRegistry(), testServiceURL), check.IsNil)
// Cases tested: syntactically valid and invalid pull lists, from the
// data manager and from unprivileged users:
//
-// 1. Valid pull list from an ordinary user
-// (expected result: 401 Unauthorized)
+// 1. Valid pull list from an ordinary user
+// (expected result: 401 Unauthorized)
//
-// 2. Invalid pull request from an ordinary user
-// (expected result: 401 Unauthorized)
+// 2. Invalid pull request from an ordinary user
+// (expected result: 401 Unauthorized)
//
-// 3. Valid pull request from the data manager
-// (expected result: 200 OK with request body "Received 3 pull
-// requests"
+// 3. Valid pull request from the data manager
+// (expected result: 200 OK with request body "Received 3 pull
+// requests"
//
-// 4. Invalid pull request from the data manager
-// (expected result: 400 Bad Request)
+// 4. Invalid pull request from the data manager
+// (expected result: 400 Bad Request)
//
// Test that in the end, the pull manager received a good pull list with
// the expected number of requests.
// TODO(twp): test concurrency: launch 100 goroutines to update the
// pull list simultaneously. Make sure that none of them return 400
// Bad Request and that pullq.GetList() returns a valid list.
-//
func (s *HandlerSuite) TestPullHandler(c *check.C) {
c.Assert(s.handler.setup(context.Background(), s.cluster, "", prometheus.NewRegistry(), testServiceURL), check.IsNil)
// Cases tested: syntactically valid and invalid trash lists, from the
// data manager and from unprivileged users:
//
-// 1. Valid trash list from an ordinary user
-// (expected result: 401 Unauthorized)
+// 1. Valid trash list from an ordinary user
+// (expected result: 401 Unauthorized)
//
-// 2. Invalid trash list from an ordinary user
-// (expected result: 401 Unauthorized)
+// 2. Invalid trash list from an ordinary user
+// (expected result: 401 Unauthorized)
//
-// 3. Valid trash list from the data manager
-// (expected result: 200 OK with request body "Received 3 trash
-// requests"
+// 3. Valid trash list from the data manager
+// (expected result: 200 OK with request body "Received 3 trash
+// requests"
//
-// 4. Invalid trash list from the data manager
-// (expected result: 400 Bad Request)
+// 4. Invalid trash list from the data manager
+// (expected result: 400 Bad Request)
//
// Test that in the end, the trash collector received a good list
// trash list with the expected number of requests.
// TODO(twp): test concurrency: launch 100 goroutines to update the
// pull list simultaneously. Make sure that none of them return 400
// Bad Request and that replica.Dump() returns a valid list.
-//
func (s *HandlerSuite) TestTrashHandler(c *check.C) {
c.Assert(s.handler.setup(context.Background(), s.cluster, "", prometheus.NewRegistry(), testServiceURL), check.IsNil)
// Replace the router's trashq -- which the worker goroutines
}
}
-type notifyingResponseRecorder struct {
- *httptest.ResponseRecorder
- closer chan bool
-}
-
-func (r *notifyingResponseRecorder) CloseNotify() <-chan bool {
- return r.closer
-}
-
func (s *HandlerSuite) TestGetHandlerClientDisconnect(c *check.C) {
s.cluster.Collections.BlobSigning = false
c.Assert(s.handler.setup(context.Background(), s.cluster, "", prometheus.NewRegistry(), testServiceURL), check.IsNil)
bufs = newBufferPool(ctxlog.TestLogger(c), 1, BlockSize)
defer bufs.Put(bufs.Get(BlockSize))
- if err := s.handler.volmgr.AllWritable()[0].Put(context.Background(), TestHash, TestBlock); err != nil {
- c.Error(err)
- }
-
- resp := ¬ifyingResponseRecorder{
- ResponseRecorder: httptest.NewRecorder(),
- closer: make(chan bool, 1),
- }
- if _, ok := http.ResponseWriter(resp).(http.CloseNotifier); !ok {
- c.Fatal("notifyingResponseRecorder is broken")
- }
- // If anyone asks, the client has disconnected.
- resp.closer <- true
+ err := s.handler.volmgr.AllWritable()[0].Put(context.Background(), TestHash, TestBlock)
+ c.Assert(err, check.IsNil)
+ resp := httptest.NewRecorder()
ok := make(chan struct{})
go func() {
- req, _ := http.NewRequest("GET", fmt.Sprintf("/%s+%d", TestHash, len(TestBlock)), nil)
+ ctx, cancel := context.WithCancel(context.Background())
+ req, _ := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("/%s+%d", TestHash, len(TestBlock)), nil)
+ cancel()
s.handler.ServeHTTP(resp, req)
ok <- struct{}{}
}()
case <-ok:
}
- ExpectStatusCode(c, "client disconnect", http.StatusServiceUnavailable, resp.ResponseRecorder)
+ ExpectStatusCode(c, "client disconnect", http.StatusServiceUnavailable, resp)
for i, v := range s.handler.volmgr.AllWritable() {
if calls := v.Volume.(*MockVolume).called["GET"]; calls != 0 {
c.Errorf("volume %d got %d calls, expected 0", i, calls)