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) }