Skip to content

Commit

Permalink
Add support for rewriting headers
Browse files Browse the repository at this point in the history
  • Loading branch information
buger committed Jun 27, 2017
1 parent 03767d1 commit 21531ce
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 0 deletions.
15 changes: 15 additions & 0 deletions http_modifier.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ func NewHTTPModifier(config *HTTPModifierConfig) *HTTPModifier {
if len(config.urlRegexp) == 0 &&
len(config.urlNegativeRegexp) == 0 &&
len(config.urlRewrite) == 0 &&
len(config.headerRewrite) == 0 &&
len(config.headerFilters) == 0 &&
len(config.headerNegativeFilters) == 0 &&
len(config.headerHashFilters) == 0 &&
Expand Down Expand Up @@ -153,5 +154,19 @@ func (m *HTTPModifier) Rewrite(payload []byte) (response []byte) {
}
}

if len(m.config.headerRewrite) > 0 {
for _, f := range m.config.headerRewrite {
value := proto.Header(payload, f.header)
if len(value) == 0 {
break
}

if f.src.Match(value) {
newValue := f.src.ReplaceAll(value, f.target)
payload = proto.SetHeader(payload, f.header, newValue)
}
}
}

return payload
}
38 changes: 38 additions & 0 deletions http_modifier_settings.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ type HTTPModifierConfig struct {
urlNegativeRegexp HTTPUrlRegexp
urlRegexp HTTPUrlRegexp
urlRewrite UrlRewriteMap
headerRewrite HeaderRewriteMap
headerFilters HTTPHeaderFilters
headerNegativeFilters HTTPHeaderFilters
headerHashFilters HTTPHashFilters
Expand Down Expand Up @@ -196,6 +197,43 @@ func (r *UrlRewriteMap) Set(value string) error {
return nil
}

//
// Handling of --http-rewrite-header option
//
type headerRewrite struct {
header []byte
src *regexp.Regexp
target []byte
}

type HeaderRewriteMap []headerRewrite

func (r *HeaderRewriteMap) String() string {
return fmt.Sprint(*r)
}

func (r *HeaderRewriteMap) Set(value string) error {
headerArr := strings.SplitN(value, ":", 2)
if len(headerArr) < 2 {
return errors.New("need both header, regexp and rewrite target, colon-delimited (ex. Header: regexp,target)")
}

header := headerArr[0]
valArr := strings.SplitN(strings.TrimSpace(headerArr[1]), ",", 2)

if len(valArr) < 2 {
return errors.New("need both header, regexp and rewrite target, colon-delimited (ex. Header: regexp,target)")
}

regexp, err := regexp.Compile(valArr[0])
if err != nil {
return err
}
*r = append(*r, headerRewrite{header: []byte(header), src: regexp, target: []byte(valArr[1])})
return nil
}


//
// Handling of --http-allow-url option
//
Expand Down
21 changes: 21 additions & 0 deletions http_modifier_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,27 @@ func TestHTTPModifierURLRewrite(t *testing.T) {
}
}

func TestHTTPModifierHeaderRewrite(t *testing.T) {
var header, newHeader []byte

rewrites := HeaderRewriteMap{}
payload := []byte("GET / HTTP/1.1\r\nContent-Length: 7\r\nHost: www.w3.org\r\n\r\na=1&b=2")

err := rewrites.Set("Host: (.*).w3.org,$1.beta.w3.org")
if err != nil {
t.Error("Should not error", err)
}

modifier := NewHTTPModifier(&HTTPModifierConfig{
headerRewrite: rewrites,
})

header = []byte("www.beta.w3.org")
if newHeader = proto.Header(modifier.Rewrite(payload), []byte("Host")); !bytes.Equal(newHeader, header) {
t.Error("Request header should have been rewritten, wasn't", string(newHeader), string(header))
}
}

func TestHTTPModifierHeaderHashFilters(t *testing.T) {
filters := HTTPHashFilters{}
filters.Set("Header2:1/2")
Expand Down
2 changes: 2 additions & 0 deletions settings.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,8 @@ func init() {
flag.Var(&Settings.modifierConfig.headers, "http-set-header", "Inject additional headers to http reqest:\n\tgor --input-raw :8080 --output-http staging.com --http-set-header 'User-Agent: Gor'")
flag.Var(&Settings.modifierConfig.headers, "output-http-header", "WARNING: `--output-http-header` DEPRECATED, use `--http-set-header` instead")

flag.Var(&Settings.modifierConfig.headerRewrite, "http-rewrite-header", "Rewrite the request header based on a mapping:\n\tgor --input-raw :8080 --output-http staging.com --http-rewrite-header Host: (.*).example.com,$1.beta.example.com")

flag.Var(&Settings.modifierConfig.params, "http-set-param", "Set request url param, if param already exists it will be overwritten:\n\tgor --input-raw :8080 --output-http staging.com --http-set-param api_key=1")

flag.Var(&Settings.modifierConfig.methods, "http-allow-method", "Whitelist of HTTP methods to replay. Anything else will be dropped:\n\tgor --input-raw :8080 --output-http staging.com --http-allow-method GET --http-allow-method OPTIONS")
Expand Down

2 comments on commit 21531ce

@frankfarmer
Copy link

Choose a reason for hiding this comment

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

@buger it looks like -output-http blows away -http-rewrite-header

There's special logic to protect -http-set-header 'host: foo' from being disrupted by -output-http:
a79e54c#diff-2663f1d9618ebe24ecdbe9e29bc5af4fR101

I suspect this should be replicated for -http-rewrite-header as well

@killdash9
Copy link

Choose a reason for hiding this comment

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

Thank you @buger for building this! A workaround for @frankfarmer's issue above would be to also use -http-original-host to keep the header from being disrupted by -output-http.

One other issue is that the Host header gets special treatment in proto.go's SetHost function in order to be compatible with HTTP 1.0 where host names are specified as part of the path after the method instead of in a Host: header. (e.g. GET http://www.foo.com/path HTTP/1.0) The newly-added header rewriting logic does not handle this special case for the Host header.

Please sign in to comment.