Refactor HTTPHeaderModifiers.MergeDefaults based on feedback

This commit is contained in:
Paul Banks 2021-08-27 15:28:13 +01:00
parent 9e4e204e96
commit 1079089f20
1 changed files with 45 additions and 44 deletions

View File

@ -677,8 +677,8 @@ type ServiceSplit struct {
// MergeParent is called by the discovery chain compiler when a split directs to // MergeParent is called by the discovery chain compiler when a split directs to
// another splitter. We refer to the first ServiceSplit as the parent and the // another splitter. We refer to the first ServiceSplit as the parent and the
// ServiceSplits of the second splitter as its children. The parent ends up // ServiceSplits of the second splitter as its children. The parent ends up
// "flattened" by the compiler, i.e. replaced with it's children recursively // "flattened" by the compiler, i.e. replaced with its children recursively with
// with the weights modified as necessary. // the weights modified as necessary.
// //
// Since the parent is never included in the output, any request processing // Since the parent is never included in the output, any request processing
// config attached to it (e.g. header manipulation) would be lost and not take // config attached to it (e.g. header manipulation) would be lost and not take
@ -721,7 +721,7 @@ func (s *ServiceSplit) MergeParent(parent *ServiceSplit) (*ServiceSplit, error)
} }
// Merge any request handling from parent _unless_ it's overridden by us. // Merge any request handling from parent _unless_ it's overridden by us.
copy.RequestHeaders, err = s.RequestHeaders.MergeDefaults(parentReq) copy.RequestHeaders, err = MergeHTTPHeaderModifiers(parentReq, s.RequestHeaders)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -735,7 +735,7 @@ func (s *ServiceSplit) MergeParent(parent *ServiceSplit) (*ServiceSplit, error)
// time since responses flow the other way so the unflattened behavior would // time since responses flow the other way so the unflattened behavior would
// be that the parent processing happens _after_ ours potentially overriding // be that the parent processing happens _after_ ours potentially overriding
// it. // it.
copy.ResponseHeaders, err = parentResp.MergeDefaults(s.ResponseHeaders) copy.ResponseHeaders, err = MergeHTTPHeaderModifiers(s.ResponseHeaders, parentResp)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -1580,46 +1580,6 @@ func (m *HTTPHeaderModifiers) Validate(protocol string) error {
return nil return nil
} }
// MergeDefaults takes another HTTPHeaderModifiers and merges it's fields. The
// fields from this object take precedence over the passed in defaults if there
// is a collision. The resulting object is returned leaving both m and defaults
// unchanged. The semantics in the case of `Add` are that our Add will override
// the default if they affect the same key since we have no way to express
// multiple adds to the same key. We could change that, but it makes the config
// syntax more complex for a huge edgecase.
func (m *HTTPHeaderModifiers) MergeDefaults(defaults *HTTPHeaderModifiers) (*HTTPHeaderModifiers, error) {
if defaults.IsZero() {
return m.Clone()
}
if m == nil {
return defaults.Clone()
}
res, err := defaults.Clone()
if err != nil {
return nil, err
}
for k, v := range m.Add {
res.Add[k] = v
}
for k, v := range m.Set {
res.Set[k] = v
}
// Deduplicate removes.
removed := make(map[string]struct{})
for _, k := range res.Remove {
removed[k] = struct{}{}
}
for _, k := range m.Remove {
if _, ok := removed[k]; !ok {
res.Remove = append(res.Remove, k)
}
}
return res, nil
}
// Clone returns a deep-copy of m unless m is nil // Clone returns a deep-copy of m unless m is nil
func (m *HTTPHeaderModifiers) Clone() (*HTTPHeaderModifiers, error) { func (m *HTTPHeaderModifiers) Clone() (*HTTPHeaderModifiers, error) {
if m == nil { if m == nil {
@ -1633,3 +1593,44 @@ func (m *HTTPHeaderModifiers) Clone() (*HTTPHeaderModifiers, error) {
m = cpy.(*HTTPHeaderModifiers) m = cpy.(*HTTPHeaderModifiers)
return m, nil return m, nil
} }
// MergeHTTPHeaderModifiers takes a base HTTPHeaderModifiers and merges in field
// defined in overrides. Precedence is given to the overrides field if there is
// a collision. The resulting object is returned leaving both base and overrides
// unchanged. The `Add` field in override also replaces same-named keys of base
// since we have no way to express multiple adds to the same key. We could
// change that, but it makes the config syntax more complex for a huge edgecase.
func MergeHTTPHeaderModifiers(base, overrides *HTTPHeaderModifiers) (*HTTPHeaderModifiers, error) {
if base.IsZero() {
return overrides.Clone()
}
merged, err := base.Clone()
if err != nil {
return nil, err
}
if overrides.IsZero() {
return merged, nil
}
for k, v := range overrides.Add {
merged.Add[k] = v
}
for k, v := range overrides.Set {
merged.Set[k] = v
}
// Deduplicate removes.
removed := make(map[string]struct{})
for _, k := range merged.Remove {
removed[k] = struct{}{}
}
for _, k := range overrides.Remove {
if _, ok := removed[k]; !ok {
merged.Remove = append(merged.Remove, k)
}
}
return merged, nil
}