github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/apiserver/apiserverhttp/mux_test.go (about) 1 // Copyright 2018 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package apiserverhttp_test 5 6 import ( 7 "net/http" 8 "net/http/httptest" 9 "sync" 10 "time" 11 12 "github.com/juju/testing" 13 jc "github.com/juju/testing/checkers" 14 gc "gopkg.in/check.v1" 15 16 "github.com/juju/juju/apiserver/apiserverhttp" 17 coretesting "github.com/juju/juju/testing" 18 ) 19 20 type MuxSuite struct { 21 testing.IsolationSuite 22 mux *apiserverhttp.Mux 23 server *httptest.Server 24 client *http.Client 25 } 26 27 var _ = gc.Suite(&MuxSuite{}) 28 29 func (s *MuxSuite) SetUpTest(c *gc.C) { 30 s.IsolationSuite.SetUpTest(c) 31 s.mux = apiserverhttp.NewMux() 32 s.server = httptest.NewServer(s.mux) 33 s.client = s.server.Client() 34 s.AddCleanup(func(c *gc.C) { 35 s.server.Close() 36 }) 37 } 38 39 func (s *MuxSuite) TestNotFound(c *gc.C) { 40 resp, err := s.client.Get(s.server.URL + "/") 41 c.Assert(err, jc.ErrorIsNil) 42 defer resp.Body.Close() 43 44 c.Assert(resp.StatusCode, gc.Equals, http.StatusNotFound) 45 } 46 47 func (s *MuxSuite) TestAddHandler(c *gc.C) { 48 err := s.mux.AddHandler("GET", "/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})) 49 c.Assert(err, jc.ErrorIsNil) 50 51 resp, err := s.client.Get(s.server.URL + "/") 52 c.Assert(err, jc.ErrorIsNil) 53 defer resp.Body.Close() 54 55 c.Assert(resp.StatusCode, gc.Equals, http.StatusOK) 56 } 57 58 func (s *MuxSuite) TestAddRemoveNotFound(c *gc.C) { 59 s.mux.AddHandler("GET", "/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})) 60 s.mux.RemoveHandler("GET", "/") 61 62 resp, err := s.client.Get(s.server.URL + "/") 63 c.Assert(err, jc.ErrorIsNil) 64 defer resp.Body.Close() 65 66 c.Assert(resp.StatusCode, gc.Equals, http.StatusNotFound) 67 } 68 69 func (s *MuxSuite) TestAddHandlerExists(c *gc.C) { 70 s.mux.AddHandler("GET", "/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})) 71 err := s.mux.AddHandler("GET", "/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})) 72 c.Assert(err, gc.ErrorMatches, `handler for GET "/" already exists`) 73 } 74 75 func (s *MuxSuite) TestRemoveHandlerMissing(c *gc.C) { 76 s.mux.RemoveHandler("GET", "/") // no-op 77 } 78 79 func (s *MuxSuite) TestMethodNotSupported(c *gc.C) { 80 s.mux.AddHandler("POST", "/", http.NotFoundHandler()) 81 resp, err := s.client.Get(s.server.URL + "/") 82 c.Assert(err, jc.ErrorIsNil) 83 defer resp.Body.Close() 84 85 c.Assert(resp.StatusCode, gc.Equals, http.StatusMethodNotAllowed) 86 } 87 88 func (s *MuxSuite) TestConcurrentAddHandler(c *gc.C) { 89 err := s.mux.AddHandler("GET", "/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})) 90 c.Assert(err, jc.ErrorIsNil) 91 92 // Concurrently add and remove another handler to show that 93 // adding and removing handlers will not race with request 94 // handling. 95 const N = 1000 96 var wg sync.WaitGroup 97 wg.Add(1) 98 go func() { 99 defer wg.Done() 100 for i := 0; i < N; i++ { 101 s.mux.AddHandler("POST", "/", http.NotFoundHandler()) 102 s.mux.RemoveHandler("POST", "/") 103 } 104 }() 105 defer wg.Wait() 106 107 for i := 0; i < N; i++ { 108 resp, err := s.client.Get(s.server.URL + "/") 109 c.Assert(err, jc.ErrorIsNil) 110 resp.Body.Close() 111 c.Assert(resp.StatusCode, gc.Equals, http.StatusOK) 112 } 113 } 114 115 func (s *MuxSuite) TestConcurrentRemoveHandler(c *gc.C) { 116 h := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}) 117 118 // Concurrently add and remove another handler to show that 119 // adding and removing handlers will not race with request 120 // handling. 121 const N = 500 122 var wg sync.WaitGroup 123 wg.Add(1) 124 go func() { 125 defer wg.Done() 126 for i := 0; i < N; i++ { 127 s.mux.AddHandler("GET", "/", h) 128 // Sleep to give the client a 129 // chance to hit the endpoint. 130 time.Sleep(time.Millisecond) 131 s.mux.RemoveHandler("GET", "/") 132 } 133 }() 134 defer wg.Wait() 135 136 var ok, notfound int 137 for i := 0; i < N; i++ { 138 resp, err := s.client.Get(s.server.URL + "/") 139 c.Assert(err, jc.ErrorIsNil) 140 resp.Body.Close() 141 switch resp.StatusCode { 142 case http.StatusOK: 143 ok++ 144 case http.StatusNotFound: 145 notfound++ 146 default: 147 c.Fatalf( 148 "got status %d, expected %d or %d", 149 resp.StatusCode, 150 http.StatusOK, 151 http.StatusNotFound, 152 ) 153 } 154 time.Sleep(time.Millisecond) 155 } 156 c.Assert(ok, gc.Not(gc.Equals), 0) 157 c.Assert(notfound, gc.Not(gc.Equals), 0) 158 } 159 160 func (s *MuxSuite) TestWait(c *gc.C) { 161 // Check that mux.Wait() blocks until clients are all finished 162 // with it. 163 s.mux.AddClient() 164 s.mux.AddClient() 165 finished := make(chan struct{}) 166 go func() { 167 defer close(finished) 168 s.mux.Wait() 169 }() 170 171 select { 172 case <-finished: 173 c.Fatalf("should wait when there are clients") 174 case <-time.After(coretesting.ShortWait): 175 } 176 177 s.mux.ClientDone() 178 select { 179 case <-finished: 180 c.Fatalf("should wait when there is still a client") 181 case <-time.After(coretesting.ShortWait): 182 } 183 184 s.mux.ClientDone() 185 select { 186 case <-finished: 187 case <-time.After(coretesting.LongWait): 188 c.Fatalf("should finish once clients are done") 189 } 190 }