|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268 |
- // Copyright 2015 Google Inc. All rights reserved.
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
-
- // Package fifo provides Group, which is a list of modifiers that are executed
- // consecutively. By default, when an error is returned by a modifier, the
- // execution of the modifiers is halted, and the error is returned. Optionally,
- // when errror aggregation is enabled (by calling SetAggretateErrors(true)), modifier
- // execution is not halted, and errors are aggretated and returned after all
- // modifiers have been executed.
- package fifo
-
- import (
- "encoding/json"
- "net/http"
- "sync"
-
- "github.com/google/martian"
- "github.com/google/martian/log"
- "github.com/google/martian/parse"
- "github.com/google/martian/verify"
- )
-
- // Group is a martian.RequestResponseModifier that maintains lists of
- // request and response modifiers executed on a first-in, first-out basis.
- type Group struct {
- reqmu sync.RWMutex
- reqmods []martian.RequestModifier
-
- resmu sync.RWMutex
- resmods []martian.ResponseModifier
-
- aggregateErrors bool
- }
-
- type groupJSON struct {
- Modifiers []json.RawMessage `json:"modifiers"`
- Scope []parse.ModifierType `json:"scope"`
- AggregateErrors bool `json:"aggregateErrors"`
- }
-
- func init() {
- parse.Register("fifo.Group", groupFromJSON)
- }
-
- // NewGroup returns a modifier group.
- func NewGroup() *Group {
- return &Group{}
- }
-
- // SetAggregateErrors sets the error behavior for the Group. When true, the Group will
- // continue to execute consecutive modifiers when a modifier in the group encounters an
- // error. The Group will then return all errors returned by each modifier after all
- // modifiers have been executed. When false, if an error is returned by a modifier, the
- // error is returned by ModifyRequest/Response and no further modifiers are run.
- // By default, error aggregation is disabled.
- func (g *Group) SetAggregateErrors(aggerr bool) {
- g.aggregateErrors = aggerr
- }
-
- // AddRequestModifier adds a RequestModifier to the group's list of request modifiers.
- func (g *Group) AddRequestModifier(reqmod martian.RequestModifier) {
- g.reqmu.Lock()
- defer g.reqmu.Unlock()
-
- g.reqmods = append(g.reqmods, reqmod)
- }
-
- // AddResponseModifier adds a ResponseModifier to the group's list of response modifiers.
- func (g *Group) AddResponseModifier(resmod martian.ResponseModifier) {
- g.resmu.Lock()
- defer g.resmu.Unlock()
-
- g.resmods = append(g.resmods, resmod)
- }
-
- // ModifyRequest modifies the request. By default, aggregateErrors is false; if an error is
- // returned by a RequestModifier the error is returned and no further modifiers are run. When
- // aggregateErrors is set to true, the errors returned by each modifier in the group are
- // aggregated.
- func (g *Group) ModifyRequest(req *http.Request) error {
- log.Debugf("fifo.ModifyRequest: %s", req.URL)
- g.reqmu.RLock()
- defer g.reqmu.RUnlock()
-
- merr := martian.NewMultiError()
-
- for _, reqmod := range g.reqmods {
- if err := reqmod.ModifyRequest(req); err != nil {
- if g.aggregateErrors {
- merr.Add(err)
- continue
- }
-
- return err
- }
- }
-
- if merr.Empty() {
- return nil
- }
-
- return merr
- }
-
- // ModifyResponse modifies the request. By default, aggregateErrors is false; if an error is
- // returned by a RequestModifier the error is returned and no further modifiers are run. When
- // aggregateErrors is set to true, the errors returned by each modifier in the group are
- // aggregated.
- func (g *Group) ModifyResponse(res *http.Response) error {
- requ := ""
- if res.Request != nil {
- requ = res.Request.URL.String()
- log.Debugf("fifo.ModifyResponse: %s", requ)
- }
- g.resmu.RLock()
- defer g.resmu.RUnlock()
-
- merr := martian.NewMultiError()
-
- for _, resmod := range g.resmods {
- if err := resmod.ModifyResponse(res); err != nil {
- if g.aggregateErrors {
- merr.Add(err)
- continue
- }
-
- return err
- }
- }
-
- if merr.Empty() {
- return nil
- }
-
- return merr
- }
-
- // VerifyRequests returns a MultiError containing all the
- // verification errors returned by request verifiers.
- func (g *Group) VerifyRequests() error {
- log.Debugf("fifo.VerifyRequests()")
- g.reqmu.Lock()
- defer g.reqmu.Unlock()
-
- merr := martian.NewMultiError()
- for _, reqmod := range g.reqmods {
- reqv, ok := reqmod.(verify.RequestVerifier)
- if !ok {
- continue
- }
-
- if err := reqv.VerifyRequests(); err != nil {
- merr.Add(err)
- }
- }
-
- if merr.Empty() {
- return nil
- }
-
- return merr
- }
-
- // VerifyResponses returns a MultiError containing all the
- // verification errors returned by response verifiers.
- func (g *Group) VerifyResponses() error {
- log.Debugf("fifo.VerifyResponses()")
- g.resmu.Lock()
- defer g.resmu.Unlock()
-
- merr := martian.NewMultiError()
- for _, resmod := range g.resmods {
- resv, ok := resmod.(verify.ResponseVerifier)
- if !ok {
- continue
- }
-
- if err := resv.VerifyResponses(); err != nil {
- merr.Add(err)
- }
- }
-
- if merr.Empty() {
- return nil
- }
-
- return merr
- }
-
- // ResetRequestVerifications resets the state of the contained request verifiers.
- func (g *Group) ResetRequestVerifications() {
- log.Debugf("fifo.ResetRequestVerifications()")
- g.reqmu.Lock()
- defer g.reqmu.Unlock()
-
- for _, reqmod := range g.reqmods {
- if reqv, ok := reqmod.(verify.RequestVerifier); ok {
- reqv.ResetRequestVerifications()
- }
- }
- }
-
- // ResetResponseVerifications resets the state of the contained request verifiers.
- func (g *Group) ResetResponseVerifications() {
- log.Debugf("fifo.ResetResponseVerifications()")
- g.resmu.Lock()
- defer g.resmu.Unlock()
-
- for _, resmod := range g.resmods {
- if resv, ok := resmod.(verify.ResponseVerifier); ok {
- resv.ResetResponseVerifications()
- }
- }
- }
-
- // groupFromJSON builds a fifo.Group from JSON.
- //
- // Example JSON:
- // {
- // "fifo.Group" : {
- // "scope": ["request", "result"],
- // "modifiers": [
- // { ... },
- // { ... },
- // ]
- // }
- // }
- func groupFromJSON(b []byte) (*parse.Result, error) {
- msg := &groupJSON{}
- if err := json.Unmarshal(b, msg); err != nil {
- return nil, err
- }
-
- g := NewGroup()
- if msg.AggregateErrors {
- g.SetAggregateErrors(true)
- }
-
- for _, m := range msg.Modifiers {
- r, err := parse.FromJSON(m)
- if err != nil {
- return nil, err
- }
-
- reqmod := r.RequestModifier()
- if reqmod != nil {
- g.AddRequestModifier(reqmod)
- }
-
- resmod := r.ResponseModifier()
- if resmod != nil {
- g.AddResponseModifier(resmod)
- }
- }
-
- return parse.NewResult(g, msg.Scope)
- }
|