github.com/wfusion/gofusion@v1.1.14/common/infra/asynq/servemux.go (about) 1 // Copyright 2020 Kentaro Hibino. All rights reserved. 2 // Use of this source code is governed by a MIT license 3 // that can be found in the LICENSE file. 4 5 package asynq 6 7 import ( 8 "context" 9 "fmt" 10 "sort" 11 "strings" 12 "sync" 13 ) 14 15 // ServeMux is a multiplexer for asynchronous tasks. 16 // It matches the type of each task against a list of registered patterns 17 // and calls the handler for the pattern that most closely matches the 18 // task's type name. 19 // 20 // Longer patterns take precedence over shorter ones, so that if there are 21 // handlers registered for both "images" and "images:thumbnails", 22 // the latter handler will be called for tasks with a type name beginning with 23 // "images:thumbnails" and the former will receive tasks with type name beginning 24 // with "images". 25 type ServeMux struct { 26 mu sync.RWMutex 27 m map[string]muxEntry 28 es []muxEntry // slice of entries sorted from longest to shortest. 29 mws []MiddlewareFunc 30 } 31 32 type muxEntry struct { 33 h Handler 34 pattern string 35 } 36 37 // MiddlewareFunc is a function which receives an asynq.Handler and returns another asynq.Handler. 38 // Typically, the returned handler is a closure which does something with the context and task passed 39 // to it, and then calls the handler passed as parameter to the MiddlewareFunc. 40 type MiddlewareFunc func(Handler) Handler 41 42 // NewServeMux allocates and returns a new ServeMux. 43 func NewServeMux() *ServeMux { 44 return new(ServeMux) 45 } 46 47 // ProcessTask dispatches the task to the handler whose 48 // pattern most closely matches the task type. 49 func (mux *ServeMux) ProcessTask(ctx context.Context, task *Task) error { 50 h, _ := mux.Handler(task) 51 return h.ProcessTask(ctx, task) 52 } 53 54 // Handler returns the handler to use for the given task. 55 // It always return a non-nil handler. 56 // 57 // Handler also returns the registered pattern that matches the task. 58 // 59 // If there is no registered handler that applies to the task, 60 // handler returns a 'not found' handler which returns an error. 61 func (mux *ServeMux) Handler(t *Task) (h Handler, pattern string) { 62 mux.mu.RLock() 63 defer mux.mu.RUnlock() 64 65 h, pattern = mux.match(t.Type()) 66 if h == nil { 67 h, pattern = NotFoundHandler(), "" 68 } 69 for i := len(mux.mws) - 1; i >= 0; i-- { 70 h = mux.mws[i](h) 71 } 72 return h, pattern 73 } 74 75 // Find a handler on a handler map given a typename string. 76 // Most-specific (longest) pattern wins. 77 func (mux *ServeMux) match(typename string) (h Handler, pattern string) { 78 // Check for exact match first. 79 v, ok := mux.m[typename] 80 if ok { 81 return v.h, v.pattern 82 } 83 84 // Check for longest valid match. 85 // mux.es contains all patterns from longest to shortest. 86 for _, e := range mux.es { 87 if strings.HasPrefix(typename, e.pattern) { 88 return e.h, e.pattern 89 } 90 } 91 return nil, "" 92 93 } 94 95 // Handle registers the handler for the given pattern. 96 // If a handler already exists for pattern, Handle panics. 97 func (mux *ServeMux) Handle(pattern string, handler Handler) { 98 mux.mu.Lock() 99 defer mux.mu.Unlock() 100 101 if strings.TrimSpace(pattern) == "" { 102 panic("asynq: invalid pattern") 103 } 104 if handler == nil { 105 panic("asynq: nil handler") 106 } 107 if _, exist := mux.m[pattern]; exist { 108 panic("asynq: multiple registrations for " + pattern) 109 } 110 111 if mux.m == nil { 112 mux.m = make(map[string]muxEntry) 113 } 114 e := muxEntry{h: handler, pattern: pattern} 115 mux.m[pattern] = e 116 mux.es = appendSorted(mux.es, e) 117 } 118 119 func appendSorted(es []muxEntry, e muxEntry) []muxEntry { 120 n := len(es) 121 i := sort.Search(n, func(i int) bool { 122 return len(es[i].pattern) < len(e.pattern) 123 }) 124 if i == n { 125 return append(es, e) 126 } 127 // we now know that i points at where we want to insert. 128 es = append(es, muxEntry{}) // try to grow the slice in place, any entry works. 129 copy(es[i+1:], es[i:]) // shift shorter entries down. 130 es[i] = e 131 return es 132 } 133 134 // HandleFunc registers the handler function for the given pattern. 135 func (mux *ServeMux) HandleFunc(pattern string, handler func(context.Context, *Task) error) { 136 if handler == nil { 137 panic("asynq: nil handler") 138 } 139 mux.Handle(pattern, HandlerFunc(handler)) 140 } 141 142 // Use appends a MiddlewareFunc to the chain. 143 // Middlewares are executed in the order that they are applied to the ServeMux. 144 func (mux *ServeMux) Use(mws ...MiddlewareFunc) { 145 mux.mu.Lock() 146 defer mux.mu.Unlock() 147 mux.mws = append(mux.mws, mws...) 148 } 149 150 // NotFound returns an error indicating that the handler was not found for the given task. 151 func NotFound(ctx context.Context, task *Task) error { 152 return fmt.Errorf("handler not found for task %q", task.Type()) 153 } 154 155 // NotFoundHandler returns a simple task handler that returns a ``not found`` error. 156 func NotFoundHandler() Handler { return HandlerFunc(NotFound) }