github.com/google/martian/v3@v3.3.3/priority/priority_group.go (about) 1 // Copyright 2015 Google Inc. All rights reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // Package priority allows grouping modifiers and applying them in priority order. 16 package priority 17 18 import ( 19 "encoding/json" 20 "errors" 21 "net/http" 22 "sync" 23 24 "github.com/google/martian/v3" 25 "github.com/google/martian/v3/parse" 26 ) 27 28 var ( 29 // ErrModifierNotFound is the error returned when attempting to remove a 30 // modifier when the modifier does not exist in the group. 31 ErrModifierNotFound = errors.New("modifier not found in group") 32 ) 33 34 // priorityRequestModifier is a request modifier with a priority. 35 type priorityRequestModifier struct { 36 reqmod martian.RequestModifier 37 priority int64 38 } 39 40 // priorityResponseModifier is a response modifier with a priority. 41 type priorityResponseModifier struct { 42 resmod martian.ResponseModifier 43 priority int64 44 } 45 46 // Group is a group of request and response modifiers ordered by their priority. 47 type Group struct { 48 reqmu sync.RWMutex 49 reqmods []*priorityRequestModifier 50 51 resmu sync.RWMutex 52 resmods []*priorityResponseModifier 53 } 54 55 type groupJSON struct { 56 Modifiers []modifierJSON `json:"modifiers"` 57 Scope []parse.ModifierType `json:"scope"` 58 } 59 60 type modifierJSON struct { 61 Priority int64 `json:"priority"` 62 Modifier json.RawMessage `json:"modifier"` 63 } 64 65 func init() { 66 parse.Register("priority.Group", groupFromJSON) 67 } 68 69 // NewGroup returns a priority group. 70 func NewGroup() *Group { 71 return &Group{} 72 } 73 74 // AddRequestModifier adds a RequestModifier with the given priority. 75 // 76 // If a modifier is added with a priority that is equal to an existing priority 77 // the newer modifier will be added before the existing modifier in the chain. 78 func (pg *Group) AddRequestModifier(reqmod martian.RequestModifier, priority int64) { 79 pg.reqmu.Lock() 80 defer pg.reqmu.Unlock() 81 82 preqmod := &priorityRequestModifier{ 83 reqmod: reqmod, 84 priority: priority, 85 } 86 87 for i, m := range pg.reqmods { 88 if preqmod.priority >= m.priority { 89 pg.reqmods = append(pg.reqmods, nil) 90 copy(pg.reqmods[i+1:], pg.reqmods[i:]) 91 pg.reqmods[i] = preqmod 92 return 93 } 94 } 95 96 // Either this is the first modifier in the list, or the priority is less 97 // than all existing modifiers. 98 pg.reqmods = append(pg.reqmods, preqmod) 99 } 100 101 // RemoveRequestModifier removes the the highest priority given RequestModifier. 102 // Returns ErrModifierNotFound if the given modifier does not exist in the group. 103 func (pg *Group) RemoveRequestModifier(reqmod martian.RequestModifier) error { 104 pg.reqmu.Lock() 105 defer pg.reqmu.Unlock() 106 107 for i, m := range pg.reqmods { 108 if m.reqmod == reqmod { 109 copy(pg.reqmods[i:], pg.reqmods[i+1:]) 110 pg.reqmods[len(pg.reqmods)-1] = nil 111 pg.reqmods = pg.reqmods[:len(pg.reqmods)-1] 112 return nil 113 } 114 } 115 116 return ErrModifierNotFound 117 } 118 119 // AddResponseModifier adds a ResponseModifier with the given priority. 120 // 121 // If a modifier is added with a priority that is equal to an existing priority 122 // the newer modifier will be added before the existing modifier in the chain. 123 func (pg *Group) AddResponseModifier(resmod martian.ResponseModifier, priority int64) { 124 pg.resmu.Lock() 125 defer pg.resmu.Unlock() 126 127 presmod := &priorityResponseModifier{ 128 resmod: resmod, 129 priority: priority, 130 } 131 132 for i, m := range pg.resmods { 133 if presmod.priority >= m.priority { 134 pg.resmods = append(pg.resmods, nil) 135 copy(pg.resmods[i+1:], pg.resmods[i:]) 136 pg.resmods[i] = presmod 137 return 138 } 139 } 140 141 // Either this is the first modifier in the list, or the priority is less 142 // than all existing modifiers. 143 pg.resmods = append(pg.resmods, presmod) 144 } 145 146 // RemoveResponseModifier removes the the highest priority given ResponseModifier. 147 // Returns ErrModifierNotFound if the given modifier does not exist in the group. 148 func (pg *Group) RemoveResponseModifier(resmod martian.ResponseModifier) error { 149 pg.resmu.Lock() 150 defer pg.resmu.Unlock() 151 152 for i, m := range pg.resmods { 153 if m.resmod == resmod { 154 copy(pg.resmods[i:], pg.resmods[i+1:]) 155 pg.resmods[len(pg.resmods)-1] = nil 156 pg.resmods = pg.resmods[:len(pg.resmods)-1] 157 return nil 158 } 159 } 160 161 return ErrModifierNotFound 162 } 163 164 // ModifyRequest modifies the request. Modifiers are run in descending order of 165 // their priority. If an error is returned by a RequestModifier the error is 166 // returned and no further modifiers are run. 167 func (pg *Group) ModifyRequest(req *http.Request) error { 168 pg.reqmu.RLock() 169 defer pg.reqmu.RUnlock() 170 171 for _, m := range pg.reqmods { 172 if err := m.reqmod.ModifyRequest(req); err != nil { 173 return err 174 } 175 } 176 177 return nil 178 } 179 180 // ModifyResponse modifies the response. Modifiers are run in descending order 181 // of their priority. If an error is returned by a ResponseModifier the error 182 // is returned and no further modifiers are run. 183 func (pg *Group) ModifyResponse(res *http.Response) error { 184 pg.resmu.RLock() 185 defer pg.resmu.RUnlock() 186 187 for _, m := range pg.resmods { 188 if err := m.resmod.ModifyResponse(res); err != nil { 189 return err 190 } 191 } 192 193 return nil 194 } 195 196 // groupFromJSON builds a priority.Group from JSON. 197 // 198 // Example JSON: 199 // { 200 // "priority.Group": { 201 // "scope": ["request", "response"], 202 // "modifiers": [ 203 // { 204 // "priority": 100, // Will run first. 205 // "modifier": { ... }, 206 // }, 207 // { 208 // "priority": 0, // Will run last. 209 // "modifier": { ... }, 210 // } 211 // ] 212 // } 213 // } 214 func groupFromJSON(b []byte) (*parse.Result, error) { 215 msg := &groupJSON{} 216 if err := json.Unmarshal(b, msg); err != nil { 217 return nil, err 218 } 219 220 pg := NewGroup() 221 222 for _, m := range msg.Modifiers { 223 r, err := parse.FromJSON(m.Modifier) 224 if err != nil { 225 return nil, err 226 } 227 228 reqmod := r.RequestModifier() 229 if reqmod != nil { 230 pg.AddRequestModifier(reqmod, m.Priority) 231 } 232 233 resmod := r.ResponseModifier() 234 if resmod != nil { 235 pg.AddResponseModifier(resmod, m.Priority) 236 } 237 } 238 239 return parse.NewResult(pg, msg.Scope) 240 }