Merge setting files

This commit is contained in:
Leonid Bugaev
2015-07-08 20:09:43 +05:00
parent 18c0473239
commit 686d7436ae
15 changed files with 279 additions and 307 deletions
+175
View File
@@ -0,0 +1,175 @@
package main
import (
"errors"
"fmt"
"regexp"
"strings"
"strconv"
"bytes"
)
//
// Handling of --http-allow-header options
//
type headerFilter struct {
name []byte
regexp *regexp.Regexp
}
type HTTPHeaderFilters []headerFilter
func (h *HTTPHeaderFilters) String() string {
return fmt.Sprint(*h)
}
func (h *HTTPHeaderFilters) Set(value string) error {
valArr := strings.SplitN(value, ":", 2)
if len(valArr) < 2 {
return errors.New("need both header and value, colon-delimited (ex. user_id:^169$).")
}
r, err := regexp.Compile(valArr[1])
if err != nil {
return err
}
*h = append(*h, headerFilter{name: []byte(valArr[0]), regexp: r})
return nil
}
//
// Handling of --http-allow-header-hash options
//
type headerHashFilter struct {
name []byte
percent uint32
}
type HTTPHeaderHashFilters []headerHashFilter
func (h *HTTPHeaderHashFilters) String() string {
return fmt.Sprint(*h)
}
func (h *HTTPHeaderHashFilters) Set(value string) error {
valArr := strings.SplitN(value, ":", 2)
if len(valArr) < 2 {
return errors.New("need both header and value, colon-delimited (ex. user_id:1/2).")
}
fracArr := strings.Split(valArr[1], "/")
if len(fracArr) < 2 {
return errors.New("need both a numerator and denominator specified, slash-delimited (ex. user_id:1/4).")
}
var num, den uint64
num, _ = strconv.ParseUint(fracArr[0], 10, 64)
den, _ = strconv.ParseUint(fracArr[1], 10, 64)
var f headerHashFilter
f.name = []byte(valArr[0])
f.percent = uint32((float64(num) / float64(den)) * 100)
*h = append(*h, f)
return nil
}
//
// Handling of --http-set-header option
//
type HTTPHeaders []HTTPHeader
type HTTPHeader struct {
Name string
Value string
}
func (h *HTTPHeaders) String() string {
return fmt.Sprint(*h)
}
func (h *HTTPHeaders) Set(value string) error {
v := strings.SplitN(value, ":", 2)
if len(v) != 2 {
return errors.New("Expected `Key: Value`")
}
header := HTTPHeader{
strings.TrimSpace(v[0]),
strings.TrimSpace(v[1]),
}
*h = append(*h, header)
return nil
}
//
// Handling of --http-allow-method option
//
type HTTPMethods [][]byte
func (h *HTTPMethods) String() string {
return fmt.Sprint(*h)
}
func (h *HTTPMethods) Set(value string) error {
*h = append(*h, []byte(value))
return nil
}
func (h *HTTPMethods) Contains(value []byte) bool {
for _, method := range *h {
if bytes.Equal(value, method) {
return true
}
}
return false
}
//
// Handling of --http-rewrite-url option
//
type urlRewrite struct {
src *regexp.Regexp
target []byte
}
type UrlRewriteMap []urlRewrite
func (r *UrlRewriteMap) String() string {
return fmt.Sprint(*r)
}
func (r *UrlRewriteMap) Set(value string) error {
valArr := strings.SplitN(value, ":", 2)
if len(valArr) < 2 {
return errors.New("need both src and target, colon-delimited (ex. /a:/b).")
}
regexp, err := regexp.Compile(valArr[0])
if err != nil {
return err
}
*r = append(*r, urlRewrite{src: regexp, target: []byte(valArr[1])})
return nil
}
//
// Handling of --http-allow-url option
//
type HTTPUrlRegexp struct {
regexp *regexp.Regexp
}
func (r *HTTPUrlRegexp) String() string {
if r.regexp == nil {
return ""
}
return r.regexp.String()
}
func (r *HTTPUrlRegexp) Set(value string) error {
regexp, err := regexp.Compile(value)
r.regexp = regexp
return err
}
+68
View File
@@ -0,0 +1,68 @@
package main
import (
"testing"
)
func TestHTTPHeaderFilters(t *testing.T) {
filters := HTTPHeaderFilters{}
err := filters.Set("Header1:^$")
if err != nil {
t.Error("Should not error on Header1:^$")
}
err = filters.Set("Header2:^:$")
if err != nil {
t.Error("Should not error on Header2:^:$")
}
// Missing colon
err = filters.Set("Header3-^$")
if err == nil {
t.Error("Should error on Header2:^:$")
}
}
func TestHTTPHeaderHashFilters(t *testing.T) {
filters := HTTPHeaderHashFilters{}
err := filters.Set("Header1:1/2")
if err != nil {
t.Error("Should not error on Header1:^$")
}
err = filters.Set("Header2:1")
if err == nil {
t.Error("Should error on Header2:^:$")
}
}
func TestHTTPMethods(t *testing.T) {
methods := HTTPMethods{}
methods.Set("GET")
methods.Set("POST")
if !methods.Contains([]byte("GET")) {
t.Error("Does not contain GET")
}
if !methods.Contains([]byte("POST")) {
t.Error("Does not contain POST")
}
}
func TestUrlRewriteMap(t *testing.T) {
var err error
rewrites := UrlRewriteMap{}
if err = rewrites.Set("/v1/user/([^\\/]+)/ping:/v2/user/$1/ping"); err != nil {
t.Error("Should set mapping", err)
}
if err = rewrites.Set("/v1/user/([^\\/]+)/ping"); err == nil {
t.Error("Should not set mapping without :")
}
}
+36 -6
View File
@@ -11,6 +11,19 @@ const (
VERSION = "0.9.4"
)
// Allows to specify multiple flags with same name and collects all values to array
type MultiOption []string
func (h *MultiOption) String() string {
return fmt.Sprint(*h)
}
func (h *MultiOption) Set(value string) error {
*h = append(*h, value)
return nil
}
type AppSettings struct {
verbose bool
stats bool
@@ -69,15 +82,32 @@ func init() {
flag.Var(&Settings.outputHTTP, "output-http", "Forwards incoming requests to given http address.\n\t# Redirect all incoming requests to staging.com address \n\tgor --input-raw :80 --output-http http://staging.com")
flag.IntVar(&Settings.outputHTTPConfig.workers, "output-http-workers", 0, "Gor uses dynamic worker scaling by default. Enter a number to run a set number of workers.")
flag.IntVar(&Settings.outputHTTPConfig.redirectLimit, "output-http-redirects", 0, "Enable how often redirects should be followed.")
flag.Var(&Settings.modifierConfig.headers, "output-http-header", "Inject additional headers to http reqest:\n\tgor --input-raw :8080 --output-http staging.com --output-http-header 'User-Agent: Gor'")
flag.Var(&Settings.modifierConfig.methods, "output-http-method", "Whitelist of HTTP methods to replay. Anything else will be dropped:\n\tgor --input-raw :8080 --output-http staging.com --output-http-method GET --output-http-method OPTIONS")
flag.Var(&Settings.modifierConfig.urlRegexp, "output-http-url-regexp", "A regexp to match requests against. Anything else will be dropped:\n\t gor --input-raw :8080 --output-http staging.com --output-http-url-regexp ^www.")
flag.Var(&Settings.modifierConfig.urlRewrite, "output-http-rewrite-url", "Rewrite the requst url based on a mapping:\n\tgor --input-raw :8080 --output-http staging.com --output-http-rewrite-url /xml_test/interface.php:/api/service.do")
flag.Var(&Settings.modifierConfig.headerFilters, "output-http-header-filter", "A regexp to match a specific header against. Requests with non-matching headers will be dropped:\n\t gor --input-raw :8080 --output-http staging.com --output-http-header-filter api-version:^v1")
flag.Var(&Settings.modifierConfig.headerHashFilters, "output-http-header-hash-filter", "Takes a fraction of requests, consistently taking or rejecting a request based on the FNV32-1A hash of a specific header. The fraction must have a denominator that is a power of two:\n\t gor --input-raw :8080 --output-http staging.com --output-http-header-hash-filter user-id:1/4")
flag.BoolVar(&Settings.outputHTTPConfig.stats, "output-http-stats", false, "Report http output queue stats to console every 5 seconds.")
flag.StringVar(&Settings.outputHTTPConfig.elasticSearch, "output-http-elasticsearch", "", "Send request and response stats to ElasticSearch:\n\tgor --input-raw :8080 --output-http staging.com --output-http-elasticsearch 'es_host:api_port/index_name'")
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.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")
flag.Var(&Settings.modifierConfig.methods, "output-http-method", "WARNING: `--output-http-method` DEPRECATED, use `--http-allow-method` instead")
flag.Var(&Settings.modifierConfig.urlRegexp, "http-allow-url", "A regexp to match requests against. Filter get matched agains full url with domain. Anything else will be dropped:\n\t gor --input-raw :8080 --output-http staging.com --http-filter-url ^www.")
flag.Var(&Settings.modifierConfig.urlRegexp, "output-http-url-regexp", "WARNING: `--output-http-url-regexp` DEPRECATED, use `--http-allow-url` instead")
flag.Var(&Settings.modifierConfig.urlRewrite, "http-rewrite-url", "Rewrite the requst url based on a mapping:\n\tgor --input-raw :8080 --output-http staging.com --http-rewrite-url /v1/user/([^\\/]+)/ping:/v2/user/$1/ping")
flag.Var(&Settings.modifierConfig.urlRewrite, "output-http-rewrite-url", "WARNING: `--output-http-rewrite-url` DEPRECATED, use `--http-rewrite-url` instead")
flag.Var(&Settings.modifierConfig.headerFilters, "http-allow-header", "A regexp to match a specific header against. Requests with non-matching headers will be dropped:\n\t gor --input-raw :8080 --output-http staging.com --http-allow-header api-version:^v1")
flag.Var(&Settings.modifierConfig.headerFilters, "output-http-header-filter", "WARNING: `--output-http-header-filter` DEPRECATED, use `--http-allow-header` instead")
flag.Var(&Settings.modifierConfig.headerHashFilters, "http-allow-header-hash", "Takes a fraction of requests, consistently taking or rejecting a request based on the FNV32-1A hash of a specific header:\n\t gor --input-raw :8080 --output-http staging.com --http-allow-header-hash user-id:1/4")
flag.Var(&Settings.modifierConfig.headerHashFilters, "output-http-header-hash-filter", "WARNING: `output-http-header-hash-filter` DEPRECATED, use `--http-allow-header-hash` instead")
}
func Debug(args ...interface{}) {
-34
View File
@@ -1,34 +0,0 @@
package main
import (
"errors"
"fmt"
"regexp"
"strings"
)
type headerFilter struct {
name []byte
regexp *regexp.Regexp
}
type HTTPHeaderFilters []headerFilter
func (h *HTTPHeaderFilters) String() string {
return fmt.Sprint(*h)
}
func (h *HTTPHeaderFilters) Set(value string) error {
valArr := strings.SplitN(value, ":", 2)
if len(valArr) < 2 {
return errors.New("need both header and value, colon-delimited (ex. user_id:^169$).")
}
r, err := regexp.Compile(valArr[1])
if err != nil {
return err
}
*h = append(*h, headerFilter{name: []byte(valArr[0]), regexp: r})
return nil
}
-25
View File
@@ -1,25 +0,0 @@
package main
import (
"testing"
)
func TestHTTPHeaderFilters(t *testing.T) {
filters := HTTPHeaderFilters{}
err := filters.Set("Header1:^$")
if err != nil {
t.Error("Should not error on Header1:^$")
}
err = filters.Set("Header2:^:$")
if err != nil {
t.Error("Should not error on Header2:^:$")
}
// Missing colon
err = filters.Set("Header3-^$")
if err == nil {
t.Error("Should error on Header2:^:$")
}
}
-42
View File
@@ -1,42 +0,0 @@
package main
import (
"errors"
"fmt"
"strconv"
"strings"
)
type headerHashFilter struct {
name []byte
percent uint32
}
type HTTPHeaderHashFilters []headerHashFilter
func (h *HTTPHeaderHashFilters) String() string {
return fmt.Sprint(*h)
}
func (h *HTTPHeaderHashFilters) Set(value string) error {
valArr := strings.SplitN(value, ":", 2)
if len(valArr) < 2 {
return errors.New("need both header and value, colon-delimited (ex. user_id:1/2).")
}
fracArr := strings.Split(valArr[1], "/")
if len(fracArr) < 2 {
return errors.New("need both a numerator and denominator specified, slash-delimited (ex. user_id:1/4).")
}
var num, den uint64
num, _ = strconv.ParseUint(fracArr[0], 10, 64)
den, _ = strconv.ParseUint(fracArr[1], 10, 64)
var f headerHashFilter
f.name = []byte(valArr[0])
f.percent = uint32((float64(num) / float64(den)) * 100)
*h = append(*h, f)
return nil
}
-19
View File
@@ -1,19 +0,0 @@
package main
import (
"testing"
)
func TestHTTPHeaderHashFilters(t *testing.T) {
filters := HTTPHeaderHashFilters{}
err := filters.Set("Header1:1/2")
if err != nil {
t.Error("Should not error on Header1:^$")
}
err = filters.Set("Header2:1")
if err == nil {
t.Error("Should error on Header2:^:$")
}
}
-32
View File
@@ -1,32 +0,0 @@
package main
import (
"errors"
"fmt"
"strings"
)
type HTTPHeaders []HTTPHeader
type HTTPHeader struct {
Name string
Value string
}
func (h *HTTPHeaders) String() string {
return fmt.Sprint(*h)
}
func (h *HTTPHeaders) Set(value string) error {
v := strings.SplitN(value, ":", 2)
if len(v) != 2 {
return errors.New("Expected `Key: Value`")
}
header := HTTPHeader{
strings.TrimSpace(v[0]),
strings.TrimSpace(v[1]),
}
*h = append(*h, header)
return nil
}
-27
View File
@@ -1,27 +0,0 @@
package main
import (
"bytes"
"fmt"
"strings"
)
type HTTPMethods [][]byte
func (h *HTTPMethods) String() string {
return fmt.Sprint(*h)
}
func (h *HTTPMethods) Set(value string) error {
*h = append(*h, []byte(strings.ToUpper(value)))
return nil
}
func (h *HTTPMethods) Contains(value []byte) bool {
for _, method := range *h {
if bytes.Equal(value, method) {
return true
}
}
return false
}
-24
View File
@@ -1,24 +0,0 @@
package main
import (
"testing"
)
func TestHTTPMethods(t *testing.T) {
methods := HTTPMethods{}
methods.Set("lower")
methods.Set("UPPER")
if !methods.Contains([]byte("LOWER")) {
t.Error("Does not contain LOWER")
}
if !methods.Contains([]byte("UPPER")) {
t.Error("Does not contain UPPER")
}
if methods.Contains([]byte("ABSENT")) {
t.Error("Does contain ABSENT")
}
}
-16
View File
@@ -1,16 +0,0 @@
package main
import (
"fmt"
)
type MultiOption []string
func (h *MultiOption) String() string {
return fmt.Sprint(*h)
}
func (h *MultiOption) Set(value string) error {
*h = append(*h, value)
return nil
}
-32
View File
@@ -1,32 +0,0 @@
package main
import (
"errors"
"fmt"
"regexp"
"strings"
)
type urlRewrite struct {
src *regexp.Regexp
target []byte
}
type UrlRewriteMap []urlRewrite
func (r *UrlRewriteMap) String() string {
return fmt.Sprint(*r)
}
func (r *UrlRewriteMap) Set(value string) error {
valArr := strings.SplitN(value, ":", 2)
if len(valArr) < 2 {
return errors.New("need both src and target, colon-delimited (ex. /a:/b).")
}
regexp, err := regexp.Compile(valArr[0])
if err != nil {
return err
}
*r = append(*r, urlRewrite{src: regexp, target: []byte(valArr[1])})
return nil
}
-18
View File
@@ -1,18 +0,0 @@
package main
import (
"testing"
)
func TestUrlRewriteMap(t *testing.T) {
var err error
rewrites := UrlRewriteMap{}
if err = rewrites.Set("/v1/user/([^\\/]+)/ping:/v2/user/$1/ping"); err != nil {
t.Error("Should set mapping", err)
}
if err = rewrites.Set("/v1/user/([^\\/]+)/ping"); err == nil {
t.Error("Should not set mapping without :")
}
}
-22
View File
@@ -1,22 +0,0 @@
package main
import (
"regexp"
)
type HTTPUrlRegexp struct {
regexp *regexp.Regexp
}
func (r *HTTPUrlRegexp) String() string {
if r.regexp == nil {
return ""
}
return r.regexp.String()
}
func (r *HTTPUrlRegexp) Set(value string) error {
regexp, err := regexp.Compile(value)
r.regexp = regexp
return err
}
-10
View File
@@ -1,10 +0,0 @@
package main
import (
"testing"
)
func TestHTTPUrlRegexp(t *testing.T) {
filter := HTTPUrlRegexp{}
filter.Set("^www.google.com/admin/")
}