From 3b405bd9f648aa54f7a903761265073779789356 Mon Sep 17 00:00:00 2001 From: pancho horrillo Date: Tue, 8 Oct 2019 15:06:08 +0200 Subject: [PATCH] Add ServeHTTP() method to swappableMux struct MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Roberto Abdelkader Martínez Pérez --- internal/server/user/mux.go | 11 ++-- internal/server/user/mux_test.go | 90 ++++++++++++++++++++++++++++---- 2 files changed, 89 insertions(+), 12 deletions(-) diff --git a/internal/server/user/mux.go b/internal/server/user/mux.go index c47aaca..fb3919d 100644 --- a/internal/server/user/mux.go +++ b/internal/server/user/mux.go @@ -1,6 +1,7 @@ package user import ( + "net/http" "sync" "github.com/gorilla/mux" @@ -8,18 +9,22 @@ import ( type swappableMux struct { m sync.RWMutex - root mux.Router + root *mux.Router } -func (sm *swappableMux) get() mux.Router { +func (sm *swappableMux) get() *mux.Router { sm.m.RLock() defer sm.m.RUnlock() return sm.root } -func (sm *swappableMux) set(mux mux.Router) { +func (sm *swappableMux) set(mux *mux.Router) { sm.m.Lock() sm.root = mux sm.m.Unlock() } + +func (sm *swappableMux) ServeHTTP(w http.ResponseWriter, r *http.Request) { + sm.get().ServeHTTP(w, r) +} diff --git a/internal/server/user/mux_test.go b/internal/server/user/mux_test.go index b7c96d2..08c8459 100644 --- a/internal/server/user/mux_test.go +++ b/internal/server/user/mux_test.go @@ -1,6 +1,8 @@ package user import ( + "net/http" + "net/http/httptest" "reflect" "testing" "time" @@ -30,7 +32,7 @@ func TestSwappableMuxGetWaitsForTheMutexToBeReleased(t *testing.T) { sm.m.Lock() defer sm.m.Unlock() - c := make(chan mux.Router) + c := make(chan *mux.Router) go func() { c <- sm.get() }() time.Sleep(10 * time.Millisecond) @@ -48,7 +50,7 @@ func TestSwappableMuxGetIsAbleToReadWhileOthersAreReading(t *testing.T) { sm.m.RLock() defer sm.m.RUnlock() - c := make(chan mux.Router) + c := make(chan *mux.Router) go func() { c <- sm.get() }() time.Sleep(10 * time.Millisecond) @@ -62,7 +64,7 @@ func TestSwappableMuxGetIsAbleToReadWhileOthersAreReading(t *testing.T) { func TestSwappableMuxSetSetsTheGivenMux(t *testing.T) { sm := swappableMux{} - mux := mux.Router{ + mux := &mux.Router{ KeepContext: true, } @@ -74,14 +76,14 @@ func TestSwappableMuxSetSetsTheGivenMux(t *testing.T) { } } -func TestSwappableMuxSetSetsADifferentInstance(t *testing.T) { +func TestSwappableMuxSetSetsTheSameInstance(t *testing.T) { sm := swappableMux{} - mux := mux.Router{} + mux := &mux.Router{} sm.set(mux) - if &mux == &sm.root { - t.Error("Set mux is the same instance") + if mux != sm.root { + t.Error("Set mux is not the same instance") } } @@ -92,7 +94,7 @@ func TestSwappableMuxSetWaitsForWriterToReleaseMutex(t *testing.T) { defer sm.m.Unlock() c := make(chan bool) - go func() { sm.set(mux.Router{}); c <- true }() + go func() { sm.set(&mux.Router{}); c <- true }() time.Sleep(10 * time.Millisecond) @@ -110,7 +112,7 @@ func TestSwappableMuxSetWaitsForReadersToReleaseMutex(t *testing.T) { defer sm.m.RUnlock() c := make(chan bool) - go func() { sm.set(mux.Router{}); c <- true }() + go func() { sm.set(&mux.Router{}); c <- true }() time.Sleep(10 * time.Millisecond) @@ -120,3 +122,73 @@ func TestSwappableMuxSetWaitsForReadersToReleaseMutex(t *testing.T) { default: } } + +func TestServeHTTPCallsInnerMux(t *testing.T) { + called := false + + mux := &mux.Router{} + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { called = true }) + + sm := swappableMux{root: mux} + + req := httptest.NewRequest("GET", "/", nil) + w := httptest.NewRecorder() + + sm.ServeHTTP(w, req) + + if !called { + t.Error("Inner mux wasn't called") + } +} + +// TODO: test that a read lock does not impede calling ServeHTTP + +func TestServeHTTPCanServeWhenMuxIsReadLocked(t *testing.T) { + called := false + + mux := &mux.Router{} + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { called = true }) + + sm := swappableMux{root: mux} + sm.m.RLock() + + req := httptest.NewRequest("GET", "/", nil) + w := httptest.NewRecorder() + + go sm.ServeHTTP(w, req) + + time.Sleep(10 * time.Millisecond) + + if !called { + t.Error("Inner mux not called while mutex is read locked") + } +} + +func TestServeHTTPCallsInnerMuxAfterAcquiringLock(t *testing.T) { + called := false + + mux := &mux.Router{} + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { called = true }) + + sm := swappableMux{root: mux} + sm.m.Lock() + + req := httptest.NewRequest("GET", "/", nil) + w := httptest.NewRecorder() + + go sm.ServeHTTP(w, req) + + time.Sleep(10 * time.Millisecond) + + if called { + t.Fatal("Mutex not acquired") + } + + sm.m.Unlock() + + time.Sleep(10 * time.Millisecond) + + if !called { + t.Error("Inner mux wasn't called after mutex released") + } +}