Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add append header modifier #247

Merged
merged 8 commits into from
Jun 1, 2018
Merged

Add append header modifier #247

merged 8 commits into from
Jun 1, 2018

Conversation

hnovikov
Copy link
Contributor

No description provided.

return m.setOrAddHeader(proxyutil.ResponseHeader(res))
}

func (m *modifier) setOrAddHeader(h *proxyutil.Header) error {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no need to pull this out in a function - it's fine to just have this in ModifyRequest / ModifyResponse

func (m *modifier) setOrAddHeader(h *proxyutil.Header) error {
if m.add {
return h.Add(m.name, m.value)
} else {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

don't need the "else" - have the Set happen in the normal flow

}

// NewModifier returns a modifier that will set the header at name with
// the given value for both requests and responses. If the header name already
// exists all values will be overwritten.
func NewModifier(name, value string) martian.RequestResponseModifier {
func NewModifier(name, value string, add bool) martian.RequestResponseModifier {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rather than have this in the constructor, just let the default behavior be Set (same as current)

Add a func on modifier that changes it called SetBehavior

@@ -29,31 +29,42 @@ func init() {

type modifier struct {
name, value string
add bool
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm trying to think of a better way to name / represent this...

We have 2 "modes" - Append or Replace - so maybe instead of the add bool, we could have a Mode that's an int that represents the modes that are defined with something like:

const (
  ReplaceValues = iota
  AppendValues
)

and then the struct could be:

type modifier struct {
  name, value string
  behavior    int
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What would that look like on the wire?

{
  "name": ...
 ...
  "behavior": "replace" | "append"
}

?

Another option might be to still have a boolean flag but invert it and default it to true

{
  "name": ...
  ...
  "replace": false
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I was thinking there would be a "behavior" key that was either "replace" or "append"

Your modifierJSON struct would have a

Behavior string `json:"behavior"`

and then in modifierFromJSON you'd have a check like

if msg.Behavior == "append" {
  modifier.SetBehavior(AppendValues)
}

@@ -27,31 +27,52 @@ func init() {
parse.Register("header.Modifier", modifierFromJSON)
}

type modifier struct {
type HeaderSetBehavior int
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

type HeaderSetBehavior int

const (
ReplaceValues HeaderSetBehavior = iota
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AppendValues
)

type Modifier struct {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

func NewModifier(name, value string) martian.RequestResponseModifier {
return &modifier{
func NewModifier(name, value string) *Modifier {
return &Modifier{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

set the default behavior here:

mod := &Modifier{Name: http.CanonicalHeaderKey(name), Value: value}
mod.SetBehavior(ReplaceValues)
return mod

}

func (m *Modifier) SetBehavior(b HeaderSetBehavior) {
m.behavior = b
}

// NewModifier returns a modifier that will set the header at name with
// the given value for both requests and responses. If the header name already
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

update godoc to say that "by default, if header name already exists..."

@@ -74,5 +95,13 @@ func modifierFromJSON(b []byte) (*parse.Result, error) {

modifier := NewModifier(msg.Name, msg.Value)

if msg.Behavior == "" || msg.Behavior == "replace" {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if the default behavior is set in NewModifier, you should only need to do:

if strings.ToLower(msg.Behavior) == "append" {
  modifier.SetBehavior(AppendValues)
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could just assume that anything that isn't append gives you the default replace behavior.

But, if you'd like to be explicit and return an error on a value that is neither append or default, use a switch:

switch strings.ToLower(msg.Behavior) {
case "append":
  modifier.SetBehavior(AppendValues)
case "replace":
  modifier.SetBehavior(ReplaceValues)
default:
  return nil, fmt.Errorf("Invalid header modifier behavior %q. Must be either %q or %q", msg.Behavior, "append", "replace")
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Went with a switch here, I think silently ignoring the other val would be annoying to debug

return h.Set(m.name, m.value)
}

func (m *Modifier) SetBehavior(b HeaderSetBehavior) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@@ -27,13 +27,17 @@ func init() {
parse.Register("header.Modifier", modifierFromJSON)
}

// Behavior when setting a new header with regard to duplicate header names.
type HeaderSetBehavior int
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment should be of the form:
HeaderSetBehavior sets the behavior of...

type HeaderSetBehavior int

const (
// Replace all previous headers with the same canonicalized name.
ReplaceValues HeaderSetBehavior = iota
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment should be of the form:
ReplaceValues replaces ...

ReplaceValues HeaderSetBehavior = iota
// Append new header without replacing existing ones.
AppendValues
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment should be of the form:
AppendValues appends...

if msg.Behavior == "" || msg.Behavior == "replace" {
modifier.SetBehavior(ReplaceValues)
} else if msg.Behavior == "append" {
switch msg.Behavior {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

downcase this with strings.ToLower(msg.Behavior)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we have any other case insensitive tokens in the Martian API? I'd argue against that sort of behavior only in one place (e.g. why make the behavior enumeration case insensitive and not the "behavior" key itself, or all the JSON keys for that matter?) especially since the API errors if the enumeration doesn't match one of the known values.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah ok yeah that's fair

} else if msg.Behavior == "append" {
switch msg.Behavior {
case "":
fallthrough
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we never really use fallthrough do this in Martian, instead:

case "append", "":
  modifier.SetBehavior(AppendValues)

@bramhaghosh
Copy link
Member

from golint:

header/header_modifier.go:31:6: type name will be used as header.HeaderSetBehavior by other packages, and that stutters; consider calling this SetBehavior

@hnovikov
Copy link
Contributor Author

Makes the header set behavior setter's signature sorta interesting

func (m *Modifier) SetBehavior(b SetBehavior) {

What about DuplicateBehavior?

// DuplicateBehavior defines the behavior of setting a new header that already exists.
type DuplicateBehavior int

const (
        // ReplaceValues replaces all previous headers with the same canonicalized name.
        ReplaceValues SetBehavior = iota
        // AppendValues appends the new header without replacing existing ones.
        AppendValues
)

func (m *Modifier) SetBehavior(b DuplicateBehavior) {

@hnovikov hnovikov changed the title add option to append headers Add append header modifier Jun 1, 2018
return proxyutil.ResponseHeader(res).Add(m.name, m.value)
}

// NewAppendModifier returns a appendModifier that will append a header with
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/a/an

@hnovikov hnovikov merged commit 74cdea9 into google:master Jun 1, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants