github.com/lingyao2333/mo-zero@v1.4.1/rest/chain/chain.go (about)

     1  package chain
     2  
     3  // This is a modified version of https://github.com/justinas/alice
     4  // The original code is licensed under the MIT license.
     5  // It's modified for couple reasons:
     6  // - Added the Chain interface
     7  // - Added support for the Chain.Prepend(...) method
     8  
     9  import "net/http"
    10  
    11  type (
    12  	// Chain defines a chain of middleware.
    13  	Chain interface {
    14  		Append(middlewares ...Middleware) Chain
    15  		Prepend(middlewares ...Middleware) Chain
    16  		Then(h http.Handler) http.Handler
    17  		ThenFunc(fn http.HandlerFunc) http.Handler
    18  	}
    19  
    20  	// Middleware is an HTTP middleware.
    21  	Middleware func(http.Handler) http.Handler
    22  
    23  	// chain acts as a list of http.Handler middlewares.
    24  	// chain is effectively immutable:
    25  	// once created, it will always hold
    26  	// the same set of middlewares in the same order.
    27  	chain struct {
    28  		middlewares []Middleware
    29  	}
    30  )
    31  
    32  // New creates a new Chain, memorizing the given list of middleware middlewares.
    33  // New serves no other function, middlewares are only called upon a call to Then() or ThenFunc().
    34  func New(middlewares ...Middleware) Chain {
    35  	return chain{middlewares: append(([]Middleware)(nil), middlewares...)}
    36  }
    37  
    38  // Append extends a chain, adding the specified middlewares as the last ones in the request flow.
    39  //
    40  //     c := chain.New(m1, m2)
    41  //     c.Append(m3, m4)
    42  //     // requests in c go m1 -> m2 -> m3 -> m4
    43  func (c chain) Append(middlewares ...Middleware) Chain {
    44  	return chain{middlewares: join(c.middlewares, middlewares)}
    45  }
    46  
    47  // Prepend extends a chain by adding the specified chain as the first one in the request flow.
    48  //
    49  //     c := chain.New(m3, m4)
    50  //     c1 := chain.New(m1, m2)
    51  //     c.Prepend(c1)
    52  //     // requests in c go m1 -> m2 -> m3 -> m4
    53  func (c chain) Prepend(middlewares ...Middleware) Chain {
    54  	return chain{middlewares: join(middlewares, c.middlewares)}
    55  }
    56  
    57  // Then chains the middleware and returns the final http.Handler.
    58  //     New(m1, m2, m3).Then(h)
    59  // is equivalent to:
    60  //     m1(m2(m3(h)))
    61  // When the request comes in, it will be passed to m1, then m2, then m3
    62  // and finally, the given handler
    63  // (assuming every middleware calls the following one).
    64  //
    65  // A chain can be safely reused by calling Then() several times.
    66  //     stdStack := chain.New(ratelimitHandler, csrfHandler)
    67  //     indexPipe = stdStack.Then(indexHandler)
    68  //     authPipe = stdStack.Then(authHandler)
    69  // Note that middlewares are called on every call to Then() or ThenFunc()
    70  // and thus several instances of the same middleware will be created
    71  // when a chain is reused in this way.
    72  // For proper middleware, this should cause no problems.
    73  //
    74  // Then() treats nil as http.DefaultServeMux.
    75  func (c chain) Then(h http.Handler) http.Handler {
    76  	if h == nil {
    77  		h = http.DefaultServeMux
    78  	}
    79  
    80  	for i := range c.middlewares {
    81  		h = c.middlewares[len(c.middlewares)-1-i](h)
    82  	}
    83  
    84  	return h
    85  }
    86  
    87  // ThenFunc works identically to Then, but takes
    88  // a HandlerFunc instead of a Handler.
    89  //
    90  // The following two statements are equivalent:
    91  //     c.Then(http.HandlerFunc(fn))
    92  //     c.ThenFunc(fn)
    93  //
    94  // ThenFunc provides all the guarantees of Then.
    95  func (c chain) ThenFunc(fn http.HandlerFunc) http.Handler {
    96  	// This nil check cannot be removed due to the "nil is not nil" common mistake in Go.
    97  	// Required due to: https://stackoverflow.com/questions/33426977/how-to-golang-check-a-variable-is-nil
    98  	if fn == nil {
    99  		return c.Then(nil)
   100  	}
   101  	return c.Then(fn)
   102  }
   103  
   104  func join(a, b []Middleware) []Middleware {
   105  	mids := make([]Middleware, 0, len(a)+len(b))
   106  	mids = append(mids, a...)
   107  	mids = append(mids, b...)
   108  	return mids
   109  }