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  }