github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/apiserver/apiserverhttp/mux.go (about) 1 // Copyright 2018 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package apiserverhttp 5 6 import ( 7 "net/http" 8 "sync" 9 10 "github.com/bmizerany/pat" 11 "github.com/juju/errors" 12 ) 13 14 // Mux is a pattern-based HTTP muxer, based on top of 15 // bmizerany/pat, adding support for dynamic registration 16 // and deregistration of handlers. When a handler is 17 // added or removed, the underlying pat mux is swapped 18 // out with a new one. 19 // 20 // Adding and removing handlers is expensive: each of those 21 // calls will create a new mux underneath. These operations 22 // are not expected to be frequently occurring. 23 type Mux struct { 24 pmu sync.Mutex 25 p *pat.PatternServeMux 26 27 // mu protects added; added records the handlers 28 // added by AddHandler, so we can recreate the 29 // mux as necessary. The handlers are recorded 30 // in the order they are added, per method, as 31 // is done by pat. 32 mu sync.Mutex 33 added map[string][]patternHandler 34 35 // Clients who are using the mux can add themselves to prevent the 36 // httpserver from stopping until they're done. 37 clients sync.WaitGroup 38 } 39 40 type patternHandler struct { 41 pat string 42 h http.Handler 43 } 44 45 // NewMux returns a new, empty mux. 46 func NewMux(opts ...muxOption) *Mux { 47 m := &Mux{ 48 p: pat.New(), 49 added: make(map[string][]patternHandler), 50 } 51 for _, opt := range opts { 52 opt(m) 53 } 54 return m 55 } 56 57 type muxOption func(*Mux) 58 59 // ServeHTTP is part of the http.Handler interface. 60 // 61 // ServeHTTP routes the request to a handler registered with 62 // AddHandler, according to the rules defined by bmizerany/pat. 63 func (m *Mux) ServeHTTP(w http.ResponseWriter, r *http.Request) { 64 m.pmu.Lock() 65 p := m.p 66 m.pmu.Unlock() 67 p.ServeHTTP(w, r) 68 } 69 70 // AddHandler adds an http.Handler for the given method and pattern. 71 // AddHandler returns an error if there already exists a handler for 72 // the method and pattern. 73 // 74 // This is safe to call concurrently with m.ServeHTTP and m.RemoveHandler. 75 func (m *Mux) AddHandler(meth, pat string, h http.Handler) error { 76 m.mu.Lock() 77 defer m.mu.Unlock() 78 for _, ph := range m.added[meth] { 79 if ph.pat == pat { 80 return errors.AlreadyExistsf("handler for %s %q", meth, pat) 81 } 82 } 83 m.added[meth] = append(m.added[meth], patternHandler{pat, h}) 84 m.recreate() 85 return nil 86 } 87 88 // RemoveHandler removes the http.Handler for the given method and pattern, 89 // if any. If there is no handler registered with the method and pattern, 90 // this is a no-op. 91 // 92 // This is safe to call concurrently with m.ServeHTTP and m.AddHandler. 93 func (m *Mux) RemoveHandler(meth, pat string) { 94 m.mu.Lock() 95 defer m.mu.Unlock() 96 phs, ok := m.added[meth] 97 if !ok { 98 return 99 } 100 for i, ph := range phs { 101 if ph.pat != pat { 102 continue 103 } 104 head, tail := phs[:i], phs[i+1:] 105 m.added[meth] = append(head, tail...) 106 m.recreate() 107 return 108 } 109 } 110 111 // AddClient tells the mux there's another client that should keep it 112 // alive. 113 func (m *Mux) AddClient() { 114 m.clients.Add(1) 115 } 116 117 // ClientDone indicates that a client has finished and no longer needs 118 // the mux. 119 func (m *Mux) ClientDone() { 120 m.clients.Done() 121 } 122 123 // Wait will block until all of the clients have indicated that 124 // they're done. 125 func (m *Mux) Wait() { 126 m.clients.Wait() 127 } 128 129 func (m *Mux) recreate() { 130 p := pat.New() 131 for meth, phs := range m.added { 132 for _, ph := range phs { 133 p.Add(meth, ph.pat, ph.h) 134 } 135 } 136 m.pmu.Lock() 137 m.p = p 138 m.pmu.Unlock() 139 }