go.uber.org/yarpc@v1.72.1/internal/inboundmiddleware/chain.go (about) 1 // Copyright (c) 2022 Uber Technologies, Inc. 2 // 3 // Permission is hereby granted, free of charge, to any person obtaining a copy 4 // of this software and associated documentation files (the "Software"), to deal 5 // in the Software without restriction, including without limitation the rights 6 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 // copies of the Software, and to permit persons to whom the Software is 8 // furnished to do so, subject to the following conditions: 9 // 10 // The above copyright notice and this permission notice shall be included in 11 // all copies or substantial portions of the Software. 12 // 13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 // THE SOFTWARE. 20 21 package inboundmiddleware 22 23 import ( 24 "context" 25 26 "go.uber.org/yarpc/api/middleware" 27 "go.uber.org/yarpc/api/transport" 28 ) 29 30 // UnaryChain combines a series of `UnaryInbound`s into a single `InboundMiddleware`. 31 func UnaryChain(mw ...middleware.UnaryInbound) middleware.UnaryInbound { 32 unchained := make([]middleware.UnaryInbound, 0, len(mw)) 33 for _, m := range mw { 34 if m == nil { 35 continue 36 } 37 if c, ok := m.(unaryChain); ok { 38 unchained = append(unchained, c...) 39 continue 40 } 41 unchained = append(unchained, m) 42 } 43 44 switch len(unchained) { 45 case 0: 46 return middleware.NopUnaryInbound 47 case 1: 48 return unchained[0] 49 default: 50 return unaryChain(unchained) 51 } 52 } 53 54 type unaryChain []middleware.UnaryInbound 55 56 func (c unaryChain) Handle(ctx context.Context, req *transport.Request, resw transport.ResponseWriter, h transport.UnaryHandler) error { 57 return unaryChainExec{ 58 Chain: []middleware.UnaryInbound(c), 59 Final: h, 60 }.Handle(ctx, req, resw) 61 } 62 63 // unaryChainExec adapts a series of `UnaryInbound`s into a UnaryHandler. 64 // It is scoped to a single request to the `Handler` and is not thread-safe. 65 type unaryChainExec struct { 66 Chain []middleware.UnaryInbound 67 Final transport.UnaryHandler 68 } 69 70 func (x unaryChainExec) Handle(ctx context.Context, req *transport.Request, resw transport.ResponseWriter) error { 71 if len(x.Chain) == 0 { 72 return x.Final.Handle(ctx, req, resw) 73 } 74 next := x.Chain[0] 75 x.Chain = x.Chain[1:] 76 return next.Handle(ctx, req, resw, x) 77 } 78 79 // OnewayChain combines a series of `OnewayInbound`s into a single `InboundMiddleware`. 80 func OnewayChain(mw ...middleware.OnewayInbound) middleware.OnewayInbound { 81 unchained := make([]middleware.OnewayInbound, 0, len(mw)) 82 for _, m := range mw { 83 if m == nil { 84 continue 85 } 86 if c, ok := m.(onewayChain); ok { 87 unchained = append(unchained, c...) 88 continue 89 } 90 unchained = append(unchained, m) 91 } 92 93 switch len(unchained) { 94 case 0: 95 return middleware.NopOnewayInbound 96 case 1: 97 return unchained[0] 98 default: 99 return onewayChain(unchained) 100 } 101 } 102 103 type onewayChain []middleware.OnewayInbound 104 105 func (c onewayChain) HandleOneway(ctx context.Context, req *transport.Request, h transport.OnewayHandler) error { 106 return onewayChainExec{ 107 Chain: []middleware.OnewayInbound(c), 108 Final: h, 109 }.HandleOneway(ctx, req) 110 } 111 112 // onewayChainExec adapts a series of `OnewayInbound`s into a OnewayHandler. 113 // It is scoped to a single request to the `Handler` and is not thread-safe. 114 type onewayChainExec struct { 115 Chain []middleware.OnewayInbound 116 Final transport.OnewayHandler 117 } 118 119 func (x onewayChainExec) HandleOneway(ctx context.Context, req *transport.Request) error { 120 if len(x.Chain) == 0 { 121 return x.Final.HandleOneway(ctx, req) 122 } 123 next := x.Chain[0] 124 x.Chain = x.Chain[1:] 125 return next.HandleOneway(ctx, req, x) 126 } 127 128 // StreamChain combines a series of `StreamInbound`s into a single `InboundMiddleware`. 129 func StreamChain(mw ...middleware.StreamInbound) middleware.StreamInbound { 130 unchained := make([]middleware.StreamInbound, 0, len(mw)) 131 for _, m := range mw { 132 if m == nil { 133 continue 134 } 135 if c, ok := m.(streamChain); ok { 136 unchained = append(unchained, c...) 137 continue 138 } 139 unchained = append(unchained, m) 140 } 141 142 switch len(unchained) { 143 case 0: 144 return middleware.NopStreamInbound 145 case 1: 146 return unchained[0] 147 default: 148 return streamChain(unchained) 149 } 150 } 151 152 type streamChain []middleware.StreamInbound 153 154 func (c streamChain) HandleStream(s *transport.ServerStream, h transport.StreamHandler) error { 155 return streamChainExec{ 156 Chain: []middleware.StreamInbound(c), 157 Final: h, 158 }.HandleStream(s) 159 } 160 161 // streamChainExec adapts a series of `StreamInbound`s into a StreamHandler. 162 // It is scoped to a single request to the `Handler` and is not thread-safe. 163 type streamChainExec struct { 164 Chain []middleware.StreamInbound 165 Final transport.StreamHandler 166 } 167 168 func (x streamChainExec) HandleStream(s *transport.ServerStream) error { 169 if len(x.Chain) == 0 { 170 return x.Final.HandleStream(s) 171 } 172 next := x.Chain[0] 173 x.Chain = x.Chain[1:] 174 return next.HandleStream(s, x) 175 }