diff --git a/.gitignore b/.gitignore index afd1f677571..a1af91a75c8 100644 --- a/.gitignore +++ b/.gitignore @@ -50,7 +50,6 @@ session_state_gen_test.go __pycache__/ tyk.test tyk-gateway.pid -*.go-e tyk_linux_* /dist/ diff --git a/cli/linter/schema.json b/cli/linter/schema.json index 2f77cd046eb..ce17033a275 100644 --- a/cli/linter/schema.json +++ b/cli/linter/schema.json @@ -648,15 +648,6 @@ "type": "string", "enum": ["", "standard", "json"] }, - "access_logs": { - "type": ["object", "null"], - "additionalProperties": false, - "properties": { - "enabled": { - "type": "boolean" - } - } - }, "enable_http_profiler": { "type": "boolean" }, diff --git a/config/config.go b/config/config.go index 6bb89c07839..115324b6d1e 100644 --- a/config/config.go +++ b/config/config.go @@ -246,12 +246,6 @@ type AnalyticsConfigConfig struct { SerializerType string `json:"serializer_type"` } -// AccessLogsConfig defines the type of transactions logs printed to stdout -type AccessLogsConfig struct { - // Enable the transaction logs. Default: false - Enabled bool `json:"enabled"` -} - type HealthCheckConfig struct { // Setting this value to `true` will enable the health-check endpoint on /Tyk/health. EnableHealthChecks bool `json:"enable_health_checks"` @@ -1009,10 +1003,6 @@ type Config struct { // If not set or left empty, it will default to `standard`. LogFormat string `json:"log_format"` - // You can configure the transaction logs to be turned on - // If not set or left empty, it will default to 'false' - AccessLogs AccessLogsConfig `json:"access_logs"` - // Section for configuring OpenTracing support // Deprecated: use OpenTelemetry instead. Tracer Tracer `json:"tracing"` diff --git a/gateway/handler_error.go b/gateway/handler_error.go index cd240f12a07..0cda01903a8 100644 --- a/gateway/handler_error.go +++ b/gateway/handler_error.go @@ -311,13 +311,6 @@ func (e *ErrorHandler) HandleError(w http.ResponseWriter, r *http.Request, errMs log.WithError(err).Error("could not store analytic record") } } - - // Print the transaction logs for error situations if enabled. Success transaction - // logs will be handled by the "handler_success.go" - if e.Spec.GlobalConfig.AccessLogs.Enabled { - e.recordAccessLog(r, response, nil) - } - // Report in health check reportHealthValue(e.Spec, BlockedRequestLog, "-1") } diff --git a/gateway/handler_error_test.go b/gateway/handler_error_test.go index 76fb1dccb78..72cd761966d 100644 --- a/gateway/handler_error_test.go +++ b/gateway/handler_error_test.go @@ -9,8 +9,6 @@ import ( "strings" "testing" - "github.com/TykTechnologies/tyk/config" - "github.com/TykTechnologies/tyk/header" "github.com/TykTechnologies/tyk/test" ) @@ -124,52 +122,3 @@ func TestHandleDefaultErrorJSON(t *testing.T) { }) } - -func BenchmarkErrorLogTransaction(b *testing.B) { - b.Run("AccessLogs enabled with Hashkeys set to true", func(b *testing.B) { - conf := func(globalConf *config.Config) { - globalConf.HashKeys = true - globalConf.AccessLogs.Enabled = true - } - benchmarkErrorLogTransaction(b, conf) - - }) - b.Run("AccessLogs enabled with Hashkeys set to false", func(b *testing.B) { - conf := func(globalConf *config.Config) { - globalConf.HashKeys = false - globalConf.AccessLogs.Enabled = true - } - benchmarkErrorLogTransaction(b, conf) - }) - - b.Run("AccessLogs disabled with Hashkeys set to true", func(b *testing.B) { - conf := func(globalConf *config.Config) { - globalConf.HashKeys = true - globalConf.AccessLogs.Enabled = false - } - benchmarkErrorLogTransaction(b, conf) - }) - - b.Run("AccessLogs disabled with Hashkeys set to false", func(b *testing.B) { - conf := func(globalConf *config.Config) { - globalConf.HashKeys = false - globalConf.AccessLogs.Enabled = false - } - benchmarkErrorLogTransaction(b, conf) - }) -} - -func benchmarkErrorLogTransaction(b *testing.B, conf func(globalConf *config.Config)) { - b.ReportAllocs() - b.Helper() - b.ResetTimer() - - ts := StartTest(conf) - defer ts.Close() - - for i := 0; i < b.N; i++ { - ts.Run(b, test.TestCase{ - Code: http.StatusNotFound, - }) - } -} diff --git a/gateway/handler_success.go b/gateway/handler_success.go index a2ec9fedb73..0f24aadc3f9 100644 --- a/gateway/handler_success.go +++ b/gateway/handler_success.go @@ -10,8 +10,9 @@ import ( "strings" "time" - "github.com/TykTechnologies/tyk/apidef" graphqlinternal "github.com/TykTechnologies/tyk/internal/graphql" + + "github.com/TykTechnologies/tyk/apidef" "github.com/TykTechnologies/tyk/internal/httputil" "github.com/TykTechnologies/tyk-pump/analytics" @@ -381,15 +382,8 @@ func (s *SuccessHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) *http Upstream: int64(DurationToMillisecond(resp.UpstreamLatency)), } s.RecordHit(r, latency, resp.Response.StatusCode, resp.Response, false) - - // Don't print a transaction log there is no "resp", that indicates an error. - // In error situations, transaction log is already printed by "handler_error.go" - if s.Spec.GlobalConfig.AccessLogs.Enabled { - s.recordAccessLog(r, resp.Response, &latency) - } } log.Debug("Done proxy") - return nil } diff --git a/gateway/handler_success_test.go b/gateway/handler_success_test.go index 95713917a0e..9f1c4a1d9d3 100644 --- a/gateway/handler_success_test.go +++ b/gateway/handler_success_test.go @@ -326,58 +326,3 @@ func TestAnalyticsIgnoreSubgraph(t *testing.T) { ) assert.NoError(t, err) } - -func BenchmarkSuccessLogTransaction(b *testing.B) { - b.Run("AccessLogs enabled with Hashkeys set to true", func(b *testing.B) { - conf := func(globalConf *config.Config) { - globalConf.HashKeys = true - globalConf.AccessLogs.Enabled = true - } - benchmarkSuccessLogTransaction(b, conf) - - }) - b.Run("AccessLogs enabled with Hashkeys set to false", func(b *testing.B) { - conf := func(globalConf *config.Config) { - globalConf.HashKeys = false - globalConf.AccessLogs.Enabled = true - } - benchmarkSuccessLogTransaction(b, conf) - }) - b.Run("AccessLogs disabled with Hashkeys set to true", func(b *testing.B) { - conf := func(globalConf *config.Config) { - globalConf.HashKeys = true - globalConf.AccessLogs.Enabled = false - } - benchmarkSuccessLogTransaction(b, conf) - }) - b.Run("AccessLogs disabled with Hashkeys set to false", func(b *testing.B) { - conf := func(globalConf *config.Config) { - globalConf.HashKeys = false - globalConf.AccessLogs.Enabled = false - } - benchmarkSuccessLogTransaction(b, conf) - }) -} - -func benchmarkSuccessLogTransaction(b *testing.B, conf func(globalConf *config.Config)) { - b.ReportAllocs() - b.Helper() - b.ResetTimer() - - ts := StartTest(conf) - defer ts.Close() - - API := BuildAPI(func(spec *APISpec) { - spec.Name = "test-api" - spec.APIID = "test-api-id" - spec.Proxy.ListenPath = "/" - })[0] - - ts.Gw.LoadAPI(API) - - for i := 0; i < b.N; i++ { - ts.Run(b, test.TestCase{ - Code: http.StatusOK, - }) - } -} diff --git a/gateway/middleware.go b/gateway/middleware.go index 7e57f45aaba..91e84d960bc 100644 --- a/gateway/middleware.go +++ b/gateway/middleware.go @@ -12,11 +12,8 @@ import ( "strconv" "time" - "github.com/TykTechnologies/tyk-pump/analytics" - "github.com/TykTechnologies/tyk/internal/cache" "github.com/TykTechnologies/tyk/internal/event" - "github.com/TykTechnologies/tyk/internal/httputil/accesslog" "github.com/TykTechnologies/tyk/internal/otel" "github.com/TykTechnologies/tyk/internal/policy" "github.com/TykTechnologies/tyk/rpc" @@ -361,19 +358,6 @@ func (t *BaseMiddleware) ApplyPolicies(session *user.SessionState) error { return store.Apply(session) } -// recordAccessLog is only used for Success/Error handler -func (t *BaseMiddleware) recordAccessLog(req *http.Request, resp *http.Response, latency *analytics.Latency) { - hashKeys := t.Gw.GetConfig().HashKeys - - accessLog := accesslog.NewRecord(t.Spec.APIID, t.Spec.OrgID) - accessLog.WithAuthToken(req, hashKeys, t.Gw.obfuscateKey) - accessLog.WithLatency(latency) - accessLog.WithRequest(req) - accessLog.WithResponse(resp) - - t.Logger().WithFields(accessLog.Fields()).Info() -} - func copyAllowedURLs(input []user.AccessSpec) []user.AccessSpec { if input == nil { return nil diff --git a/internal/crypto/hash.go b/internal/crypto/hash.go deleted file mode 100644 index 9b13905eea7..00000000000 --- a/internal/crypto/hash.go +++ /dev/null @@ -1,53 +0,0 @@ -package crypto - -import ( - "crypto/sha256" - "encoding/hex" - "fmt" - "hash" - - "github.com/TykTechnologies/murmur3" -) - -const ( - HashSha256 = "sha256" - HashMurmur32 = "murmur32" - HashMurmur64 = "murmur64" - HashMurmur128 = "murmur128" -) - -func hashFunction(algorithm string) (hash.Hash, error) { - switch algorithm { - case HashSha256: - return sha256.New(), nil - case HashMurmur64: - return murmur3.New64(), nil - case HashMurmur128: - return murmur3.New128(), nil - case "", HashMurmur32: - return murmur3.New32(), nil - default: - return murmur3.New32(), fmt.Errorf("Unknown key hash function: %s. Falling back to murmur32.", algorithm) - } -} - -func HashStr(in string, withAlg ...string) string { - var algo string - if len(withAlg) > 0 && withAlg[0] != "" { - algo = withAlg[0] - } else { - algo = TokenHashAlgo(in) - } - - h, _ := hashFunction(algo) - h.Write([]byte(in)) - return hex.EncodeToString(h.Sum(nil)) -} - -func HashKey(in string, hashKey bool) string { - if !hashKey { - // Not hashing? Return the raw key - return in - } - return HashStr(in) -} diff --git a/internal/crypto/token.go b/internal/crypto/token.go deleted file mode 100644 index 47edbe8330e..00000000000 --- a/internal/crypto/token.go +++ /dev/null @@ -1,83 +0,0 @@ -package crypto - -import ( - "encoding/base64" - "encoding/hex" - "fmt" - "strings" - - "github.com/buger/jsonparser" - - "github.com/TykTechnologies/tyk/internal/uuid" -) - -// `{"` in base64 -const B64JSONPrefix = "ey" - -const DefaultHashAlgorithm = "murmur64" - -const MongoBsonIdLength = 24 - -// GenerateToken generates a token. -// If hashing algorithm is empty, it uses legacy key generation. -func GenerateToken(orgID, keyID, hashAlgorithm string) (string, error) { - if keyID == "" { - keyID = uuid.NewHex() - } - - if hashAlgorithm != "" { - _, err := hashFunction(hashAlgorithm) - if err != nil { - hashAlgorithm = DefaultHashAlgorithm - } - - jsonToken := fmt.Sprintf(`{"org":"%s","id":"%s","h":"%s"}`, orgID, keyID, hashAlgorithm) - return base64.StdEncoding.EncodeToString([]byte(jsonToken)), err - } - - // Legacy keys - return orgID + keyID, nil -} - -func TokenHashAlgo(token string) string { - // Legacy tokens not b64 and not JSON records - if strings.HasPrefix(token, B64JSONPrefix) { - if jsonToken, err := base64.StdEncoding.DecodeString(token); err == nil { - hashAlgo, _ := jsonparser.GetString(jsonToken, "h") - return hashAlgo - } - } - - return "" -} - -func TokenID(token string) (id string, err error) { - jsonToken, err := base64.StdEncoding.DecodeString(token) - if err != nil { - return "", err - } - - return jsonparser.GetString(jsonToken, "id") -} - -func TokenOrg(token string) string { - if strings.HasPrefix(token, B64JSONPrefix) { - if jsonToken, err := base64.StdEncoding.DecodeString(token); err == nil { - // Checking error in case if it is a legacy tooken which just by accided has the same b64JSON prefix - if org, err := jsonparser.GetString(jsonToken, "org"); err == nil { - return org - } - } - } - - // 24 is mongo bson id length - if len(token) > MongoBsonIdLength { - newToken := token[:MongoBsonIdLength] - _, err := hex.DecodeString(newToken) - if err == nil { - return newToken - } - } - - return "" -} diff --git a/internal/httputil/Taskfile.yml b/internal/httputil/Taskfile.yml index 1e3bfeaf620..8434634a752 100644 --- a/internal/httputil/Taskfile.yml +++ b/internal/httputil/Taskfile.yml @@ -3,14 +3,13 @@ version: "3" vars: testArgs: -v - coverpkg: ./...,github.com/TykTechnologies/tyk/internal/httputil/... tasks: test: desc: "Run tests (requires redis)" cmds: - task: fmt - - go test {{.testArgs}} -count=1 -cover -coverprofile=rate.cov {{.coverpkg}} ./... + - go test {{.testArgs}} -count=1 -cover -coverprofile=rate.cov -coverpkg=./... ./... bench: desc: "Run benchmarks" diff --git a/internal/httputil/accesslog/access_log.go b/internal/httputil/accesslog/access_log.go deleted file mode 100644 index c4daaeffef4..00000000000 --- a/internal/httputil/accesslog/access_log.go +++ /dev/null @@ -1,89 +0,0 @@ -package accesslog - -import ( - "net/http" - "net/url" - - "github.com/sirupsen/logrus" - - "github.com/TykTechnologies/tyk-pump/analytics" - "github.com/TykTechnologies/tyk/ctx" - "github.com/TykTechnologies/tyk/internal/crypto" - "github.com/TykTechnologies/tyk/request" -) - -// Record is a representation of a transaction log in the Gateway. -type Record struct { - fields logrus.Fields -} - -// NewRecord returns an Record object. -func NewRecord(apiID string, orgId string) *Record { - fields := logrus.Fields{ - "APIID": apiID, - "OrgID": orgId, - "prefix": "access-log", - } - return &Record{ - fields: fields, - } -} - -// WithLatency sets the latency of the Record. -func (a *Record) WithLatency(latency *analytics.Latency) *Record { - if latency != nil { - a.fields["TotalLatency"] = latency.Total - a.fields["UpstreamLatency"] = latency.Upstream - } - return a -} - -// WithAuthToken sets the access token from the request under APIKey. -// The access token is obfuscated, or hashed depending on passed arguments. -func (a *Record) WithAuthToken(req *http.Request, hashKeys bool, obfuscate func(string) string) *Record { - if req != nil { - token := ctx.GetAuthToken(req) - if !hashKeys { - a.fields["APIKey"] = obfuscate(token) - } else { - a.fields["APIKey"] = crypto.HashKey(token, hashKeys) - } - } - return a -} - -// WithRequest sets the request of the Record. -func (a *Record) WithRequest(req *http.Request) *Record { - if req != nil { - upstreamAddress := &url.URL{ - Scheme: req.URL.Scheme, - Host: req.URL.Host, - Path: req.URL.Path, - RawQuery: req.URL.RawQuery, - } - a.fields["ClientIP"] = request.RealIP(req) - a.fields["ClientRemoteAddr"] = req.RemoteAddr - a.fields["Host"] = req.Host - a.fields["Method"] = req.Method - a.fields["Proto"] = req.Proto - a.fields["RequestURI"] = req.RequestURI - a.fields["UpstreamAddress"] = upstreamAddress.String() - a.fields["UpstreamPath"] = req.URL.Path - a.fields["UpstreamURI"] = req.URL.RequestURI() - a.fields["UserAgent"] = req.UserAgent() - } - return a -} - -// WithResponse sets the request of the Record. -func (a *Record) WithResponse(resp *http.Response) *Record { - if resp != nil { - a.fields["StatusCode"] = resp.StatusCode - } - return a -} - -// Fields returns a logrus.Fields intended for logging. -func (a *Record) Fields() logrus.Fields { - return a.fields -} diff --git a/internal/httputil/accesslog/access_log_test.go b/internal/httputil/accesslog/access_log_test.go deleted file mode 100644 index 45d261175f4..00000000000 --- a/internal/httputil/accesslog/access_log_test.go +++ /dev/null @@ -1,99 +0,0 @@ -package accesslog - -import ( - "net/http" - "testing" - - "github.com/stretchr/testify/assert" - - "github.com/TykTechnologies/tyk-pump/analytics" - "github.com/TykTechnologies/tyk/request" -) - -func TestNewRecord(t *testing.T) { - apiID := "api_id" - orgID := "org_id" - - record := NewRecord(apiID, orgID).Fields() - - assert.Equal(t, apiID, record["APIID"]) - assert.Equal(t, orgID, record["OrgID"]) - assert.NotNil(t, record["APIID"]) - assert.NotNil(t, record["OrgID"]) -} - -func TestNewRecordWithLatency(t *testing.T) { - latency := &analytics.Latency{ - Total: 99, - Upstream: 101, - } - - record := NewRecord("api_id", "org_id").WithLatency(latency).Fields() - - assert.Equal(t, latency.Total, record["TotalLatency"]) - assert.Equal(t, latency.Upstream, record["UpstreamLatency"]) - assert.NotNil(t, record["TotalLatency"]) - assert.NotNil(t, record["UpstreamLatency"]) -} - -func TestNewRecordWithRequest(t *testing.T) { - req, _ := http.NewRequest(http.MethodGet, "/", nil) - - record := NewRecord("api_id", "org_id").WithRequest(req).Fields() - - assert.Equal(t, request.RealIP(req), record["ClientIP"]) - assert.Equal(t, req.RemoteAddr, record["ClientRemoteAddr"]) - assert.Equal(t, req.Host, record["Host"]) - assert.Equal(t, req.Method, record["Method"]) - assert.Equal(t, req.Proto, record["Proto"]) - assert.Equal(t, req.RequestURI, record["RequestURI"]) - assert.Equal(t, req.URL.Path, record["UpstreamAddress"]) - assert.Equal(t, req.URL.Path, record["UpstreamPath"]) - assert.Equal(t, req.URL.RequestURI(), record["UpstreamURI"]) - assert.Equal(t, req.UserAgent(), record["UserAgent"]) -} - -func TestNewRecordWithResponse(t *testing.T) { - resp := &http.Response{ - StatusCode: http.StatusOK, - } - - record := NewRecord("api_id", "org_id").WithResponse(resp).Fields() - - assert.Equal(t, resp.StatusCode, record["StatusCode"]) - assert.NotNil(t, record["StatusCode"]) -} - -func TestNewRecordField(t *testing.T) { - latency := &analytics.Latency{ - Total: 99, - Upstream: 101, - } - - req, _ := http.NewRequest(http.MethodGet, "http://example.com/path?userid=1", nil) - req.RemoteAddr = "0.0.0.0" - req.Header.Set("User-Agent", "user-agent") - - resp := &http.Response{ - StatusCode: http.StatusOK, - } - - record := NewRecord("api_id", "org_id").WithLatency(latency).WithRequest(req).WithResponse(resp).Fields() - - assert.Equal(t, "api_id", record["APIID"]) - assert.Equal(t, "org_id", record["OrgID"]) - assert.Equal(t, "access-log", record["prefix"]) - assert.Equal(t, int64(99), record["TotalLatency"]) - assert.Equal(t, int64(101), record["UpstreamLatency"]) - assert.Equal(t, request.RealIP(req), record["ClientIP"]) - assert.Equal(t, "0.0.0.0", record["ClientRemoteAddr"]) - assert.Equal(t, "example.com", record["Host"]) - assert.Equal(t, http.MethodGet, record["Method"]) - assert.Equal(t, "HTTP/1.1", record["Proto"]) - assert.Equal(t, "", record["RequestURI"]) - assert.Equal(t, "http://example.com/path?userid=1", record["UpstreamAddress"]) - assert.Equal(t, "/path", record["UpstreamPath"]) - assert.Equal(t, "/path?userid=1", record["UpstreamURI"]) - assert.Equal(t, "user-agent", record["UserAgent"]) - assert.Equal(t, http.StatusOK, record["StatusCode"]) -} diff --git a/storage/alias.go b/storage/alias.go deleted file mode 100644 index 539df2c0a04..00000000000 --- a/storage/alias.go +++ /dev/null @@ -1,24 +0,0 @@ -package storage - -import ( - "github.com/TykTechnologies/tyk/internal/crypto" -) - -const ( - HashSha256 = crypto.HashSha256 - HashMurmur32 = crypto.HashMurmur32 - HashMurmur64 = crypto.HashMurmur64 - HashMurmur128 = crypto.HashMurmur128 -) - -var ( - HashStr = crypto.HashStr - HashKey = crypto.HashKey -) - -var ( - GenerateToken = crypto.GenerateToken - TokenHashAlgo = crypto.TokenHashAlgo - TokenID = crypto.TokenID - TokenOrg = crypto.TokenOrg -) diff --git a/storage/storage.go b/storage/storage.go index f636e8f7138..2c74e54e8a4 100644 --- a/storage/storage.go +++ b/storage/storage.go @@ -1,9 +1,20 @@ package storage import ( + "crypto/sha256" + "encoding/base64" + "encoding/hex" "errors" + "fmt" + "hash" + "strings" + "github.com/buger/jsonparser" + + "github.com/TykTechnologies/murmur3" logger "github.com/TykTechnologies/tyk/log" + + "github.com/TykTechnologies/tyk/internal/uuid" ) var log = logger.Get() @@ -13,6 +24,8 @@ var ErrKeyNotFound = errors.New("key not found") var ErrMDCBConnectionLost = errors.New("mdcb connection is lost") +const MongoBsonIdLength = 24 + // Handler is a standard interface to a storage backend, used by // AuthorisationManager to read and write key values to the backend type Handler interface { @@ -58,3 +71,115 @@ type AnalyticsHandler interface { SetExp(string, int64) error // Set key expiration GetExp(string) (int64, error) // Returns expiry of a key } + +const defaultHashAlgorithm = "murmur64" + +// If hashing algorithm is empty, use legacy key generation +func GenerateToken(orgID, keyID, hashAlgorithm string) (string, error) { + if keyID == "" { + keyID = uuid.NewHex() + } + + if hashAlgorithm != "" { + _, err := hashFunction(hashAlgorithm) + if err != nil { + hashAlgorithm = defaultHashAlgorithm + } + + jsonToken := fmt.Sprintf(`{"org":"%s","id":"%s","h":"%s"}`, orgID, keyID, hashAlgorithm) + return base64.StdEncoding.EncodeToString([]byte(jsonToken)), err + } + + // Legacy keys + return orgID + keyID, nil +} + +// `{"` in base64 +const B64JSONPrefix = "ey" + +func TokenHashAlgo(token string) string { + // Legacy tokens not b64 and not JSON records + if strings.HasPrefix(token, B64JSONPrefix) { + if jsonToken, err := base64.StdEncoding.DecodeString(token); err == nil { + hashAlgo, _ := jsonparser.GetString(jsonToken, "h") + return hashAlgo + } + } + + return "" +} + +// TODO: add checks +func TokenID(token string) (id string, err error) { + jsonToken, err := base64.StdEncoding.DecodeString(token) + if err != nil { + return "", err + } + + return jsonparser.GetString(jsonToken, "id") +} + +func TokenOrg(token string) string { + if strings.HasPrefix(token, B64JSONPrefix) { + if jsonToken, err := base64.StdEncoding.DecodeString(token); err == nil { + // Checking error in case if it is a legacy tooken which just by accided has the same b64JSON prefix + if org, err := jsonparser.GetString(jsonToken, "org"); err == nil { + return org + } + } + } + + // 24 is mongo bson id length + if len(token) > MongoBsonIdLength { + newToken := token[:MongoBsonIdLength] + _, err := hex.DecodeString(newToken) + if err == nil { + return newToken + } + } + + return "" +} + +var ( + HashSha256 = "sha256" + HashMurmur32 = "murmur32" + HashMurmur64 = "murmur64" + HashMurmur128 = "murmur128" +) + +func hashFunction(algorithm string) (hash.Hash, error) { + switch algorithm { + case HashSha256: + return sha256.New(), nil + case HashMurmur64: + return murmur3.New64(), nil + case HashMurmur128: + return murmur3.New128(), nil + case "", HashMurmur32: + return murmur3.New32(), nil + default: + return murmur3.New32(), fmt.Errorf("Unknown key hash function: %s. Falling back to murmur32.", algorithm) + } +} + +func HashStr(in string, withAlg ...string) string { + var algo string + if len(withAlg) > 0 && withAlg[0] != "" { + algo = withAlg[0] + } else { + algo = TokenHashAlgo(in) + } + + h, _ := hashFunction(algo) + h.Write([]byte(in)) + return hex.EncodeToString(h.Sum(nil)) +} + +func HashKey(in string, hashKey bool) string { + if !hashKey { + // Not hashing? Return the raw key + return in + } + return HashStr(in) +}