github.com/TykTechnologies/tyk/gateway
No package summary is available.
Package
Files: 109. Third party imports: 41. Imports from organisation: 18. Tests: 0. Benchmarks: 0.
Constants
const BackupApiKeyBase = "node-definition-backup:"
const BackupPolicyKeyBase = "node-policy-backup:"
// CoProcessDefaultKeyPrefix is used as a key prefix for this CP.
const CoProcessDefaultKeyPrefix = "coprocess-data:"
// EH_CoProcessHandler is used for event system, maintained here for backwards compatibility.
const EH_CoProcessHandler = event.CoProcessHandler
const JWKsAPIDef = "jwks_api_def_"
const ListDetailed = "detailed"
const LoopScheme = "tyk"
const (
MessageStreamingOnlySupportedInEE = "streaming is supported only in Tyk Enterprise Edition"
)
const OIDPREFIX = "openid"
const RPCKeyPrefix = "rpc:"
const RestrictedFieldValidationFailedLogMsg = "Error during GraphQL request restricted fields validation: '%s'"
// const used by cache middleware
const SAFE_METHODS = "SAFE_METHODS"
const UnexpectedSigningMethod = "Unexpected signing method"
const XTykAPIExpires = "x-tyk-api-expires"
const (
// EventQuotaExceeded is an alias maintained for backwards compatibility.
EventQuotaExceeded = event.QuotaExceeded
// RateLimitExceeded is an alias maintained for backwards compatibility.
EventRateLimitExceeded = event.RateLimitExceeded
// EventAuthFailure is an alias maintained for backwards compatibility.
EventAuthFailure = event.AuthFailure
// EventUpstreamOAuthError is an alias maintained for backwards compatibility.
UpstreamOAuthError = event.UpstreamOAuthError
// EventKeyExpired is an alias maintained for backwards compatibility.
EventKeyExpired = event.KeyExpired
// EventVersionFailure is an alias maintained for backwards compatibility.
EventVersionFailure = event.VersionFailure
// EventOrgQuotaExceeded is an alias maintained for backwards compatibility.
EventOrgQuotaExceeded = event.OrgQuotaExceeded
// EventOrgRateLimitExceeded is an alias maintained for backwards compatibility.
EventOrgRateLimitExceeded = event.OrgRateLimitExceeded
// EventTriggerExceeded is an alias maintained for backwards compatibility.
EventTriggerExceeded = event.TriggerExceeded
// EventBreakerTriggered is an alias maintained for backwards compatibility.
EventBreakerTriggered = event.BreakerTriggered
// EventBreakerTripped is an alias maintained for backwards compatibility.
EventBreakerTripped = event.BreakerTripped
// EventBreakerReset is an alias maintained for backwards compatibility.
EventBreakerReset = event.BreakerReset
// EventHOSTDOWN is an alias maintained for backwards compatibility.
EventHOSTDOWN = event.HostDown
// EventHOSTUP is an alias maintained for backwards compatibility.
EventHOSTUP = event.HostUp
// EventTokenCreated is an alias maintained for backwards compatibility.
EventTokenCreated = event.TokenCreated
// EventTokenUpdated is an alias maintained for backwards compatibility.
EventTokenUpdated = event.TokenUpdated
// EventTokenDeleted is an alias maintained for backwards compatibility.
EventTokenDeleted = event.TokenDeleted
)
// Statuses of the request, all are false-y except StatusOk and StatusOkAndIgnore
const (
VersionNotFound RequestStatus = "Version information not found"
VersionDoesNotExist RequestStatus = "This API version does not seem to exist"
VersionWhiteListStatusNotFound RequestStatus = "WhiteListStatus for path not found"
VersionExpired RequestStatus = "Api Version has expired, please check documentation or contact administrator"
APIExpired RequestStatus = "API has expired, please check documentation or contact administrator"
EndPointNotAllowed RequestStatus = "Requested endpoint is forbidden"
StatusOkAndIgnore RequestStatus = "Everything OK, passing and not filtering"
StatusOk RequestStatus = "Everything OK, passing"
StatusCached RequestStatus = "Cached path"
StatusTransform RequestStatus = "Transformed path"
StatusTransformResponse RequestStatus = "Transformed response"
StatusTransformJQ RequestStatus = "Transformed path with JQ"
StatusTransformJQResponse RequestStatus = "Transformed response with JQ"
StatusHeaderInjected RequestStatus = "Header injected"
StatusMethodTransformed RequestStatus = "Method Transformed"
StatusHeaderInjectedResponse RequestStatus = "Header injected on response"
StatusRedirectFlowByReply RequestStatus = "Exceptional action requested, redirecting flow!"
StatusHardTimeout RequestStatus = "Hard Timeout enforced on path"
StatusCircuitBreaker RequestStatus = "Circuit breaker enforced"
StatusURLRewrite RequestStatus = "URL Rewritten"
StatusVirtualPath RequestStatus = "Virtual Endpoint"
StatusRequestSizeControlled RequestStatus = "Request Size Limited"
StatusRequestTracked RequestStatus = "Request Tracked"
StatusRequestNotTracked RequestStatus = "Request Not Tracked"
StatusValidateJSON RequestStatus = "Validate JSON"
StatusValidateRequest RequestStatus = "Validate Request"
StatusInternal RequestStatus = "Internal path"
StatusGoPlugin RequestStatus = "Go plugin"
StatusPersistGraphQL RequestStatus = "Persist GraphQL"
StatusRateLimit RequestStatus = "Rate Limited"
)
const (
recordsBufferFlushInterval = 200 * time.Millisecond
recordsBufferForcedFlushInterval = 1 * time.Second
)
const (
playgroundJSTemplateName = "playground.js"
playgroundHTMLTemplateName = "index.html"
)
const (
Throttle HealthPrefix = "Throttle"
QuotaViolation HealthPrefix = "QuotaViolation"
KeyFailure HealthPrefix = "KeyFailure"
RequestLog HealthPrefix = "Request"
BlockedRequestLog HealthPrefix = "BlockedRequest"
)
const (
prefixEnv = "env://"
prefixSecrets = "secrets://"
prefixConsul = "consul://"
prefixVault = "vault://"
prefixKeys = "tyk-apis"
vaultSecretPath = "secret/data/"
)
// Constants for heartBeatStopSentinel indicators.
//
// Go 1.17 adds atomic.Value.Swap which is great, but 1.19
// adds atomic.Bool and other types. This is a go <1.13 cludge.
const (
// HeartBeatStarted Zero value - the handlers started
HeartBeatStarted = 0
// HeartBeatStopped value - the handlers invoked shutdown
HeartBeatStopped = 1
)
const (
WH_GET WebHookRequestMethod = "GET"
WH_PUT WebHookRequestMethod = "PUT"
WH_POST WebHookRequestMethod = "POST"
WH_DELETE WebHookRequestMethod = "DELETE"
WH_PATCH WebHookRequestMethod = "PATCH"
)
const (
// EH_WebHook is an alias maintained for backwards compatibility.
// it is the handler to register a webhook event.
EH_WebHook = event.WebHookHandler
// EH_JSVMHandler is aliased for backwards compatibility.
EH_JSVMHandler = event.JSVMHandler
// EH_LogHandler is an alias maintained for backwards compatibility.
// It is used to register log handler on an event.
EH_LogHandler = event.LogHandler
)
// Notification codes for new and refresh codes
const (
newAccessToken OAuthNotificationType = "new"
refreshAccessToken OAuthNotificationType = "refresh"
)
const (
defaultTemplateName = "error"
defaultTemplateFormat = "json"
defaultContentType = header.ApplicationJSON
MsgAuthFieldMissing = "Authorization field missing"
MsgApiAccessDisallowed = "Access to this API has been disallowed"
MsgBearerMailformed = "Bearer token malformed"
MsgKeyNotAuthorized = "Key not authorised"
MsgOauthClientRevoked = "Key not authorised. OAuth client access was revoked"
MsgKeyNotAuthorizedUnexpectedSigningMethod = "Key not authorized: Unexpected signing method"
MsgCertificateExpired = "Certificate has expired"
)
const (
keyDataDeveloperID = "tyk_developer_id"
keyDataDeveloperEmail = "tyk_developer_email"
)
const (
Pass = model.Pass
Fail = model.Fail
Warn = model.Warn
Datastore = model.Datastore
System = model.System
)
const (
defaultTimeout = 10
defaultSampletTriggerLimit = 3
)
const (
// Zero value - the service is open and ready to use
OPEN = 0
// Closed value - the service shouldn't be used
CLOSED = 1
)
const (
UnHealthyHostMetaDataTargetKey = "target_url"
UnHealthyHostMetaDataAPIKey = "api_id"
UnHealthyHostMetaDataHostKey = "host_name"
PollerCacheKey = "PollerActiveInstanceID"
PoolerHostSentinelKeyPrefix = "PollerCheckerInstance:"
UptimeAnalytics_KEYNAME = "tyk-uptime-analytics"
)
const (
statsdCmdKindEvent statsdCmdKind = iota
statsdCmdKindEventErr
statsdCmdKindTiming
statsdCmdKindGauge
statsdCmdKindComplete
statsdCmdKindFlush
statsdCmdKindDrain
statsdCmdKindStop
)
const (
handlerPathGraphQLProxyUpstream = "/graphql-proxy-upstream"
handlerPathGraphQLProxyUpstreamError = "/graphql-proxy-upstream-error"
handlerPathRestDataSource = "/rest-data-source"
handlerPathRestDataSourceV3 = "/rest-data-source-v2"
handlerPathGraphQLDataSource = "/graphql-data-source"
handlerPathHeadersRestDataSource = "/rest-headers-data-source"
handlerSubgraphAccounts = "/subgraph-accounts"
handlerSubgraphAccountsModified = "/subgraph-accounts-modified"
handlerSubgraphReviews = "/subgraph-reviews"
// We need a static port so that the urls can be used in static
// test data, and to prevent the requests from being randomized
// for checksums. Port 16500 should be obscure and unused.
testHttpListen = "127.0.0.1:16500"
// Accepts any http requests on /, only allows GET on /get, etc.
// All return a JSON with request info.
TestHttpAny = "http://" + testHttpListen
TestHttpGet = TestHttpAny + "/get"
testHttpPost = TestHttpAny + "/post"
testGraphQLProxyUpstream = TestHttpAny + handlerPathGraphQLProxyUpstream
testGraphQLProxyUpstreamError = TestHttpAny + handlerPathGraphQLProxyUpstreamError
testGraphQLDataSource = TestHttpAny + handlerPathGraphQLDataSource
testRESTDataSource = TestHttpAny + handlerPathRestDataSource
testRESTDataSourceV3 = TestHttpAny + handlerPathRestDataSourceV3
testRESTHeadersDataSource = TestHttpAny + handlerPathHeadersRestDataSource
testSubgraphAccounts = TestHttpAny + handlerSubgraphAccounts
testSubgraphAccountsModified = TestHttpAny + handlerSubgraphAccountsModified
testSubgraphReviews = TestHttpAny + handlerSubgraphReviews
testHttpJWK = TestHttpAny + "/jwk.json"
testHttpJWKLegacy = TestHttpAny + "/jwk-legacy.json"
testHttpBundles = TestHttpAny + "/bundles/"
testReloadGroup = TestHttpAny + "/groupReload"
// Nothing should be listening on port 16501 - useful for
// testing TCP and HTTP failures.
testHttpFailure = "127.0.0.1:16501"
testHttpFailureAny = "http://" + testHttpFailure
MockOrgID = "507f1f77bcf86cd799439011"
NonCanonicalHeaderKey = "X-CertificateOuid"
)
const (
sessionFailNone sessionFailReason = iota
sessionFailRateLimit
sessionFailQuota
sessionFailInternalServerError
)
const (
// QuotaKeyPrefix serves as a standard prefix for generating quota keys.
QuotaKeyPrefix = "quota-"
// RateLimitKeyPrefix serves as a standard prefix for generating rate limiter keys.
RateLimitKeyPrefix = rate.LimiterKeyPrefix
// SentinelRateLimitKeyPostfix is appended to the rate limiting key to combine into a sentinel key.
SentinelRateLimitKeyPostfix = ".BLOCKED"
)
const (
accessToken = "access_token"
refreshToken = "refresh_token"
)
const (
ErrOAuthAuthorizationFieldMissing = "oauth.auth_field_missing"
ErrOAuthAuthorizationFieldMalformed = "oauth.auth_field_malformed"
ErrOAuthKeyNotFound = "oauth.key_not_found"
ErrOAuthClientDeleted = "oauth.client_deleted"
)
const (
mwStatusRespond = middleware.StatusRespond
DEFAULT_ORG_SESSION_EXPIRATION = int64(604800)
)
// These enums fix the prefix to use when storing various OAuth keys and data, since we
// delegate everything to the osin framework
const (
prefixAuth = "oauth-authorize."
prefixClient = "oauth-clientid."
prefixAccess = "oauth-access."
prefixRefresh = "oauth-refresh."
prefixClientset = "oauth-clientset."
prefixClientIndexList = "oauth-client-index."
prefixClientTokens = "oauth-client-tokens."
)
const (
LDAPStorageEngine apidef.StorageEngineCode = "ldap"
RPCStorageEngine apidef.StorageEngineCode = "rpc"
)
// Enums representing the various statuses for a VersionInfo Path match during a
// proxy request
const (
_ URLStatus = iota
Ignored
WhiteList
BlackList
MockResponse
Cached
Transformed
TransformedJQ
HeaderInjected
HeaderInjectedResponse
TransformedResponse
TransformedJQResponse
HardTimeout
CircuitBreaker
URLRewrite
VirtualPath
RequestSizeLimit
MethodTransformed
RequestTracked
RequestNotTracked
ValidateJSONRequest
Internal
GoPlugin
PersistGraphQL
RateLimit
)
const (
defaultSignatureErrorCode = http.StatusUnauthorized
defaultSignatureErrorMessage = "Request signature verification failed"
)
const (
ErrAuthAuthorizationFieldMissing = "auth.auth_field_missing"
ErrAuthKeyNotFound = "auth.key_not_found"
ErrAuthCertNotFound = "auth.cert_not_found"
ErrAuthCertExpired = "auth.cert_expired"
ErrAuthKeyIsInvalid = "auth.key_is_invalid"
MsgNonExistentKey = "Attempted access with non-existent key."
MsgNonExistentCert = "Attempted access with non-existent cert."
MsgInvalidKey = "Attempted access with invalid key."
)
const (
ResetQuota string = "resetQuota"
CertificateRemoved string = "CertificateRemoved"
CertificateAdded string = "CertificateAdded"
OAuthRevokeToken string = "oAuthRevokeToken"
OAuthRevokeAccessToken string = "oAuthRevokeAccessToken"
OAuthRevokeRefreshToken string = "oAuthRevokeRefreshToken"
OAuthRevokeAllTokens string = "revoke_all_tokens"
OauthClientAdded string = "OauthClientAdded"
OauthClientRemoved string = "OauthClientRemoved"
OauthClientUpdated string = "OauthClientUpdated"
)
const (
ComplexityFailReasonNone ComplexityFailReason = iota
ComplexityFailReasonInternalError
ComplexityFailReasonDepthLimitExceeded
)
const (
GranularAccessFailReasonNone GranularAccessFailReason = iota
GranularAccessFailReasonInternalError
GranularAccessFailReasonValidationError
)
const (
metaLabel = "$tyk_meta."
contextLabel = "$tyk_context."
consulLabel = "$secret_consul."
vaultLabel = "$secret_vault."
envLabel = "$secret_env."
secretsConfLabel = "$secret_conf."
triggerKeyPrefix = "trigger"
triggerKeySep = "-"
)
const (
checkIdleMemConnInterval = 5 * time.Minute
maxIdleMemConnDuration = time.Minute
inMemNetworkName = "in-mem-network"
inMemNetworkType = "memu"
)
const (
KID = "kid"
SUB = "sub"
HMACSign = "hmac"
RSASign = "rsa"
ECDSASign = "ecdsa"
)
const (
upstreamCacheHeader = "x-tyk-cache-action-set"
upstreamCacheTTLHeader = "x-tyk-cache-action-set-ttl"
)
const (
RedisPubSubChannel = "tyk.cluster.notifications"
NoticeApiUpdated NotificationCommand = "ApiUpdated"
NoticeApiRemoved NotificationCommand = "ApiRemoved"
NoticeApiAdded NotificationCommand = "ApiAdded"
NoticeGroupReload NotificationCommand = "GroupReload"
NoticePolicyChanged NotificationCommand = "PolicyChanged"
NoticeConfigUpdate NotificationCommand = "NoticeConfigUpdated"
NoticeDashboardZeroConf NotificationCommand = "NoticeDashboardZeroConf"
NoticeDashboardConfigRequest NotificationCommand = "NoticeDashboardConfigRequest"
NoticeGatewayConfigResponse NotificationCommand = "NoticeGatewayConfigResponse"
NoticeGatewayDRLNotification NotificationCommand = "NoticeGatewayDRLNotification"
KeySpaceUpdateNotification NotificationCommand = "KeySpaceUpdateNotification"
OAuthPurgeLapsedTokens NotificationCommand = "OAuthPurgeLapsedTokens"
// NoticeDeleteAPICache is the command with which event is emitted from dashboard to invalidate cache for an API.
NoticeDeleteAPICache NotificationCommand = "DeleteAPICache"
NoticeUserKeyReset NotificationCommand = "UserKeyReset"
)
const acceptCode = "X-Tyk-Accept-Example-Code"
const acceptContentType = "Accept"
const acceptExampleName = "X-Tyk-Accept-Example-Name"
const altHeaderSpec = "x-aux-date"
const analyticsKeyName = "tyk-system-analytics"
const appName = "tyk-gateway"
const arrayName = "tyk_array"
const (
cachedResponseHeader = "x-tyk-cached-response"
)
const (
// Check OAuth client deleted interval in seconds.
checkOAuthClientDeletedInterval = 1
)
const cmdChanBuffSize = 8192 // random-ass-guess
const coreJS = `
var TykJS = {
TykMiddleware: {
MiddlewareComponentMeta: function(configuration) {
this.configuration = configuration
}
},
TykEventHandlers: {
EventHandlerComponentMeta: function() {}
}
}
TykJS.TykMiddleware.MiddlewareComponentMeta.prototype.ProcessRequest = function(request, session, config) {
log("Process Request Not Implemented")
return request
}
TykJS.TykMiddleware.MiddlewareComponentMeta.prototype.DoProcessRequest = function(request, session, config) {
request.Body = b64dec(request.Body)
var processed_request = this.ProcessRequest(request, session, config)
if (!processed_request) {
log("Middleware didn't return request object!")
return
}
// Reset the headers object
processed_request.Request.Headers = {}
processed_request.Request.Body = b64enc(processed_request.Request.Body)
return JSON.stringify(processed_request)
}
// The user-level middleware component
TykJS.TykMiddleware.NewMiddleware = function(configuration) {
TykJS.TykMiddleware.MiddlewareComponentMeta.call(this, configuration)
}
// Set up object inheritance
TykJS.TykMiddleware.NewMiddleware.prototype = Object.create(TykJS.TykMiddleware.MiddlewareComponentMeta.prototype)
TykJS.TykMiddleware.NewMiddleware.prototype.constructor = TykJS.TykMiddleware.NewMiddleware
TykJS.TykMiddleware.NewMiddleware.prototype.NewProcessRequest = function(callback) {
this.ProcessRequest = callback
}
TykJS.TykMiddleware.NewMiddleware.prototype.ReturnData = function(request, session) {
return {Request: request, SessionMeta: session}
}
TykJS.TykMiddleware.NewMiddleware.prototype.ReturnAuthData = function(request, session) {
return {Request: request, Session: session}
}
// Event Handler implementation
TykJS.TykEventHandlers.EventHandlerComponentMeta.prototype.DoProcessEvent = function(event, context) {
// call the handler
log("Calling built - in handle")
this.Handle(event, context)
return
}
TykJS.TykEventHandlers.EventHandlerComponentMeta.prototype.Handle = function(request, context) {
log("Handler not implemented!")
return request
}
// The user-level event handler component
TykJS.TykEventHandlers.NewEventHandler = function() {
TykJS.TykEventHandlers.EventHandlerComponentMeta.call(this)
}
// Set up object inheritance for events
TykJS.TykEventHandlers.NewEventHandler.prototype = Object.create(TykJS.TykEventHandlers.EventHandlerComponentMeta.prototype)
TykJS.TykEventHandlers.NewEventHandler.prototype.constructor = TykJS.TykEventHandlers.NewEventHandler
TykJS.TykEventHandlers.NewEventHandler.prototype.NewHandler = function(callback) {
this.Handle = callback
};`
const dateHeaderSpec = "Date"
const defaultBasicAuthTTL int64 = 60
const defaultJSVMTimeout = 5
// Check for recursion
const defaultLoopLevelLimit = 5
const defaultProxyTimeout float64 = 30
const jsonContentType = "application/json"
const jwkTestJson = `{
"keys": [
{
"use": "sig",
"kty": "RSA",
"kid": "12345",
"alg": "RS256",
"n": "yqZ4rwKF8qCExS7kpY4cnJa_37FMkJNkalZ3OuslLB0oRL8T4c94kdF4aeNzSFkSe2n99IBI6Ssl79vbfMZb-t06L0Q94k-_P37x7-_RJZiff4y1VGjrnrnMI2iu9l4iBBRYzNmG6eblroEMMWlgk5tysHgxB59CSNIcD9gqk1hx4n_FgOmvKsfQgWHNlPSDTRcWGWGhB2_XgNVYG2pOlQxAPqLhBHeqGTXBbPfGF9cHzixpsPr6GtbzPwhsQ_8bPxoJ7hdfn-rzztks3d6-HWURcyNTLRe0mjXjjee9Z6-gZ-H-fS4pnP9tqT7IgU6ePUWTpjoiPtLexgsAa_ctjQ",
"e": "AQAB"
}
]
}`
const jwkTestJsonLegacy = `{
"keys": [{
"alg": "RS256",
"kty": "RSA",
"use": "sig",
"x5c": ["Ci0tLS0tQkVHSU4gUFVCTElDIEtFWS0tLS0tCk1JSUJJakFOQmdrcWhraUc5dzBCQVFFRkFBT0NBUThBTUlJQkNnS0NBUUVBeXFaNHJ3S0Y4cUNFeFM3a3BZNGMKbkphLzM3Rk1rSk5rYWxaM091c2xMQjBvUkw4VDRjOTRrZEY0YWVOelNGa1NlMm45OUlCSTZTc2w3OXZiZk1aYgordDA2TDBROTRrKy9QMzd4NysvUkpaaWZmNHkxVkdqcm5ybk1JMml1OWw0aUJCUll6Tm1HNmVibHJvRU1NV2xnCms1dHlzSGd4QjU5Q1NOSWNEOWdxazFoeDRuL0ZnT212S3NmUWdXSE5sUFNEVFJjV0dXR2hCMi9YZ05WWUcycE8KbFF4QVBxTGhCSGVxR1RYQmJQZkdGOWNIeml4cHNQcjZHdGJ6UHdoc1EvOGJQeG9KN2hkZm4rcnp6dGtzM2Q2KwpIV1VSY3lOVExSZTBtalhqamVlOVo2K2daK0grZlM0cG5QOXRxVDdJZ1U2ZVBVV1Rwam9pUHRMZXhnc0FhL2N0CmpRSURBUUFCCi0tLS0tRU5EIFBVQkxJQyBLRVktLS0tLQo="],
"n": "xofiG8gsnv9-I_g-5OWTLhaZtgAGq1QEsBCPK9lmLqhuonHe8lT-nK1DM49f6J9QgaOjZ3DB50QkhBysnIFNcXFyzaYIPMoccvuHLPgdBawX4WYKm5gficD0WB0XnTt4sqTI5usFpuop9vvW44BwVGhRqMT7c11gA8TSWMBxDI4A5ARc4MuQtfm64oN-JQodSztArwb9wcmH8WrBvSUkR4pyi9MT8W27gqJ2e2Xn8jgGnswNQWOyCTN84PawOYaN-2ORHeIea1g-URln1bofcHN73vZCIrVbE6iA2D7Ybh22AVrCfunekEDEe2GZfLZLejiZiBWG7enJhcrQIzAQGw",
"e": "AQAB",
"kid": "12345",
"x5t": "12345"
}]
}`
const jwtECDSAPrivateKey = `-----BEGIN PRIVATE KEY-----
MHcCAQEEIFjaz7TJpBOHmQttPypGRh3rqaXvRpsWE/EWUiLzc6veoAoGCCqGSM49
AwEHoUQDQgAEDmKdIVHH9D5xkUiMJvo4T9H8yU+QYOIBlX5DYpJFtEvzTs4SsXYC
tFsPk7c31tOpMuS8aQiLsXR82VMLqQBf1w==
-----END PRIVATE KEY-----`
const jwtECDSAPublicKey = `-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEDmKdIVHH9D5xkUiMJvo4T9H8yU+Q
YOIBlX5DYpJFtEvzTs4SsXYCtFsPk7c31tOpMuS8aQiLsXR82VMLqQBf1w==
-----END PUBLIC KEY-----`
// openssl genrsa -out app.rsa
const jwtRSAPrivKey = `
-----BEGIN RSA PRIVATE KEY-----
MIIEpQIBAAKCAQEAyqZ4rwKF8qCExS7kpY4cnJa/37FMkJNkalZ3OuslLB0oRL8T
4c94kdF4aeNzSFkSe2n99IBI6Ssl79vbfMZb+t06L0Q94k+/P37x7+/RJZiff4y1
VGjrnrnMI2iu9l4iBBRYzNmG6eblroEMMWlgk5tysHgxB59CSNIcD9gqk1hx4n/F
gOmvKsfQgWHNlPSDTRcWGWGhB2/XgNVYG2pOlQxAPqLhBHeqGTXBbPfGF9cHzixp
sPr6GtbzPwhsQ/8bPxoJ7hdfn+rzztks3d6+HWURcyNTLRe0mjXjjee9Z6+gZ+H+
fS4pnP9tqT7IgU6ePUWTpjoiPtLexgsAa/ctjQIDAQABAoIBAECWvnBJRZgHQUn3
oDiECup9wbnyMI0D7UVXObk1qSteP69pl1SpY6xWLyLQs7WjbhiXt7FuEc7/SaAh
Wttx/W7/g8P85Bx1fmcmdsYakXaCJpPorQKyTibQ4ReIDfvIFN9n/MWNr0ptpVbx
GonFJFrneK52IGplgCLllLwYEbnULYcJc6E25Ro8U2gQjF2r43PDa07YiDrmB/GV
QQW4HTo+CA9rdK0bP8GpXgc0wpmBhx/t/YdnDg6qhzyUMk9As7JrAzYPjHO0cRun
vhA/aG/mdMmRumY75nj7wB5U5DgstsN2ER75Pjr1xe1knftIyNm15AShCPfLaLGo
dA2IpwECgYEA5E8h6ssa7QroCGwp/N0wSJW41hFYGygbOEg6yPWTJkqmMZVduD8X
/KFqJK4LcIbFQuR28+hWJpHm/RF1AMRhbbWkAj6h02gv5izFwDiFKev5paky4Evg
G8WfUOmSZ1D+fVxwaoG0OaRZpCovUTxYig3xrI659DMeKqpQ7e8l9ekCgYEA4zql
l4P4Dn0ydr+TI/s4NHIQHkaLQAVk3OWwyKowijXd8LCtuZRA1NKSpqQ4ZXi0B17o
9zzF5jEUjws3qWv4PKWdxJu3y+h/etsg7wxUeNizbY2ooUGeMbk0tWxJihbgaI7E
XxLIT50F3Ky4EJ2cUL9GmJ+gLCw0KIaVbkiyYAUCgYEA0WyVHB76r/2VIkS1rzHm
HG7ageKfAyoi7dmzsqsxM6q+EDWHJn8Zra8TAlp0O+AkClwvkUTJ4c9sJy9gODfr
dwtrSnPRVW74oRbovo4Z+H5xHbi65mwzQsZggYP/u63cA3pL1Cbt/wH3CFN52/aS
8PAhg7vYb1yEi3Z3jgoUtCECgYEAhSPX4u9waQzyhKG7lVmdlR1AVH0BGoIOl1/+
NZWC23i0klLzd8lmM00uoHWYldwjoC38UuFJE5eudCIeeybITMC9sHWNO+z+xP2g
TnDrDePrPkXCiLnp9ziNqb/JVyAQXTNJ3Gsk84EN7j9Fmna/IJDyzHq7XyaHaTdy
VyxBWAECgYEA4jYS07bPx5UMhKiMJDqUmDfLNFD97XwPoJIkOdn6ezqeOSmlmo7t
jxHLbCmsDOAsCU/0BlLXg9wMU7n5QKSlfTVGok/PU0rq2FUXQwyKGnellrqODwFQ
YGivtXBGXk1hlVYlje1RB+W6RQuDAegI5h8vl8pYJS9JQH0wjatsDaE=
-----END RSA PRIVATE KEY-----
`
const jwtSecret = "9879879878787878"
const letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
// identifies that field value was hidden before output to the log
const logHiddenValue = "<hidden>"
const mascot = `
. ╲╲╲╲╲╲
. ╲╲╲╲╲╲╲╲╲
. *******╲╲╲╲╲╲╲╲╲╲╲
. **********╲╲╲╲╲╲╲╲╲╲
. ***************╲╲╲╲╲╲╲╲╲╲
. ******************╲╲╲╲╲╲╲╲╲
. ********************╲╲╲╲╲╲╲╲╲
. ***********************╲╲╲╲╲╲╲╲
. *************************** ╲╲╲╲╲╲
. **************************** ╲╲╲
. ***** ******* **** _______ _ _
. ****** ***** ***** |__ __| | | (_)
. **************************** | | _ _ | | __ _ ___
. ***************************** | || | | || |/ / | | / _ \
. ****************************** | || |_| || < _ | || (_) |
. ******************************** |_| \__, ||_|\_\(_)|_| \___/
. ********************************* __/ |
. *** ************* ********* **** |___/
. *********************
. *****************
. ******* ******
. ***** *****
. ****** ******
`
const mascotHeaderKeyFormat = "X-Mascot-%03d"
const maxUdpBytes = 1440 // 1500(Ethernet MTU) - 60(Max UDP header size
const oAuthClientNotFound = "OAuth client not found"
const (
oAuthClientTokensKeyPattern = "oauth-data.*oauth-client-tokens.*"
)
const oAuthNotPropagatedErr = "OAuth client list isn't available or hasn't been propagated yet."
const oauthClientIdEmpty = "client_id is required"
const oauthClientSecretEmpty = "client_secret is required"
const oauthClientSecretWrong = "client secret is wrong"
const oauthTokenEmpty = "token is required"
const (
rateLimitEndpoint = "/tyk/rate-limits/"
)
const sampleAPI = `{
"api_id": "test",
"org_id": "default",
"use_keyless": true,
"definition": {
"location": "header",
"key": "version"
},
"auth": {
"auth_header_name": "authorization"
},
"version_data": {
"default_version": "Default",
"not_versioned": true,
"versions": {
"v1": {
"name": "v1",
"use_extended_paths": true
}
}
},
"proxy": {
"listen_path": "/sample",
"target_url": "` + TestHttpAny + `"
},
"graphql": {
"enabled": false,
"execution_mode": "executionEngine",
"version": "",
"schema": "` + testComposedSchema + `",
"type_field_configurations": [
` + testGraphQLDataSourceConfiguration + `,
` + testRESTDataSourceConfiguration + `
],
"engine": {
"field_configs": [
{
"type_name": "Query",
"field_name": "people",
"disable_default_mapping": true,
"path": [""]
},
{
"type_name": "Query",
"field_name": "headers",
"disable_default_mapping": true,
"path": [""]
}
],
"data_sources": [
` + testRESTDataSourceConfigurationV2 + `,
` + testGraphQLDataSourceConfigurationV2 + `,
` + testRESTHeadersDataSourceConfigurationV2 + `
]
},
"playground": {
"enabled": false,
"path": "/playground"
}
}
}`
const testComposedSchema = "type Query {countries: [Country] headers: [Header]} " +
"extend type Query {people: [Person]}" +
"type Person {name: String country: Country} " +
"type Country {code: String name: String} " +
"type Header {name:String value: String}"
const testComposedSchemaNotExtended = "type Query {countries: [Country] headers: [Header] people: [Person]} " +
"type Person {name: String country: Country} " +
"type Country {code: String name: String} " +
"type Header {name:String value: String}"
const testGraphQLDataSourceConfiguration = `
{
"type_name": "Query",
"field_name": "countries",
"mapping": {
"disabled": false,
"path": "countries"
},
"data_source": {
"kind": "GraphQLDataSource",
"data_source_config": {
"url": "` + testGraphQLDataSource + `",
"method": "POST"
}
}
}
`
const testGraphQLDataSourceConfigurationV2 = `
{
"kind": "GraphQL",
"name": "countries",
"internal": true,
"root_fields": [
{ "type": "Query", "fields": ["countries"] }
],
"config": {
"url": "` + testGraphQLDataSource + `",
"method": "POST"
}
}`
const testRESTDataSourceConfiguration = `
{
"type_name": "Query",
"field_name": "people",
"mapping": {
"disabled": false,
"path": ""
},
"data_source": {
"kind": "HTTPJSONDataSource",
"data_source_config": {
"url": "` + testRESTDataSource + `",
"method": "GET",
"body": "",
"headers": [],
"default_type_name": "People",
"status_code_type_name_mappings": [
{
"status_code": 200,
"type_name": ""
}
]
}
}
}`
const testRESTDataSourceConfigurationV2 = `
{
"kind": "REST",
"name": "people",
"internal": true,
"root_fields": [
{ "type": "Query", "fields": ["people"] }
],
"config": {
"url": "` + testRESTDataSource + `",
"method": "GET",
"headers": {},
"query": [],
"body": ""
}
}`
const testRESTDataSourceConfigurationV3 = `
{
"kind": "REST",
"name": "people",
"internal": true,
"root_fields": [
{ "type": "Query", "fields": ["people"] }
],
"config": {
"url": "` + testRESTDataSourceV3 + `",
"method": "GET",
"headers": {},
"query": [],
"body": ""
}
}`
const testRESTHeadersDataSourceConfigurationV2 = `
{
"kind": "REST",
"name": "headers",
"internal": true,
"root_fields": [
{ "type": "Query", "fields": ["headers"] }
],
"config": {
"url": "` + testRESTHeadersDataSource + `",
"method": "GET",
"headers": {
"static": "barbaz",
"injected": "{{ .request.headers.injected }}",
"context": "$tyk_context.headers_From_Request",
"does-exist-already": "ds-does-exist-already"
},
"query": [],
"body": ""
}
}`
Vars
var (
// ErrEventHandlerDisabled is returned when the event handler is disabled.
ErrEventHandlerDisabled = errors.New("event handler disabled")
)
var (
ErrPoliciesFetchFailed = errors.New("fetch policies request login failure")
)
var (
ErrRequestMalformed = errors.New("request malformed")
)
GatewayFireSystemEvent declared as global variable, set during gw start
var GatewayFireSystemEvent func(name apidef.TykEvent, meta interface{})
var GetJWK = getJWK
var JWKCache cache.Repository = cache.New(240, 30)
var LoopHostRE = regexp.MustCompile("tyk://([^/]+)")
var NonAlphaNumRE = regexp.MustCompile("[^A-Za-z0-9]+")
var TykErrors = make(map[string]config.TykError)
var (
grpcConnection *grpc.ClientConn
grpcClient coprocess.DispatcherClient
)
var (
GlobalRate = ratecounter.NewRateCounter(1 * time.Second)
orgSessionExpiryCache singleflight.Group
)
var (
globalMu sync.Mutex
log = logger.Get()
mainLog = log.WithField("prefix", "main")
pubSubLog = log.WithField("prefix", "pub-sub")
rawLog = logger.GetRaw()
memProfFile *os.File
// confPaths is the series of paths to try to use as config files. The
// first one to exist will be used. If none exists, a default config
// will be written to the first path in the list.
//
// When --conf=foo is used, this will be replaced by []string{"foo"}.
confPaths = []string{
"tyk.conf",
// TODO: add ~/.config/tyk/tyk.conf here?
"/etc/tyk/tyk.conf",
}
ErrSyncResourceNotKnown = errors.New("unknown resource to sync")
)
var (
// List of common OAuth Client ID claims used by IDPs:
oauthClientIDClaims = []string{
"clientId", // Keycloak
"cid", // OKTA
"client_id", // Gluu
}
ErrNoSuitableUserIDClaimFound = errors.New("no suitable claims for user ID were found")
ErrEmptyUserIDInSubClaim = errors.New("found an empty user ID in sub claim")
)
var (
ProxyingRequestFailedErr = errors.New("there was a problem proxying the request")
GraphQLDepthLimitExceededErr = errors.New("depth limit exceeded")
)
var (
externalOAuthJWKCache cache.Repository = cache.New(240, 30)
externalOAuthIntrospectionCache *introspectionCache
ErrTokenValidationFailed = errors.New("error happened during the access token validation")
ErrKIDNotAString = errors.New("kid is not a string")
ErrNoMatchingKIDFound = errors.New("no matching KID could be found")
)
var (
bundleBackoffMultiplier float64 = 2
bundleMaxBackoffRetries uint64 = 4
)
var (
supportedDrivers = []apidef.MiddlewareDriver{apidef.PythonDriver, apidef.LuaDriver, apidef.GrpcDriver}
loadedDrivers = map[apidef.MiddlewareDriver]coprocess.Dispatcher{}
)
var (
VERSION = build.Version
Commit = build.Commit
)
var (
ctxData = httpctx.NewValue[map[string]any](ctx.ContextData)
ctxGetData = ctxData.Get
ctxSetData = ctxData.Set
setContext = core.SetContext
// how is type safety avoided: exhibit A, old school generics
setCtxValue = func(h *http.Request, key, value any) {
ctxvalue := httpctx.NewValue[any](key)
h = ctxvalue.Set(h, value)
}
EncodeRequestToEvent = event.EncodeRequestToEvent
)
var (
onceStartAllHostsDown sync.Once
allHostsDownURL string
)
var applicationGCStats = debug.GCStats{}
var basicAuthCache = cache.New(60, 3600)
var cacheGroup singleflight.Group
var certLog = log.WithField("prefix", "certs")
Deprecated: use tls.CipherSuites() now
var cipherSuites = map[string]uint16{
"TLS_RSA_WITH_RC4_128_SHA": 0x0005,
"TLS_RSA_WITH_3DES_EDE_CBC_SHA": 0x000a,
"TLS_RSA_WITH_AES_128_CBC_SHA": 0x002f,
"TLS_RSA_WITH_AES_256_CBC_SHA": 0x0035,
"TLS_RSA_WITH_AES_128_CBC_SHA256": 0x003c,
"TLS_RSA_WITH_AES_128_GCM_SHA256": 0x009c,
"TLS_RSA_WITH_AES_256_GCM_SHA384": 0x009d,
"TLS_ECDHE_ECDSA_WITH_RC4_128_SHA": 0xc007,
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA": 0xc009,
"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA": 0xc00a,
"TLS_ECDHE_RSA_WITH_RC4_128_SHA": 0xc011,
"TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA": 0xc012,
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA": 0xc013,
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA": 0xc014,
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256": 0xc023,
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256": 0xc027,
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256": 0xc02f,
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256": 0xc02b,
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384": 0xc030,
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384": 0xc02c,
"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305": 0xcca8,
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305": 0xcca9,
}
var consulMatch = regexp.MustCompile(`\$secret_consul.([A-Za-z0-9\/\-\.]+)`)
var contextMatch = regexp.MustCompile(`\$tyk_context.([A-Za-z0-9_\-\.]+)`)
var corsHeaders = []string{
"Access-Control-Allow-Origin",
"Access-Control-Expose-Headers",
"Access-Control-Max-Age",
"Access-Control-Allow-Credentials",
"Access-Control-Allow-Methods",
"Access-Control-Allow-Headers"}
var createOauthClientSecret = func() string {
secret := uuid.New()
return base64.StdEncoding.EncodeToString([]byte(secret))
}
var dashClient *http.Client
var dashLog = log.WithField("prefix", "dashboard")
var defaultStatsDOptions = StatsDSinkOptions{SanitizationFunc: sanitizeKey}
var defaultUserAgent = "Tyk/" + VERSION
var defaultWorkerPoolSize = runtime.NumCPU()
var (
dispatcherFuncs = map[string]interface{}{
"Login": func(clientAddr, userKey string) bool {
return false
},
"LoginWithGroup": func(clientAddr string, groupData *model.GroupLoginRequest) bool {
return false
},
"GetKey": func(keyName string) (string, error) {
return "", nil
},
"SetKey": func(ibd *model.InboundData) error {
return nil
},
"GetExp": func(keyName string) (int64, error) {
return 0, nil
},
"GetKeys": func(keyName string) ([]string, error) {
return nil, nil
},
"DeleteKey": func(keyName string) (bool, error) {
return true, nil
},
"DeleteRawKey": func(keyName string) (bool, error) {
return true, nil
},
"GetKeysAndValues": func(searchString string) (*model.KeysValuesPair, error) {
return nil, nil
},
"GetKeysAndValuesWithFilter": func(searchString string) (*model.KeysValuesPair, error) {
return nil, nil
},
"DeleteKeys": func(keys []string) (bool, error) {
return true, nil
},
"DeleteRawKeys": func(keys []string) (bool, error) {
return true, nil
},
"Decrement": func(keyName string) error {
return nil
},
"IncrememntWithExpire": func(ibd *model.InboundData) (int64, error) {
return 0, nil
},
"AppendToSet": func(ibd *model.InboundData) error {
return nil
},
"SetRollingWindow": func(ibd *model.InboundData) (int, error) {
return 0, nil
},
"GetApiDefinitions": func(dr *model.DefRequest) (string, error) {
return "", nil
},
"GetPolicies": func(orgId string) (string, error) {
return "", nil
},
"PurgeAnalyticsData": func(data string) error {
return nil
},
"CheckReload": func(clientAddr, orgId string) (bool, error) {
return false, nil
},
"GetKeySpaceUpdate": func(clientAddr, orgId string) ([]string, error) {
return nil, nil
},
"GetGroupKeySpaceUpdate": func(clientAddr string, groupData *model.GroupKeySpaceRequest) ([]string, error) {
return nil, nil
},
"Ping": func() bool {
return false
},
"Disconnect": func(clientAddr string, groupData *model.GroupLoginRequest) error {
return nil
},
}
)
var dollarMatch = regexp.MustCompile(`\$\d+`)
var envRegex = regexp.MustCompile(`env://([^"]+)`)
var envValueMatch = regexp.MustCompile(`\$secret_env.([A-Za-z0-9_\-\.]+)`)
var errCustomBodyResponse = errors.New("errCustomBodyResponse")
var errUnauthorized = errors.New("Unauthorized")
var getIpAddress = netutil.GetIpAddress
Hop-by-hop headers. These are removed when sent to the backend. http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html
var hopHeaders = []string{
"Connection",
"Proxy-Connection", // non-standard but still sent by libcurl and rejected by e.g. google
"Keep-Alive",
"Proxy-Authenticate",
"Proxy-Authorization",
"Te", // canonicalized version of "TE"
"Trailer", // not Trailers per URL above; http://www.rfc-editor.org/errata_search.php?eid=4522
"Transfer-Encoding",
"Upgrade",
}
httpScheme matches http://* and https://*, case insensitive
var httpScheme = regexp.MustCompile(`^(?i)https?://`)
var idleConnTimeout = 90
var instrument = health.NewStream()
var instrumentationEnabled bool
memConnClient is used to make request to internal APIs.
var memConnClient = &http.Client{
Transport: &http.Transport{
DialContext: func(ctx context.Context, _, addr string) (net.Conn, error) {
provider, err := getMemConnProvider(addr)
if err != nil {
return nil, err
}
return provider.DialContext(ctx, inMemNetworkType, inMemNetworkName)
},
},
}
var memConnProviders = &struct {
mtx sync.RWMutex
m map[string]*memConnProvider
}{
m: make(map[string]*memConnProvider),
}
var metaMatch = regexp.MustCompile(`\$tyk_meta.([A-Za-z0-9_\-\.]+)`)
var oneMascot sync.Once
var orgActiveMap sync.Map
var orgChanMap = orgChanMapMu{channels: map[string](chan bool){}}
var playgroundTemplate *texttemplate.Template
var redisInsecureWarn sync.Once
var sdMu sync.RWMutex
var secretsConfMatch = regexp.MustCompile(`\$secret_conf.([A-Za-z0-9[.\-\_]+)`)
As for the HTTP methods spec:
HTTP request bodies are theoretically allowed for all methods except TRACE,
however they are not commonly used except in PUT, POST and PATCH. Because of this,
they may not be supported properly by some client frameworks, and you should not allow
request bodies for GET, DELETE, TRACE, OPTIONS and HEAD methods.
var skippedMethods = map[string]struct{}{
http.MethodGet: {},
http.MethodDelete: {},
http.MethodTrace: {},
http.MethodOptions: {},
http.MethodHead: {},
}
var supportedAlgorithms = []string{"hmac-sha1", "hmac-sha256", "hmac-sha384", "hmac-sha512", "rsa-sha256"}
var tlsConfigCache = cache.New(60, 3600)
var tlsConfigMu sync.Mutex
var vaultMatch = regexp.MustCompile(`\$secret_vault.([A-Za-z0-9\/\-\.]+)`)
Types
APIAllCertificateBasics
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| Certs |
|
No comment on field. |
type APIAllCertificateBasics struct {
Certs []*certs.CertificateBasics `json:"certs"`
}
APIAllCertificates
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| CertIDs |
|
No comment on field. |
type APIAllCertificates struct {
CertIDs []string `json:"certs"`
}
APICertificateStatusMessage
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| CertID |
|
No comment on field. |
| Status |
|
No comment on field. |
| Message |
|
No comment on field. |
type APICertificateStatusMessage struct {
CertID string `json:"id"`
Status string `json:"status"`
Message string `json:"message"`
}
APIDefinitionLoader
APIDefinitionLoader will load an Api definition from a storage system.
| Field name | Field type | Comment |
|---|---|---|
| Gw |
|
No comment on field. |
type APIDefinitionLoader struct {
Gw *Gateway `json:"-"`
}
APIError
APIError is generic error object returned if there is something wrong with the request
| Field name | Field type | Comment |
|---|---|---|
| Message |
|
No comment on field. |
type APIError struct {
Message htmltemplate.HTML
}
APISpec
APISpec represents a path specification for an API, to avoid enumerating multiple nested lists, a single flattened URL list is checked for matching paths and then it's status evaluated if found.
| Field name | Field type | Comment |
|---|---|---|
|
No comment on field. | |
| OAS |
|
No comment on field. |
|
No comment on field. | |
| Checksum |
|
No comment on field. |
| RxPaths |
|
No comment on field. |
| WhiteListEnabled |
|
No comment on field. |
| target |
|
No comment on field. |
| AuthManager |
|
No comment on field. |
| OAuthManager |
|
No comment on field. |
| OrgSessionManager |
|
No comment on field. |
| EventPaths |
|
No comment on field. |
| Health |
|
No comment on field. |
| JSVM |
|
No comment on field. |
| ResponseChain |
|
No comment on field. |
| RoundRobin |
|
No comment on field. |
| URLRewriteEnabled |
|
No comment on field. |
| CircuitBreakerEnabled |
|
No comment on field. |
| EnforcedTimeoutEnabled |
|
No comment on field. |
| LastGoodHostList |
|
No comment on field. |
| HasRun |
|
No comment on field. |
| ServiceRefreshInProgress |
|
No comment on field. |
| HTTPTransport |
|
No comment on field. |
| HTTPTransportCreated |
|
No comment on field. |
| WSTransport |
|
No comment on field. |
| WSTransportCreated |
|
No comment on field. |
| GlobalConfig |
|
No comment on field. |
| OrgHasNoSession |
|
No comment on field. |
| AnalyticsPluginConfig |
|
No comment on field. |
| middlewareChain |
|
No comment on field. |
| unloadHooks |
|
No comment on field. |
| network |
|
No comment on field. |
| GraphEngine |
|
No comment on field. |
| HasMock |
|
No comment on field. |
| HasValidateRequest |
|
No comment on field. |
| OASRouter |
|
No comment on field. |
type APISpec struct {
*apidef.APIDefinition
OAS oas.OAS
sync.RWMutex
Checksum string
RxPaths map[string][]URLSpec
WhiteListEnabled map[string]bool
target *url.URL
AuthManager SessionHandler
OAuthManager OAuthManagerInterface
OrgSessionManager SessionHandler
EventPaths map[apidef.TykEvent][]config.TykEventHandler
Health HealthChecker
JSVM JSVM
ResponseChain []TykResponseHandler
RoundRobin RoundRobin
URLRewriteEnabled bool
CircuitBreakerEnabled bool
EnforcedTimeoutEnabled bool
LastGoodHostList *apidef.HostList
HasRun bool
ServiceRefreshInProgress bool
HTTPTransport *TykRoundTripper
HTTPTransportCreated time.Time
WSTransport http.RoundTripper
WSTransportCreated time.Time
GlobalConfig config.Config
OrgHasNoSession bool
AnalyticsPluginConfig *GoAnalyticsPlugin
middlewareChain *ChainObject
unloadHooks []func()
network analytics.NetworkStats
GraphEngine graphengine.Engine
HasMock bool
HasValidateRequest bool
OASRouter routers.Router
}
AccessRightsCheck
AccessRightsCheck is a middleware that will check if the key bing used to access the API has permission to access the specific version. If no permission data is in the user.SessionState, then it is assumed that the user can go through.
| Field name | Field type | Comment |
|---|---|---|
|
No comment on field. |
type AccessRightsCheck struct {
*BaseMiddleware
}
AuthKey
KeyExists will check if the key being used to access the API is in the request data, and then if the key is in the storage engine
| Field name | Field type | Comment |
|---|---|---|
|
No comment on field. |
type AuthKey struct {
*BaseMiddleware
}
BaseExtractor
BaseExtractor is the base structure for an ID extractor, it implements the IdExtractor interface. Other extractors may override some of its methods.
| Field name | Field type | Comment |
|---|---|---|
| Config |
|
No comment on field. |
| IDExtractorConfig |
|
No comment on field. |
|
No comment on field. | |
| Spec |
|
No comment on field. |
type BaseExtractor struct {
Config *apidef.MiddlewareIdExtractor
IDExtractorConfig apidef.IDExtractorConfig
*BaseMiddleware
Spec *APISpec
}
BaseMiddleware
BaseMiddleware wraps up the ApiSpec and Proxy objects to be included in a middleware handler, this can probably be handled better.
| Field name | Field type | Comment |
|---|---|---|
| Spec |
|
No comment on field. |
| Proxy |
|
No comment on field. |
| Gw |
|
No comment on field. |
| loggerMu |
|
No comment on field. |
| logger |
|
No comment on field. |
type BaseMiddleware struct {
Spec *APISpec
Proxy ReturningHttpHandler
Gw *Gateway `json:"-"`
loggerMu sync.Mutex
logger *logrus.Entry
}
BaseTykResponseHandler
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| Spec |
|
No comment on field. |
| Gw |
|
No comment on field. |
type BaseTykResponseHandler struct {
Spec *APISpec `json:"-"`
Gw *Gateway `json:"-"`
}
BasicAuthKeyIsValid
BasicAuthKeyIsValid uses a username instead of
| Field name | Field type | Comment |
|---|---|---|
|
No comment on field. | |
| bodyUserRegexp |
|
No comment on field. |
| bodyPasswordRegexp |
|
No comment on field. |
type BasicAuthKeyIsValid struct {
*BaseMiddleware
bodyUserRegexp *regexp.Regexp
bodyPasswordRegexp *regexp.Regexp
}
BatchReplyUnit
BatchReplyUnit encodes a request suitable for replying to a batch request
| Field name | Field type | Comment |
|---|---|---|
| RelativeURL |
|
No comment on field. |
| Code |
|
No comment on field. |
| Headers |
|
No comment on field. |
| Body |
|
No comment on field. |
type BatchReplyUnit struct {
RelativeURL string `json:"relative_url"`
Code int `json:"code"`
Headers http.Header `json:"headers"`
Body string `json:"body"`
}
BatchRequestHandler
BatchRequestHandler handles batch requests on /tyk/batch for any API Definition that has the feature enabled
| Field name | Field type | Comment |
|---|---|---|
| Gw |
|
No comment on field. |
| API |
|
No comment on field. |
type BatchRequestHandler struct {
Gw *Gateway `json:"-"`
API *APISpec
}
BatchRequestStructure
BatchRequestStructure defines a batch request order
| Field name | Field type | Comment |
|---|---|---|
| Requests |
|
No comment on field. |
| SuppressParallelExecution |
|
No comment on field. |
type BatchRequestStructure struct {
Requests []RequestDefinition `json:"requests"`
SuppressParallelExecution bool `json:"suppress_parallel_execution"`
}
Bundle
Bundle is the basic bundle data structure, it holds the bundle name and the data.
| Field name | Field type | Comment |
|---|---|---|
| Name |
|
No comment on field. |
| Data |
|
No comment on field. |
| Path |
|
No comment on field. |
| Spec |
|
No comment on field. |
| Manifest |
|
No comment on field. |
| Gw |
|
No comment on field. |
type Bundle struct {
Name string
Data []byte
Path string
Spec *APISpec
Manifest apidef.BundleManifest
Gw *Gateway `json:"-"`
}
BundleGetter
BundleGetter is used for downloading bundle data, see HttpBundleGetter for reference.
| Field name | Field type | Comment |
|---|---|---|
| type |
|
No comment on field. |
type BundleGetter interface {
Get() ([]byte, error)
}
BundleSaver
BundleSaver is an interface used by bundle saver structures.
| Field name | Field type | Comment |
|---|---|---|
| type |
|
No comment on field. |
type BundleSaver interface {
Save(*Bundle, string, *APISpec) error
}
CertificateCheckMW
CertificateCheckMW is used if domain was not detected or multiple APIs bind on the same domain. In this case authentification check happens not on TLS side but on HTTP level using this middleware
| Field name | Field type | Comment |
|---|---|---|
|
No comment on field. |
type CertificateCheckMW struct {
*BaseMiddleware
}
ChainObject
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| ThisHandler |
|
No comment on field. |
| RateLimitChain |
|
No comment on field. |
| Open |
|
No comment on field. |
| Skip |
|
No comment on field. |
type ChainObject struct {
ThisHandler http.Handler
RateLimitChain http.Handler
Open bool
Skip bool
}
CoProcessEventHandler
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| methodName |
|
No comment on field. |
| Spec |
|
No comment on field. |
| SpecJSON |
|
No comment on field. |
type CoProcessEventHandler struct {
methodName string
Spec *APISpec
SpecJSON json.RawMessage
}
CoProcessEventWrapper
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| Event |
|
No comment on field. |
| Handler |
|
No comment on field. |
| SpecJSON |
|
No comment on field. |
type CoProcessEventWrapper struct {
Event config.EventMessage `json:"message"`
Handler string `json:"handler_name"`
SpecJSON *json.RawMessage `json:"spec"`
}
CoProcessMiddleware
CoProcessMiddleware is the basic CP middleware struct.
| Field name | Field type | Comment |
|---|---|---|
|
No comment on field. | |
| HookType |
|
No comment on field. |
| HookName |
|
No comment on field. |
| MiddlewareDriver |
|
No comment on field. |
| RawBodyOnly |
|
No comment on field. |
| successHandler |
|
No comment on field. |
type CoProcessMiddleware struct {
*BaseMiddleware
HookType coprocess.HookType
HookName string
MiddlewareDriver apidef.MiddlewareDriver
RawBodyOnly bool
successHandler *SuccessHandler
}
CoProcessor
CoProcessor represents a CoProcess during the request.
| Field name | Field type | Comment |
|---|---|---|
| Middleware |
|
No comment on field. |
type CoProcessor struct {
Middleware *CoProcessMiddleware
}
ComplexityFailReason
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| type |
|
No comment on field. |
type ComplexityFailReason int
ConfigPayload
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| Configuration |
|
No comment on field. |
| ForHostname |
|
No comment on field. |
| ForNodeID |
|
No comment on field. |
| TimeStamp |
|
No comment on field. |
type ConfigPayload struct {
Configuration config.Config
ForHostname string
ForNodeID string
TimeStamp int64
}
CustomMiddlewareResponseHook
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
|
No comment on field. | |
| mw |
|
No comment on field. |
type CustomMiddlewareResponseHook struct {
BaseTykResponseHandler
mw *CoProcessMiddleware
}
DBAccessDefinition
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| APIName |
|
No comment on field. |
| APIID |
|
No comment on field. |
| Versions |
|
No comment on field. |
| AllowedURLs |
|
No comment on field. |
| RestrictedTypes |
|
No comment on field. |
| AllowedTypes |
|
No comment on field. |
| DisableIntrospection |
|
No comment on field. |
| FieldAccessRights |
|
No comment on field. |
| Limit |
|
No comment on field. |
| Endpoints |
|
Endpoints contains endpoint rate limit settings. |
type DBAccessDefinition struct {
APIName string `json:"api_name"`
APIID string `json:"api_id"`
Versions []string `json:"versions"`
AllowedURLs []user.AccessSpec `bson:"allowed_urls" json:"allowed_urls"` // mapped string MUST be a valid regex
RestrictedTypes []graphql.Type `json:"restricted_types"`
AllowedTypes []graphql.Type `json:"allowed_types"`
DisableIntrospection bool `json:"disable_introspection"`
FieldAccessRights []user.FieldAccessDefinition `json:"field_access_rights"`
Limit *user.APILimit `json:"limit"`
// Endpoints contains endpoint rate limit settings.
Endpoints user.Endpoints `json:"endpoints,omitempty"`
}
DBPolicy
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
|
No comment on field. | |
| AccessRights |
|
No comment on field. |
type DBPolicy struct {
user.Policy
AccessRights map[string]DBAccessDefinition `bson:"access_rights" json:"access_rights"`
}
DashboardServiceSender
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| type |
|
No comment on field. |
type DashboardServiceSender interface {
Init() error
Register() error
DeRegister() error
StartBeating() error
StopBeating()
Ping() error
NotifyDashboardOfEvent(interface{}) error
}
DefaultHealthChecker
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| Gw |
|
No comment on field. |
| storage |
|
No comment on field. |
| APIID |
|
No comment on field. |
type DefaultHealthChecker struct {
Gw *Gateway `json:"-"`
storage storage.Handler
APIID string
}
DefaultKeyGenerator
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| Gw |
|
No comment on field. |
type DefaultKeyGenerator struct {
Gw *Gateway `json:"-"`
}
DefaultSessionManager
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| store |
|
No comment on field. |
| orgID |
|
No comment on field. |
| Gw |
|
No comment on field. |
type DefaultSessionManager struct {
store storage.Handler
orgID string
Gw *Gateway `json:"-"`
}
DummyProxyHandler
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| SH |
|
No comment on field. |
| Gw |
|
No comment on field. |
type DummyProxyHandler struct {
SH SuccessHandler
Gw *Gateway `json:"-"`
}
DynamicMiddleware
DynamicMiddleware is a generic middleware that will execute JS code before continuing
| Field name | Field type | Comment |
|---|---|---|
|
No comment on field. | |
| MiddlewareClassName |
|
No comment on field. |
| Pre |
|
No comment on field. |
| UseSession |
|
No comment on field. |
| Auth |
|
No comment on field. |
type DynamicMiddleware struct {
*BaseMiddleware
MiddlewareClassName string
Pre bool
UseSession bool
Auth bool
}
EndPointCacheMeta
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| Method |
|
No comment on field. |
| CacheKeyRegex |
|
No comment on field. |
| CacheOnlyResponseCodes |
|
No comment on field. |
| Timeout |
|
No comment on field. |
type EndPointCacheMeta struct {
Method string
CacheKeyRegex string
CacheOnlyResponseCodes []int
Timeout int64
}
ErrorHandler
ErrorHandler is invoked whenever there is an issue with a proxied request, most middleware will invoke the ErrorHandler if something is wrong with the request and halt the request processing through the chain
| Field name | Field type | Comment |
|---|---|---|
|
No comment on field. |
type ErrorHandler struct {
*BaseMiddleware
}
EventCurcuitBreakerMeta
EventCurcuitBreakerMeta is the event status for a circuit breaker tripping
| Field name | Field type | Comment |
|---|---|---|
|
No comment on field. | |
| Path |
|
No comment on field. |
| APIID |
|
No comment on field. |
| CircuitEvent |
|
No comment on field. |
type EventCurcuitBreakerMeta struct {
EventMetaDefault
Path string
APIID string
CircuitEvent circuit.BreakerEvent
}
EventHostStatusMeta
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
|
No comment on field. | |
| HostInfo |
|
No comment on field. |
type EventHostStatusMeta struct {
EventMetaDefault
HostInfo HostHealthReport
}
EventKeyFailureMeta
EventKeyFailureMeta is the metadata structure for any failure related to a key, such as quota or auth failures.
| Field name | Field type | Comment |
|---|---|---|
|
No comment on field. | |
| Path |
|
No comment on field. |
| Origin |
|
No comment on field. |
| Key |
|
No comment on field. |
type EventKeyFailureMeta struct {
EventMetaDefault
Path string
Origin string
Key string
}
EventMetaDefault
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| type |
|
No comment on field. |
type EventMetaDefault = model.EventMetaDefault
EventTokenMeta
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
|
No comment on field. | |
| Org |
|
No comment on field. |
| Key |
|
No comment on field. |
type EventTokenMeta struct {
EventMetaDefault
Org string
Key string
}
EventTriggerExceededMeta
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
|
No comment on field. | |
| OrgID |
|
No comment on field. |
| Key |
|
No comment on field. |
| TriggerLimit |
|
No comment on field. |
| UsagePercentage |
|
No comment on field. |
type EventTriggerExceededMeta struct {
EventMetaDefault
OrgID string `json:"org_id"`
Key string `json:"key"`
TriggerLimit int64 `json:"trigger_limit"`
UsagePercentage int64 `json:"usage_percentage"`
}
EventVersionFailureMeta
EventVersionFailureMeta is the metadata structure for an auth failure (EventKeyExpired)
| Field name | Field type | Comment |
|---|---|---|
|
No comment on field. | |
| Path |
|
No comment on field. |
| Origin |
|
No comment on field. |
| Key |
|
No comment on field. |
| Reason |
|
No comment on field. |
type EventVersionFailureMeta struct {
EventMetaDefault
Path string
Origin string
Key string
Reason string
}
ExtendedCircuitBreakerMeta
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
|
No comment on field. | |
| CB |
|
No comment on field. |
type ExtendedCircuitBreakerMeta struct {
apidef.CircuitBreakerMeta
CB *circuit.Breaker `json:"-"`
}
ExtendedOsinClientInterface
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| type |
|
No comment on field. |
type ExtendedOsinClientInterface interface {
osin.Client
GetDescription() string
}
ExtendedOsinStorageInterface
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| type |
|
No comment on field. |
type ExtendedOsinStorageInterface interface {
osin.Storage
// Create OAuth clients
SetClient(id string, orgID string, client osin.Client, ignorePrefix bool) error
// Custom getter to handle prefixing issues in Redis
GetClientNoPrefix(id string) (osin.Client, error)
GetClientTokens(id string) ([]OAuthClientToken, error)
GetPaginatedClientTokens(id string, page int) ([]OAuthClientToken, int, error)
GetExtendedClient(id string) (ExtendedOsinClientInterface, error)
// Custom getter to handle prefixing issues in Redis
GetExtendedClientNoPrefix(id string) (ExtendedOsinClientInterface, error)
GetClients(filter string, orgID string, ignorePrefix bool) ([]ExtendedOsinClientInterface, error)
DeleteClient(id string, orgID string, ignorePrefix bool) error
// GetUser retrieves a Basic Access user token type from the key store
GetUser(string) (*user.SessionState, error)
// SetUser updates a Basic Access user token type in the key store
SetUser(string, *user.SessionState, int64) error
}
ExternalOAuthMiddleware
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
|
No comment on field. |
type ExternalOAuthMiddleware struct {
*BaseMiddleware
}
FileBundleGetter
FileBundleGetter is a BundleGetter for testing.
| Field name | Field type | Comment |
|---|---|---|
| URL |
|
No comment on field. |
| InsecureSkipVerify |
|
No comment on field. |
type FileBundleGetter struct {
URL string
InsecureSkipVerify bool
}
GRPCDispatcher
GRPCDispatcher implements a coprocess.Dispatcher
| Field name | Field type | Comment |
|---|---|---|
|
No comment on field. |
type GRPCDispatcher struct {
coprocess.Dispatcher
}
Gateway
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| DefaultProxyMux |
|
No comment on field. |
| config |
|
No comment on field. |
| configMu |
|
No comment on field. |
| ctx |
|
No comment on field. |
| nodeIDMu |
|
No comment on field. |
| nodeID |
|
No comment on field. |
| drlOnce |
|
No comment on field. |
| DRLManager |
|
No comment on field. |
| reloadMu |
|
No comment on field. |
| Analytics |
|
No comment on field. |
| GlobalEventsJSVM |
|
No comment on field. |
| MainNotifier |
|
No comment on field. |
| DefaultOrgStore |
|
No comment on field. |
| DefaultQuotaStore |
|
No comment on field. |
| GlobalSessionManager |
|
No comment on field. |
| MonitoringHandler |
|
No comment on field. |
| RPCListener |
|
No comment on field. |
| DashService |
|
No comment on field. |
| CertificateManager |
|
No comment on field. |
| GlobalHostChecker |
|
No comment on field. |
| ConnectionWatcher |
|
No comment on field. |
| HostCheckTicker |
|
No comment on field. |
| HostCheckerClient |
|
No comment on field. |
| TracerProvider |
|
No comment on field. |
| NewRelicApplication |
|
No comment on field. |
| keyGen |
|
No comment on field. |
| SessionLimiter |
|
No comment on field. |
| SessionMonitor |
|
No comment on field. |
| RPCGlobalCache |
|
RPCGlobalCache stores keys |
| RPCCertCache |
|
RPCCertCache stores certificates |
| SessionCache |
|
key session memory cache |
| ExpiryCache |
|
org session memory cache |
| UtilCache |
|
memory cache to store arbitrary items |
| ServiceCache |
|
ServiceCache is the service discovery cache |
| ServiceNonce |
|
Nonce to use when interacting with the dashboard service |
| ServiceNonceMutex |
|
No comment on field. |
| apisMu |
|
No comment on field. |
| apiSpecs |
|
No comment on field. |
| apisByID |
|
No comment on field. |
| apisHandlesByID |
|
No comment on field. |
| policiesMu |
|
No comment on field. |
| policiesByID |
|
No comment on field. |
| dnsCacheManager |
|
No comment on field. |
| consulKVStore |
|
No comment on field. |
| vaultKVStore |
|
No comment on field. |
| signatureVerifier |
|
signatureVerifier is used to verify signatures with config.PublicKeyPath. |
| RedisPurgeOnce |
|
No comment on field. |
| RpcPurgeOnce |
|
No comment on field. |
| OnConnect |
|
OnConnect this is a callback which is called whenever we transition redis Disconnected to connected |
| SessionID |
|
SessionID is the unique session id which is used while connecting to dashboard to prevent multiple node allocation. |
| runningTestsMu |
|
No comment on field. |
| testMode |
|
No comment on field. |
| reloadQueue |
|
reloadQueue is used by reloadURLStructure to queue a reload. It's not buffered, as reloadQueueLoop should pick these up immediately. |
| requeueLock |
|
No comment on field. |
| requeue |
|
This is a list of callbacks to execute on the next reload. It is protected by requeueLock for concurrent use. |
| ReloadTestCase |
|
ReloadTestCase use this when in any test for gateway reloads |
| TestBundles |
|
map[bundleName]map[fileName]fileContent used for tests |
| TestBundleMu |
|
No comment on field. |
| templates |
|
No comment on field. |
| templatesRaw |
|
No comment on field. |
| StorageConnectionHandler |
|
RedisController keeps track of redis connection and singleton |
| hostDetails |
|
No comment on field. |
| healthCheckInfo |
|
No comment on field. |
| dialCtxFn |
|
No comment on field. |
type Gateway struct {
DefaultProxyMux *proxyMux
config atomic.Value
configMu sync.Mutex
ctx context.Context
nodeIDMu sync.Mutex
nodeID string
drlOnce sync.Once
DRLManager *drl.DRL
reloadMu sync.Mutex
Analytics RedisAnalyticsHandler
GlobalEventsJSVM JSVM
MainNotifier RedisNotifier
DefaultOrgStore DefaultSessionManager
DefaultQuotaStore DefaultSessionManager
GlobalSessionManager SessionHandler
MonitoringHandler config.TykEventHandler
RPCListener RPCStorageHandler
DashService DashboardServiceSender
CertificateManager certs.CertificateManager
GlobalHostChecker *HostCheckerManager
ConnectionWatcher *httputil.ConnectionWatcher
HostCheckTicker chan struct{}
HostCheckerClient *http.Client
TracerProvider otel.TracerProvider
NewRelicApplication *newrelic.Application
keyGen DefaultKeyGenerator
SessionLimiter SessionLimiter
SessionMonitor Monitor
// RPCGlobalCache stores keys
RPCGlobalCache cache.Repository
// RPCCertCache stores certificates
RPCCertCache cache.Repository
// key session memory cache
SessionCache cache.Repository
// org session memory cache
ExpiryCache cache.Repository
// memory cache to store arbitrary items
UtilCache cache.Repository
// ServiceCache is the service discovery cache
ServiceCache cache.Repository
// Nonce to use when interacting with the dashboard service
ServiceNonce string
ServiceNonceMutex sync.RWMutex
apisMu sync.RWMutex
apiSpecs []*APISpec
apisByID map[string]*APISpec
apisHandlesByID *sync.Map
policiesMu sync.RWMutex
policiesByID map[string]user.Policy
dnsCacheManager dnscache.IDnsCacheManager
consulKVStore kv.Store
vaultKVStore kv.Store
// signatureVerifier is used to verify signatures with config.PublicKeyPath.
signatureVerifier atomic.Pointer[goverify.Verifier]
RedisPurgeOnce sync.Once
RpcPurgeOnce sync.Once
// OnConnect this is a callback which is called whenever we transition redis Disconnected to connected
OnConnect func()
// SessionID is the unique session id which is used while connecting to dashboard to prevent multiple node allocation.
SessionID string
runningTestsMu sync.RWMutex
testMode bool
// reloadQueue is used by reloadURLStructure to queue a reload. It's not
// buffered, as reloadQueueLoop should pick these up immediately.
reloadQueue chan func()
requeueLock sync.Mutex
// This is a list of callbacks to execute on the next reload. It is protected by
// requeueLock for concurrent use.
requeue []func()
// ReloadTestCase use this when in any test for gateway reloads
ReloadTestCase *ReloadMachinery
// map[bundleName]map[fileName]fileContent used for tests
TestBundles map[string]map[string]string
TestBundleMu sync.Mutex
templates *htmltemplate.Template
templatesRaw *texttemplate.Template
// RedisController keeps track of redis connection and singleton
StorageConnectionHandler *storage.ConnectionHandler
hostDetails model.HostDetails
healthCheckInfo atomic.Value
dialCtxFn test.DialContext
}
GetConfigPayload
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| FromHostname |
|
No comment on field. |
| FromNodeID |
|
No comment on field. |
| TimeStamp |
|
No comment on field. |
type GetConfigPayload struct {
FromHostname string
FromNodeID string
TimeStamp int64
}
GoAnalyticsPlugin
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| Path |
|
No comment on field. |
| FuncName |
|
No comment on field. |
| handler |
|
No comment on field. |
| logger |
|
No comment on field. |
type GoAnalyticsPlugin struct {
Path string // path to .so file
FuncName string // function symbol to look up
handler func(record *analytics.AnalyticsRecord)
logger *logrus.Entry
}
GoPluginMiddleware
GoPluginMiddleware is a generic middleware that will execute Go-plugin code before continuing
| Field name | Field type | Comment |
|---|---|---|
|
No comment on field. | |
| Path |
|
No comment on field. |
| SymbolName |
|
No comment on field. |
| handler |
|
No comment on field. |
| logger |
|
No comment on field. |
| successHandler |
|
No comment on field. |
| Meta |
|
No comment on field. |
| APILevel |
|
No comment on field. |
type GoPluginMiddleware struct {
*BaseMiddleware
Path string // path to .so file
SymbolName string // function symbol to look up
handler http.HandlerFunc
logger *logrus.Entry
successHandler *SuccessHandler // to record analytics
Meta apidef.GoPluginMeta
APILevel bool
}
GranularAccessFailReason
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| type |
|
No comment on field. |
type GranularAccessFailReason int
GranularAccessMiddleware
GranularAccessMiddleware will check if a URL is specifically enabled for the key
| Field name | Field type | Comment |
|---|---|---|
|
No comment on field. |
type GranularAccessMiddleware struct {
*BaseMiddleware
}
GraphQLComplexityMiddleware
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
|
No comment on field. |
type GraphQLComplexityMiddleware struct {
*BaseMiddleware
}
GraphQLGranularAccessMiddleware
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
|
No comment on field. |
type GraphQLGranularAccessMiddleware struct {
*BaseMiddleware
}
GraphQLMiddleware
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
|
No comment on field. |
type GraphQLMiddleware struct {
*BaseMiddleware
}
GraphQLRequest
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| Query |
|
No comment on field. |
| Variables |
|
No comment on field. |
type GraphQLRequest struct {
Query string `json:"query"`
Variables json.RawMessage `json:"variables"`
}
GraphqlComplexityChecker
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| logger |
|
No comment on field. |
type GraphqlComplexityChecker struct {
logger *logrus.Entry
}
GraphqlGranularAccessChecker
This type doesn't have documentation.
type GraphqlGranularAccessChecker struct{}
GraphqlGranularAccessResult
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| failReason |
|
No comment on field. |
| validationResult |
|
No comment on field. |
| internalErr |
|
No comment on field. |
type GraphqlGranularAccessResult struct {
failReason GranularAccessFailReason
validationResult *graphql.RequestFieldsValidationResult
internalErr error
}
HMACFieldValues
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| KeyID |
|
No comment on field. |
| Algorthm |
|
No comment on field. |
| Headers |
|
No comment on field. |
| Signature |
|
No comment on field. |
type HMACFieldValues struct {
KeyID string
Algorthm string
Headers []string
Signature string
}
HTTPBundleGetter
HTTPBundleGetter is a simple HTTP BundleGetter.
| Field name | Field type | Comment |
|---|---|---|
| URL |
|
No comment on field. |
| InsecureSkipVerify |
|
No comment on field. |
type HTTPBundleGetter struct {
URL string
InsecureSkipVerify bool
}
HTTPDashboardHandler
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| RegistrationEndpoint |
|
No comment on field. |
| DeRegistrationEndpoint |
|
No comment on field. |
| HeartBeatEndpoint |
|
No comment on field. |
| KeyQuotaTriggerEndpoint |
|
No comment on field. |
| Secret |
|
No comment on field. |
| heartBeatStopSentinel |
|
No comment on field. |
| Gw |
|
No comment on field. |
type HTTPDashboardHandler struct {
RegistrationEndpoint string
DeRegistrationEndpoint string
HeartBeatEndpoint string
KeyQuotaTriggerEndpoint string
Secret string
heartBeatStopSentinel int32
Gw *Gateway `json:"-"`
}
HTTPSignatureValidationMiddleware
HTTPSignatureValidationMiddleware will check if the request has a signature, and if the request is allowed through
| Field name | Field type | Comment |
|---|---|---|
|
No comment on field. | |
| lowercasePattern |
|
No comment on field. |
type HTTPSignatureValidationMiddleware struct {
*BaseMiddleware
lowercasePattern *regexp.Regexp
}
HeaderInjector
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
|
No comment on field. | |
| config |
|
No comment on field. |
type HeaderInjector struct {
BaseTykResponseHandler
config HeaderInjectorOptions
}
HeaderInjectorOptions
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| AddHeaders |
|
No comment on field. |
| RemoveHeaders |
|
No comment on field. |
type HeaderInjectorOptions struct {
AddHeaders map[string]string `mapstructure:"add_headers" bson:"add_headers" json:"add_headers"`
RemoveHeaders []string `mapstructure:"remove_headers" bson:"remove_headers" json:"remove_headers"`
}
HeaderTransform
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
|
No comment on field. | |
| config |
|
No comment on field. |
type HeaderTransform struct {
BaseTykResponseHandler
config HeaderTransformOptions
}
HeaderTransformOptions
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| RevProxyTransform |
|
No comment on field. |
type HeaderTransformOptions struct {
RevProxyTransform RevProxyTransform `mapstructure:"rev_proxy_header_cleanup" bson:"rev_proxy_header_cleanup" json:"rev_proxy_header_cleanup"`
}
HealthCheckValues
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| ThrottledRequestsPS |
|
No comment on field. |
| QuotaViolationsPS |
|
No comment on field. |
| KeyFailuresPS |
|
No comment on field. |
| AvgUpstreamLatency |
|
No comment on field. |
| AvgRequestsPS |
|
No comment on field. |
type HealthCheckValues struct {
ThrottledRequestsPS float64 `bson:"throttle_reqests_per_second,omitempty" json:"throttle_reqests_per_second"`
QuotaViolationsPS float64 `bson:"quota_violations_per_second,omitempty" json:"quota_violations_per_second"`
KeyFailuresPS float64 `bson:"key_failures_per_second,omitempty" json:"key_failures_per_second"`
AvgUpstreamLatency float64 `bson:"average_upstream_latency,omitempty" json:"average_upstream_latency"`
AvgRequestsPS float64 `bson:"average_requests_per_second,omitempty" json:"average_requests_per_second"`
}
HealthChecker
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| type |
|
No comment on field. |
type HealthChecker interface {
Init(storage.Handler)
ApiHealthValues() (HealthCheckValues, error)
StoreCounterVal(HealthPrefix, string)
}
HealthPrefix
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| type |
|
No comment on field. |
type HealthPrefix string
HostCheckCallBacks
HostCheckCallBacks defines call backs which will be invoked on different states of the health check
| Field name | Field type | Comment |
|---|---|---|
| Up |
|
Up is a callback invoked when the host checker identifies a host to be up. |
| Ping |
|
Ping when provided this callback will be invoked on every every call to a remote host. |
| Fail |
|
Fail is invoked when the host checker decides a host is not healthy. |
type HostCheckCallBacks struct {
// Up is a callback invoked when the host checker identifies a host to be up.
Up func(context.Context, HostHealthReport)
// Ping when provided this callback will be invoked on every every call to a
// remote host.
Ping func(context.Context, HostHealthReport)
// Fail is invoked when the host checker decides a host is not healthy.
Fail func(context.Context, HostHealthReport)
}
HostCheckerManager
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| Gw |
|
No comment on field. |
| Id |
|
No comment on field. |
| store |
|
No comment on field. |
| checkerMu |
|
No comment on field. |
| checker |
|
No comment on field. |
| stopLoop |
|
No comment on field. |
| pollerStarted |
|
No comment on field. |
| unhealthyHostList |
|
No comment on field. |
| currentHostList |
|
No comment on field. |
| resetsInitiated |
|
No comment on field. |
type HostCheckerManager struct {
Gw *Gateway `json:"-"`
Id string
store storage.Handler
checkerMu sync.Mutex
checker *HostUptimeChecker
stopLoop bool
pollerStarted bool
unhealthyHostList *sync.Map
currentHostList map[string]HostData
resetsInitiated map[string]bool
}
HostData
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| CheckURL |
|
No comment on field. |
| Protocol |
|
No comment on field. |
| Timeout |
|
No comment on field. |
| EnableProxyProtocol |
|
No comment on field. |
| Commands |
|
No comment on field. |
| Method |
|
No comment on field. |
| Headers |
|
No comment on field. |
| Body |
|
No comment on field. |
| MetaData |
|
No comment on field. |
type HostData struct {
CheckURL string
Protocol string
Timeout time.Duration
EnableProxyProtocol bool
Commands []apidef.CheckCommand
Method string
Headers map[string]string
Body string
MetaData map[string]string
}
HostHealthReport
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
|
No comment on field. | |
| ResponseCode |
|
No comment on field. |
| Latency |
|
No comment on field. |
| IsTCPError |
|
No comment on field. |
type HostHealthReport struct {
HostData
ResponseCode int
Latency float64
IsTCPError bool
}
HostSample
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| count |
|
No comment on field. |
| reachedLimit |
|
No comment on field. |
type HostSample struct {
count int
reachedLimit bool
}
HostUptimeChecker
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| cb |
|
No comment on field. |
| workerPoolSize |
|
No comment on field. |
| sampleTriggerLimit |
|
No comment on field. |
| checkTimeout |
|
No comment on field. |
| HostList |
|
No comment on field. |
| unHealthyList |
|
No comment on field. |
| pool |
|
No comment on field. |
| errorChan |
|
No comment on field. |
| okChan |
|
No comment on field. |
| samples |
|
No comment on field. |
| stopLoop |
|
No comment on field. |
| muStopLoop |
|
No comment on field. |
| resetListMu |
|
No comment on field. |
| doResetList |
|
No comment on field. |
| newList |
|
No comment on field. |
| Gw |
|
No comment on field. |
| isClosed |
|
No comment on field. |
type HostUptimeChecker struct {
cb HostCheckCallBacks
workerPoolSize int
sampleTriggerLimit int
checkTimeout int
HostList map[string]HostData
unHealthyList map[string]bool
pool *tunny.Pool
errorChan chan HostHealthReport
okChan chan HostHealthReport
samples *sync.Map
stopLoop bool
muStopLoop sync.RWMutex
resetListMu sync.Mutex
doResetList bool
newList map[string]HostData
Gw *Gateway `json:"-"`
isClosed int32
}
IPBlackListMiddleware
IPBlackListMiddleware lets you define a list of IPs to block from upstream
| Field name | Field type | Comment |
|---|---|---|
|
No comment on field. |
type IPBlackListMiddleware struct {
*BaseMiddleware
}
IPWhiteListMiddleware
IPWhiteListMiddleware lets you define a list of IPs to allow upstream
| Field name | Field type | Comment |
|---|---|---|
|
No comment on field. |
type IPWhiteListMiddleware struct {
*BaseMiddleware
}
IdExtractor
IdExtractor is the base interface for an ID extractor.
| Field name | Field type | Comment |
|---|---|---|
| type |
|
No comment on field. |
type IdExtractor interface {
ExtractAndCheck(*http.Request) (string, ReturnOverrides)
GenerateSessionID(string, *BaseMiddleware) string
}
InterfaceNotification
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| Type |
|
No comment on field. |
| Message |
|
No comment on field. |
| OrgID |
|
No comment on field. |
| Timestamp |
|
No comment on field. |
type InterfaceNotification struct {
Type string
Message string
OrgID string
Timestamp time.Time
}
JSVM
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| Spec |
|
No comment on field. |
| VM |
|
No comment on field. |
| Timeout |
|
No comment on field. |
| Log |
|
No comment on field. |
| RawLog |
|
No comment on field. |
| Gw |
|
No comment on field. |
type JSVM struct {
Spec *APISpec
VM *otto.Otto `json:"-"`
Timeout time.Duration
Log *logrus.Entry `json:"-"` // logger used by the JS code
RawLog *logrus.Logger `json:"-"` // logger used by `rawlog` func to avoid formatting
Gw *Gateway `json:"-"`
}
JSVMContextGlobal
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| APIID |
|
No comment on field. |
| OrgID |
|
No comment on field. |
type JSVMContextGlobal struct {
APIID string
OrgID string
}
JSVMEventHandler
JSVMEventHandler is a scriptable event handler
| Field name | Field type | Comment |
|---|---|---|
| conf |
|
No comment on field. |
| Spec |
|
No comment on field. |
| SpecJSON |
|
No comment on field. |
| Gw |
|
No comment on field. |
type JSVMEventHandler struct {
conf apidef.JSVMEventHandlerConf
Spec *APISpec
SpecJSON string
Gw *Gateway `json:"-"`
}
JWK
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| Alg |
|
No comment on field. |
| Kty |
|
No comment on field. |
| Use |
|
No comment on field. |
| X5c |
|
No comment on field. |
| N |
|
No comment on field. |
| E |
|
No comment on field. |
| KID |
|
No comment on field. |
| X5t |
|
No comment on field. |
type JWK struct {
Alg string `json:"alg"`
Kty string `json:"kty"`
Use string `json:"use"`
X5c []string `json:"x5c"`
N string `json:"n"`
E string `json:"e"`
KID string `json:"kid"`
X5t string `json:"x5t"`
}
JWKs
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| Keys |
|
No comment on field. |
type JWKs struct {
Keys []JWK `json:"keys"`
}
JWTMiddleware
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
|
No comment on field. |
type JWTMiddleware struct {
*BaseMiddleware
}
KeyExpired
KeyExpired middleware will check if the requesting key is expired or not. It makes use of the authManager to do so.
| Field name | Field type | Comment |
|---|---|---|
|
No comment on field. |
type KeyExpired struct {
*BaseMiddleware
}
LDAPStorageHandler
LDAPStorageHandler implements storage.Handler, this is a read-only implementation to access keys from an LDAP service
| Field name | Field type | Comment |
|---|---|---|
| LDAPServer |
|
No comment on field. |
| LDAPPort |
|
No comment on field. |
| BaseDN |
|
No comment on field. |
| Attributes |
|
No comment on field. |
| SessionAttributeName |
|
No comment on field. |
| SearchString |
|
No comment on field. |
| store |
|
No comment on field. |
type LDAPStorageHandler struct {
LDAPServer string
LDAPPort uint16
BaseDN string
Attributes []string
SessionAttributeName string
SearchString string
store *ldap.LDAPConnection
}
LogEventMessage
LogEventMessage is an interface that provides a method for formatting log events into a string with a given prefix.
| Field name | Field type | Comment |
|---|---|---|
| type |
|
No comment on field. |
type LogEventMessage interface {
LogMessage(prefix string) string
}
LogMessageEventHandler
LogMessageEventHandler is a sample Event Handler
| Field name | Field type | Comment |
|---|---|---|
| conf |
|
No comment on field. |
| logger |
|
No comment on field. |
| Gw |
|
No comment on field. |
type LogMessageEventHandler struct {
conf apidef.LogEventHandlerConf
logger *logrus.Logger
Gw *Gateway `json:"-"`
}
MethodNotAllowedHandler
This type doesn't have documentation.
type MethodNotAllowedHandler struct{}
MiddlewareContextVars
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
|
No comment on field. |
type MiddlewareContextVars struct {
*BaseMiddleware
}
MiniRequestObject
MiniRequestObject is marshalled to JSON string and passed into JSON middleware
| Field name | Field type | Comment |
|---|---|---|
| Headers |
|
No comment on field. |
| SetHeaders |
|
No comment on field. |
| DeleteHeaders |
|
No comment on field. |
| Body |
|
No comment on field. |
| URL |
|
No comment on field. |
| Params |
|
No comment on field. |
| AddParams |
|
No comment on field. |
| ExtendedParams |
|
No comment on field. |
| DeleteParams |
|
No comment on field. |
| ReturnOverrides |
|
No comment on field. |
| IgnoreBody |
|
No comment on field. |
| Method |
|
No comment on field. |
| RequestURI |
|
No comment on field. |
| Scheme |
|
No comment on field. |
type MiniRequestObject struct {
Headers map[string][]string
SetHeaders map[string]string
DeleteHeaders []string
Body []byte
URL string
Params map[string][]string
AddParams map[string]string
ExtendedParams map[string][]string
DeleteParams []string
ReturnOverrides ReturnOverrides
IgnoreBody bool
Method string
RequestURI string
Scheme string
}
MockErrorReader
MockErrorReader is a mock io.Reader that returns an error on Read
| Field name | Field type | Comment |
|---|---|---|
| ReturnError |
|
No comment on field. |
type MockErrorReader struct {
ReturnError error
}
MockReadCloser
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| Reader |
|
No comment on field. |
| CloseError |
|
No comment on field. |
| CloseCalled |
|
No comment on field. |
type MockReadCloser struct {
Reader io.Reader
CloseError error
CloseCalled bool
}
Monitor
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| Gw |
|
No comment on field. |
type Monitor struct {
Gw *Gateway `json:"-"`
}
MultiTargetProxy
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| versionProxies |
|
No comment on field. |
| specReference |
|
No comment on field. |
| defaultProxy |
|
No comment on field. |
type MultiTargetProxy struct {
versionProxies map[string]*ReverseProxy
specReference *APISpec
defaultProxy *ReverseProxy
}
NewClientRequest
NewClientRequest is an outward facing JSON object translated from osin OAuthClients
swagger:model NewClientRequest
| Field name | Field type | Comment |
|---|---|---|
| ClientID |
|
No comment on field. |
| ClientRedirectURI |
|
No comment on field. |
| APIID |
|
No comment on field. |
| PolicyID |
|
No comment on field. |
| ClientSecret |
|
No comment on field. |
| MetaData |
|
No comment on field. |
| Description |
|
No comment on field. |
type NewClientRequest struct {
ClientID string `json:"client_id"`
ClientRedirectURI string `json:"redirect_uri"`
APIID string `json:"api_id,omitempty"`
PolicyID string `json:"policy_id,omitempty"`
ClientSecret string `json:"secret"`
MetaData interface{} `json:"meta_data"`
Description string `json:"description"`
}
NewOAuthNotification
NewOAuthNotification is a notification sent to a web-hook when an access request or a refresh request comes in.
| Field name | Field type | Comment |
|---|---|---|
| AuthCode |
|
No comment on field. |
| NewOAuthToken |
|
No comment on field. |
| RefreshToken |
|
No comment on field. |
| OldRefreshToken |
|
No comment on field. |
| NotificationType |
|
No comment on field. |
type NewOAuthNotification struct {
AuthCode string `json:"auth_code"`
NewOAuthToken string `json:"new_oauth_token"`
RefreshToken string `json:"refresh_token"`
OldRefreshToken string `json:"old_refresh_token"`
NotificationType OAuthNotificationType `json:"notification_type"`
}
NodeResponseOK
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| Status |
|
No comment on field. |
| Message |
|
No comment on field. |
| Nonce |
|
No comment on field. |
type NodeResponseOK struct {
Status string
Message map[string]string
Nonce string
}
Notification
Notification is a type that encodes a message published to a pub sub channel (shared between implementations)
| Field name | Field type | Comment |
|---|---|---|
| Command |
|
No comment on field. |
| Payload |
|
No comment on field. |
| Signature |
|
No comment on field. |
| SignatureAlgo |
|
No comment on field. |
| Gw |
|
No comment on field. |
type Notification struct {
Command NotificationCommand `json:"command"`
Payload string `json:"payload"`
Signature string `json:"signature"`
SignatureAlgo crypto.Hash `json:"algorithm"`
Gw *Gateway `json:"-"`
}
NotificationCommand
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| type |
|
No comment on field. |
type NotificationCommand string
OASSchemaResponse
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| Status |
|
No comment on field. |
| Message |
|
No comment on field. |
| Schema |
|
No comment on field. |
type OASSchemaResponse struct {
Status string `json:"status"`
Message string `json:"message"`
Schema json.RawMessage `json:"schema,omitempty"`
}
OAuthClient
OAuthClient is a representation within an APISpec of a client
| Field name | Field type | Comment |
|---|---|---|
| ClientID |
|
No comment on field. |
| ClientSecret |
|
No comment on field. |
| ClientRedirectURI |
|
No comment on field. |
| MetaData |
|
No comment on field. |
| PolicyID |
|
No comment on field. |
| Description |
|
No comment on field. |
type OAuthClient struct {
ClientID string `json:"id"`
ClientSecret string `json:"secret"`
ClientRedirectURI string `json:"redirecturi"`
MetaData interface{} `json:"meta_data,omitempty"`
PolicyID string `json:"policyid"`
Description string `json:"description"`
}
OAuthClientToken
swagger:model
| Field name | Field type | Comment |
|---|---|---|
| Token |
|
No comment on field. |
| Expires |
|
No comment on field. |
type OAuthClientToken struct {
Token string `json:"code"`
Expires int64 `json:"expires"`
}
OAuthHandlers
OAuthHandlers are the HTTP Handlers that manage the Tyk OAuth flow
| Field name | Field type | Comment |
|---|---|---|
| Manager |
|
No comment on field. |
type OAuthHandlers struct {
Manager OAuthManager
}
OAuthManager
OAuthManager handles and wraps osin OAuth2 functions to handle authorise and access requests
| Field name | Field type | Comment |
|---|---|---|
| API |
|
No comment on field. |
| OsinServer |
|
No comment on field. |
| Gw |
|
No comment on field. |
type OAuthManager struct {
API *APISpec
OsinServer *TykOsinServer
Gw *Gateway `json:"-"`
}
OAuthManagerInterface
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| type |
|
No comment on field. |
type OAuthManagerInterface interface {
Storage() ExtendedOsinStorageInterface
}
OAuthNotificationType
OAuthNotificationType const to reduce risk of collisions
| Field name | Field type | Comment |
|---|---|---|
| type |
|
No comment on field. |
type OAuthNotificationType string
Oauth2KeyExists
Oauth2KeyExists will check if the key being used to access the API is in the request data, and then if the key is in the storage engine
| Field name | Field type | Comment |
|---|---|---|
|
No comment on field. |
type Oauth2KeyExists struct {
*BaseMiddleware
}
OpenIDMW
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
|
No comment on field. | |
| providerConfiguration |
|
No comment on field. |
| provider_client_policymap |
|
No comment on field. |
| lock |
|
No comment on field. |
type OpenIDMW struct {
*BaseMiddleware
providerConfiguration *openid.Configuration
provider_client_policymap map[string]map[string]string
lock sync.RWMutex
}
Operation
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
|
No comment on field. | |
| route |
|
No comment on field. |
| pathParams |
|
No comment on field. |
type Operation struct {
*oas.Operation
route *routers.Route
pathParams map[string]string
}
OrganizationMonitor
RateLimitAndQuotaCheck will check the incoming request and key whether it is within it's quota and within it's rate limit, it makes use of the SessionLimiter object to do this
| Field name | Field type | Comment |
|---|---|---|
|
No comment on field. | |
| sessionlimiter |
|
No comment on field. |
| mon |
|
No comment on field. |
type OrganizationMonitor struct {
*BaseMiddleware
sessionlimiter SessionLimiter
mon Monitor
}
PersistGraphQLOperationMiddleware
PersistGraphQLOperationMiddleware lets you convert any HTTP request into a GraphQL Operation
| Field name | Field type | Comment |
|---|---|---|
|
No comment on field. |
type PersistGraphQLOperationMiddleware struct {
*BaseMiddleware
}
PolicyUpdateObj
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| Policy |
|
No comment on field. |
| ApplyPolicies |
|
No comment on field. |
type PolicyUpdateObj struct {
Policy string `json:"policy"`
ApplyPolicies []string `json:"apply_policies"`
}
ProxyResponse
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| Response |
|
No comment on field. |
| UpstreamLatency |
|
UpstreamLatency the time it takes to do roundtrip to upstream. Total time taken for the gateway to receive response from upstream host. |
type ProxyResponse struct {
Response *http.Response
// UpstreamLatency the time it takes to do roundtrip to upstream. Total time
// taken for the gateway to receive response from upstream host.
UpstreamLatency time.Duration
}
PublicSession
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| Quota |
|
No comment on field. |
| RateLimit |
|
No comment on field. |
type PublicSession struct {
Quota struct {
QuotaMax int64 `json:"quota_max"`
QuotaRemaining int64 `json:"quota_remaining"`
QuotaRenews int64 `json:"quota_renews"`
} `json:"quota"`
RateLimit struct {
Rate float64 `json:"requests"`
Per float64 `json:"per_unit"`
} `json:"rate_limit"`
}
Purger
Purger is an interface that will define how the in-memory store will be purged of analytics data to prevent it growing too large
| Field name | Field type | Comment |
|---|---|---|
| type |
|
No comment on field. |
type Purger interface {
PurgeCache()
PurgeLoop(<-chan time.Time)
}
RPCDataLoader
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| type |
|
No comment on field. |
type RPCDataLoader interface {
Connect() bool
GetApiDefinitions(orgId string, tags []string) string
GetPolicies(orgId string) string
}
RPCStorageHandler
RPCStorageHandler is a storage manager that uses the redis database.
| Field name | Field type | Comment |
|---|---|---|
| KeyPrefix |
|
No comment on field. |
| HashKeys |
|
No comment on field. |
| SuppressRegister |
|
No comment on field. |
| DoReload |
|
No comment on field. |
| Gw |
|
No comment on field. |
type RPCStorageHandler struct {
KeyPrefix string
HashKeys bool
SuppressRegister bool
DoReload func()
Gw *Gateway `json:"-"`
}
RateCheckMW
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
|
No comment on field. |
type RateCheckMW struct {
*BaseMiddleware
}
RateLimitAndQuotaCheck
RateLimitAndQuotaCheck will check the incomming request and key whether it is within it's quota and within it's rate limit, it makes use of the SessionLimiter object to do this
| Field name | Field type | Comment |
|---|---|---|
|
No comment on field. |
type RateLimitAndQuotaCheck struct {
*BaseMiddleware
}
RateLimitForAPI
RateLimitAndQuotaCheck will check the incoming request and key whether it is within it's quota and within it's rate limit, it makes use of the SessionLimiter object to do this
| Field name | Field type | Comment |
|---|---|---|
|
No comment on field. | |
| keyName |
|
No comment on field. |
| apiSess |
|
No comment on field. |
type RateLimitForAPI struct {
*BaseMiddleware
keyName string
apiSess *user.SessionState
}
RedisAnalyticsHandler
RedisAnalyticsHandler will record analytics data to a redis back end as defined in the Config object
| Field name | Field type | Comment |
|---|---|---|
| Store |
|
No comment on field. |
| GeoIPDB |
|
No comment on field. |
| globalConf |
|
No comment on field. |
| recordsChan |
|
No comment on field. |
| workerBufferSize |
|
No comment on field. |
| shouldStop |
|
No comment on field. |
| poolWg |
|
No comment on field. |
| enableMultipleAnalyticsKeys |
|
No comment on field. |
| Clean |
|
No comment on field. |
| Gw |
|
No comment on field. |
| mu |
|
No comment on field. |
| analyticsSerializer |
|
No comment on field. |
| mockEnabled |
|
testing purposes |
| mockRecordHit |
|
No comment on field. |
type RedisAnalyticsHandler struct {
Store storage.AnalyticsHandler
GeoIPDB *maxminddb.Reader
globalConf config.Config
recordsChan chan *analytics.AnalyticsRecord
workerBufferSize uint64
shouldStop uint32
poolWg sync.WaitGroup
enableMultipleAnalyticsKeys bool
Clean Purger
Gw *Gateway `json:"-"`
mu sync.Mutex
analyticsSerializer serializer.AnalyticsSerializer
// testing purposes
mockEnabled bool
mockRecordHit func(record *analytics.AnalyticsRecord)
}
RedisCacheMiddleware
RedisCacheMiddleware is a caching middleware that will pull data from Redis instead of the upstream proxy
| Field name | Field type | Comment |
|---|---|---|
|
No comment on field. | |
| store |
|
No comment on field. |
| sh |
|
No comment on field. |
type RedisCacheMiddleware struct {
*BaseMiddleware
store storage.Handler
sh SuccessHandler
}
RedisNotifier
RedisNotifier will use redis pub/sub channels to send notifications
| Field name | Field type | Comment |
|---|---|---|
| store |
|
No comment on field. |
| channel |
|
No comment on field. |
|
No comment on field. |
type RedisNotifier struct {
store *storage.RedisCluster
channel string
*Gateway
}
RedisOsinStorageInterface
TODO: Refactor this to move prefix handling into a checker method, then it can be an unexported setting in the struct. RedisOsinStorageInterface implements osin.Storage interface to use Tyk's own storage mechanism
| Field name | Field type | Comment |
|---|---|---|
| store |
|
No comment on field. |
| sessionManager |
|
No comment on field. |
| redisStore |
|
No comment on field. |
| orgID |
|
No comment on field. |
| Gw |
|
No comment on field. |
type RedisOsinStorageInterface struct {
store storage.Handler
sessionManager SessionHandler
redisStore storage.Handler
orgID string
Gw *Gateway `json:"-"`
}
RedisPurger
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| Store |
|
No comment on field. |
| Gw |
|
No comment on field. |
type RedisPurger struct {
Store storage.Handler
Gw *Gateway `json:"-"`
}
RegexExtractor
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
|
No comment on field. | |
| compiledExpr |
|
No comment on field. |
type RegexExtractor struct {
BaseExtractor
compiledExpr *regexp.Regexp
}
ReloadMachinery
ReloadMachinery is a helper struct to use when writing tests that do manual gateway reloads
| Field name | Field type | Comment |
|---|---|---|
| run |
|
No comment on field. |
| count |
|
No comment on field. |
| cycles |
|
No comment on field. |
| mu |
|
No comment on field. |
| reloadTick |
|
to simulate time ticks for tests that do reloads |
| stop |
|
No comment on field. |
| started |
|
No comment on field. |
type ReloadMachinery struct {
run bool
count int
cycles int
mu sync.RWMutex
// to simulate time ticks for tests that do reloads
reloadTick chan time.Time
stop chan struct{}
started bool
}
RequestDefinition
RequestDefinition defines a batch request
| Field name | Field type | Comment |
|---|---|---|
| Method |
|
No comment on field. |
| Headers |
|
No comment on field. |
| Body |
|
No comment on field. |
| RelativeURL |
|
No comment on field. |
type RequestDefinition struct {
Method string `json:"method"`
Headers map[string]string `json:"headers"`
Body string `json:"body"`
RelativeURL string `json:"relative_url"`
}
RequestObject
RequestObject is marshalled to JSON string and passed into JSON middleware
| Field name | Field type | Comment |
|---|---|---|
| Headers |
|
No comment on field. |
| Body |
|
No comment on field. |
| URL |
|
No comment on field. |
| Params |
|
No comment on field. |
| Scheme |
|
No comment on field. |
type RequestObject struct {
Headers map[string][]string
Body string
URL string
Params map[string][]string
Scheme string
}
RequestSigning
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
|
No comment on field. |
type RequestSigning struct {
*BaseMiddleware
}
RequestSizeLimitMiddleware
RequestSizeLimitMiddleware is a middleware that will enforce a limit on the request body size. The request has already been copied to memory when this middleware is called. Therefore, this middleware can't protect the gateway itself from large requests.
| Field name | Field type | Comment |
|---|---|---|
|
No comment on field. |
type RequestSizeLimitMiddleware struct {
*BaseMiddleware
}
RequestStatus
RequestStatus is a custom type to avoid collisions
| Field name | Field type | Comment |
|---|---|---|
| type |
|
No comment on field. |
type RequestStatus string
ResponseCacheMiddleware
ResponseCacheMiddleware is a caching middleware that will pull data from Redis instead of the upstream proxy
| Field name | Field type | Comment |
|---|---|---|
|
No comment on field. | |
| store |
|
No comment on field. |
type ResponseCacheMiddleware struct {
BaseTykResponseHandler
store storage.Handler
}
ResponseGoPluginMiddleware
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
|
No comment on field. | |
| Path |
|
No comment on field. |
| SymbolName |
|
No comment on field. |
| logger |
|
No comment on field. |
| ResHandler |
|
No comment on field. |
type ResponseGoPluginMiddleware struct {
BaseTykResponseHandler
Path string // path to .so file
SymbolName string // function symbol to look up
logger *logrus.Entry
ResHandler func(rw http.ResponseWriter, res *http.Response, req *http.Request)
}
ResponseObject
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| Body |
|
No comment on field. |
| Headers |
|
No comment on field. |
| Code |
|
No comment on field. |
type ResponseObject struct {
Body string
Headers map[string]string
Code int
}
ResponseTransformJQMiddleware
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
|
No comment on field. |
type ResponseTransformJQMiddleware struct {
BaseTykResponseHandler
}
ResponseTransformMiddleware
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
|
No comment on field. |
type ResponseTransformMiddleware struct {
BaseTykResponseHandler
}
ReturnConfigPayload
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| FromHostname |
|
No comment on field. |
| FromNodeID |
|
No comment on field. |
| Configuration |
|
No comment on field. |
| TimeStamp |
|
No comment on field. |
type ReturnConfigPayload struct {
FromHostname string
FromNodeID string
Configuration map[string]interface{}
TimeStamp int64
}
ReturnOverrides
Lets the user override and return a response from middleware
| Field name | Field type | Comment |
|---|---|---|
| ResponseCode |
|
No comment on field. |
| ResponseError |
|
No comment on field. |
| ResponseBody |
|
No comment on field. |
| ResponseHeaders |
|
No comment on field. |
| OverrideError |
|
No comment on field. |
type ReturnOverrides struct {
ResponseCode int
ResponseError string
ResponseBody string
ResponseHeaders map[string]string
OverrideError bool
}
ReturningHttpHandler
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| type |
|
No comment on field. |
type ReturningHttpHandler interface {
ServeHTTP(http.ResponseWriter, *http.Request) ProxyResponse
ServeHTTPForCache(http.ResponseWriter, *http.Request) ProxyResponse
CopyResponse(io.Writer, io.Reader, time.Duration)
}
RevProxyTransform
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| Headers |
|
No comment on field. |
| Target_host |
|
No comment on field. |
type RevProxyTransform struct {
Headers []string // List of HTTP headers to be modified
Target_host string // Target host for reverse proxy
}
ReverseProxy
ReverseProxy is an HTTP Handler that takes an incoming request and sends it to another server, proxying the response back to the client.
| Field name | Field type | Comment |
|---|---|---|
| Director |
|
Director must be a function which modifies the request into a new request to be sent using Transport. Its response is then copied back to the original client unmodified. |
| Transport |
|
The transport used to perform proxy requests. If nil, http.DefaultTransport is used. |
| FlushInterval |
|
FlushInterval specifies the flush interval to flush to the client while copying the response body. If zero, no periodic flushing is done. |
| TLSClientConfig |
|
TLSClientConfig specifies the TLS configuration to use for 'wss'. If nil, the default configuration is used. |
| wsUpgrader |
|
wsUpgrader takes care of upgrading the incoming connection to a websocket connection. |
| TykAPISpec |
|
No comment on field. |
| ErrorHandler |
|
No comment on field. |
| logger |
|
No comment on field. |
| sp |
|
No comment on field. |
| Gw |
|
No comment on field. |
type ReverseProxy struct {
// Director must be a function which modifies
// the request into a new request to be sent
// using Transport. Its response is then copied
// back to the original client unmodified.
Director func(*http.Request)
// The transport used to perform proxy requests.
// If nil, http.DefaultTransport is used.
Transport http.RoundTripper
// FlushInterval specifies the flush interval
// to flush to the client while copying the
// response body.
// If zero, no periodic flushing is done.
FlushInterval time.Duration
// TLSClientConfig specifies the TLS configuration to use for 'wss'.
// If nil, the default configuration is used.
TLSClientConfig *tls.Config
// wsUpgrader takes care of upgrading the incoming connection
// to a websocket connection.
wsUpgrader websocket.Upgrader
TykAPISpec *APISpec
ErrorHandler ErrorHandler
logger *logrus.Entry
sp sync.Pool
Gw *Gateway `json:"-"`
}
RoundRobin
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| pos |
|
No comment on field. |
type RoundRobin struct {
pos uint32
}
SafeHealthCheck
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| info |
|
No comment on field. |
| mux |
|
No comment on field. |
type SafeHealthCheck struct {
info map[string]HealthCheckItem
mux sync.Mutex
}
ServiceDiscovery
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| spec |
|
No comment on field. |
| isNested |
|
No comment on field. |
| isTargetList |
|
No comment on field. |
| endpointReturnsList |
|
No comment on field. |
| portSeperate |
|
No comment on field. |
| dataPath |
|
No comment on field. |
| parentPath |
|
No comment on field. |
| portPath |
|
No comment on field. |
| targetPath |
|
No comment on field. |
type ServiceDiscovery struct {
spec *apidef.ServiceDiscoveryConfiguration
isNested bool
isTargetList bool
endpointReturnsList bool
portSeperate bool
dataPath string
parentPath string
portPath string
targetPath string
}
SessionHandler
SessionHandler handles all update/create/access session functions and deals exclusively with user.SessionState objects, not identity
| Field name | Field type | Comment |
|---|---|---|
| type |
|
No comment on field. |
type SessionHandler interface {
Init(store storage.Handler)
Store() storage.Handler
UpdateSession(keyName string, session *user.SessionState, resetTTLTo int64, hashed bool) error
RemoveSession(orgID string, keyName string, hashed bool) bool
SessionDetail(orgID string, keyName string, hashed bool) (user.SessionState, bool)
KeyExpired(newSession *user.SessionState) bool
Sessions(filter string) []string
ResetQuota(string, *user.SessionState, bool)
Stop()
}
SessionLimiter
SessionLimiter is the rate limiter for the API, use ForwardMessage() to check if a message should pass through or not
| Field name | Field type | Comment |
|---|---|---|
| ctx |
|
No comment on field. |
| drlManager |
|
No comment on field. |
| config |
|
No comment on field. |
| bucketStore |
|
No comment on field. |
| limiterStorage |
|
No comment on field. |
| smoothing |
|
No comment on field. |
type SessionLimiter struct {
ctx context.Context
drlManager *drl.DRL
config *config.Config
bucketStore model.BucketStorage
limiterStorage redis.UniversalClient
smoothing *rate.Smoothing
}
SlaveDataCenter
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| SlaveOptions |
|
No comment on field. |
| Redis |
|
No comment on field. |
type SlaveDataCenter struct {
SlaveOptions config.SlaveOptionsConfig
Redis config.StorageOptionsConf
}
StatsDSink
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| options |
|
No comment on field. |
| cmdChan |
|
No comment on field. |
| drainDoneChan |
|
No comment on field. |
| stopDoneChan |
|
No comment on field. |
| flushPeriod |
|
No comment on field. |
| udpBuf |
|
No comment on field. |
| timingBuf |
|
No comment on field. |
| udpConn |
|
No comment on field. |
| udpAddr |
|
No comment on field. |
| prefixBuffers |
|
map of {job,event,suffix} to a re-usable buffer prefixed with the key. Since each timing/gauge has a unique component (the time), we'll truncate to the prefix, write the timing, and write the statsD suffix (eg, "|ms\n"). Then copy that to the UDP buffer. |
type StatsDSink struct {
options StatsDSinkOptions
cmdChan chan statsdEmitCmd
drainDoneChan chan struct{}
stopDoneChan chan struct{}
flushPeriod time.Duration
udpBuf bytes.Buffer
timingBuf []byte
udpConn *net.UDPConn
udpAddr *net.UDPAddr
// map of {job,event,suffix} to a re-usable buffer prefixed with the key.
// Since each timing/gauge has a unique component (the time), we'll truncate to the prefix, write the timing,
// and write the statsD suffix (eg, "|ms\n"). Then copy that to the UDP buffer.
prefixBuffers map[eventKey]prefixBuffer
}
StatsDSinkOptions
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| Prefix |
|
Prefix is something like "metroid" Events emitted to StatsD would be metroid.myevent.wat Eg, don't include a trailing dot in the prefix. It can be "", that's fine. |
| SanitizationFunc |
|
SanitizationFunc sanitizes jobs and events before sending them to statsd |
| SkipNestedEvents |
|
SkipNestedEvents will skip {events,timers,gauges} from sending the job.event version and will only send the event version. |
| SkipTopLevelEvents |
|
SkipTopLevelEvents will skip {events,timers,gauges} from sending the event version and will only send the job.event version. |
type StatsDSinkOptions struct {
// Prefix is something like "metroid"
// Events emitted to StatsD would be metroid.myevent.wat
// Eg, don't include a trailing dot in the prefix.
// It can be "", that's fine.
Prefix string
// SanitizationFunc sanitizes jobs and events before sending them to statsd
SanitizationFunc StatsDSinkSanitizationFunc
// SkipNestedEvents will skip {events,timers,gauges} from sending the job.event version
// and will only send the event version.
SkipNestedEvents bool
// SkipTopLevelEvents will skip {events,timers,gauges} from sending the event version
// and will only send the job.event version.
SkipTopLevelEvents bool
}
StatsDSinkSanitizationFunc
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| type |
|
No comment on field. |
type StatsDSinkSanitizationFunc func(*bytes.Buffer, string)
StripAuth
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
|
No comment on field. |
type StripAuth struct {
*BaseMiddleware
}
SuccessHandler
SuccessHandler represents the final ServeHTTP() request for a proxied API request
| Field name | Field type | Comment |
|---|---|---|
|
No comment on field. |
type SuccessHandler struct {
*BaseMiddleware
}
TemplateExecutor
TemplateExecutor is an interface used to switch between text/templates and html/template. It only switch to text/template (templatesRaw) when contentType is XML related
| Field name | Field type | Comment |
|---|---|---|
| type |
|
No comment on field. |
type TemplateExecutor interface {
Execute(wr io.Writer, data interface{}) error
}
Test
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| URL |
|
No comment on field. |
| testRunner |
|
No comment on field. |
| GlobalConfig |
|
GlobalConfig deprecate this and instead use GW.getConfig() |
| config |
|
No comment on field. |
| Gw |
|
No comment on field. |
| HttpHandler |
|
No comment on field. |
| TestServerRouter |
|
No comment on field. |
| MockHandle |
|
No comment on field. |
| ctx |
|
No comment on field. |
| cancel |
|
No comment on field. |
| dynamicHandlers |
|
No comment on field. |
type Test struct {
URL string
testRunner *test.HTTPTestRunner
// GlobalConfig deprecate this and instead use GW.getConfig()
GlobalConfig config.Config
config TestConfig
Gw *Gateway `json:"-"`
HttpHandler *http.Server
TestServerRouter *mux.Router
MockHandle *test.DnsMockHandle
ctx context.Context
cancel context.CancelFunc
dynamicHandlers map[string]http.HandlerFunc
}
TestConfig
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| SeparateControlAPI |
|
No comment on field. |
| Delay |
|
No comment on field. |
| HotReload |
|
No comment on field. |
| overrideDefaults |
|
No comment on field. |
| CoprocessConfig |
|
No comment on field. |
| EnableTestDNSMock |
|
No comment on field. |
type TestConfig struct {
SeparateControlAPI bool
Delay time.Duration
HotReload bool
overrideDefaults bool
CoprocessConfig config.CoProcessConfig
EnableTestDNSMock bool
}
TestHttpResponse
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| Method |
|
No comment on field. |
| URI |
|
No comment on field. |
| Path |
|
No comment on field. |
| Url |
|
No comment on field. |
| Body |
|
No comment on field. |
| Headers |
|
No comment on field. |
| Form |
|
No comment on field. |
type TestHttpResponse struct {
Method string
URI string
Path string
Url string
Body string
Headers map[string]string
Form map[string]string
}
TraceMiddleware
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
|
No comment on field. |
type TraceMiddleware struct {
TykMiddleware
}
TrackEndpointMiddleware
TrackEndpointMiddleware sets context variables to enable or disable whether Tyk should record analytitcs for a specific path.
| Field name | Field type | Comment |
|---|---|---|
|
No comment on field. |
type TrackEndpointMiddleware struct {
*BaseMiddleware
}
TransformHeaders
TransformMiddleware is a middleware that will apply a template to a request body to transform it's contents ready for an upstream API
| Field name | Field type | Comment |
|---|---|---|
|
No comment on field. |
type TransformHeaders struct {
*BaseMiddleware
}
TransformJQMiddleware
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
|
No comment on field. |
type TransformJQMiddleware struct {
*BaseMiddleware
}
TransformJQSpec
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
|
No comment on field. |
type TransformJQSpec struct {
apidef.TransformJQMeta
}
TransformMethod
TransformMiddleware is a middleware that will apply a template to a request body to transform it's contents ready for an upstream API
| Field name | Field type | Comment |
|---|---|---|
|
No comment on field. |
type TransformMethod struct {
*BaseMiddleware
}
TransformMiddleware
TransformMiddleware is a middleware that will apply a template to a request body to transform it's contents ready for an upstream API
| Field name | Field type | Comment |
|---|---|---|
|
No comment on field. |
type TransformMiddleware struct {
*BaseMiddleware
}
TransformSpec
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
|
No comment on field. | |
| Template |
|
No comment on field. |
type TransformSpec struct {
apidef.TemplateMeta
Template *texttemplate.Template
}
TykGoPluginResponseHandler
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| type |
|
No comment on field. |
type TykGoPluginResponseHandler interface {
TykResponseHandler
HandleGoPluginResponse(http.ResponseWriter, *http.Response, *http.Request) error
}
TykJSHttpRequest
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| Method |
|
No comment on field. |
| Body |
|
No comment on field. |
| Headers |
|
No comment on field. |
| Domain |
|
No comment on field. |
| Resource |
|
No comment on field. |
| FormData |
|
No comment on field. |
type TykJSHttpRequest struct {
Method string
Body string
Headers map[string]string
Domain string
Resource string
FormData map[string]string
}
TykJSHttpResponse
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| Code |
|
No comment on field. |
| Body |
|
No comment on field. |
| Headers |
|
No comment on field. |
| CodeComp |
|
Make this compatible with BatchReplyUnit |
| BodyComp |
|
No comment on field. |
| HeadersComp |
|
No comment on field. |
type TykJSHttpResponse struct {
Code int
Body string
Headers map[string][]string
// Make this compatible with BatchReplyUnit
CodeComp int `json:"code"`
BodyComp string `json:"body"`
HeadersComp map[string][]string `json:"headers"`
}
TykMiddleware
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| type |
|
No comment on field. |
type TykMiddleware interface {
Base() *BaseMiddleware
GetSpec() *APISpec
Init()
Logger() *logrus.Entry
Config() (interface{}, error)
ProcessRequest(w http.ResponseWriter, r *http.Request, conf interface{}) (error, int) // Handles request
EnabledForSpec() bool
Name() string
Unload()
}
TykOsinServer
TykOsinServer subclasses osin.Server so we can add the SetClient method without wrecking the lbrary
| Field name | Field type | Comment |
|---|---|---|
|
No comment on field. | |
| Config |
|
No comment on field. |
| Storage |
|
No comment on field. |
| AuthorizeTokenGen |
|
No comment on field. |
| AccessTokenGen |
|
No comment on field. |
type TykOsinServer struct {
osin.Server
Config *osin.ServerConfig
Storage ExtendedOsinStorageInterface
AuthorizeTokenGen osin.AuthorizeTokenGen
AccessTokenGen osin.AccessTokenGen
}
TykResponseHandler
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| type |
|
No comment on field. |
type TykResponseHandler interface {
Enabled() bool
Init(interface{}, *APISpec) error
Name() string
HandleResponse(http.ResponseWriter, *http.Response, *http.Request, *user.SessionState) error
HandleError(http.ResponseWriter, *http.Request)
Base() *BaseTykResponseHandler
}
TykRoundTripper
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| transport |
|
No comment on field. |
| h2ctransport |
|
No comment on field. |
| logger |
|
No comment on field. |
| Gw |
|
No comment on field. |
type TykRoundTripper struct {
transport *http.Transport
h2ctransport *http2.Transport
logger *logrus.Entry
Gw *Gateway `json:"-"`
}
URLRewriteMiddleware
URLRewriteMiddleware Will rewrite an inbund URL to a matching outbound one, it can also handle dynamic variable substitution
| Field name | Field type | Comment |
|---|---|---|
|
No comment on field. |
type URLRewriteMiddleware struct {
*BaseMiddleware
}
URLSpec
URLSpec represents a flattened specification for URLs, used to check if a proxy URL path is on any of the white, black or ignored lists. This is generated as part of the configuration init
| Field name | Field type | Comment |
|---|---|---|
| spec |
|
No comment on field. |
| Status |
|
No comment on field. |
| MethodActions |
|
No comment on field. |
| Whitelist |
|
No comment on field. |
| Blacklist |
|
No comment on field. |
| Ignored |
|
No comment on field. |
| MockResponse |
|
No comment on field. |
| CacheConfig |
|
No comment on field. |
| TransformAction |
|
No comment on field. |
| TransformResponseAction |
|
No comment on field. |
| TransformJQAction |
|
No comment on field. |
| TransformJQResponseAction |
|
No comment on field. |
| InjectHeaders |
|
No comment on field. |
| InjectHeadersResponse |
|
No comment on field. |
| HardTimeout |
|
No comment on field. |
| CircuitBreaker |
|
No comment on field. |
| URLRewrite |
|
No comment on field. |
| VirtualPathSpec |
|
No comment on field. |
| RequestSize |
|
No comment on field. |
| MethodTransform |
|
No comment on field. |
| TrackEndpoint |
|
No comment on field. |
| DoNotTrackEndpoint |
|
No comment on field. |
| ValidatePathMeta |
|
No comment on field. |
| Internal |
|
No comment on field. |
| GoPluginMeta |
|
No comment on field. |
| PersistGraphQL |
|
No comment on field. |
| RateLimit |
|
No comment on field. |
| IgnoreCase |
|
No comment on field. |
type URLSpec struct {
spec *regexp.Regexp
Status URLStatus
MethodActions map[string]apidef.EndpointMethodMeta
Whitelist apidef.EndPointMeta
Blacklist apidef.EndPointMeta
Ignored apidef.EndPointMeta
MockResponse apidef.MockResponseMeta
CacheConfig EndPointCacheMeta
TransformAction TransformSpec
TransformResponseAction TransformSpec
TransformJQAction TransformJQSpec
TransformJQResponseAction TransformJQSpec
InjectHeaders apidef.HeaderInjectionMeta
InjectHeadersResponse apidef.HeaderInjectionMeta
HardTimeout apidef.HardTimeoutMeta
CircuitBreaker ExtendedCircuitBreakerMeta
URLRewrite *apidef.URLRewriteMeta
VirtualPathSpec apidef.VirtualMeta
RequestSize apidef.RequestSizeMeta
MethodTransform apidef.MethodTransformMeta
TrackEndpoint apidef.TrackEndpointMeta
DoNotTrackEndpoint apidef.TrackEndpointMeta
ValidatePathMeta apidef.ValidatePathMeta
Internal apidef.InternalMeta
GoPluginMeta GoPluginMiddleware
PersistGraphQL apidef.PersistGraphQLMeta
RateLimit apidef.RateLimitMeta
IgnoreCase bool
}
URLStatus
URLStatus is a custom enum type to avoid collisions
| Field name | Field type | Comment |
|---|---|---|
| type |
|
No comment on field. |
type URLStatus int
UptimeReportData
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| URL |
|
No comment on field. |
| RequestTime |
|
No comment on field. |
| ResponseCode |
|
No comment on field. |
| TCPError |
|
No comment on field. |
| ServerError |
|
No comment on field. |
| Day |
|
No comment on field. |
| Month |
|
No comment on field. |
| Year |
|
No comment on field. |
| Hour |
|
No comment on field. |
| Minute |
|
No comment on field. |
| TimeStamp |
|
No comment on field. |
| ExpireAt |
|
No comment on field. |
| APIID |
|
No comment on field. |
| OrgID |
|
No comment on field. |
type UptimeReportData struct {
URL string
RequestTime int64
ResponseCode int
TCPError bool
ServerError bool
Day int
Month time.Month
Year int
Hour int
Minute int
TimeStamp time.Time
ExpireAt time.Time `bson:"expireAt" json:"expireAt"`
APIID string
OrgID string
}
VMResponseObject
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| Response |
|
No comment on field. |
| SessionMeta |
|
No comment on field. |
type VMResponseObject struct {
Response ResponseObject
SessionMeta map[string]interface{}
}
VMReturnObject
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| Request |
|
No comment on field. |
| SessionMeta |
|
No comment on field. |
| Session |
|
No comment on field. |
| AuthValue |
|
No comment on field. |
type VMReturnObject struct {
Request MiniRequestObject
SessionMeta map[string]string
Session user.SessionState
AuthValue string
}
ValidateJSON
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
|
No comment on field. |
type ValidateJSON struct {
*BaseMiddleware
}
ValidateRequest
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
|
No comment on field. |
type ValidateRequest struct {
*BaseMiddleware
}
ValueExtractor
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
|
No comment on field. |
type ValueExtractor struct {
BaseExtractor
}
VersionCheck
VersionCheck will check whether the version of the requested API the request is accessing has any restrictions on URL endpoints
| Field name | Field type | Comment |
|---|---|---|
|
No comment on field. | |
| sh |
|
No comment on field. |
type VersionCheck struct {
*BaseMiddleware
sh SuccessHandler
}
VersionMeta
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| ID |
|
No comment on field. |
| Name |
|
No comment on field. |
| VersionName |
|
No comment on field. |
| Internal |
|
No comment on field. |
| ExpirationDate |
|
No comment on field. |
| IsDefaultVersion |
|
No comment on field. |
type VersionMeta struct {
ID string `json:"id"`
Name string `json:"name"`
VersionName string `json:"versionName"`
Internal bool `json:"internal"`
ExpirationDate string `json:"expirationDate"`
IsDefaultVersion bool `json:"isDefaultVersion"`
}
VersionMetas
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| Status |
|
No comment on field. |
| Metas |
|
No comment on field. |
type VersionMetas struct {
Status string `json:"status"`
Metas []VersionMeta `json:"apis"`
}
VersionsHandler
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| getApiDef |
|
No comment on field. |
type VersionsHandler struct {
getApiDef func(string) (*apidef.APIDefinition, error)
}
VirtualEndpoint
DynamicMiddleware is a generic middleware that will execute JS code before continuing
| Field name | Field type | Comment |
|---|---|---|
|
No comment on field. | |
| sh |
|
No comment on field. |
type VirtualEndpoint struct {
*BaseMiddleware
sh SuccessHandler
}
WebHookHandler
WebHookHandler is an event handler that triggers web hooks
| Field name | Field type | Comment |
|---|---|---|
| conf |
|
No comment on field. |
| template |
|
No comment on field. |
| store |
|
No comment on field. |
| contentType |
|
No comment on field. |
| dashboardService |
|
No comment on field. |
| Gw |
|
No comment on field. |
type WebHookHandler struct {
conf apidef.WebHookHandlerConf
template *htmltemplate.Template // non-nil if Init is run without error
store storage.Handler
contentType string
dashboardService DashboardServiceSender
Gw *Gateway
}
WebHookRequestMethod
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| type |
|
No comment on field. |
type WebHookRequestMethod string
XPathExtractor
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
|
No comment on field. | |
| path |
|
No comment on field. |
type XPathExtractor struct {
BaseExtractor
path *xmlpath.Path
}
ZipBundleSaver
ZipBundleSaver is a BundleSaver for ZIP files.
type ZipBundleSaver struct{}
HealthCheckItem, HealthCheckStatus, HealthCheckResponse
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| type |
|
No comment on field. |
| type |
|
No comment on field. |
| type |
|
No comment on field. |
type (
HealthCheckItem = model.HealthCheckItem
HealthCheckStatus = model.HealthCheckStatus
HealthCheckResponse = model.HealthCheckResponse
)
accessTokenGen
accessTokenGen is a modified authorization token generator that uses the same method used to generate tokens for Tyk authHandler
| Field name | Field type | Comment |
|---|---|---|
| Gw |
|
No comment on field. |
type accessTokenGen struct {
Gw *Gateway `json:"-"`
}
apiAllKeys
apiAllKeys represents a list of keys in the memory store swagger:model
| Field name | Field type | Comment |
|---|---|---|
| APIKeys |
|
No comment on field. |
type apiAllKeys struct {
APIKeys []string `json:"keys"`
}
apiModifyKeySuccess
apiModifyKeySuccess represents when a Key modification was successful
swagger:model apiModifyKeySuccess
| Field name | Field type | Comment |
|---|---|---|
| Key |
|
in:body |
| Status |
|
No comment on field. |
| Action |
|
No comment on field. |
| KeyHash |
|
No comment on field. |
type apiModifyKeySuccess struct {
// in:body
Key string `json:"key"`
Status string `json:"status"`
Action string `json:"action"`
KeyHash string `json:"key_hash,omitempty"`
}
apiStatusMessage
apiStatusMessage represents an API status message
swagger:model apiStatusMessage
| Field name | Field type | Comment |
|---|---|---|
| Status |
|
No comment on field. |
| Message |
|
Response details |
type apiStatusMessage struct {
Status string `json:"status"`
// Response details
Message string `json:"message"`
}
cacheOptions
cacheOptions exists to transfer options from this middleware down the chain to the cache writer
| Field name | Field type | Comment |
|---|---|---|
| key |
|
No comment on field. |
| cacheOnlyResponseCodes |
|
No comment on field. |
| timeout |
|
No comment on field. |
type cacheOptions struct {
key string
cacheOnlyResponseCodes []int
timeout int64
}
customResponseWriter
customResponseWriter is a wrapper around standard http.ResponseWriter plus it tracks if response was sent and what status code was sent
| Field name | Field type | Comment |
|---|---|---|
|
No comment on field. | |
| responseSent |
|
No comment on field. |
| statusCodeSent |
|
No comment on field. |
| copyData |
|
No comment on field. |
| data |
|
No comment on field. |
| dataLength |
|
No comment on field. |
type customResponseWriter struct {
http.ResponseWriter
responseSent bool
statusCodeSent int
copyData bool
data []byte
dataLength int64
}
dashboardConfigPayload
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| DashboardConfig |
|
No comment on field. |
| TimeStamp |
|
No comment on field. |
type dashboardConfigPayload struct {
DashboardConfig struct {
Hostname string
Port int
UseTLS bool
}
TimeStamp int64
}
dialFn
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| type |
|
No comment on field. |
type dialFn func(network string, address string) (net.Conn, error)
dummyStreamingMiddleware
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
|
No comment on field. |
type dummyStreamingMiddleware struct {
*BaseMiddleware
}
eventKey
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| job |
|
No comment on field. |
| event |
|
No comment on field. |
| suffix |
|
No comment on field. |
type eventKey struct {
job string
event string
suffix string
}
explicitRouteHandler
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| prefix |
|
No comment on field. |
| handler |
|
No comment on field. |
type explicitRouteHandler struct {
prefix string
handler http.Handler
}
generalStores
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| redisStore |
|
No comment on field. |
type generalStores struct {
redisStore, redisOrgStore, healthStore, rpcAuthStore, rpcOrgStore storage.Handler
}
h2cWrapper
h2cWrapper tracks handleWrapper for swapping w.router on reloads.
| Field name | Field type | Comment |
|---|---|---|
| w |
|
No comment on field. |
| h |
|
No comment on field. |
type h2cWrapper struct {
w *handleWrapper
h http.Handler
}
handleWrapper
handleWrapper's only purpose is to allow router to be dynamically replaced
| Field name | Field type | Comment |
|---|---|---|
| router |
|
No comment on field. |
| maxContentLength |
|
No comment on field. |
| maxRequestBodySize |
|
No comment on field. |
type handleWrapper struct {
router http.Handler // *mux.Router
maxContentLength int64
maxRequestBodySize int64
}
httpProxyHandler
Taken from https://medium.com/@mlowicki/http-s-proxy-in-golang-in-less-than-100-lines-of-code-6a51c2f2c38c
| Field name | Field type | Comment |
|---|---|---|
| proto |
|
No comment on field. |
| URL |
|
No comment on field. |
| server |
|
No comment on field. |
| listener |
|
No comment on field. |
type httpProxyHandler struct {
proto string
URL string
server *http.Server
listener net.Listener
}
introspectionCache
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
|
No comment on field. |
type introspectionCache struct {
*storage.RedisCluster
}
maxLatencyWriter
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| dst |
|
No comment on field. |
| latency |
|
No comment on field. |
| mu |
|
No comment on field. |
| t |
|
No comment on field. |
| flushPending |
|
No comment on field. |
type maxLatencyWriter struct {
dst writeFlusher
latency time.Duration // non-zero; negative means to flush immediately
mu sync.Mutex // protects t, flushPending, and dst.Flush
t *time.Timer
flushPending bool
}
memConnProvider
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| listener |
|
No comment on field. |
| provider |
|
No comment on field. |
| expireAt |
|
No comment on field. |
type memConnProvider struct {
listener net.Listener
provider *memconn.Provider
expireAt time.Time
}
noopUpstreamBasicAuth
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
|
No comment on field. |
type noopUpstreamBasicAuth struct {
*BaseMiddleware
}
noopUpstreamOAuth
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
|
No comment on field. |
type noopUpstreamOAuth struct {
*BaseMiddleware
}
nopCloser
nopCloser is just like ioutil's, but here to let us re-read the same buffer inside by moving position to the start every time we done with reading
| Field name | Field type | Comment |
|---|---|---|
|
No comment on field. |
type nopCloser struct {
io.ReadSeeker
}
nopCloserBuffer
nopCloserBuffer is like nopCloser above but uses pointer receiver for seeking within an internal bytes.Buffer reference.
| Field name | Field type | Comment |
|---|---|---|
| reader |
|
No comment on field. |
| once |
|
No comment on field. |
| buf |
|
No comment on field. |
| position |
|
No comment on field. |
type nopCloserBuffer struct {
reader io.ReadCloser
once sync.Once
buf bytes.Buffer
position int64
}
orgChanMapMu
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
|
No comment on field. | |
| channels |
|
No comment on field. |
type orgChanMapMu struct {
sync.Mutex
channels map[string](chan bool)
}
paginatedOAuthClientTokens
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| Pagination |
|
No comment on field. |
| Tokens |
|
No comment on field. |
type paginatedOAuthClientTokens struct {
Pagination paginationStatus
Tokens []OAuthClientToken
}
paginationStatus
paginationStatus provides more information about a paginated data set
| Field name | Field type | Comment |
|---|---|---|
| PageNum |
|
No comment on field. |
| PageTotal |
|
No comment on field. |
| PageSize |
|
No comment on field. |
type paginationStatus struct {
PageNum int `json:"page_num"`
PageTotal int `json:"page_total"`
PageSize int `json:"page_size"`
}
postReceiveHttpHook
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| m |
|
No comment on field. |
type postReceiveHttpHook struct {
m *GraphQLMiddleware
}
preSendHttpHook
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| m |
|
No comment on field. |
type preSendHttpHook struct {
m *GraphQLMiddleware
}
prefixBuffer
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
|
No comment on field. | |
| prefixLen |
|
No comment on field. |
type prefixBuffer struct {
*bytes.Buffer
prefixLen int
}
proxy
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| listener |
|
No comment on field. |
| port |
|
No comment on field. |
| protocol |
|
No comment on field. |
| useProxyProtocol |
|
No comment on field. |
| router |
|
No comment on field. |
| httpServer |
|
No comment on field. |
| tcpProxy |
|
No comment on field. |
| started |
|
No comment on field. |
type proxy struct {
listener net.Listener
port int
protocol string
useProxyProtocol bool
router *mux.Router
httpServer *http.Server
tcpProxy *tcp.Proxy
started bool
}
proxyMux
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
|
No comment on field. | |
| proxies |
|
No comment on field. |
| again |
|
No comment on field. |
| track404Logs |
|
No comment on field. |
type proxyMux struct {
sync.RWMutex
proxies []*proxy
again again.Again
track404Logs bool
}
redisChannelHook
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| notifier |
|
No comment on field. |
| formatter |
|
No comment on field. |
type redisChannelHook struct {
notifier RedisNotifier
formatter logrus.Formatter
}
sessionFailReason
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| type |
|
No comment on field. |
type sessionFailReason uint
statsdCmdKind
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| type |
|
No comment on field. |
type statsdCmdKind int
statsdEmitCmd
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| Kind |
|
No comment on field. |
| Job |
|
No comment on field. |
| Event |
|
No comment on field. |
| Nanos |
|
No comment on field. |
| Value |
|
No comment on field. |
| Status |
|
No comment on field. |
type statsdEmitCmd struct {
Kind statsdCmdKind
Job string
Event string
Nanos int64
Value float64
Status health.CompletionStatus
}
swaggerParameterBodies
parameterBodies swagger:response parameterBodies
| Field name | Field type | Comment |
|---|---|---|
| APIStatusMessage |
|
in: body |
| APIModifyKeySuccess |
|
in: body |
| NewClientRequest |
|
in: body |
| APIDefinition |
|
in: body |
| SessionState |
|
in: body |
| APIAllKeys |
|
in:body |
| OAuthClientToken |
|
in: body |
type swaggerParameterBodies struct {
// in: body
APIStatusMessage apiStatusMessage
// in: body
APIModifyKeySuccess apiModifyKeySuccess
// in: body
NewClientRequest NewClientRequest
// in: body
APIDefinition apidef.APIDefinition
// in: body
SessionState user.SessionState
// in:body
APIAllKeys apiAllKeys
// in: body
OAuthClientToken OAuthClientToken
}
switchProtocolCopier
switchProtocolCopier exists so goroutines proxying data back and forth have nice names in stacks.
| Field name | Field type | Comment |
|---|---|---|
| user |
|
No comment on field. |
type switchProtocolCopier struct {
user, backend io.ReadWriter
}
testMessageAdapter
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| Msg |
|
No comment on field. |
type testMessageAdapter struct {
Msg string
}
traceHttpRequest
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| Method |
|
No comment on field. |
| Path |
|
No comment on field. |
| Body |
|
No comment on field. |
| Headers |
|
No comment on field. |
type traceHttpRequest struct {
Method string `json:"method"`
Path string `json:"path"`
Body string `json:"body"`
Headers http.Header `json:"headers"`
}
traceRequest
TraceRequest is for tracing an HTTP request swagger:model TraceRequest
| Field name | Field type | Comment |
|---|---|---|
| Request |
|
No comment on field. |
| Spec |
|
No comment on field. |
| OAS |
|
No comment on field. |
type traceRequest struct {
Request *traceHttpRequest `json:"request"`
Spec *apidef.APIDefinition `json:"spec"`
OAS *oas.OAS `json:"oas"`
}
traceResponse
TraceResponse is for tracing an HTTP response swagger:model TraceResponse
| Field name | Field type | Comment |
|---|---|---|
| Message |
|
No comment on field. |
| Response |
|
No comment on field. |
| Logs |
|
No comment on field. |
type traceResponse struct {
Message string `json:"message"`
Response string `json:"response"`
Logs string `json:"logs"`
}
variableReplaceRoundTripper
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| next |
|
No comment on field. |
| outReq |
|
No comment on field. |
| gw |
|
No comment on field. |
type variableReplaceRoundTripper struct {
next http.RoundTripper
outReq *http.Request
gw *Gateway
}
wrapMiddleware
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
|
No comment on field. | |
| mw |
|
No comment on field. |
type wrapMiddleware struct {
*BaseMiddleware
mw model.Middleware
}
writeFlusher
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| type |
|
No comment on field. |
type writeFlusher interface {
io.Writer
http.Flusher
}
Functions
func APILoopingName
func APILoopingName(name string) string {
return replaceNonAlphaNumeric(trimCategories(name))
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func AuthFailed
TODO: move this method to base middleware?
func AuthFailed(m TykMiddleware, r *http.Request, token string) {
m.Base().FireEvent(EventAuthFailure, EventKeyFailureMeta{
EventMetaDefault: EventMetaDefault{Message: "Auth Failure", OriginatingRequest: EncodeRequestToEvent(r)},
Path: r.URL.Path,
Origin: request.RealIP(r),
Key: token,
})
}
Cognitive complexity: 2, Cyclomatic complexity: 1
func BuildAPI
func BuildAPI(apiGens ...func(spec *APISpec)) (specs []*APISpec) {
if len(apiGens) == 0 {
apiGens = append(apiGens, func(spec *APISpec) {})
}
for idx, gen := range apiGens {
spec := &APISpec{APIDefinition: &apidef.APIDefinition{}}
if err := json.Unmarshal([]byte(sampleAPI), spec.APIDefinition); err != nil {
panic(err)
}
if idx > 0 {
spec.APIID = randStringBytes(8)
}
gen(spec)
specs = append(specs, spec)
}
return specs
}
Cognitive complexity: 12, Cyclomatic complexity: 5
func BuildOASAPI
func BuildOASAPI(oasGens ...func(oasDef *oas.OAS)) (specs []*APISpec) {
for _, gen := range oasGens {
oasAPI := getSampleOASAPI()
gen(&oasAPI)
var nativeAPIDefinition apidef.APIDefinition
oasAPI.ExtractTo(&nativeAPIDefinition)
nativeAPIDefinition.IsOAS = true
spec := &APISpec{APIDefinition: &nativeAPIDefinition, OAS: oasAPI}
specs = append(specs, spec)
}
return specs
}
Cognitive complexity: 4, Cyclomatic complexity: 2
func CheckPortWhiteList
func CheckPortWhiteList(w map[string]config.PortWhiteList, listenPort int, protocol string) error {
if w != nil {
if ls, ok := w[protocol]; ok {
if ls.Match(listenPort) {
return nil
}
}
}
return fmt.Errorf("%s:%d trying to open disabled port", protocol, listenPort)
}
Cognitive complexity: 6, Cyclomatic complexity: 4
func CloneAPI
func CloneAPI(a *APISpec) *APISpec {
new := &APISpec{}
new.APIDefinition = &apidef.APIDefinition{}
*new.APIDefinition = *a.APIDefinition
return new
}
Cognitive complexity: 2, Cyclomatic complexity: 1
func CoProcessLog
CoProcessLog is a bridge for using Tyk log from CP.
func CoProcessLog(CMessage, CLogLevel * /*line :88:40*/ _Ctype_char /*line :88:46*/) {
message := ( /*line :89:13*/ _Cfunc_GoString /*line :89:22*/)(CMessage)
logLevel := ( /*line :90:14*/ _Cfunc_GoString /*line :90:23*/)(CLogLevel)
switch logLevel {
case "debug":
log.WithFields(logrus.Fields{
"prefix": "python",
}).Debug(message)
case "error":
log.WithFields(logrus.Fields{
"prefix": "python",
}).Error(message)
case "warning":
log.WithFields(logrus.Fields{
"prefix": "python",
}).Warning(message)
default:
log.WithFields(logrus.Fields{
"prefix": "python",
}).Info(message)
}
}
Cognitive complexity: 9, Cyclomatic complexity: 5
func CreateCoProcessMiddleware
CreateCoProcessMiddleware initializes a new CP middleware, takes hook type (pre, post, etc.), hook name ("my_hook") and driver ("python").
func CreateCoProcessMiddleware(hookName string, hookType coprocess.HookType, mwDriver apidef.MiddlewareDriver, baseMid *BaseMiddleware) func(http.Handler) http.Handler {
dMiddleware := &CoProcessMiddleware{
BaseMiddleware: baseMid,
HookType: hookType,
HookName: hookName,
MiddlewareDriver: mwDriver,
successHandler: &SuccessHandler{baseMid.Copy()},
}
return baseMid.Gw.createMiddleware(dMiddleware)
}
Cognitive complexity: 2, Cyclomatic complexity: 1
func CreateJWKToken
func CreateJWKToken(jGen ...func(*jwt.Token)) string {
// Create the token
token := jwt.New(jwt.GetSigningMethod("RS512"))
// Set the token ID
if len(jGen) > 0 {
jGen[0](token)
}
// Sign and get the complete encoded token as a string
signKey, err := jwt.ParseRSAPrivateKeyFromPEM([]byte(jwtRSAPrivKey))
if err != nil {
panic("Couldn't extract private key: " + err.Error())
}
tokenString, err := token.SignedString(signKey)
if err != nil {
panic("Couldn't create JWT token: " + err.Error())
}
return tokenString
}
Cognitive complexity: 6, Cyclomatic complexity: 4
func CreateJWKTokenECDSA
func CreateJWKTokenECDSA(jGen ...func(*jwt.Token)) string {
// Create the token
token := jwt.New(jwt.GetSigningMethod("ES256"))
// Set the token ID
if len(jGen) > 0 {
jGen[0](token)
}
// Sign and get the complete encoded token as a string
signKey, err := jwt.ParseECPrivateKeyFromPEM([]byte(jwtECDSAPrivateKey))
if err != nil {
panic("Couldn't extract private key: " + err.Error())
}
tokenString, err := token.SignedString(signKey)
if err != nil {
panic("Couldn't create JWT token: " + err.Error())
}
return tokenString
}
Cognitive complexity: 6, Cyclomatic complexity: 4
func CreateSession
Deprecated: Use Test.CreateSession instead.
func CreateSession(gw *Gateway, sGen ...func(s *user.SessionState)) string {
key := gw.generateToken("default", "")
session := CreateStandardSession()
if len(sGen) > 0 {
sGen[0](session)
}
if session.Certificate != "" {
key = gw.generateToken("default", session.Certificate)
}
hashKeys := gw.GetConfig().HashKeys
hashedKey := storage.HashKey(key, hashKeys)
err := gw.GlobalSessionManager.UpdateSession(hashedKey, session, 60, hashKeys)
if err != nil {
log.WithError(err).Error("updating session.")
}
return key
}
Cognitive complexity: 6, Cyclomatic complexity: 4
func CreateStandardPolicy
func CreateStandardPolicy() *user.Policy {
return &user.Policy{
OrgID: "default",
Rate: 1000.0,
Per: 1.0,
QuotaMax: -1,
QuotaRenewalRate: -1,
AccessRights: map[string]user.AccessDefinition{},
Active: true,
KeyExpiresIn: 60,
}
}
Cognitive complexity: 2, Cyclomatic complexity: 1
func CreateStandardSession
func CreateStandardSession() *user.SessionState {
session := user.NewSessionState()
session.Rate = 10000
session.Allowance = session.Rate
session.LastCheck = time.Now().Unix()
session.Per = 60
session.Expires = -1
session.QuotaRenewalRate = 300 // 5 minutes
session.QuotaRenews = time.Now().Unix() + 20
session.QuotaRemaining = 10
session.QuotaMax = -1
session.Tags = []string{}
session.MetaData = make(map[string]interface{})
session.OrgID = "default"
return session
}
Cognitive complexity: 2, Cyclomatic complexity: 1
func DoCoprocessReload
func DoCoprocessReload() {
log.WithFields(logrus.Fields{
"prefix": "coprocess",
}).Info("Reloading middlewares")
if dispatcher := loadedDrivers[apidef.PythonDriver]; dispatcher != nil {
dispatcher.Reload()
}
}
Cognitive complexity: 3, Cyclomatic complexity: 2
func DurationToMillisecond
func DurationToMillisecond(d time.Duration) float64 {
return float64(d) / 1e6
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func EnsureTransport
EnsureTransport sanitizes host/protocol pairs and returns a valid URL.
func EnsureTransport(host, protocol string) string {
host = strings.TrimSpace(host)
protocol = strings.TrimSpace(protocol)
// sanitize protocol
if protocol == "" {
protocol = "http"
}
// if host has no protocol, amend it
if !strings.Contains(host, "://") {
host = protocol + "://" + host
}
host = strings.Replace(host, "h2c://", "http://", 1)
u, err := url.Parse(host)
if err != nil {
return host
}
return u.String()
}
Cognitive complexity: 5, Cyclomatic complexity: 4
func GenerateTestBinaryData
func GenerateTestBinaryData() (buf *bytes.Buffer) {
buf = new(bytes.Buffer)
type testData struct {
a float32
b float64
c uint32
}
for i := 0; i < 10; i++ {
s := &testData{mathrand.Float32(), mathrand.Float64(), mathrand.Uint32()}
binary.Write(buf, binary.BigEndian, s)
}
return buf
}
Cognitive complexity: 4, Cyclomatic complexity: 2
func GetAccessDefinitionByAPIIDOrSession
func GetAccessDefinitionByAPIIDOrSession(session *user.SessionState, api *APISpec) (accessDef *user.AccessDefinition, allowanceScope string, err error) {
accessDef = &user.AccessDefinition{}
if len(session.AccessRights) > 0 {
if rights, ok := session.AccessRights[api.APIID]; !ok {
return nil, "", errors.New("unexpected apiID")
} else {
accessDef.Limit = rights.Limit
accessDef.FieldAccessRights = rights.FieldAccessRights
accessDef.RestrictedTypes = rights.RestrictedTypes
accessDef.AllowedTypes = rights.AllowedTypes
accessDef.DisableIntrospection = rights.DisableIntrospection
accessDef.Endpoints = rights.Endpoints
allowanceScope = rights.AllowanceScope
}
}
if accessDef.Limit.IsEmpty() {
accessDef.Limit = session.APILimit()
}
return accessDef, allowanceScope, nil
}
Cognitive complexity: 9, Cyclomatic complexity: 4
func GetCoProcessGrpcServerTargetUrlAsString
func GetCoProcessGrpcServerTargetUrlAsString(targetUrl *url.URL) string {
return strings.TrimPrefix(targetUrl.String(), "tcp://")
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func GetTLSClient
func GetTLSClient(cert *tls.Certificate, caCert []byte) *http.Client {
// Setup HTTPS client
tlsConfig := GetTLSConfig(cert, caCert)
transport := &http.Transport{TLSClientConfig: tlsConfig}
return &http.Client{Transport: transport}
}
Cognitive complexity: 2, Cyclomatic complexity: 1
func GetTLSConfig
func GetTLSConfig(cert *tls.Certificate, caCert []byte) *tls.Config {
tlsConfig := &tls.Config{}
if cert != nil {
tlsConfig.Certificates = []tls.Certificate{*cert}
}
if len(caCert) > 0 {
caCertPool := x509.NewCertPool()
caCertPool.AppendCertsFromPEM(caCert)
tlsConfig.RootCAs = caCertPool
tlsConfig.BuildNameToCertificate()
} else {
tlsConfig.InsecureSkipVerify = true
}
return tlsConfig
}
Cognitive complexity: 8, Cyclomatic complexity: 3
func InitTestMain
func InitTestMain(ctx context.Context, m *testing.M) int {
test.InitTestMain(ctx, m)
bundleBackoffMultiplier = 0
bundleMaxBackoffRetries = 0
if EnableTestDNSMock {
var errMock error
MockHandle, errMock = test.InitDNSMock(test.DomainsToAddresses, nil)
if errMock != nil {
panic(errMock)
}
defer MockHandle.ShutdownDnsMock()
}
exitCode := m.Run()
return exitCode
}
Cognitive complexity: 4, Cyclomatic complexity: 3
func InstrumentationMW
InstrumentationMW will set basic instrumentation events, variables and timers on API jobs
func InstrumentationMW(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
job := instrument.NewJob("SystemAPICall")
next.ServeHTTP(w, r)
job.EventKv("called", health.Kvs{
"from_ip": request.RealIP(r),
"method": r.Method,
"endpoint": r.URL.Path,
"raw_url": r.URL.String(),
"size": strconv.Itoa(int(r.ContentLength)),
})
job.Complete(health.Success)
})
}
Cognitive complexity: 2, Cyclomatic complexity: 1
func JSONToFormValues
JSONToFormValues if r has header Content-Type set to application/json this will decode request body as json to map[string]string and adds the key/value pairs in r.Form.
func JSONToFormValues(r *http.Request) error {
if r.Header.Get("Content-Type") == "application/json" {
var o map[string]string
err := json.NewDecoder(r.Body).Decode(&o)
if err != nil {
return err
}
if len(o) > 0 {
if r.Form == nil {
r.Form = make(url.Values)
}
for k, v := range o {
r.Form.Set(k, v)
}
}
}
return nil
}
Cognitive complexity: 11, Cyclomatic complexity: 6
func LoadPoliciesFromDir
func LoadPoliciesFromDir(dir string) (map[string]user.Policy, error) {
policies := make(map[string]user.Policy)
// Grab json files from directory
paths, err := filepath.Glob(filepath.Join(dir, "*.json"))
if err != nil {
log.Error("error fetch policies path from policies path: ", err)
return nil, err
}
for _, path := range paths {
log.Info("Loading policy from dir ", path)
f, err := os.Open(path)
if err != nil {
log.Error("Couldn't open policy file from dir: ", err)
continue
}
pol := &user.Policy{}
if err := json.NewDecoder(f).Decode(pol); err != nil {
log.Errorf("Couldn't unmarshal policy configuration from dir: %v : %v", path, err)
}
f.Close()
policies[pol.ID] = *pol
}
return policies, nil
}
Cognitive complexity: 10, Cyclomatic complexity: 5
func LoadPoliciesFromFile
func LoadPoliciesFromFile(filePath string) (map[string]user.Policy, error) {
f, err := os.Open(filePath)
if err != nil {
log.WithFields(logrus.Fields{
"prefix": "policy",
}).Error("Couldn't open policy file: ", err)
return nil, err
}
defer f.Close()
var policies map[string]user.Policy
if err := json.NewDecoder(f).Decode(&policies); err != nil {
log.WithFields(logrus.Fields{
"prefix": "policy",
}).Error("Couldn't unmarshal policies: ", err)
return nil, err
}
return policies, nil
}
Cognitive complexity: 6, Cyclomatic complexity: 3
func LoopingUrl
func LoopingUrl(host string) string {
return LoopScheme + "://" + replaceNonAlphaNumeric(host)
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func NewBaseMiddleware
NewBaseMiddleware creates a new *BaseMiddleware. The passed logrus.Entry is duplicated. BaseMiddleware keeps the pointer to *Gateway and *APISpec, as well as Proxy. The logger duplication is used so that basemiddleware copies can be created for different middleware.
func NewBaseMiddleware(gw *Gateway, spec *APISpec, proxy ReturningHttpHandler, logger *logrus.Entry) *BaseMiddleware {
if logger == nil {
logger = logrus.NewEntry(log)
}
baseMid := &BaseMiddleware{
Spec: spec,
Proxy: proxy,
logger: logger.Dup(),
Gw: gw,
}
for _, v := range baseMid.Spec.VersionData.Versions {
if len(v.ExtendedPaths.CircuitBreaker) > 0 {
baseMid.Spec.CircuitBreakerEnabled = true
}
if len(v.ExtendedPaths.HardTimeouts) > 0 {
baseMid.Spec.EnforcedTimeoutEnabled = true
}
}
return baseMid
}
Cognitive complexity: 10, Cyclomatic complexity: 5
func NewGateway
func NewGateway(config config.Config, ctx context.Context) *Gateway {
gw := &Gateway{
DefaultProxyMux: &proxyMux{
again: again.New(),
},
ctx: ctx,
}
gw.SetConfig(config)
gw.Analytics = RedisAnalyticsHandler{Gw: gw}
sessionManager := DefaultSessionManager{Gw: gw}
gw.GlobalSessionManager = SessionHandler(&sessionManager)
gw.DefaultOrgStore = DefaultSessionManager{Gw: gw}
gw.DefaultQuotaStore = DefaultSessionManager{Gw: gw}
gw.SessionMonitor = Monitor{Gw: gw}
gw.HostCheckTicker = make(chan struct{})
gw.HostCheckerClient = &http.Client{
Timeout: 500 * time.Millisecond,
}
gw.ConnectionWatcher = httputil.NewConnectionWatcher()
gw.cacheCreate()
gw.apisByID = map[string]*APISpec{}
gw.apisHandlesByID = new(sync.Map)
gw.policiesByID = make(map[string]user.Policy)
// reload
gw.reloadQueue = make(chan func())
// only for tests
gw.ReloadTestCase = NewReloadMachinery()
gw.TestBundles = map[string]map[string]string{}
gw.StorageConnectionHandler = storage.NewConnectionHandler(ctx)
gw.SetNodeID("solo-" + uuid.New())
gw.SessionID = uuid.New()
return gw
}
Cognitive complexity: 11, Cyclomatic complexity: 1
func NewReloadMachinery
func NewReloadMachinery() *ReloadMachinery {
return &ReloadMachinery{
reloadTick: make(chan time.Time),
}
}
Cognitive complexity: 1, Cyclomatic complexity: 1
func NewSessionLimiter
NewSessionLimiter initializes the session limiter.
The session limiter initializes the storage required for rate limiters.
It supports two storage types: redis and local. If redis storage is
configured, then redis will be used. If local storage is configured, then
in-memory counters will be used. If no storage is configured, it falls
back onto the default gateway storage configuration.
func NewSessionLimiter(ctx context.Context, conf *config.Config, drlManager *drl.DRL) SessionLimiter {
sessionLimiter := SessionLimiter{
ctx: ctx,
drlManager: drlManager,
config: conf,
bucketStore: memorycache.New(ctx),
}
log.Infof("[RATELIMIT] %s", conf.RateLimit.String())
storageConf := conf.GetRateLimiterStorage()
switch storageConf.Type {
case "redis":
sessionLimiter.limiterStorage = rate.NewStorage(storageConf)
}
sessionLimiter.smoothing = rate.NewSmoothing(sessionLimiter.limiterStorage)
return sessionLimiter
}
Cognitive complexity: 4, Cyclomatic complexity: 3
func NewStatsDSink
func NewStatsDSink(addr string, options *StatsDSinkOptions) (*StatsDSink, error) {
c, err := net.ListenPacket("udp", ":0")
if err != nil {
return nil, err
}
ra, err := net.ResolveUDPAddr("udp", addr)
if err != nil {
return nil, err
}
s := &StatsDSink{
udpConn: c.(*net.UDPConn),
udpAddr: ra,
cmdChan: make(chan statsdEmitCmd, cmdChanBuffSize),
drainDoneChan: make(chan struct{}),
stopDoneChan: make(chan struct{}),
flushPeriod: 100 * time.Millisecond,
prefixBuffers: map[eventKey]prefixBuffer{},
}
if options != nil {
s.options = *options
if s.options.SanitizationFunc == nil {
s.options.SanitizationFunc = sanitizeKey
}
} else {
s.options = defaultStatsDOptions
}
go s.loop()
return s, nil
}
Cognitive complexity: 14, Cyclomatic complexity: 5
func NewVersionHandler
func NewVersionHandler(getApiDef func(string) (*apidef.APIDefinition, error)) *VersionsHandler {
return &VersionsHandler{getApiDef: getApiDef}
}
Cognitive complexity: 1, Cyclomatic complexity: 1
func NormalisePath
func NormalisePath(a *analytics.AnalyticsRecord, globalConfig *config.Config) {
if globalConfig.AnalyticsConfig.NormaliseUrls.NormaliseUUIDs {
a.Path = globalConfig.AnalyticsConfig.NormaliseUrls.CompiledPatternSet.UUIDs.ReplaceAllString(a.Path, "{uuid}")
}
if globalConfig.AnalyticsConfig.NormaliseUrls.NormaliseULIDs {
a.Path = globalConfig.AnalyticsConfig.NormaliseUrls.CompiledPatternSet.ULIDs.ReplaceAllString(a.Path, "{ulid}")
}
if globalConfig.AnalyticsConfig.NormaliseUrls.NormaliseNumbers {
a.Path = globalConfig.AnalyticsConfig.NormaliseUrls.CompiledPatternSet.IDs.ReplaceAllString(a.Path, "/{id}")
}
for _, r := range globalConfig.AnalyticsConfig.NormaliseUrls.CompiledPatternSet.Custom {
a.Path = r.ReplaceAllString(a.Path, "{var}")
}
}
Cognitive complexity: 9, Cyclomatic complexity: 5
func ParseRSAPublicKey
func ParseRSAPublicKey(data []byte) (interface{}, error) {
input := data
block, _ := pem.Decode(data)
if block != nil {
input = block.Bytes
}
var pub interface{}
var err error
pub, err = x509.ParsePKIXPublicKey(input)
if err != nil {
cert, err0 := x509.ParseCertificate(input)
if err0 != nil {
return nil, err0
}
pub = cert.PublicKey
err = nil
}
return pub, err
}
Cognitive complexity: 8, Cyclomatic complexity: 4
func ProtoMap
ProtoMap is a helper function for maps with string slice values.
func ProtoMap(inputMap map[string][]string) map[string]string {
newMap := make(map[string]string)
if inputMap != nil {
for k, v := range inputMap {
newMap[k] = v[0]
}
}
return newMap
}
Cognitive complexity: 5, Cyclomatic complexity: 3
func ProtoSessionState
ProtoSessionState takes a standard SessionState and outputs a SessionState object compatible with Protocol Buffers.
func ProtoSessionState(session *user.SessionState) *coprocess.SessionState {
accessDefinitions := make(map[string]*coprocess.AccessDefinition, len(session.AccessRights))
for key, accessDefinition := range session.AccessRights {
var allowedUrls []*coprocess.AccessSpec
for _, allowedURL := range accessDefinition.AllowedURLs {
accessSpec := &coprocess.AccessSpec{
Url: allowedURL.URL,
Methods: allowedURL.Methods,
}
allowedUrls = append(allowedUrls, accessSpec)
}
accessDefinitions[key] = &coprocess.AccessDefinition{
ApiName: accessDefinition.APIName,
ApiId: accessDefinition.APIID,
Versions: accessDefinition.Versions,
AllowedUrls: allowedUrls,
}
}
basicAuthData := &coprocess.BasicAuthData{
Password: session.BasicAuthData.Password,
Hash: string(session.BasicAuthData.Hash),
}
jwtData := &coprocess.JWTData{
Secret: session.JWTData.Secret,
}
monitor := &coprocess.Monitor{
TriggerLimits: session.Monitor.TriggerLimits,
}
metadata := make(map[string]string)
if len(session.MetaData) > 0 {
for k, v := range session.MetaData {
switch v.(type) {
case string:
metadata[k] = v.(string)
default:
jsonValue, err := json.Marshal(v)
if err != nil {
log.WithFields(logrus.Fields{
"prefix": "coprocess",
}).WithError(err).Error("Couldn't encode session metadata")
continue
}
metadata[k] = string(jsonValue)
}
}
}
return &coprocess.SessionState{
LastCheck: session.LastCheck,
Allowance: session.Allowance,
Rate: session.Rate,
Per: session.Per,
Expires: session.Expires,
QuotaMax: session.QuotaMax,
QuotaRenews: session.QuotaRenews,
QuotaRemaining: session.QuotaRemaining,
QuotaRenewalRate: session.QuotaRenewalRate,
AccessRights: accessDefinitions,
OrgId: session.OrgID,
OauthClientId: session.OauthClientID,
OauthKeys: session.OauthKeys,
BasicAuthData: basicAuthData,
JwtData: jwtData,
HmacEnabled: session.HMACEnabled,
HmacSecret: session.HmacSecret,
IsInactive: session.IsInactive,
ApplyPolicyId: session.ApplyPolicyID,
ApplyPolicies: session.ApplyPolicies,
DataExpires: session.DataExpires,
Monitor: monitor,
Metadata: metadata,
EnableDetailedRecording: session.EnableDetailRecording || session.EnableDetailedRecording,
Tags: session.Tags,
Alias: session.Alias,
LastUpdated: session.LastUpdated,
IdExtractorDeadline: session.IdExtractorDeadline,
SessionLifetime: session.SessionLifetime,
}
}
Cognitive complexity: 23, Cyclomatic complexity: 9
func ProxyHandler
ProxyHandler Proxies requests through to their final destination, if they make it through the middleware chain.
func ProxyHandler(p *ReverseProxy, apiSpec *APISpec) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
baseMid := &BaseMiddleware{Spec: apiSpec, Proxy: p, Gw: p.Gw}
handler := SuccessHandler{baseMid}
// Skip all other execution
handler.ServeHTTP(w, r)
})
}
Cognitive complexity: 3, Cyclomatic complexity: 1
func RevokeAllTokens
func RevokeAllTokens(storage ExtendedOsinStorageInterface, clientId, clientSecret string) (int, []string, error) {
resp := []string{}
client, err := storage.GetClient(clientId)
log.Debug("Revoke all tokens")
if err != nil {
return http.StatusNotFound, resp, errors.New("error getting oauth client")
}
if client.GetSecret() != clientSecret {
return http.StatusUnauthorized, resp, errors.New(oauthClientSecretWrong)
}
clientTokens, err := storage.GetClientTokens(clientId)
if err != nil {
return http.StatusBadRequest, resp, errors.New("cannot retrieve client tokens")
}
log.Debug("Tokens found to be revoked:", len(clientTokens))
for _, token := range clientTokens {
access, err := storage.LoadAccess(token.Token)
if err == nil {
resp = append(resp, access.AccessToken)
storage.RemoveAccess(access.AccessToken)
storage.RemoveRefresh(access.RefreshToken)
} else {
log.Debug("error loading access:", err.Error())
}
}
return http.StatusOK, resp, nil
}
Cognitive complexity: 14, Cyclomatic complexity: 6
func RevokeToken
func RevokeToken(storage ExtendedOsinStorageInterface, token, tokenTypeHint string) {
switch tokenTypeHint {
case accessToken:
storage.RemoveAccess(token)
case refreshToken:
storage.RemoveRefresh(token)
default:
storage.RemoveAccess(token)
storage.RemoveRefresh(token)
}
}
Cognitive complexity: 4, Cyclomatic complexity: 4
func Start
func Start() {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
// Set up signal handling for graceful shutdown
sigChan := make(chan os.Signal, 1)
// Only listen for SIGTERM which is what Kubernetes sends
signal.Notify(sigChan, syscall.SIGTERM)
// Initialize everything else as normal
cli.Init(confPaths)
cli.Parse()
// Stop gateway process if not running in "start" mode:
if !cli.DefaultMode {
os.Exit(0)
}
gwConfig := config.Config{}
if err := config.Load(confPaths, &gwConfig); err != nil {
mainLog.Errorf("Error loading config, using defaults: %v", err)
defaultConfig, err := config.NewDefaultWithEnv()
if err != nil {
mainLog.Fatalf("Error falling back to default config with env: %v", err)
}
gwConfig = *defaultConfig
}
gw := NewGateway(gwConfig, ctx)
gwConfig = gw.GetConfig()
go func() {
sig := <-sigChan
// This case handles SIGTERM for Kubernetes shutdowns
mainLog.Infof("SIGTERM received: %v. Initiating graceful shutdown...", sig)
// Cancel the context to notify all goroutines
cancel()
// Perform graceful shutdown
shutdownCtx, shutdownCancel := context.WithTimeout(
context.Background(), time.Duration(gwConfig.GracefulShutdownTimeoutDuration)*time.Second)
defer shutdownCancel()
if err := gw.gracefulShutdown(shutdownCtx); err != nil {
mainLog.Errorf("Graceful shutdown error: %v", err)
}
}()
if err := gw.initSystem(); err != nil {
mainLog.Fatalf("Error initialising system: %v", err)
}
if !gw.isRunningTests() && gwConfig.ControlAPIPort == 0 {
mainLog.Warn("The control_api_port should be changed for production")
}
gw.setupPortsWhitelist()
gw.keyGen = DefaultKeyGenerator{Gw: gw}
onFork := func() {
mainLog.Warning("PREPARING TO FORK")
// if controlListener != nil {
// if err := controlListener.Close(); err != nil {
// mainLog.Error("Control listen handler exit: ", err)
// }
// mainLog.Info("Control listen closed")
// }
if gwConfig.UseDBAppConfigs {
mainLog.Info("Stopping heartbeat")
gw.DashService.StopBeating()
mainLog.Info("Waiting to de-register")
time.Sleep(10 * time.Second)
os.Setenv("TYK_SERVICE_NONCE", gw.ServiceNonce)
os.Setenv("TYK_SERVICE_NODEID", gw.GetNodeID())
}
}
err := again.ListenFrom(&gw.DefaultProxyMux.again, onFork)
if err != nil {
mainLog.Errorf("Initializing again %s", err)
}
if tr := gwConfig.Tracer; tr.Enabled {
mainLog.Warn("OpenTracing is deprecated, use OpenTelemetry instead.")
trace.SetupTracing(tr.Name, tr.Options)
trace.SetLogger(mainLog)
defer trace.Close()
}
gw.TracerProvider = otel.InitOpenTelemetry(gw.ctx, mainLog.Logger, &gwConfig.OpenTelemetry,
gw.GetNodeID(),
VERSION,
gw.GetConfig().SlaveOptions.UseRPC,
gw.GetConfig().SlaveOptions.GroupID,
gw.GetConfig().DBAppConfOptions.NodeIsSegmented,
gw.GetConfig().DBAppConfOptions.Tags)
gw.start()
unix := time.Now().Unix()
var (
memprofile = fmt.Sprintf("tyk.%d.mprof", unix)
cpuprofile = fmt.Sprintf("tyk.%d.prof", unix)
)
if *cli.MemProfile {
mainLog.Debug("Memory profiling active")
var err error
if memProfFile, err = os.Create(memprofile); err != nil {
panic(err)
}
defer func() {
pprof.WriteHeapProfile(memProfFile)
memProfFile.Close()
}()
}
if *cli.CPUProfile {
mainLog.Info("Cpu profiling active")
cpuProfFile, err := os.Create(cpuprofile)
if err != nil {
panic(err)
}
pprof.StartCPUProfile(cpuProfFile)
defer pprof.StopCPUProfile()
}
if *cli.BlockProfile {
mainLog.Info("Block profiling active")
runtime.SetBlockProfileRate(1)
}
if *cli.MutexProfile {
mainLog.Info("Mutex profiling active")
runtime.SetMutexProfileFraction(1)
}
// set var as global so we can export TykTriggerEvent(CEventName, CPayload *C.char)
GatewayFireSystemEvent = gw.FireSystemEvent
// TODO: replace goagain with something that support multiple listeners
// Example: https://gravitational.com/blog/golang-ssh-bastion-graceful-restarts/
gw.startServer()
if again.Child() {
// This is a child process, we need to murder the parent now
if err := again.Kill(); err != nil {
mainLog.Fatal(err)
}
}
_, err = again.Wait(&gw.DefaultProxyMux.again)
if err != nil {
mainLog.WithError(err).Error("waiting")
}
mainLog.Info("Stop signal received.")
if err = gw.DefaultProxyMux.again.Close(); err != nil {
mainLog.Error("Closing listeners: ", err)
}
// stop analytics workers
if gwConfig.EnableAnalytics && gw.Analytics.Store == nil {
gw.Analytics.Stop()
}
// write pprof profiles
writeProfiles()
if gwConfig.UseDBAppConfigs {
mainLog.Info("Stopping heartbeat...")
gw.DashService.StopBeating()
time.Sleep(2 * time.Second)
err := gw.DashService.DeRegister()
if err != nil {
mainLog.WithError(err).Error("deregistering in dashboard")
}
}
if gwConfig.SlaveOptions.UseRPC {
store := RPCStorageHandler{
DoReload: gw.DoReload,
Gw: gw,
}
err := store.Disconnect()
if err != nil {
mainLog.WithError(err).Error("deregistering in MDCB")
}
}
mainLog.Info("Terminating.")
time.Sleep(time.Second)
}
Cognitive complexity: 54, Cyclomatic complexity: 27
func StartTest
func StartTest(genConf func(globalConf *config.Config), testConfig ...TestConfig) *Test {
t := &Test{
dynamicHandlers: make(map[string]http.HandlerFunc),
}
// DNS mock enabled by default
t.config.EnableTestDNSMock = false
if len(testConfig) > 0 {
t.config = testConfig[0]
}
t.Gw = t.start(genConf)
return t
}
Cognitive complexity: 3, Cyclomatic complexity: 2
func TestHelperSSEServer
func TestHelperSSEServer(tb testing.TB) *httptest.Server {
tb.Helper()
return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.Header().Set("Content-Type", "text/event-stream")
w.Header().Set("Connection", "keep-alive")
flusher, ok := w.(http.Flusher)
assert.True(tb, ok)
for i := 0; i < 5; i++ {
fmt.Fprintf(w, "data: %d\n", i)
flusher.Flush()
time.Sleep(50 * time.Millisecond)
}
}))
}
Cognitive complexity: 3, Cyclomatic complexity: 2
func TestHelperSSEStreamClient
func TestHelperSSEStreamClient(tb testing.TB, ts *Test, enableWebSockets bool) error {
tb.Helper()
req, err := http.NewRequestWithContext(context.Background(), http.MethodGet, ts.URL, nil)
assert.NoError(tb, err)
req.Header.Set("Accept", "text/event-stream")
client := http.Client{}
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
globalConf := ts.Gw.GetConfig()
globalConf.HttpServerOptions.EnableWebSockets = enableWebSockets
ts.Gw.SetConfig(globalConf)
res, err := client.Do(req)
assert.NoError(tb, err)
reader := bufio.NewReader(res.Body)
defer res.Body.Close()
i := 0
okChan := make(chan error)
go func() {
for {
line, err := reader.ReadBytes('\n')
if err != nil && assert.ErrorContains(tb, err, io.EOF.Error()) {
err = nil
}
assert.NoError(tb, err)
if len(line) == 0 {
break
}
assert.Equal(tb, fmt.Sprintf("data: %v\n", i), string(line))
i++
}
close(okChan)
}()
select {
case <-ctx.Done():
return ctx.Err()
case <-okChan:
}
assert.Equal(tb, i, 5)
return nil
}
Cognitive complexity: 11, Cyclomatic complexity: 7
func TestReq
func TestReq(t testing.TB, method, urlStr string, body interface{}) *http.Request {
return httptest.NewRequest(method, urlStr, TestReqBody(t, body))
}
Cognitive complexity: 1, Cyclomatic complexity: 1
func TestReqBody
func TestReqBody(t testing.TB, body interface{}) io.Reader {
switch x := body.(type) {
case []byte:
return bytes.NewReader(x)
case string:
return strings.NewReader(x)
case io.Reader:
return x
case nil:
return nil
default: // JSON objects (structs)
bs := test.MarshalJSON(t)(x)
return bytes.NewReader(bs)
}
}
Cognitive complexity: 7, Cyclomatic complexity: 6
func TykGetData
TykGetData is a CoProcess API function for fetching data.
func TykGetData(CKey * /*line :53:23*/ _Ctype_char /*line :53:29*/) * /*line :53:32*/ _Ctype_char /*line :53:38*/ {
key := ( /*line :54:9*/ _Cfunc_GoString /*line :54:18*/)(CKey)
// Timeout storing data after 1 second
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
store := getStorageForPython(ctx)
val, err := store.GetKey(key)
if err != nil {
log.WithError(err).Error("could not get key")
}
return ( /*line :67:9*/ _Cfunc_CString /*line :67:17*/)(val)
}
Cognitive complexity: 2, Cyclomatic complexity: 2
func TykSessionState
TykSessionState takes a coprocess.SessionState (as returned by the Protocol Buffer binding), and outputs a standard Tyk SessionState.
func TykSessionState(session *coprocess.SessionState) *user.SessionState {
accessDefinitions := make(map[string]user.AccessDefinition, len(session.AccessRights))
for key, protoAccDef := range session.AccessRights {
allowedUrls := make([]user.AccessSpec, len(protoAccDef.AllowedUrls))
for _, protoAllowedURL := range protoAccDef.AllowedUrls {
allowedUrls = append(allowedUrls, user.AccessSpec{
URL: protoAllowedURL.Url,
Methods: protoAllowedURL.Methods,
})
}
accessDefinitions[key] = user.AccessDefinition{
APIName: protoAccDef.ApiName,
APIID: protoAccDef.ApiId,
Versions: protoAccDef.Versions,
AllowedURLs: allowedUrls,
}
}
var basicAuthData struct {
Password string `json:"password" msg:"password"`
Hash user.HashType `json:"hash_type" msg:"hash_type"`
}
if session.BasicAuthData != nil {
basicAuthData.Password = session.BasicAuthData.Password
basicAuthData.Hash = user.HashType(session.BasicAuthData.Hash)
}
var jwtData struct {
Secret string `json:"secret" msg:"secret"`
}
if session.JwtData != nil {
jwtData.Secret = session.JwtData.Secret
}
var monitor struct {
TriggerLimits []float64 `json:"trigger_limits" msg:"trigger_limits"`
}
if session.Monitor != nil {
monitor.TriggerLimits = session.Monitor.TriggerLimits
}
metadata := make(map[string]interface{})
if session.Metadata != nil {
for k, v := range session.Metadata {
metadata[k] = v
}
}
return &user.SessionState{
LastCheck: session.LastCheck,
Allowance: session.Allowance,
Rate: session.Rate,
Per: session.Per,
MaxQueryDepth: int(session.MaxQueryDepth),
Expires: session.Expires,
QuotaMax: session.QuotaMax,
QuotaRenews: session.QuotaRenews,
QuotaRemaining: session.QuotaRemaining,
QuotaRenewalRate: session.QuotaRenewalRate,
AccessRights: accessDefinitions,
OrgID: session.OrgId,
OauthClientID: session.OauthClientId,
OauthKeys: session.OauthKeys,
Certificate: session.Certificate,
BasicAuthData: basicAuthData,
JWTData: jwtData,
HMACEnabled: session.HmacEnabled,
HmacSecret: session.HmacSecret,
IsInactive: session.IsInactive,
ApplyPolicyID: session.ApplyPolicyId,
ApplyPolicies: session.ApplyPolicies,
DataExpires: session.DataExpires,
MetaData: metadata,
Monitor: monitor,
EnableDetailedRecording: session.EnableDetailedRecording,
Tags: session.Tags,
Alias: session.Alias,
LastUpdated: session.LastUpdated,
IdExtractorDeadline: session.IdExtractorDeadline,
SessionLifetime: session.SessionLifetime,
}
}
Cognitive complexity: 24, Cyclomatic complexity: 8
func TykStoreData
TykStoreData is a CoProcess API function for storing data.
func TykStoreData(CKey, CValue * /*line :33:33*/ _Ctype_char /*line :33:39*/, CTTL /*line :33:46*/ _Ctype_int /*line :33:51*/) {
key := ( /*line :34:9*/ _Cfunc_GoString /*line :34:18*/)(CKey)
value := ( /*line :35:11*/ _Cfunc_GoString /*line :35:20*/)(CValue)
ttl := int64(CTTL)
// Timeout storing data after 1 second
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
store := getStorageForPython(ctx)
err := store.SetKey(key, value, ttl)
if err != nil {
log.WithError(err).Error("could not set key")
}
}
Cognitive complexity: 2, Cyclomatic complexity: 2
func TykTriggerEvent
TykTriggerEvent is a CoProcess API function for triggering Tyk system events.
func TykTriggerEvent(CEventName, CPayload * /*line :76:44*/ _Ctype_char /*line :76:50*/) {
eventName := ( /*line :77:15*/ _Cfunc_GoString /*line :77:24*/)(CEventName)
payload := ( /*line :78:13*/ _Cfunc_GoString /*line :78:22*/)(CPayload)
GatewayFireSystemEvent(apidef.TykEvent(eventName), EventMetaDefault{
Message: payload,
})
}
Cognitive complexity: 1, Cyclomatic complexity: 1
func UpdateAPIVersion
func UpdateAPIVersion(spec *APISpec, name string, verGen func(version *apidef.VersionInfo)) {
version := spec.VersionData.Versions[name]
verGen(&version)
spec.VersionData.Versions[name] = version
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func WrapMiddleware
WrapMiddleware returns a new TykMiddleware with the provided base middleware, and the smaller model.Middleware interface. It allows to implement model.Middleware, and use it as a TykMiddleware.
func WrapMiddleware(base *BaseMiddleware, in model.Middleware) TykMiddleware {
return &wrapMiddleware{
BaseMiddleware: base.Copy(),
mw: in,
}
}
Cognitive complexity: 1, Cyclomatic complexity: 1
func WrappedCharsetReader
func WrappedCharsetReader(s string, i io.Reader) (io.Reader, error) {
return charset.NewReader(i, s)
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*APISpec) AddUnloadHook
AddUnloadHook adds a function to be called when the API spec is unloaded
func (s *APISpec) AddUnloadHook(hook func()) {
s.unloadHooks = append(s.unloadHooks, hook)
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*APISpec) CheckSpecMatchesStatus
CheckSpecMatchesStatus checks if a URL spec has a specific status. Deprecated: The function doesn't follow go return conventions (T, ok); use FindSpecMatchesStatus;
func (a *APISpec) CheckSpecMatchesStatus(r *http.Request, rxPaths []URLSpec, mode URLStatus) (bool, interface{}) {
matchPath, method := a.getMatchPathAndMethod(r, mode)
for i := range rxPaths {
if rxPaths[i].Status != mode {
continue
}
if !rxPaths[i].matchesMethod(method) {
continue
}
if !rxPaths[i].matchesPath(matchPath, a) {
continue
}
if spec, ok := rxPaths[i].modeSpecificSpec(mode); ok {
return true, spec
}
}
return false, nil
}
Cognitive complexity: 12, Cyclomatic complexity: 6
func (*APISpec) Expired
func (a *APISpec) Expired() bool {
// Never expires
if a.Expiration == "" || a.Expiration == "-1" {
return false
}
// otherwise use parsed timestamp
if a.ExpirationTs.IsZero() {
log.Error("Could not parse expiration date, disallow")
return true
}
return time.Since(a.ExpirationTs) >= 0
}
Cognitive complexity: 4, Cyclomatic complexity: 4
func (*APISpec) FindSpecMatchesStatus
FindSpecMatchesStatus checks if a URL spec has a specific status and returns the URLSpec for it.
func (a *APISpec) FindSpecMatchesStatus(r *http.Request, rxPaths []URLSpec, mode URLStatus) (*URLSpec, bool) {
matchPath, method := a.getMatchPathAndMethod(r, mode)
for i := range rxPaths {
if rxPaths[i].Status != mode {
continue
}
if !rxPaths[i].matchesMethod(method) {
continue
}
if !rxPaths[i].matchesPath(matchPath, a) {
continue
}
return &rxPaths[i], true
}
return nil, false
}
Cognitive complexity: 9, Cyclomatic complexity: 5
func (*APISpec) FireEvent
func (s *APISpec) FireEvent(name apidef.TykEvent, meta interface{}) {
fireEvent(name, meta, s.EventPaths)
}
Cognitive complexity: 1, Cyclomatic complexity: 1
func (*APISpec) GetSessionLifetimeRespectsKeyExpiration
GetSessionLifetimeRespectsKeyExpiration returns a boolean to tell whether session lifetime should respect to key expiration or not.
The global config takes the precedence. If the global one is true, value of the one in api level doesn't matter.
func (a *APISpec) GetSessionLifetimeRespectsKeyExpiration() bool {
if a.GlobalConfig.SessionLifetimeRespectsKeyExpiration {
return true
}
return a.SessionLifetimeRespectsKeyExpiration
}
Cognitive complexity: 2, Cyclomatic complexity: 2
func (*APISpec) Init
func (a *APISpec) Init(authStore, sessionStore, healthStore, orgStore storage.Handler) {
a.AuthManager.Init(authStore)
a.Health.Init(healthStore)
a.OrgSessionManager.Init(orgStore)
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*APISpec) RequestValid
RequestValid will check if an incoming request has valid version data and return a RequestStatus that describes the status of the request
func (a *APISpec) RequestValid(r *http.Request) (bool, RequestStatus) {
versionInfo, status := a.Version(r)
// Screwed up version info - fail and pass through
if status != StatusOk {
return false, status
}
// Load path data and whitelist data for version
versionPaths, ok := a.RxPaths[versionInfo.Name]
if !ok {
log.Error("no RX Paths found for version ", versionInfo.Name)
return false, VersionDoesNotExist
}
whiteListStatus, ok := a.WhiteListEnabled[versionInfo.Name]
if !ok {
log.Error("no whitelist data found")
return false, VersionWhiteListStatusNotFound
}
if a.VersionData.NotVersioned && a.Expired() {
return false, APIExpired
} else if !a.VersionData.NotVersioned && versionInfo.Expired() { // Deprecated
return false, VersionExpired
}
// not expired, let's check path info
status, _ = a.URLAllowedAndIgnored(r, versionPaths, whiteListStatus)
switch status {
case EndPointNotAllowed:
return false, status
case StatusRedirectFlowByReply:
return true, status
case StatusOkAndIgnore, StatusCached, StatusTransform,
StatusHeaderInjected, StatusMethodTransformed:
return true, status
default:
return true, StatusOk
}
}
Cognitive complexity: 15, Cyclomatic complexity: 12
func (*APISpec) SanitizeProxyPaths
func (a *APISpec) SanitizeProxyPaths(r *http.Request) {
if !a.Proxy.StripListenPath {
return
}
log.Debug("Stripping proxy listen path: ", a.Proxy.ListenPath)
r.URL.Path = a.StripListenPath(r.URL.Path)
if r.URL.RawPath != "" {
r.URL.RawPath = a.StripListenPath(r.URL.RawPath)
}
log.Debug("Upstream path is: ", r.URL.Path)
}
Cognitive complexity: 4, Cyclomatic complexity: 3
func (*APISpec) StopSessionManagerPool
func (a *APISpec) StopSessionManagerPool() {
a.OrgSessionManager.Stop()
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*APISpec) StripListenPath
StripListenPath will strip the listen path from the URL, keeping version in tact.
func (a *APISpec) StripListenPath(reqPath string) string {
return httputil.StripListenPath(a.Proxy.ListenPath, reqPath)
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*APISpec) StripVersionPath
StripVersionPath will strip the version from the URL. The input URL should already have listen path stripped.
func (a *APISpec) StripVersionPath(reqPath string) string {
// First part of the url is the version fragment
part := strings.Split(strings.Trim(reqPath, "/"), "/")[0]
matchesUrlVersioningPattern := true
if a.VersionDefinition.UrlVersioningPattern != "" {
re, err := regexp.Compile(a.VersionDefinition.UrlVersioningPattern)
if err != nil {
log.Error("Error compiling versioning pattern: ", err)
} else {
matchesUrlVersioningPattern = re.Match([]byte(part))
}
}
if (a.VersionDefinition.StripVersioningData || a.VersionDefinition.StripPath) && matchesUrlVersioningPattern {
return strings.Replace(reqPath, "/"+part+"/", "/", 1)
}
return reqPath
}
Cognitive complexity: 8, Cyclomatic complexity: 6
func (*APISpec) URLAllowedAndIgnored
URLAllowedAndIgnored checks if a url is allowed and ignored.
func (a *APISpec) URLAllowedAndIgnored(r *http.Request, rxPaths []URLSpec, whiteListStatus bool) (RequestStatus, interface{}) {
for i := range rxPaths {
if !rxPaths[i].matchesPath(r.URL.Path, a) {
continue
}
if r.Method == rxPaths[i].Internal.Method && rxPaths[i].Status == Internal && !ctxLoopingEnabled(r) {
return EndPointNotAllowed, nil
}
}
// Check if ignored
for i := range rxPaths {
if !rxPaths[i].matchesPath(r.URL.Path, a) {
continue
}
if rxPaths[i].MethodActions == nil {
switch rxPaths[i].Status {
case WhiteList:
if rxPaths[i].Whitelist.Method != "" {
if rxPaths[i].Whitelist.Method != r.Method {
continue
}
return a.getURLStatus(rxPaths[i].Status), nil
}
case BlackList:
if rxPaths[i].Blacklist.Method != "" {
if rxPaths[i].Blacklist.Method != r.Method {
continue
}
return a.getURLStatus(rxPaths[i].Status), nil
}
case Ignored:
if rxPaths[i].Ignored.Method != "" {
if rxPaths[i].Ignored.Method != r.Method {
continue
}
}
return a.getURLStatus(rxPaths[i].Status), nil
case MockResponse:
if rxPaths[i].MockResponse.Method != r.Method {
continue
}
return StatusRedirectFlowByReply, rxPaths[i].MockResponse
}
} else { // Deprecated
// We are using an extended path set, check for the method
methodMeta, matchMethodOk := rxPaths[i].MethodActions[r.Method]
if !matchMethodOk {
continue
}
// Matched the method, check what status it is
// TODO: Extend here for additional reply options
switch methodMeta.Action {
case apidef.NoAction:
// NoAction status means we're not treating this request in any special or exceptional way
return a.getURLStatus(rxPaths[i].Status), nil
case apidef.Reply:
return StatusRedirectFlowByReply, &methodMeta
default:
log.Error("URL Method Action was not set to NoAction, blocking.")
return EndPointNotAllowed, nil
}
}
if whiteListStatus {
// We have a whitelist, nothing gets through unless specifically defined
switch rxPaths[i].Status {
case WhiteList, BlackList, Ignored:
default:
if rxPaths[i].Status == Internal && r.Method == rxPaths[i].Internal.Method && ctxLoopingEnabled(r) {
return a.getURLStatus(rxPaths[i].Status), nil
} else {
return EndPointNotAllowed, nil
}
}
}
if rxPaths[i].TransformAction.Template != nil {
return a.getURLStatus(rxPaths[i].Status), &rxPaths[i].TransformAction
}
if rxPaths[i].TransformJQAction.Filter != "" {
return a.getURLStatus(rxPaths[i].Status), &rxPaths[i].TransformJQAction
}
// TODO: Fix, Not a great detection method
if len(rxPaths[i].InjectHeaders.Path) > 0 {
return a.getURLStatus(rxPaths[i].Status), &rxPaths[i].InjectHeaders
}
// Using a legacy path, handle it raw.
return a.getURLStatus(rxPaths[i].Status), nil
}
// Nothing matched - should we still let it through?
if whiteListStatus {
// We have a whitelist, nothing gets through unless specifically defined
return EndPointNotAllowed, nil
}
// No whitelist, but also not in any of the other lists, let it through and filter
return StatusOk, nil
}
Cognitive complexity: 60, Cyclomatic complexity: 35
func (*APISpec) Unload
Release releases all resources associated with API spec
func (s *APISpec) Unload() {
s.Lock()
defer s.Unlock()
// release circuit breaker resources
for _, path := range s.RxPaths {
for _, urlSpec := range path {
if urlSpec.CircuitBreaker.CB != nil {
// this will force CB-event reading Go-routine and subscriber Go-routine to exit
urlSpec.CircuitBreaker.CB.Stop()
}
}
}
// cancel execution contexts
if s.GraphEngine != nil {
s.GraphEngine.Cancel()
}
// release all other resources associated with spec
// JSVM object is a circular dependecy hell, but we can check if it initialized like this
if s.JSVM.VM != nil {
s.JSVM.DeInit()
}
if s.HTTPTransport != nil {
// Prevent new idle connections to be generated.
s.HTTPTransport.transport.DisableKeepAlives = true
s.HTTPTransport.transport.CloseIdleConnections()
s.HTTPTransport = nil
}
for _, hook := range s.unloadHooks {
hook()
}
s.unloadHooks = nil
}
Cognitive complexity: 17, Cyclomatic complexity: 8
func (*APISpec) Validate
Validate returns nil if s is a valid spec and an error stating why the spec is not valid.
func (s *APISpec) Validate(oasConfig config.OASConfig) error {
if s.IsOAS {
err := s.OAS.Validate(context.Background(), oas.GetValidationOptionsFromConfig(oasConfig)...)
if err != nil {
return err
}
}
// For tcp services we need to make sure we can bind to the port.
switch s.Protocol {
case "tcp", "tls":
return s.validateTCP()
default:
return s.validateHTTP()
}
}
Cognitive complexity: 7, Cyclomatic complexity: 5
func (*APISpec) Version
Version attempts to extract the version data from a request, depending on where it is stored in the request (currently only "header" is supported)
func (a *APISpec) Version(r *http.Request) (*apidef.VersionInfo, RequestStatus) {
var version apidef.VersionInfo
// try the context first
if v := ctxGetVersionInfo(r); v != nil {
version = *v
} else {
// Are we versioned?
if a.VersionData.NotVersioned {
// Get the first one in the list
for _, v := range a.VersionData.Versions {
version = v
break
}
} else {
// Extract Version Info
// First checking for if default version is set
vName := a.getVersionFromRequest(r)
if vName == "" {
if a.VersionData.DefaultVersion == "" {
return &version, VersionNotFound
}
vName = a.VersionData.DefaultVersion
ctxSetDefaultVersion(r)
}
// Load Version Data - General
var ok bool
if version, ok = a.VersionData.Versions[vName]; !ok {
if a.VersionDefinition.FallbackToDefault {
log.Debugf("fallback to default version: %s", a.VersionData.DefaultVersion)
if version, ok = a.VersionData.Versions[a.VersionData.DefaultVersion]; ok {
return &version, StatusOk
}
}
return &version, VersionDoesNotExist
}
}
// cache for the future
ctxSetVersionInfo(r, &version)
}
return &version, StatusOk
}
Cognitive complexity: 21, Cyclomatic complexity: 9
func (*AccessRightsCheck) Name
func (a *AccessRightsCheck) Name() string {
return "AccessRightsCheck"
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*AccessRightsCheck) ProcessRequest
ProcessRequest will run any checks on the request on the way through the system, return an error to have the chain fail
func (a *AccessRightsCheck) ProcessRequest(w http.ResponseWriter, r *http.Request, _ interface{}) (error, int) {
if ctxGetRequestStatus(r) == StatusOkAndIgnore {
return nil, http.StatusOK
}
session := ctxGetSession(r)
// If there's nothing in our profile, we let them through to the next phase
if len(session.AccessRights) == 0 {
return nil, http.StatusOK
}
// Otherwise, run auth checks
versionList, apiExists := session.AccessRights[a.Spec.APIID]
if !apiExists {
a.Logger().Info("Attempted access to unauthorised API")
return errors.New("Access to this API has been disallowed"), http.StatusForbidden
}
if a.Spec.VersionData.NotVersioned {
return nil, http.StatusOK
}
targetVersion := a.Spec.getVersionFromRequest(r)
if targetVersion == "" {
targetVersion = a.Spec.VersionData.DefaultVersion
}
for _, vName := range versionList.Versions {
if vName == targetVersion {
return nil, http.StatusOK
}
}
if a.Spec.VersionDefinition.FallbackToDefault && targetVersion != a.Spec.VersionData.DefaultVersion {
for _, vName := range versionList.Versions {
if vName == a.Spec.VersionData.DefaultVersion {
return nil, http.StatusOK
}
}
}
a.Logger().Info("Attempted access to unauthorised API version.")
return errors.New("Access to this API has been disallowed"), http.StatusForbidden
}
Cognitive complexity: 23, Cyclomatic complexity: 12
func (*AuthKey) Name
func (k *AuthKey) Name() string {
return "AuthKey"
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*AuthKey) ProcessRequest
func (k *AuthKey) ProcessRequest(_ http.ResponseWriter, r *http.Request, _ interface{}) (error, int) {
if ctxGetRequestStatus(r) == StatusOkAndIgnore {
return nil, http.StatusOK
}
// skip auth key check if the request is looped.
if ses := ctxGetSession(r); ses != nil && httpctx.IsSelfLooping(r) {
return nil, http.StatusOK
}
key, authConfig := k.getAuthToken(k.getAuthType(), r)
var certHash string
keyExists := false
var session user.SessionState
updateSession := false
if key != "" {
key = stripBearer(key)
} else if authConfig.UseCertificate && key == "" && r.TLS != nil && len(r.TLS.PeerCertificates) > 0 {
log.Debug("Trying to find key by client certificate")
certHash = k.Spec.OrgID + crypto.HexSHA256(r.TLS.PeerCertificates[0].Raw)
if time.Now().After(r.TLS.PeerCertificates[0].NotAfter) {
return errorAndStatusCode(ErrAuthCertExpired)
}
key = k.Gw.generateToken(k.Spec.OrgID, certHash)
} else {
k.Logger().Info("Attempted access with malformed header, no auth header found.")
return errorAndStatusCode(ErrAuthAuthorizationFieldMissing)
}
session, keyExists = k.CheckSessionAndIdentityForValidKey(key, r)
key = session.KeyID
if !keyExists {
// fallback to search by cert
session, keyExists = k.CheckSessionAndIdentityForValidKey(certHash, r)
if !keyExists {
return k.reportInvalidKey(key, r, MsgNonExistentKey, ErrAuthKeyNotFound)
}
}
if authConfig.UseCertificate {
certLookup := session.Certificate
if r.TLS != nil && len(r.TLS.PeerCertificates) > 0 {
certLookup = certHash
if session.Certificate != certHash {
session.Certificate = certHash
updateSession = true
}
}
if _, err := k.Gw.CertificateManager.GetRaw(certLookup); err != nil {
return k.reportInvalidKey(key, r, MsgNonExistentCert, ErrAuthCertNotFound)
}
}
// Set session state on context, we will need it later
switch k.Spec.BaseIdentityProvidedBy {
case apidef.AuthToken, apidef.UnsetAuth:
hashKeys := k.Gw.GetConfig().HashKeys
ctxSetSession(r, &session, updateSession, hashKeys)
k.setContextVars(r, key)
attributes := []otel.SpanAttribute{otel.APIKeyAliasAttribute(session.Alias)}
if hashKeys {
attributes = append(attributes, otel.APIKeyAttribute(session.KeyHash()))
}
ctxSetSpanAttributes(r, k.Name(), attributes...)
}
// Try using org-key format first:
if strings.HasPrefix(key, session.OrgID) {
err, statusCode := k.validateSignature(r, key[len(session.OrgID):])
if err == nil && statusCode == http.StatusOK {
return err, statusCode
}
}
// As a second approach, try to use the internal ID that's part of the B64 JSON key:
keyID, err := storage.TokenID(key)
if err == nil {
err, statusCode := k.validateSignature(r, keyID)
if err == nil {
return nil, statusCode
}
}
// Last try is to take the key as is:
return k.validateSignature(r, key)
}
Cognitive complexity: 39, Cyclomatic complexity: 25
func (*BaseExtractor) Error
Error is a helper for logging the extractor errors. It always returns HTTP 400 (so we don't expose any details).
func (e *BaseExtractor) Error(r *http.Request, err error, message string) (returnOverrides ReturnOverrides) {
logEntry := e.Gw.getLogEntryForRequest(e.Logger(), r, "", nil)
logEntry.Info("Extractor error: ", message, ", ", err)
return ReturnOverrides{
ResponseCode: 400,
ResponseError: "Authorization field missing",
}
}
Cognitive complexity: 1, Cyclomatic complexity: 1
func (*BaseExtractor) ExtractAndCheck
ExtractAndCheck is called from the CP middleware, if ID extractor is enabled for the current API.
func (e *BaseExtractor) ExtractAndCheck(_ *http.Request) (sessionID string, returnOverrides ReturnOverrides) {
log.WithFields(logrus.Fields{
"prefix": "idextractor",
}).Error("This extractor doesn't implement an extraction method, rejecting.")
return "", ReturnOverrides{ResponseCode: 403, ResponseError: "Key not authorised"}
}
Cognitive complexity: 2, Cyclomatic complexity: 1
func (*BaseExtractor) ExtractBody
ExtractBody is used when BodySource is specified.
func (e *BaseExtractor) ExtractBody(r *http.Request) (string, error) {
body, err := ioutil.ReadAll(r.Body)
if err != nil {
return "", err
}
return string(body), err
}
Cognitive complexity: 2, Cyclomatic complexity: 2
func (*BaseExtractor) ExtractForm
ExtractForm is used when a FormSource is specified.
func (e *BaseExtractor) ExtractForm(r *http.Request, paramName string) (formValue string, err error) {
parseForm(r)
if paramName == "" {
return "", errors.New("no form param name set")
}
values := r.Form[paramName]
if len(values) == 0 {
return "", errors.New("no form value")
}
return strings.Join(values, ""), nil
}
Cognitive complexity: 4, Cyclomatic complexity: 3
func (*BaseExtractor) ExtractHeader
ExtractHeader is used when a HeaderSource is specified.
func (e *BaseExtractor) ExtractHeader(r *http.Request) (headerValue string, err error) {
headerName := e.IDExtractorConfig.HeaderName
headerValue = r.Header.Get(headerName)
if headerValue == "" {
err = errors.New("bad header value")
}
return headerValue, err
}
Cognitive complexity: 2, Cyclomatic complexity: 2
func (*BaseExtractor) GenerateSessionID
GenerateSessionID is a helper for generating session IDs, it takes an input (usually the extractor output) and a middleware pointer.
func (e *BaseExtractor) GenerateSessionID(input string, mw *BaseMiddleware) (sessionID string) {
data := []byte(input)
tokenID := fmt.Sprintf("%x", md5.Sum(data))
sessionID = e.Gw.generateToken(mw.Spec.OrgID, tokenID)
return sessionID
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*BaseMiddleware) ApplyPolicies
ApplyPolicies will check if any policies are loaded. If any are, it will overwrite the session state to use the policy values.
func (t *BaseMiddleware) ApplyPolicies(session *user.SessionState) error {
var orgID *string
if t.Spec != nil {
orgID = &t.Spec.OrgID
}
store := policy.New(orgID, t.Gw, log)
return store.Apply(session)
}
Cognitive complexity: 2, Cyclomatic complexity: 2
func (*BaseMiddleware) Base
Base serves to provide the full BaseMiddleware API. It's part of the TykMiddleware interface. It escapes to a wider API surface than TykMiddleware, used by middlewares, etc.
func (t *BaseMiddleware) Base() *BaseMiddleware {
return t
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*BaseMiddleware) CheckSessionAndIdentityForValidKey
CheckSessionAndIdentityForValidKey will check first the Session store for a valid key, if not found, it will try the Auth Handler, if not found it will fail
func (t *BaseMiddleware) CheckSessionAndIdentityForValidKey(originalKey string, r *http.Request) (user.SessionState, bool) {
key := originalKey
minLength := t.Spec.GlobalConfig.MinTokenLength
if minLength == 0 {
// See https://github.com/TykTechnologies/tyk/issues/1681
minLength = 3
}
if len(key) <= minLength {
return user.SessionState{IsInactive: true}, false
}
// Try and get the session from the session store
t.Logger().Debug("Querying local cache")
keyHash := key
cacheKey := key
if t.Spec.GlobalConfig.HashKeys {
cacheKey = storage.HashStr(key, storage.HashMurmur64) // always hash cache keys with murmur64 to prevent collisions
}
// Check in-memory cache
if !t.Spec.GlobalConfig.LocalSessionCache.DisableCacheSessionState {
cachedVal, found := t.Gw.SessionCache.Get(cacheKey)
if found {
t.Logger().Debug("--> Key found in local cache")
session := cachedVal.(user.SessionState).Clone()
if err := t.ApplyPolicies(&session); err != nil {
t.Logger().Error(err)
return session, false
}
return session, true
}
}
// Check session store
t.Logger().Debug("Querying keystore")
session, found := t.Gw.GlobalSessionManager.SessionDetail(t.Spec.OrgID, key, false)
if found {
if t.Spec.GlobalConfig.HashKeys {
keyHash = storage.HashStr(session.KeyID)
}
session := session.Clone()
session.SetKeyHash(keyHash)
// If exists, assume it has been authorized and pass on
// cache it
if !t.Spec.GlobalConfig.LocalSessionCache.DisableCacheSessionState {
t.Gw.SessionCache.Set(cacheKey, session, cache.DefaultExpiration)
}
// Check for a policy, if there is a policy, pull it and overwrite the session values
if err := t.ApplyPolicies(&session); err != nil {
t.Logger().Error(err)
return session, false
}
t.Logger().Debug("Got key")
return session, true
}
if _, ok := t.Spec.AuthManager.Store().(*RPCStorageHandler); ok && rpc.IsEmergencyMode() {
return session.Clone(), false
}
// Only search in RPC if it's not in emergency mode
t.Logger().Debug("Querying authstore")
// 2. If not there, get it from the AuthorizationHandler
session, found = t.Spec.AuthManager.SessionDetail(t.Spec.OrgID, key, false)
if found {
key = session.KeyID
session := session.Clone()
session.SetKeyHash(keyHash)
// If not in Session, and got it from AuthHandler, create a session with a new TTL
t.Logger().Info("Recreating session for key: ", t.Gw.obfuscateKey(key))
// cache it
if !t.Spec.GlobalConfig.LocalSessionCache.DisableCacheSessionState {
go t.Gw.SessionCache.Set(cacheKey, session, cache.DefaultExpiration)
}
// Check for a policy, if there is a policy, pull it and overwrite the session values
if err := t.ApplyPolicies(&session); err != nil {
t.Logger().Error(err)
return session, false
}
t.Logger().Debug("Lifetime is: ", session.Lifetime(t.Spec.GetSessionLifetimeRespectsKeyExpiration(), t.Spec.SessionLifetime, t.Gw.GetConfig().ForceGlobalSessionLifetime, t.Gw.GetConfig().GlobalSessionLifetime))
session.Touch()
return session, found
}
// session not found
session.KeyID = key
return session, false
}
Cognitive complexity: 29, Cyclomatic complexity: 16
func (*BaseMiddleware) Config
func (t *BaseMiddleware) Config() (interface{}, error) {
return nil, nil
}
Cognitive complexity: 1, Cyclomatic complexity: 1
func (*BaseMiddleware) Copy
Copy provides a new BaseMiddleware with it's own logger scope (copy). The Spec, Proxy and Gw values are not copied.
func (m *BaseMiddleware) Copy() *BaseMiddleware {
return &BaseMiddleware{
logger: m.logger.Dup(),
Spec: m.Spec,
Proxy: m.Proxy,
Gw: m.Gw,
}
}
Cognitive complexity: 1, Cyclomatic complexity: 1
func (*BaseMiddleware) EnabledForSpec
func (t *BaseMiddleware) EnabledForSpec() bool {
return true
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*BaseMiddleware) FireEvent
FireEvent is added to the BaseMiddleware object so it is available across the entire stack
func (t *BaseMiddleware) FireEvent(name apidef.TykEvent, meta interface{}) {
fireEvent(name, meta, t.Spec.EventPaths)
}
Cognitive complexity: 1, Cyclomatic complexity: 1
func (*BaseMiddleware) GetSpec
GetSpec returns the spec of the middleware
func (t *BaseMiddleware) GetSpec() *APISpec {
return t.Spec
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*BaseMiddleware) Init
func (t *BaseMiddleware) Init() {}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*BaseMiddleware) Logger
Logger is used by middleware process functions.
func (t *BaseMiddleware) Logger() (logger *logrus.Entry) {
t.loggerMu.Lock()
defer t.loggerMu.Unlock()
if t.logger == nil {
t.logger = logrus.NewEntry(log)
}
return t.logger
}
Cognitive complexity: 2, Cyclomatic complexity: 2
func (*BaseMiddleware) OrgSession
func (t *BaseMiddleware) OrgSession(orgID string) (user.SessionState, bool) {
if rpc.IsEmergencyMode() {
return user.SessionState{}, false
}
// Try and get the session from the session store
session, found := t.Spec.OrgSessionManager.SessionDetail(orgID, orgID, false)
if found && t.Spec.GlobalConfig.EnforceOrgDataAge {
// If exists, assume it has been authorized and pass on
// We cache org expiry data
t.Logger().Debug("Setting data expiry: ", orgID)
t.Gw.ExpiryCache.Set(session.OrgID, session.DataExpires, cache.DefaultExpiration)
}
session.SetKeyHash(storage.HashKey(orgID, t.Gw.GetConfig().HashKeys))
return session.Clone(), found
}
Cognitive complexity: 5, Cyclomatic complexity: 4
func (*BaseMiddleware) OrgSessionExpiry
func (t *BaseMiddleware) OrgSessionExpiry(orgid string) int64 {
t.Logger().Debug("Checking: ", orgid)
// Cache failed attempt
id, err, _ := orgSessionExpiryCache.Do(orgid, func() (interface{}, error) {
cachedVal, found := t.Gw.ExpiryCache.Get(orgid)
if found {
return cachedVal, nil
}
s, found := t.OrgSession(orgid)
if found && t.Spec.GlobalConfig.EnforceOrgDataAge {
return s.DataExpires, nil
}
return 0, errors.New("missing session")
})
if err != nil {
t.Logger().Debug("no cached entry found, returning 7 days")
t.SetOrgExpiry(orgid, DEFAULT_ORG_SESSION_EXPIRATION)
return DEFAULT_ORG_SESSION_EXPIRATION
}
return id.(int64)
}
Cognitive complexity: 8, Cyclomatic complexity: 5
func (*BaseMiddleware) RecordAccessLog
RecordAccessLog is used for Success/Error handler logging. It emits a log entry with populated access log fields.
func (t *BaseMiddleware) RecordAccessLog(req *http.Request, resp *http.Response, latency analytics.Latency) {
if !t.Spec.GlobalConfig.AccessLogs.Enabled {
return
}
gw := t.Gw
gwConfig := gw.GetConfig()
hashKeys := gwConfig.HashKeys
allowedFields := gwConfig.AccessLogs.Template
// Set the access log fields
accessLog := accesslog.NewRecord()
accessLog.WithApiKey(req, hashKeys, gw.obfuscateKey)
accessLog.WithRequest(req, latency)
accessLog.WithResponse(resp)
logFields := accessLog.Fields(allowedFields)
t.Logger().WithFields(logFields).Info()
}
Cognitive complexity: 2, Cyclomatic complexity: 2
func (*BaseMiddleware) SetName
func (t *BaseMiddleware) SetName(name string) {
t.loggerMu.Lock()
defer t.loggerMu.Unlock()
if t.logger == nil {
t.logger = logrus.NewEntry(log)
}
t.logger = t.logger.WithField("mw", name)
}
Cognitive complexity: 2, Cyclomatic complexity: 2
func (*BaseMiddleware) SetOrgExpiry
func (t *BaseMiddleware) SetOrgExpiry(orgid string, expiry int64) {
t.Gw.ExpiryCache.Set(orgid, expiry, cache.DefaultExpiration)
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*BaseMiddleware) SetRequestLogger
func (t *BaseMiddleware) SetRequestLogger(r *http.Request) *logrus.Entry {
t.loggerMu.Lock()
defer t.loggerMu.Unlock()
if t.logger == nil {
t.logger = logrus.NewEntry(log)
}
t.logger = t.Gw.getLogEntryForRequest(t.logger, r, ctxGetAuthToken(r), nil)
return t.logger
}
Cognitive complexity: 2, Cyclomatic complexity: 2
func (*BaseMiddleware) Unload
Unload unloads the middleware and frees resources
func (t *BaseMiddleware) Unload() {
// methos created to satisfy middleware contract
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*BaseMiddleware) UpdateRequestSession
func (t *BaseMiddleware) UpdateRequestSession(r *http.Request) bool {
session := ctxGetSession(r)
token := ctxGetAuthToken(r)
if session == nil || token == "" {
return false
}
if !session.IsModified() {
return false
}
lifetime := session.Lifetime(t.Spec.GetSessionLifetimeRespectsKeyExpiration(), t.Spec.SessionLifetime, t.Gw.GetConfig().ForceGlobalSessionLifetime, t.Gw.GetConfig().GlobalSessionLifetime)
if err := t.Gw.GlobalSessionManager.UpdateSession(token, session, lifetime, false); err != nil {
t.Logger().WithError(err).Error("Can't update session")
return false
}
// Reset session state, useful for benchmarks when request object stays the same.
session.Reset()
if !t.Spec.GlobalConfig.LocalSessionCache.DisableCacheSessionState {
t.Gw.SessionCache.Set(session.KeyHash(), session.Clone(), cache.DefaultExpiration)
}
return true
}
Cognitive complexity: 8, Cyclomatic complexity: 6
func (*BaseTykResponseHandler) Enabled
func (b *BaseTykResponseHandler) Enabled() bool {
return true
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*BaseTykResponseHandler) HandleError
func (b *BaseTykResponseHandler) HandleError(writer http.ResponseWriter, h *http.Request) {}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*BaseTykResponseHandler) HandleResponse
func (b *BaseTykResponseHandler) HandleResponse(rw http.ResponseWriter, res *http.Response, req *http.Request, ses *user.SessionState) error {
return nil
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*BaseTykResponseHandler) Init
func (b *BaseTykResponseHandler) Init(i interface{}, spec *APISpec) error {
return nil
}
Cognitive complexity: 1, Cyclomatic complexity: 1
func (*BaseTykResponseHandler) Name
func (b *BaseTykResponseHandler) Name() string {
return "BaseTykResponseHandler"
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*BasicAuthKeyIsValid) EnabledForSpec
EnabledForSpec checks if UseBasicAuth is set in the API definition.
func (k *BasicAuthKeyIsValid) EnabledForSpec() bool {
if !k.Spec.UseBasicAuth {
return false
}
var err error
if k.Spec.BasicAuth.ExtractFromBody {
if k.Spec.BasicAuth.BodyUserRegexp == "" || k.Spec.BasicAuth.BodyPasswordRegexp == "" {
k.Logger().Error("Basic Auth configured to extract credentials from body, but regexps are empty")
return false
}
k.bodyUserRegexp, err = regexp.Compile(k.Spec.BasicAuth.BodyUserRegexp)
if err != nil {
k.Logger().WithError(err).Error("Invalid user body regexp")
return false
}
k.bodyPasswordRegexp, err = regexp.Compile(k.Spec.BasicAuth.BodyPasswordRegexp)
if err != nil {
k.Logger().WithError(err).Error("Invalid user password regexp")
return false
}
}
return true
}
Cognitive complexity: 10, Cyclomatic complexity: 7
func (*BasicAuthKeyIsValid) Name
func (k *BasicAuthKeyIsValid) Name() string {
return "BasicAuthKeyIsValid"
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*BasicAuthKeyIsValid) ProcessRequest
ProcessRequest will run any checks on the request on the way through the system, return an error to have the chain fail
func (k *BasicAuthKeyIsValid) ProcessRequest(w http.ResponseWriter, r *http.Request, _ interface{}) (error, int) {
if ctxGetRequestStatus(r) == StatusOkAndIgnore {
return nil, http.StatusOK
}
username, password, err, code := k.basicAuthHeaderCredentials(w, r)
token := r.Header.Get(header.Authorization)
if err != nil {
if k.Spec.BasicAuth.ExtractFromBody {
w.Header().Del(header.WWWAuthenticate)
username, password, err, code = k.basicAuthBodyCredentials(w, r)
} else {
k.Logger().Warn("Attempted access with malformed header, no auth header found.")
}
if err != nil {
return err, code
}
}
// Check if API key valid
keyName := username
logger := k.Logger().WithField("key", k.Gw.obfuscateKey(keyName))
session, keyExists := k.CheckSessionAndIdentityForValidKey(keyName, r)
keyName = session.KeyID
if !keyExists {
if k.Gw.GetConfig().HashKeyFunction == "" {
logger.Warning("Attempted access with non-existent user.")
return k.handleAuthFail(w, r, token)
} else { // check for key with legacy format "org_id" + "user_name"
logger.Info("Could not find user, falling back to legacy format key.")
legacyKeyName := strings.TrimPrefix(username, k.Spec.OrgID)
keyName, _ = storage.GenerateToken(k.Spec.OrgID, legacyKeyName, "")
session, keyExists = k.CheckSessionAndIdentityForValidKey(keyName, r)
keyName = session.KeyID
if !keyExists {
logger.Warning("Attempted access with non-existent user.")
return k.handleAuthFail(w, r, token)
}
}
}
if err := k.checkPassword(&session, password, logger); err != nil {
logger.WithError(err).Warn("Attempted access with existing user, failed password check.")
return k.handleAuthFail(w, r, token)
}
// Set session state on context, we will need it later
switch k.Spec.BaseIdentityProvidedBy {
case apidef.BasicAuthUser, apidef.UnsetAuth:
ctxSetSession(r, &session, false, k.Gw.GetConfig().HashKeys)
}
return nil, http.StatusOK
}
Cognitive complexity: 24, Cyclomatic complexity: 11
func (*BatchRequestHandler) ConstructRequests
func (b *BatchRequestHandler) ConstructRequests(batchRequest BatchRequestStructure, unsafe bool) ([]*http.Request, error) {
requestSet := []*http.Request{}
ignoreCanonical := b.Gw.GetConfig().IgnoreCanonicalMIMEHeaderKey
for i, requestDef := range batchRequest.Requests {
// We re-build the URL to ensure that the requested URL is actually for the API in question
// URLs need to be built absolute so they go through the rate limiting and request limiting machinery
var absURL string
if !unsafe {
absUrlHeader := "http://localhost:" + strconv.Itoa(b.Gw.GetConfig().ListenPort)
absURL = strings.Join([]string{absUrlHeader, strings.Trim(b.API.Proxy.ListenPath, "/"), requestDef.RelativeURL}, "/")
} else {
absURL = requestDef.RelativeURL
}
request, err := http.NewRequest(requestDef.Method, absURL, strings.NewReader(requestDef.Body))
if err != nil {
log.Error("Failure generating batch request for request spec index: ", i)
return nil, err
}
// Add headers
for k, v := range requestDef.Headers {
setCustomHeader(request.Header, k, v, ignoreCanonical)
}
requestSet = append(requestSet, request)
}
return requestSet, nil
}
Cognitive complexity: 10, Cyclomatic complexity: 5
func (*BatchRequestHandler) DecodeBatchRequest
func (b *BatchRequestHandler) DecodeBatchRequest(r *http.Request) (BatchRequestStructure, error) {
var batchRequest BatchRequestStructure
err := json.NewDecoder(r.Body).Decode(&batchRequest)
return batchRequest, err
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*BatchRequestHandler) HandleBatchRequest
HandleBatchRequest is the actual http handler for a batch request on an API definition
func (b *BatchRequestHandler) HandleBatchRequest(w http.ResponseWriter, r *http.Request) {
if r.Method != "POST" {
return
}
// Decode request
batchRequest, err := b.DecodeBatchRequest(r)
if err != nil {
log.Error("Could not decode batch request, decoding failed: ", err)
doJSONWrite(w, http.StatusBadRequest, apiError("Batch request malformed"))
return
}
// Construct the requests
requestSet, err := b.ConstructRequests(batchRequest, false)
if err != nil {
doJSONWrite(w, http.StatusBadRequest, apiError(fmt.Sprintf("Batch request creation failed , request structure malformed")))
return
}
// Run requests and collate responses
replySet := b.MakeRequests(batchRequest, requestSet)
// Respond
doJSONWrite(w, http.StatusOK, replySet)
}
Cognitive complexity: 6, Cyclomatic complexity: 4
func (*BatchRequestHandler) MakeRequests
func (b *BatchRequestHandler) MakeRequests(batchRequest BatchRequestStructure, requestSet []*http.Request) []BatchReplyUnit {
replySet := []BatchReplyUnit{}
if len(batchRequest.Requests) != len(requestSet) {
log.Error("Something went wrong creating requests, they are of mismatched lengths!", len(batchRequest.Requests), len(requestSet))
}
if !batchRequest.SuppressParallelExecution {
replies := make(chan BatchReplyUnit)
for i, req := range requestSet {
go func(i int, req *http.Request) {
reply := b.doRequest(req, batchRequest.Requests[i].RelativeURL)
replies <- reply
}(i, req)
}
for range batchRequest.Requests {
replySet = append(replySet, <-replies)
}
} else {
for i, req := range requestSet {
reply := b.doRequest(req, batchRequest.Requests[i].RelativeURL)
replySet = append(replySet, reply)
}
}
return replySet
}
Cognitive complexity: 17, Cyclomatic complexity: 6
func (*BatchRequestHandler) ManualBatchRequest
HandleBatchRequest is the actual http handler for a batch request on an API definition
func (b *BatchRequestHandler) ManualBatchRequest(requestObject []byte) ([]byte, error) {
// Decode request
var batchRequest BatchRequestStructure
if err := json.Unmarshal(requestObject, &batchRequest); err != nil {
return nil, fmt.Errorf("could not decode batch request, decoding failed: %w", err)
}
// Construct the unsafe requests
requestSet, err := b.ConstructRequests(batchRequest, true)
if err != nil {
return nil, fmt.Errorf("batch request creation failed , request structure malformed: %w", err)
}
// Run requests and collate responses
replySet := b.MakeRequests(batchRequest, requestSet)
// Encode responses
replyMessage, err := json.Marshal(&replySet)
if err != nil {
return nil, fmt.Errorf("couldn't encode response to string: %w", err)
}
return replyMessage, nil
}
Cognitive complexity: 6, Cyclomatic complexity: 4
func (*Bundle) AddToSpec
AddToSpec attaches the custom middleware settings to an API definition.
func (b *Bundle) AddToSpec() {
b.Spec.CustomMiddleware = b.Manifest.CustomMiddleware
// Load Python interpreter if the
if loadedDrivers[b.Spec.CustomMiddleware.Driver] == nil && b.Spec.CustomMiddleware.Driver == apidef.PythonDriver {
var err error
loadedDrivers[apidef.PythonDriver], err = NewPythonDispatcher(b.Gw.GetConfig())
if err != nil {
log.WithFields(logrus.Fields{
"prefix": "coprocess",
}).WithError(err).Error("Couldn't load Python dispatcher")
return
}
log.WithFields(logrus.Fields{
"prefix": "coprocess",
}).Info("Python dispatcher was initialized")
}
dispatcher := loadedDrivers[b.Spec.CustomMiddleware.Driver]
if dispatcher != nil {
dispatcher.HandleMiddlewareCache(&b.Manifest, b.Path)
}
}
Cognitive complexity: 8, Cyclomatic complexity: 5
func (*Bundle) Verify
Verify performs a signature verification on the bundle file.
func (b *Bundle) Verify() error {
log.WithFields(logrus.Fields{
"prefix": "main",
}).Info("----> Verifying bundle: ", b.Spec.CustomMiddlewareBundle)
var useSignature = b.Gw.GetConfig().PublicKeyPath != ""
var (
verifier goverify.Verifier
err error
)
if useSignature {
// Perform signature verification if a public key path is set:
if b.Manifest.Signature == "" {
// Error: A public key is set, but the bundle isn't signed.
return errors.New("Bundle isn't signed")
}
verifier, err = b.Gw.SignatureVerifier()
if err != nil {
return err
}
}
var bundleData bytes.Buffer
for _, f := range b.Manifest.FileList {
extractedPath := filepath.Join(b.Path, f)
f, err := os.Open(extractedPath)
if err != nil {
return err
}
_, err = io.Copy(&bundleData, f)
f.Close()
if err != nil {
return err
}
}
checksum := fmt.Sprintf("%x", md5.Sum(bundleData.Bytes()))
if checksum != b.Manifest.Checksum {
return errors.New("Invalid checksum")
}
if useSignature {
signed, err := base64.StdEncoding.DecodeString(b.Manifest.Signature)
if err != nil {
return err
}
return verifier.Verify(bundleData.Bytes(), signed)
}
return nil
}
Cognitive complexity: 20, Cyclomatic complexity: 10
func (*CertificateCheckMW) EnabledForSpec
func (m *CertificateCheckMW) EnabledForSpec() bool {
return m.Spec.UseMutualTLSAuth
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*CertificateCheckMW) Name
func (m *CertificateCheckMW) Name() string {
return "CertificateCheckMW"
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*CertificateCheckMW) ProcessRequest
func (m *CertificateCheckMW) ProcessRequest(w http.ResponseWriter, r *http.Request, _ interface{}) (error, int) {
if ctxGetRequestStatus(r) == StatusOkAndIgnore {
return nil, http.StatusOK
}
if m.Spec.UseMutualTLSAuth {
certIDs := append(m.Spec.ClientCertificates, m.Spec.GlobalConfig.Security.Certificates.API...)
apiCerts := m.Gw.CertificateManager.List(certIDs, certs.CertificatePublic)
if err := crypto.ValidateRequestCerts(r, apiCerts); err != nil {
return err, http.StatusForbidden
}
}
return nil, http.StatusOK
}
Cognitive complexity: 7, Cyclomatic complexity: 4
func (*CoProcessEventHandler) HandleEvent
func (l *CoProcessEventHandler) HandleEvent(em config.EventMessage) {
dispatcher := loadedDrivers[l.Spec.CustomMiddleware.Driver]
if dispatcher == nil {
return
}
eventWrapper := CoProcessEventWrapper{
Event: em,
Handler: l.methodName,
SpecJSON: &l.SpecJSON,
}
// JSON-encode the event data object
msgAsJSON, err := json.Marshal(eventWrapper)
if err != nil {
log.Error("Failed to encode event data: ", err)
return
}
dispatcher.DispatchEvent(msgAsJSON)
}
Cognitive complexity: 5, Cyclomatic complexity: 3
func (*CoProcessEventHandler) Init
func (l *CoProcessEventHandler) Init(handlerConf interface{}) error {
l.methodName = handlerConf.(map[string]interface{})["name"].(string)
// Set the VM globals
globalVals := JSVMContextGlobal{
APIID: l.Spec.APIID,
OrgID: l.Spec.OrgID,
}
gValAsJSON, err := json.Marshal(globalVals)
if err != nil {
return fmt.Errorf("failed to marshal globals: %w", err)
}
l.SpecJSON = json.RawMessage(gValAsJSON)
return nil
}
Cognitive complexity: 5, Cyclomatic complexity: 2
func (*CoProcessMiddleware) EnabledForSpec
EnabledForSpec checks if this middleware should be enabled for a given API.
func (m *CoProcessMiddleware) EnabledForSpec() bool {
if !m.Gw.GetConfig().CoProcessOptions.EnableCoProcess {
log.WithFields(logrus.Fields{
"prefix": "coprocess",
}).Error("Your API specifies a CP custom middleware, either Tyk wasn't build with CP support or CP is not enabled in your Tyk configuration file!")
return false
}
var supported bool
for _, driver := range supportedDrivers {
if m.Spec.CustomMiddleware.Driver == driver {
supported = true
}
}
if !supported {
log.WithFields(logrus.Fields{
"prefix": "coprocess",
}).Errorf("Unsupported driver '%s'", m.Spec.CustomMiddleware.Driver)
return false
}
if d, _ := loadedDrivers[m.Spec.CustomMiddleware.Driver]; d == nil {
log.WithFields(logrus.Fields{
"prefix": "coprocess",
}).Errorf("Driver '%s' isn't loaded", m.Spec.CustomMiddleware.Driver)
return false
}
log.WithFields(logrus.Fields{
"prefix": "coprocess",
}).Debug("Enabling CP middleware.")
m.successHandler = &SuccessHandler{m.BaseMiddleware.Copy()}
return true
}
Cognitive complexity: 16, Cyclomatic complexity: 6
func (*CoProcessMiddleware) Name
func (m *CoProcessMiddleware) Name() string {
return "CoProcessMiddleware"
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*CoProcessMiddleware) ProcessRequest
ProcessRequest will run any checks on the request on the way through the system, return an error to have the chain fail
func (m *CoProcessMiddleware) ProcessRequest(w http.ResponseWriter, r *http.Request, _ interface{}) (error, int) {
if m.HookType == coprocess.HookType_CustomKeyCheck {
if ctxGetRequestStatus(r) == StatusOkAndIgnore {
return nil, http.StatusOK
}
}
logger := m.Logger()
logger.Debug("CoProcess Request, HookType: ", m.HookType)
originalURL := r.URL
authToken, _ := m.getAuthToken(apidef.CoprocessType, r)
extractor := getIDExtractor(m.Spec)
var returnOverrides ReturnOverrides
var sessionID string
if m.HookType == coprocess.HookType_CustomKeyCheck && extractor != nil {
sessionID, returnOverrides = extractor.ExtractAndCheck(r)
if returnOverrides.ResponseCode != 0 {
if returnOverrides.ResponseError == "" {
return nil, returnOverrides.ResponseCode
}
err := errors.New(returnOverrides.ResponseError)
return err, returnOverrides.ResponseCode
}
}
coProcessor := CoProcessor{
Middleware: m,
}
object, err := coProcessor.BuildObject(r, nil, m.Spec)
if err != nil {
logger.WithError(err).Error("Failed to build request object")
return errors.New("Middleware error"), 500
}
var origURL string
if rewriteUrl := ctxGetURLRewriteTarget(r); rewriteUrl != nil {
origURL = object.Request.Url
object.Request.Url = rewriteUrl.String()
object.Request.RequestUri = rewriteUrl.RequestURI()
}
var origMethod string
if transformMethod := ctxGetTransformRequestMethod(r); transformMethod != "" {
origMethod = r.Method
object.Request.Method = transformMethod
}
t1 := time.Now()
returnObject, err := coProcessor.Dispatch(object)
ms := DurationToMillisecond(time.Since(t1))
if err != nil {
logger.WithError(err).Error("Dispatch error")
if m.HookType == coprocess.HookType_CustomKeyCheck {
return errors.New("Key not authorised"), 403
} else {
return errors.New("Middleware error"), 500
}
}
m.logger.WithField("ms", ms).Debug("gRPC request processing took")
err = coProcessor.ObjectPostProcess(returnObject, r, origURL, origMethod)
if err != nil {
// Restore original URL object so that it can be used by ErrorHandler:
r.URL = originalURL
logger.WithError(err).Error("Failed to post-process request object")
return errors.New("Middleware error"), 500
}
var token string
if returnObject.Session != nil {
// For compatibility purposes, inject coprocess.Object.Metadata fields:
if returnObject.Metadata != nil {
if returnObject.Session.Metadata == nil {
returnObject.Session.Metadata = make(map[string]string)
}
for k, v := range returnObject.Metadata {
returnObject.Session.Metadata[k] = v
}
}
token = returnObject.Session.Metadata["token"]
}
if returnObject.Request.ReturnOverrides.ResponseError != "" {
returnObject.Request.ReturnOverrides.ResponseBody = returnObject.Request.ReturnOverrides.ResponseError
}
// The CP middleware indicates this is a bad auth:
if returnObject.Request.ReturnOverrides.ResponseCode >= http.StatusBadRequest && !returnObject.Request.ReturnOverrides.OverrideError {
logger.WithField("key", m.Gw.obfuscateKey(token)).Info("Attempted access with invalid key")
for h, v := range returnObject.Request.ReturnOverrides.Headers {
w.Header().Set(h, v)
}
// Fire Authfailed Event
AuthFailed(m, r, token)
// Report in health check
reportHealthValue(m.Spec, KeyFailure, "1")
errorMsg := "Key not authorised"
if returnObject.Request.ReturnOverrides.ResponseBody != "" {
errorMsg = returnObject.Request.ReturnOverrides.ResponseBody
}
return errors.New(errorMsg), int(returnObject.Request.ReturnOverrides.ResponseCode)
}
if returnObject.Request.ReturnOverrides.ResponseCode > 0 {
for h, v := range returnObject.Request.ReturnOverrides.Headers {
w.Header().Set(h, v)
}
w.WriteHeader(int(returnObject.Request.ReturnOverrides.ResponseCode))
w.Write([]byte(returnObject.Request.ReturnOverrides.ResponseBody))
// Record analytics data:
res := new(http.Response)
res.Proto = "HTTP/1.0"
res.ProtoMajor = 1
res.ProtoMinor = 0
res.StatusCode = int(returnObject.Request.ReturnOverrides.ResponseCode)
res.Body = nopCloser{
ReadSeeker: strings.NewReader(returnObject.Request.ReturnOverrides.ResponseBody),
}
res.ContentLength = int64(len(returnObject.Request.ReturnOverrides.ResponseBody))
m.successHandler.RecordHit(r, analytics.Latency(analytics.Latency{Total: int64(ms)}), int(returnObject.Request.ReturnOverrides.ResponseCode), res, false)
return nil, mwStatusRespond
}
// Is this a CP authentication middleware?
if coprocessAuthEnabled(m.Spec) && m.HookType == coprocess.HookType_CustomKeyCheck {
if extractor == nil {
sessionID = token
}
// The CP middleware didn't setup a session:
if returnObject.Session == nil || token == "" {
authHeaderValue, _ := m.getAuthToken(m.getAuthType(), r)
AuthFailed(m, r, authHeaderValue)
return errors.New(http.StatusText(http.StatusForbidden)), http.StatusForbidden
}
returnedSession := TykSessionState(returnObject.Session)
// If the returned object contains metadata, add them to the session:
for k, v := range returnObject.Metadata {
returnedSession.MetaData[k] = string(v)
}
returnedSession.OrgID = m.Spec.OrgID
// set a Key ID as default
returnedSession.KeyID = token
if err := m.ApplyPolicies(returnedSession); err != nil {
AuthFailed(m, r, authToken)
return errors.New(http.StatusText(http.StatusForbidden)), http.StatusForbidden
}
existingSession, found := m.Gw.GlobalSessionManager.SessionDetail(m.Spec.OrgID, sessionID, false)
if found {
returnedSession.QuotaRenews = existingSession.QuotaRenews
returnedSession.QuotaRemaining = existingSession.QuotaRemaining
for api := range returnedSession.AccessRights {
if _, found := existingSession.AccessRights[api]; found {
if !returnedSession.AccessRights[api].Limit.IsEmpty() {
ar := returnedSession.AccessRights[api]
ar.Limit.QuotaRenews = existingSession.AccessRights[api].Limit.QuotaRenews
ar.Limit.QuotaRemaining = existingSession.AccessRights[api].Limit.QuotaRemaining
returnedSession.AccessRights[api] = ar
}
}
}
}
// Apply it second time to fix the quota
if err := m.ApplyPolicies(returnedSession); err != nil {
AuthFailed(m, r, authToken)
return errors.New(http.StatusText(http.StatusForbidden)), http.StatusForbidden
}
returnedSession.KeyID = sessionID
switch m.Spec.BaseIdentityProvidedBy {
case apidef.CustomAuth, apidef.UnsetAuth:
ctxSetSession(r, returnedSession, true, m.Gw.GetConfig().HashKeys)
}
}
return nil, http.StatusOK
}
Cognitive complexity: 76, Cyclomatic complexity: 38
func (*CoProcessor) BuildObject
BuildObject constructs a CoProcessObject from a given http.Request.
func (c *CoProcessor) BuildObject(req *http.Request, res *http.Response, spec *APISpec) (*coprocess.Object, error) {
headers := ProtoMap(req.Header)
host := req.Host
if host == "" && req.URL != nil {
host = req.URL.Host
}
if host != "" {
headers["Host"] = host
}
scheme := "http"
if req.TLS != nil {
scheme = "https"
}
miniRequestObject := &coprocess.MiniRequestObject{
Headers: headers,
SetHeaders: map[string]string{},
DeleteHeaders: []string{},
Url: req.URL.String(),
Params: ProtoMap(req.URL.Query()),
AddParams: map[string]string{},
ExtendedParams: ProtoMap(nil),
DeleteParams: []string{},
ReturnOverrides: &coprocess.ReturnOverrides{
ResponseCode: -1,
},
Method: req.Method,
RequestUri: req.RequestURI,
Scheme: scheme,
}
if req.Body != nil {
defer req.Body.Close()
var err error
miniRequestObject.RawBody, err = ioutil.ReadAll(req.Body)
if err != nil {
return nil, err
}
if utf8.Valid(miniRequestObject.RawBody) && !c.Middleware.RawBodyOnly {
miniRequestObject.Body = string(miniRequestObject.RawBody)
}
}
object := &coprocess.Object{
Request: miniRequestObject,
HookName: c.Middleware.HookName,
HookType: c.Middleware.HookType,
}
object.Spec = make(map[string]string)
// Append spec data:
if c.Middleware != nil {
configDataAsJSON := []byte("{}")
if shouldAddConfigData(c.Middleware.Spec) {
var err error
configDataAsJSON, err = json.Marshal(c.Middleware.Spec.ConfigData)
if err != nil {
return nil, err
}
}
bundleHash, err := c.Middleware.Gw.getHashedBundleName(spec.CustomMiddlewareBundle)
if err != nil {
return nil, err
}
object.Spec = map[string]string{
"OrgID": c.Middleware.Spec.OrgID,
"APIID": c.Middleware.Spec.APIID,
"bundle_hash": bundleHash,
}
if shouldAddConfigData(c.Middleware.Spec) {
object.Spec["config_data"] = string(configDataAsJSON)
}
}
// Encode the session object (if not a pre-process & not a custom key check):
if object.HookType != coprocess.HookType_Pre && object.HookType != coprocess.HookType_CustomKeyCheck {
if session := ctxGetSession(req); session != nil {
object.Session = ProtoSessionState(session)
// For compatibility purposes:
object.Metadata = object.Session.Metadata
}
}
// Append response data if it's available:
if res != nil {
resObj := &coprocess.ResponseObject{
Headers: make(map[string]string, len(res.Header)),
}
for k, v := range res.Header {
// set univalue header
resObj.Headers[k] = v[0]
// set multivalue header
currentHeader := coprocess.Header{
Key: k,
Values: v,
}
resObj.MultivalueHeaders = append(resObj.MultivalueHeaders, ¤tHeader)
}
resObj.StatusCode = int32(res.StatusCode)
rawBody, err := ioutil.ReadAll(res.Body)
if err != nil {
return nil, err
}
resObj.RawBody = rawBody
res.Body = ioutil.NopCloser(bytes.NewReader(rawBody))
if utf8.Valid(rawBody) && !c.Middleware.RawBodyOnly {
resObj.Body = string(rawBody)
}
object.Response = resObj
}
return object, nil
}
Cognitive complexity: 45, Cyclomatic complexity: 22
func (*CoProcessor) Dispatch
func (c *CoProcessor) Dispatch(object *coprocess.Object) (*coprocess.Object, error) {
dispatcher := loadedDrivers[c.Middleware.MiddlewareDriver]
if dispatcher == nil {
err := fmt.Errorf("Couldn't dispatch request, driver '%s' isn't available", c.Middleware.MiddlewareDriver)
return nil, err
}
newObject, err := dispatcher.Dispatch(object)
if err != nil {
return nil, err
}
return newObject, nil
}
Cognitive complexity: 4, Cyclomatic complexity: 3
func (*CoProcessor) ObjectPostProcess
ObjectPostProcess does CoProcessObject post-processing (adding/removing headers or params, etc.).
func (c *CoProcessor) ObjectPostProcess(object *coprocess.Object, r *http.Request, origURL string, origMethod string) (err error) {
r.ContentLength = int64(len(object.Request.RawBody))
r.Body = ioutil.NopCloser(bytes.NewReader(object.Request.RawBody))
nopCloseRequestBody(r)
logger := c.Middleware.Logger()
for _, dh := range object.Request.DeleteHeaders {
r.Header.Del(dh)
}
ignoreCanonical := c.Middleware.Gw.GetConfig().IgnoreCanonicalMIMEHeaderKey
for h, v := range object.Request.SetHeaders {
setCustomHeader(r.Header, h, v, ignoreCanonical)
}
updatedValues := r.URL.Query()
for _, k := range object.Request.DeleteParams {
updatedValues.Del(k)
}
for p, v := range object.Request.AddParams {
updatedValues.Set(p, v)
}
parsedURL, err := url.ParseRequestURI(object.Request.Url)
if err != nil {
logger.Error(err)
return
}
rewriteURL := ctxGetURLRewriteTarget(r)
if rewriteURL != nil {
ctxSetURLRewriteTarget(r, parsedURL)
r.URL, err = url.ParseRequestURI(origURL)
if err != nil {
logger.Error(err)
return
}
} else {
r.URL = parsedURL
}
transformMethod := ctxGetTransformRequestMethod(r)
if transformMethod != "" {
ctxSetTransformRequestMethod(r, object.Request.Method)
r.Method = origMethod
} else {
r.Method = object.Request.Method
}
if !reflect.DeepEqual(r.URL.Query(), updatedValues) {
r.URL.RawQuery = updatedValues.Encode()
}
return
}
Cognitive complexity: 26, Cyclomatic complexity: 10
func (*CustomMiddlewareResponseHook) HandleError
func (h *CustomMiddlewareResponseHook) HandleError(rw http.ResponseWriter, req *http.Request) {
handler := ErrorHandler{h.mw.BaseMiddleware.Copy()}
handler.HandleError(rw, req, "Middleware error", http.StatusInternalServerError, true)
}
Cognitive complexity: 1, Cyclomatic complexity: 1
func (*CustomMiddlewareResponseHook) HandleResponse
func (h *CustomMiddlewareResponseHook) HandleResponse(rw http.ResponseWriter, res *http.Response, req *http.Request, ses *user.SessionState) error {
log.WithFields(logrus.Fields{
"prefix": "coprocess",
}).Debugf("Response hook '%s' is called", h.mw.Name())
coProcessor := CoProcessor{
Middleware: h.mw,
}
object, err := coProcessor.BuildObject(req, res, h.mw.Spec)
if err != nil {
log.WithError(err).Debug("Couldn't build request object")
return errors.New("Middleware error")
}
object.Session = ProtoSessionState(ses)
retObject, err := coProcessor.Dispatch(object)
if err != nil {
log.WithError(err).Debug("Couldn't dispatch request object")
return errors.New("Middleware error")
}
if retObject.Response == nil {
log.WithError(err).Debug("No response object returned by response hook")
return errors.New("Middleware error")
}
// Clear all response headers before populating from coprocess response object:
for k := range res.Header {
delete(res.Header, k)
}
// check if we have changes in headers
if !areMapsEqual(object.Response.Headers, retObject.Response.Headers) {
// as we have changes we need to synchronize them
retObject.Response.MultivalueHeaders = syncHeadersAndMultiValueHeaders(retObject.Response.Headers, retObject.Response.MultivalueHeaders)
}
// Set headers:
ignoreCanonical := h.mw.Gw.GetConfig().IgnoreCanonicalMIMEHeaderKey
for _, v := range retObject.Response.MultivalueHeaders {
setCustomHeaderMultipleValues(res.Header, v.Key, v.Values, ignoreCanonical)
}
// Set response body:
bodyBuf := bytes.NewBuffer(retObject.Response.RawBody)
res.Body = ioutil.NopCloser(bodyBuf)
//set response body length with the size of response body returned from the hook
//so that it is updated accordingly in the response object
responseBodyLen := len(retObject.Response.RawBody)
res.ContentLength = int64(responseBodyLen)
res.Header.Set("Content-Length", fmt.Sprintf("%d", responseBodyLen))
res.StatusCode = int(retObject.Response.StatusCode)
return nil
}
Cognitive complexity: 16, Cyclomatic complexity: 7
func (*CustomMiddlewareResponseHook) Init
func (h *CustomMiddlewareResponseHook) Init(mwDef interface{}, spec *APISpec) error {
mwDefinition := mwDef.(apidef.MiddlewareDefinition)
h.mw = &CoProcessMiddleware{
BaseMiddleware: &BaseMiddleware{
Spec: spec,
Gw: h.Gw,
},
HookName: mwDefinition.Name,
HookType: coprocess.HookType_Response,
RawBodyOnly: mwDefinition.RawBodyOnly,
MiddlewareDriver: spec.CustomMiddleware.Driver,
}
return nil
}
Cognitive complexity: 3, Cyclomatic complexity: 1
func (*CustomMiddlewareResponseHook) Name
func (h *CustomMiddlewareResponseHook) Name() string {
return "CustomMiddlewareResponseHook"
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*DBAccessDefinition) ToRegularAD
func (d *DBAccessDefinition) ToRegularAD() user.AccessDefinition {
ad := user.AccessDefinition{
APIName: d.APIName,
APIID: d.APIID,
Versions: d.Versions,
AllowedURLs: d.AllowedURLs,
RestrictedTypes: d.RestrictedTypes,
AllowedTypes: d.AllowedTypes,
DisableIntrospection: d.DisableIntrospection,
FieldAccessRights: d.FieldAccessRights,
Endpoints: d.Endpoints,
}
if d.Limit != nil {
ad.Limit = *d.Limit
}
return ad
}
Cognitive complexity: 3, Cyclomatic complexity: 2
func (*DBPolicy) ToRegularPolicy
func (d *DBPolicy) ToRegularPolicy() user.Policy {
policy := d.Policy
policy.AccessRights = make(map[string]user.AccessDefinition)
for k, v := range d.AccessRights {
policy.AccessRights[k] = v.ToRegularAD()
}
return policy
}
Cognitive complexity: 3, Cyclomatic complexity: 2
func (*DefaultHealthChecker) ApiHealthValues
func (h *DefaultHealthChecker) ApiHealthValues() (HealthCheckValues, error) {
values := HealthCheckValues{}
// Get the counted / average values
values.ThrottledRequestsPS = h.getAvgCount(Throttle)
values.QuotaViolationsPS = h.getAvgCount(QuotaViolation)
values.KeyFailuresPS = h.getAvgCount(KeyFailure)
values.AvgRequestsPS = h.getAvgCount(RequestLog)
// Get the micro latency graph, an average upstream latency
searchStr := h.APIID + "." + string(RequestLog)
log.Debug("Searching KV for: ", searchStr)
_, vals := h.storage.SetRollingWindow(searchStr, h.Gw.GetConfig().HealthCheck.HealthCheckValueTimeout, "-1", false)
log.Debug("Found: ", vals)
if len(vals) == 0 {
return values, nil
}
var runningTotal int
for _, v := range vals {
s := v.(string)
log.Debug("V is: ", s)
splitValues := strings.Split(s, ".")
if len(splitValues) > 1 {
vInt, err := strconv.Atoi(splitValues[1])
if err != nil {
log.Error("Couldn't convert tracked latency value to Int, vl is: ", err)
} else {
runningTotal += vInt
}
}
}
values.AvgUpstreamLatency = roundValue(float64(runningTotal / len(vals)))
return values, nil
}
Cognitive complexity: 12, Cyclomatic complexity: 5
func (*DefaultHealthChecker) CreateKeyName
func (h *DefaultHealthChecker) CreateKeyName(subKey HealthPrefix) string {
// Key should be API-ID.SubKey.123456789
return h.APIID + "." + string(subKey)
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*DefaultHealthChecker) Init
func (h *DefaultHealthChecker) Init(storeType storage.Handler) {
if !h.Gw.GetConfig().HealthCheck.EnableHealthChecks {
return
}
log.Info("Initializing HealthChecker")
h.storage = storeType
h.storage.Connect()
}
Cognitive complexity: 2, Cyclomatic complexity: 2
func (*DefaultHealthChecker) StoreCounterVal
func (h *DefaultHealthChecker) StoreCounterVal(counterType HealthPrefix, value string) {
searchStr := h.CreateKeyName(counterType)
log.Debug("Adding Healthcheck to: ", searchStr)
log.Debug("Val is: ", value)
//go h.storage.SetKey(searchStr, value, config.Global.HealthCheck.HealthCheckValueTimeout)
if value != "-1" {
// need to ensure uniqueness
now_string := strconv.Itoa(int(time.Now().UnixNano()))
value = now_string + "." + value
log.Debug("Set value to: ", value)
}
go h.storage.SetRollingWindow(searchStr, h.Gw.GetConfig().HealthCheck.HealthCheckValueTimeout, value, false)
}
Cognitive complexity: 2, Cyclomatic complexity: 2
func (*DefaultSessionManager) Init
func (b *DefaultSessionManager) Init(store storage.Handler) {
b.store = store
b.store.Connect()
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*DefaultSessionManager) KeyExpired
KeyExpired checks if a key has expired, if the value of user.SessionState.Expires is 0, it will be ignored
func (b *DefaultSessionManager) KeyExpired(newSession *user.SessionState) bool {
if newSession.Expires >= 1 {
return time.Now().After(time.Unix(newSession.Expires, 0))
}
return false
}
Cognitive complexity: 2, Cyclomatic complexity: 2
func (*DefaultSessionManager) RemoveSession
RemoveSession removes session from storage
func (b *DefaultSessionManager) RemoveSession(orgID string, keyName string, hashed bool) bool {
defer b.clearCacheForKey(keyName, hashed)
if hashed {
return b.store.DeleteRawKey(b.store.GetKeyPrefix() + keyName)
} else {
// support both old and new key hashing
res1 := b.store.DeleteKey(keyName)
res2 := b.store.DeleteKey(b.Gw.generateToken(orgID, keyName))
return res1 || res2
}
}
Cognitive complexity: 4, Cyclomatic complexity: 3
func (*DefaultSessionManager) ResetQuota
func (b *DefaultSessionManager) ResetQuota(keyName string, session *user.SessionState, isHashed bool) {
origKeyName := keyName
if !isHashed {
keyName = storage.HashKey(keyName, b.Gw.GetConfig().HashKeys)
}
rawKey := QuotaKeyPrefix + keyName
log.WithFields(logrus.Fields{
"prefix": "auth-mgr",
"inbound-key": b.Gw.obfuscateKey(origKeyName),
"key": b.ResetQuotaObfuscateKey(keyName),
}).Info("Reset quota for key.")
rateLimiterSentinelKey := RateLimitKeyPrefix + keyName + ".BLOCKED"
// Clear the rate limiter and
// Fix the raw key
defaultKeys := []string{rateLimiterSentinelKey, rawKey}
keys := rawKeysWithAllowanceScope(defaultKeys, keyName, session)
b.store.DeleteRawKeys(keys)
}
Cognitive complexity: 4, Cyclomatic complexity: 2
func (*DefaultSessionManager) ResetQuotaObfuscateKey
func (b *DefaultSessionManager) ResetQuotaObfuscateKey(keyName string) string {
if !b.Gw.GetConfig().HashKeys && !b.Gw.GetConfig().EnableKeyLogging {
return b.Gw.obfuscateKey(keyName)
}
return keyName
}
Cognitive complexity: 2, Cyclomatic complexity: 3
func (*DefaultSessionManager) SessionDetail
SessionDetail returns the session detail using the storage engine (either in memory or Redis)
func (b *DefaultSessionManager) SessionDetail(orgID string, keyName string, hashed bool) (user.SessionState, bool) {
var jsonKeyVal string
var err error
keyId := keyName
// get session by key
if hashed {
jsonKeyVal, err = b.store.GetRawKey(b.store.GetKeyPrefix() + keyName)
} else {
if storage.TokenOrg(keyName) != orgID {
// try to get legacy and new format key at once
toSearchList := []string{}
if !b.Gw.GetConfig().DisableKeyActionsByUsername {
toSearchList = append(toSearchList, b.Gw.generateToken(orgID, keyName))
}
toSearchList = append(toSearchList, keyName)
for _, fallback := range b.Gw.GetConfig().HashKeyFunctionFallback {
if !b.Gw.GetConfig().DisableKeyActionsByUsername {
toSearchList = append(toSearchList, b.Gw.generateToken(orgID, keyName, fallback))
}
}
var jsonKeyValList []string
jsonKeyValList, err = b.store.GetMultiKey(toSearchList)
// pick the 1st non empty from the returned list
for idx, val := range jsonKeyValList {
if val != "" {
jsonKeyVal = val
keyId = toSearchList[idx]
break
}
}
} else {
// key is not an imported one
jsonKeyVal, err = b.store.GetKey(keyName)
}
}
if err != nil {
log.WithFields(logrus.Fields{
"prefix": "auth-mgr",
"inbound-key": b.Gw.obfuscateKey(keyName),
"err": err,
}).Debug("Could not get session detail, key not found")
return user.SessionState{}, false
}
session := &user.SessionState{}
if err := json.Unmarshal([]byte(jsonKeyVal), &session); err != nil {
log.Error("Couldn't unmarshal session object (may be cache miss): ", err)
return user.SessionState{}, false
}
session.KeyID = keyId
return session.Clone(), true
}
Cognitive complexity: 29, Cyclomatic complexity: 10
func (*DefaultSessionManager) Sessions
Sessions returns all sessions in the key store that match a filter key (a prefix)
func (b *DefaultSessionManager) Sessions(filter string) []string {
return b.store.GetKeys(filter)
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*DefaultSessionManager) Stop
func (b *DefaultSessionManager) Stop() {}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*DefaultSessionManager) Store
func (b *DefaultSessionManager) Store() storage.Handler {
return b.store
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*DefaultSessionManager) UpdateSession
UpdateSession updates the session state in the storage engine
func (b *DefaultSessionManager) UpdateSession(keyName string, session *user.SessionState,
resetTTLTo int64, hashed bool) error {
defer b.clearCacheForKey(keyName, hashed)
v, err := json.Marshal(session)
if err != nil {
log.Error("Error marshalling session for sync update")
return err
}
// sync update
if hashed {
keyName = b.store.GetKeyPrefix() + keyName
err = b.store.SetRawKey(keyName, string(v), resetTTLTo)
} else {
err = b.store.SetKey(keyName, string(v), resetTTLTo)
}
return err
}
Cognitive complexity: 6, Cyclomatic complexity: 3
func (*DummyProxyHandler) ServeHTTP
func (d *DummyProxyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if newURL := ctxGetURLRewriteTarget(r); newURL != nil {
r.URL = newURL
ctxSetURLRewriteTarget(r, nil)
}
if newMethod := ctxGetTransformRequestMethod(r); newMethod != "" {
r.Method = newMethod
ctxSetTransformRequestMethod(r, "")
}
if found, err := isLoop(r); found {
if err != nil {
handler := ErrorHandler{d.SH.Base()}
handler.HandleError(w, r, err.Error(), http.StatusInternalServerError, true)
return
}
r.URL.Scheme = "http"
if methodOverride := r.URL.Query().Get("method"); methodOverride != "" {
r.Method = methodOverride
}
var handler http.Handler
if r.URL.Hostname() == "self" {
httpctx.SetSelfLooping(r, true)
if h, found := d.Gw.apisHandlesByID.Load(d.SH.Spec.APIID); found {
if chain, ok := h.(*ChainObject); ok {
handler = chain.ThisHandler
} else {
log.WithFields(logrus.Fields{"api_id": d.SH.Spec.APIID}).Debug("failed to cast stored api handles to *ChainObject")
}
}
} else {
ctxSetVersionInfo(r, nil)
if targetAPI := d.Gw.fuzzyFindAPI(r.URL.Hostname()); targetAPI != nil {
if h, found := d.Gw.apisHandlesByID.Load(targetAPI.APIID); found {
if chain, ok := h.(*ChainObject); ok {
handler = chain.ThisHandler
} else {
log.WithFields(logrus.Fields{"api_id": d.SH.Spec.APIID}).Debug("failed to cast stored api handles to *ChainObject")
}
}
} else {
handler := ErrorHandler{d.SH.Base()}
handler.HandleError(w, r, "Can't detect loop target", http.StatusInternalServerError, true)
return
}
}
// No need to handle errors, in all error cases limit will be set to 0
loopLevelLimit, _ := strconv.Atoi(r.URL.Query().Get("loop_limit"))
ctxSetCheckLoopLimits(r, r.URL.Query().Get("check_limits") == "true")
if origURL := ctxGetOrigRequestURL(r); origURL != nil {
r.URL.Host = origURL.Host
r.URL.RawQuery = origURL.RawQuery
ctxSetOrigRequestURL(r, nil)
}
ctxIncLoopLevel(r, loopLevelLimit)
handler.ServeHTTP(w, r)
return
}
if d.SH.Spec.target.Scheme == "tyk" {
handler, _, found := d.Gw.findInternalHttpHandlerByNameOrID(d.SH.Spec.target.Host)
if !found {
handler := ErrorHandler{d.SH.Base()}
handler.HandleError(w, r, "Couldn't detect target", http.StatusInternalServerError, true)
return
}
d.SH.Spec.SanitizeProxyPaths(r)
ctxSetVersionInfo(r, nil)
handler.ServeHTTP(w, r)
return
}
d.SH.ServeHTTP(w, r)
}
Cognitive complexity: 41, Cyclomatic complexity: 15
func (*DynamicMiddleware) Name
func (d *DynamicMiddleware) Name() string {
return "DynamicMiddleware"
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*DynamicMiddleware) ProcessRequest
ProcessRequest will run any checks on the request on the way through the system, return an error to have the chain fail
func (d *DynamicMiddleware) ProcessRequest(w http.ResponseWriter, r *http.Request, _ interface{}) (error, int) {
t1 := time.Now().UnixNano()
logger := d.Logger()
// Create the proxy object
originalBody, err := ioutil.ReadAll(r.Body)
if err != nil {
logger.WithError(err).Error("Failed to read request body")
return nil, http.StatusOK
}
defer r.Body.Close()
headers := r.Header
host := r.Host
if host == "" && r.URL != nil {
host = r.URL.Host
}
if host != "" {
headers = make(http.Header)
for k, v := range r.Header {
headers[k] = v
}
headers.Set("Host", host)
}
scheme := "http"
if r.TLS != nil {
scheme = "https"
}
requestData := MiniRequestObject{
Headers: headers,
SetHeaders: map[string]string{},
DeleteHeaders: []string{},
Body: originalBody,
URL: r.URL.String(),
Params: r.URL.Query(),
AddParams: map[string]string{},
ExtendedParams: map[string][]string{},
DeleteParams: []string{},
Method: r.Method,
RequestURI: r.RequestURI,
Scheme: scheme,
}
requestAsJson, err := json.Marshal(requestData)
if err != nil {
logger.WithError(err).Error("Failed to encode request object for dynamic middleware")
return nil, http.StatusOK
}
specAsJson := specToJson(d.Spec)
session := &user.SessionState{}
// Encode the session object (if not a pre-process)
if !d.Pre && d.UseSession {
session = ctxGetSession(r)
}
sessionAsJson, err := json.Marshal(session)
if err != nil {
logger.WithError(err).Error("Failed to encode session for VM")
return nil, http.StatusOK
}
// Run the middleware
middlewareClassname := d.MiddlewareClassName
if d.Spec.JSVM.VM == nil {
logger.WithError(err).Error("JSVM isn't enabled, check your gateway settings")
return errors.New("Middleware error"), 500
}
vm := d.Spec.JSVM.VM.Copy()
vm.Interrupt = make(chan func(), 1)
logger.Debug("Running: ", middlewareClassname)
// buffered, leaving no chance of a goroutine leak since the
// spawned goroutine will send 0 or 1 values.
ret := make(chan otto.Value, 1)
errRet := make(chan error, 1)
go func() {
defer func() {
// the VM executes the panic func that gets it
// to stop, so we must recover here to not crash
// the whole Go program.
recover()
}()
returnRaw, err := vm.Run(middlewareClassname + `.DoProcessRequest(` + string(requestAsJson) + `, ` + string(sessionAsJson) + `, ` + specAsJson + `);`)
ret <- returnRaw
errRet <- err
}()
var returnRaw otto.Value
t := time.NewTimer(d.Spec.JSVM.Timeout)
select {
case returnRaw = <-ret:
if err := <-errRet; err != nil {
logger.WithError(err).Error("Failed to run JS middleware")
return errors.New(http.StatusText(http.StatusInternalServerError)), http.StatusInternalServerError
}
t.Stop()
case <-t.C:
t.Stop()
logger.Error("JS middleware timed out after ", d.Spec.JSVM.Timeout)
vm.Interrupt <- func() {
// only way to stop the VM is to send it a func
// that panics.
panic("stop")
}
return errors.New(http.StatusText(http.StatusInternalServerError)), http.StatusInternalServerError
}
returnDataStr, _ := returnRaw.ToString()
// Decode the return object
newRequestData := VMReturnObject{}
if err := json.Unmarshal([]byte(returnDataStr), &newRequestData); err != nil {
logger.WithError(err).Error("Failed to decode middleware request data on return from VM. Returned data: ", returnDataStr)
return errors.New(http.StatusText(http.StatusInternalServerError)), http.StatusInternalServerError
}
// Reconstruct the request parts
if newRequestData.Request.IgnoreBody {
r.ContentLength = int64(len(originalBody))
r.Body = ioutil.NopCloser(bytes.NewReader(originalBody))
} else {
r.ContentLength = int64(len(newRequestData.Request.Body))
r.Body = ioutil.NopCloser(bytes.NewReader(newRequestData.Request.Body))
}
// make sure request's body can be re-read again
nopCloseRequestBody(r)
r.URL, err = url.Parse(newRequestData.Request.URL)
if err != nil {
return nil, http.StatusOK
}
ignoreCanonical := d.Gw.GetConfig().IgnoreCanonicalMIMEHeaderKey
// Delete and set headers
for _, dh := range newRequestData.Request.DeleteHeaders {
r.Header.Del(dh)
if ignoreCanonical {
// Make sure we delete the header in case the header key was not canonical.
delete(r.Header, dh)
}
}
for h, v := range newRequestData.Request.SetHeaders {
setCustomHeader(r.Header, h, v, ignoreCanonical)
}
// Delete and set request parameters
newRequestData.Request.ReconstructParams(r)
// Save the session data (if modified)
if !d.Pre && d.UseSession {
newMeta := mapStrsToIfaces(newRequestData.SessionMeta)
if session != nil && !reflect.DeepEqual(session.MetaData, newMeta) {
session.MetaData = newMeta
session.Touch()
}
}
logger.Debug("JSVM middleware execution took: (ns) ", time.Now().UnixNano()-t1)
if newRequestData.Request.ReturnOverrides.ResponseError != "" {
newRequestData.Request.ReturnOverrides.ResponseBody = newRequestData.Request.ReturnOverrides.ResponseError
}
if newRequestData.Request.ReturnOverrides.ResponseCode >= http.StatusBadRequest && !newRequestData.Request.ReturnOverrides.OverrideError {
for header, value := range newRequestData.Request.ReturnOverrides.ResponseHeaders {
w.Header().Set(header, value)
}
return errors.New(newRequestData.Request.ReturnOverrides.ResponseBody), newRequestData.Request.ReturnOverrides.ResponseCode
}
if newRequestData.Request.ReturnOverrides.ResponseCode != 0 {
responseObject := VMResponseObject{
Response: ResponseObject{
Body: newRequestData.Request.ReturnOverrides.ResponseBody,
Code: newRequestData.Request.ReturnOverrides.ResponseCode,
Headers: newRequestData.Request.ReturnOverrides.ResponseHeaders,
},
}
d.Gw.forceResponse(w, r, &responseObject, d.Spec, session, d.Pre, logger)
return nil, mwStatusRespond
}
if d.Auth {
newRequestData.Session.KeyID = newRequestData.AuthValue
switch d.Spec.BaseIdentityProvidedBy {
case apidef.CustomAuth, apidef.UnsetAuth:
ctxSetSession(r, &newRequestData.Session, true, d.Gw.GetConfig().HashKeys)
}
}
return nil, http.StatusOK
}
Cognitive complexity: 72, Cyclomatic complexity: 33
func (*ErrorHandler) HandleError
HandleError is the actual error handler and will store the error details in analytics if analytics processing is enabled.
func (e *ErrorHandler) HandleError(w http.ResponseWriter, r *http.Request, errMsg string, errCode int, writeResponse bool) {
defer e.Base().UpdateRequestSession(r)
response := &http.Response{}
if writeResponse {
var templateExtension string
contentType := r.Header.Get(header.ContentType)
contentType = strings.Split(contentType, ";")[0]
switch contentType {
case header.ApplicationXML:
templateExtension = "xml"
contentType = header.ApplicationXML
case header.TextXML:
templateExtension = "xml"
contentType = header.TextXML
default:
templateExtension = "json"
contentType = header.ApplicationJSON
}
w.Header().Set(header.ContentType, contentType)
response.Header = http.Header{}
response.Header.Set(header.ContentType, contentType)
templateName := "error_" + strconv.Itoa(errCode) + "." + templateExtension
// Try to use an error template that matches the HTTP error code and the content type: 500.json, 400.xml, etc.
tmpl := e.Gw.templates.Lookup(templateName)
// Fallback to a generic error template, but match the content type: error.json, error.xml, etc.
if tmpl == nil {
templateName = defaultTemplateName + "." + templateExtension
tmpl = e.Gw.templates.Lookup(templateName)
}
// If no template is available for this content type, fallback to "error.json".
if tmpl == nil {
templateName = defaultTemplateName + "." + defaultTemplateFormat
tmpl = e.Gw.templates.Lookup(templateName)
w.Header().Set(header.ContentType, defaultContentType)
response.Header.Set(header.ContentType, defaultContentType)
}
//If the config option is not set or is false, add the header
if !e.Spec.GlobalConfig.HideGeneratorHeader {
w.Header().Add(header.XGenerator, "tyk.io")
response.Header.Add(header.XGenerator, "tyk.io")
}
// Close connections
if e.Spec.GlobalConfig.CloseConnections {
w.Header().Add(header.Connection, "close")
response.Header.Add(header.Connection, "close")
}
// If error is not customized write error in default way
if errMsg != errCustomBodyResponse.Error() {
w.WriteHeader(errCode)
response.StatusCode = errCode
var tmplExecutor TemplateExecutor
tmplExecutor = tmpl
apiError := APIError{htmltemplate.HTML(htmltemplate.JSEscapeString(errMsg))}
if contentType == header.ApplicationXML || contentType == header.TextXML {
apiError.Message = htmltemplate.HTML(errMsg)
//we look up in the last defined templateName to obtain the template.
rawTmpl := e.Gw.templatesRaw.Lookup(templateName)
tmplExecutor = rawTmpl
}
var log bytes.Buffer
rsp := io.MultiWriter(w, &log)
tmplExecutor.Execute(rsp, &apiError)
response.Body = ioutil.NopCloser(&log)
}
}
if e.Spec.DoNotTrack || ctxGetDoNotTrack(r) {
return
}
// Track the key ID if it exists
token := ctxGetAuthToken(r)
var alias string
ip := request.RealIP(r)
if e.Spec.GlobalConfig.StoreAnalytics(ip) {
t := time.Now()
addVersionHeader(w, r, e.Spec.GlobalConfig)
version := e.Spec.getVersionFromRequest(r)
if version == "" {
version = "Non Versioned"
}
if e.Spec.Proxy.StripListenPath {
r.URL.Path = e.Spec.StripListenPath(r.URL.Path)
}
oauthClientID := ""
session := ctxGetSession(r)
tags := make([]string, 0, estimateTagsCapacity(session, e.Spec))
if session != nil {
oauthClientID = session.OauthClientID
alias = session.Alias
tags = append(tags, getSessionTags(session)...)
}
if len(e.Spec.TagHeaders) > 0 {
tags = tagHeaders(r, e.Spec.TagHeaders, tags)
}
if len(e.Spec.Tags) > 0 {
tags = append(tags, e.Spec.Tags...)
}
trackEP := false
trackedPath := r.URL.Path
if p := ctxGetTrackedPath(r); p != "" {
trackEP = true
trackedPath = p
}
host := r.URL.Host
if host == "" && e.Spec.target != nil {
host = e.Spec.target.Host
}
record := analytics.AnalyticsRecord{
Method: r.Method,
Host: host,
Path: trackedPath,
RawPath: r.URL.Path,
ContentLength: r.ContentLength,
UserAgent: r.Header.Get(header.UserAgent),
Day: t.Day(),
Month: t.Month(),
Year: t.Year(),
Hour: t.Hour(),
ResponseCode: errCode,
APIKey: token,
TimeStamp: t,
APIVersion: version,
APIName: e.Spec.Name,
APIID: e.Spec.APIID,
OrgID: e.Spec.OrgID,
OauthID: oauthClientID,
RequestTime: 0,
Latency: analytics.Latency{},
IPAddress: ip,
Geo: analytics.GeoData{},
Network: analytics.NetworkStats{},
Tags: tags,
Alias: alias,
TrackPath: trackEP,
ExpireAt: t,
}
recordGraphDetails(&record, r, response, e.Spec)
rawRequest := ""
rawResponse := ""
if recordDetail(r, e.Spec) {
// Get the wire format representation
var wireFormatReq bytes.Buffer
r.Write(&wireFormatReq)
rawRequest = base64.StdEncoding.EncodeToString(wireFormatReq.Bytes())
var wireFormatRes bytes.Buffer
response.Write(&wireFormatRes)
rawResponse = base64.StdEncoding.EncodeToString(wireFormatRes.Bytes())
}
record.RawRequest = rawRequest
record.RawResponse = rawResponse
if e.Spec.GlobalConfig.AnalyticsConfig.EnableGeoIP {
record.GetGeo(ip, e.Gw.Analytics.GeoIPDB)
}
if e.Spec.GraphQL.Enabled && e.Spec.GraphQL.ExecutionMode != apidef.GraphQLExecutionModeSubgraph {
record.Tags = append(record.Tags, "tyk-graph-analytics")
record.ApiSchema = base64.StdEncoding.EncodeToString([]byte(e.Spec.GraphQL.Schema))
}
expiresAfter := e.Spec.ExpireAnalyticsAfter
if e.Spec.GlobalConfig.EnforceOrgDataAge {
orgExpireDataTime := e.OrgSessionExpiry(e.Spec.OrgID)
if orgExpireDataTime > 0 {
expiresAfter = orgExpireDataTime
}
}
record.SetExpiry(expiresAfter)
if e.Spec.GlobalConfig.AnalyticsConfig.NormaliseUrls.Enabled {
NormalisePath(&record, &e.Spec.GlobalConfig)
}
if e.Spec.AnalyticsPlugin.Enabled {
_ = e.Spec.AnalyticsPluginConfig.processRecord(&record)
}
err := e.Gw.Analytics.RecordHit(&record)
if err != nil {
log.WithError(err).Error("could not store analytic record")
}
}
e.RecordAccessLog(r, response, analytics.Latency{})
// Report in health check
reportHealthValue(e.Spec, BlockedRequestLog, "-1")
}
Cognitive complexity: 60, Cyclomatic complexity: 32
func (*EventCurcuitBreakerMeta) LogMessage
func (e *EventCurcuitBreakerMeta) LogMessage(prefix string) string {
return fmt.Sprintf("%s:%s:%s: [STATUS] %s", prefix, e.APIID, e.Path, fmt.Sprint(e.CircuitEvent))
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*EventKeyFailureMeta) LogMessage
func (e *EventKeyFailureMeta) LogMessage(prefix string) string {
return fmt.Sprintf("%s:%s:%s:%s", prefix, e.Key, e.Origin, e.Path)
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*ExternalOAuthMiddleware) EnabledForSpec
func (k *ExternalOAuthMiddleware) EnabledForSpec() bool {
if k.Spec.ExternalOAuth.Enabled {
log.Warn("Support for external OAuth Middleware will be deprecated starting from 5.7.0. To avoid any disruptions, we recommend that you use JSON Web Token (JWT) instead, as explained in https://tyk.io/docs/basic-config-and-security/security/authentication-authorization/ext-oauth-middleware/")
}
return k.Spec.ExternalOAuth.Enabled
}
Cognitive complexity: 3, Cyclomatic complexity: 3
func (*ExternalOAuthMiddleware) Name
func (k *ExternalOAuthMiddleware) Name() string {
return "ExternalOAuth"
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*ExternalOAuthMiddleware) ProcessRequest
func (k *ExternalOAuthMiddleware) ProcessRequest(w http.ResponseWriter, r *http.Request, _ interface{}) (error, int) {
if ctxGetRequestStatus(r) == StatusOkAndIgnore {
return nil, http.StatusOK
}
token, _ := k.getAuthToken(k.getAuthType(), r)
if token == "" {
return errors.New("authorization field missing"), http.StatusBadRequest
}
token = stripBearer(token)
var (
valid bool
err error
identifier string
)
if len(k.Spec.ExternalOAuth.Providers) == 0 {
return errors.New("there should be at least one provider configured"), http.StatusNotFound
}
// Just the first one will be used, later there can be multiple providers supported
provider := k.Spec.ExternalOAuth.Providers[0]
if provider.JWT.Enabled {
valid, identifier, err = k.jwt(token)
} else if provider.Introspection.Enabled {
valid, identifier, err = k.introspection(token)
} else {
return errors.New("access token validation method is not specified"), http.StatusInternalServerError
}
if err != nil {
switch {
case errors.Is(err, jwt.ErrSignatureInvalid), errors.Is(err, jwt.ErrTokenMalformed), errors.Is(err, jwt.ErrTokenNotValidYet),
errors.Is(err, jwt.ErrTokenUsedBeforeIssued), errors.Is(err, jwt.ErrTokenExpired):
return err, http.StatusUnauthorized
}
return ErrTokenValidationFailed, http.StatusInternalServerError
}
if !valid {
return errors.New("access token is not valid"), http.StatusUnauthorized
}
sessionID := k.generateSessionID(identifier)
k.Logger().Debug("External OAuth Temporary session ID is: ", sessionID)
// CheckSessionAndIdentityForValidKey returns a session with keyID populated
var virtualSession user.SessionState
virtualSession, exists := k.CheckSessionAndIdentityForValidKey(sessionID, r)
if !exists {
virtualSession = k.generateVirtualSessionFor(r, sessionID)
}
ctxSetSession(r, &virtualSession, false, k.Gw.GetConfig().HashKeys)
// Request is valid, carry on
return nil, http.StatusOK
}
Cognitive complexity: 22, Cyclomatic complexity: 11
func (*FileBundleGetter) Get
Get mocks an HTTP(S) GET request.
func (g *FileBundleGetter) Get() ([]byte, error) {
return os.ReadFile(strings.TrimPrefix(g.URL, "file://"))
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*GRPCDispatcher) Dispatch
Dispatch takes a CoProcessMessage and sends it to the CP.
func (d *GRPCDispatcher) Dispatch(object *coprocess.Object) (*coprocess.Object, error) {
return grpcClient.Dispatch(context.Background(), object)
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*GRPCDispatcher) DispatchEvent
DispatchEvent dispatches a Tyk event.
func (d *GRPCDispatcher) DispatchEvent(eventJSON []byte) {
eventObject := &coprocess.Event{
Payload: string(eventJSON),
}
_, err := grpcClient.DispatchEvent(context.Background(), eventObject)
if err != nil {
log.WithFields(logrus.Fields{
"prefix": "coprocess",
}).Error(err)
}
}
Cognitive complexity: 4, Cyclomatic complexity: 2
func (*GRPCDispatcher) HandleMiddlewareCache
HandleMiddlewareCache isn't used by gRPC.
func (d *GRPCDispatcher) HandleMiddlewareCache(b *apidef.BundleManifest, basePath string) {}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*GRPCDispatcher) Reload
Reload triggers a reload affecting CP middlewares and event handlers.
func (d *GRPCDispatcher) Reload() {}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*Gateway) ApplyLifetime
ApplyLifetime calculates the lifetime for the key. It considers the access rights and the bigger lifetime will be used
func (gw *Gateway) ApplyLifetime(sess *user.SessionState, specs ...*APISpec) int64 {
var lifetime int64
if len(sess.AccessRights) > 0 {
specs = gw.GetApiSpecsFromAccessRights(sess)
}
for _, spec := range specs {
if spec != nil {
sessionLifeTime := sess.Lifetime(spec.GetSessionLifetimeRespectsKeyExpiration(), spec.SessionLifetime, gw.GetConfig().ForceGlobalSessionLifetime, gw.GetConfig().GlobalSessionLifetime)
// uses the greater lifetime
if sessionLifeTime > lifetime {
lifetime = sessionLifeTime
}
}
}
return lifetime
}
Cognitive complexity: 9, Cyclomatic complexity: 5
func (*Gateway) BuildAndLoadAPI
func (gw *Gateway) BuildAndLoadAPI(apiGens ...func(spec *APISpec)) (specs []*APISpec) {
return gw.LoadAPI(BuildAPI(apiGens...)...)
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*Gateway) CoProcessInit
CoProcessInit creates a new CoProcessDispatcher, it will be called when Tyk starts.
func (gw *Gateway) CoProcessInit() {
if !gw.GetConfig().CoProcessOptions.EnableCoProcess {
log.WithFields(logrus.Fields{
"prefix": "coprocess",
}).Info("Rich plugins are disabled")
return
}
// Load gRPC dispatcher:
if gw.GetConfig().CoProcessOptions.CoProcessGRPCServer != "" {
var err error
loadedDrivers[apidef.GrpcDriver], err = gw.NewGRPCDispatcher()
if err == nil {
log.WithFields(logrus.Fields{
"prefix": "coprocess",
}).Info("gRPC dispatcher was initialized")
} else {
log.WithFields(logrus.Fields{
"prefix": "coprocess",
}).WithError(err).Error("Couldn't load gRPC dispatcher")
}
}
}
Cognitive complexity: 11, Cyclomatic complexity: 4
func (*Gateway) CreateDefinitionFromString
func (gw *Gateway) CreateDefinitionFromString(defStr string) *APISpec {
loader := APIDefinitionLoader{Gw: gw}
def := loader.ParseDefinition(strings.NewReader(defStr))
spec, _ := loader.MakeSpec(&model.MergedAPI{APIDefinition: &def}, nil)
return spec
}
Cognitive complexity: 2, Cyclomatic complexity: 1
func (*Gateway) DoReload
func (gw *Gateway) DoReload() {
gw.reloadMu.Lock()
defer gw.reloadMu.Unlock()
// Initialize/reset the JSVM
if gw.GetConfig().EnableJSVM {
gw.GlobalEventsJSVM.DeInit()
gw.GlobalEventsJSVM.Init(nil, logrus.NewEntry(log), gw)
}
// Load the API Policies
if _, err := syncResourcesWithReload("policies", gw.GetConfig(), gw.syncPolicies); err != nil {
mainLog.Error("Error during syncing policies")
return
}
// load the specs
if count, err := syncResourcesWithReload("apis", gw.GetConfig(), gw.syncAPISpecs); err != nil {
mainLog.Error("Error during syncing apis")
return
} else {
// skip re-loading only if dashboard service reported 0 APIs
// and current registry had 0 APIs
if count == 0 && gw.apisByIDLen() == 0 {
mainLog.Warning("No API Definitions found, not reloading")
return
}
}
gw.loadGlobalApps()
mainLog.Info("API reload complete")
}
Cognitive complexity: 10, Cyclomatic complexity: 6
func (*Gateway) EventHandlerByName
EventHandlerByName is a convenience function to get event handler instances from an API Definition
func (gw *Gateway) EventHandlerByName(handlerConf apidef.EventHandlerTriggerConfig, spec *APISpec) (config.TykEventHandler, error) {
conf := handlerConf.HandlerMeta
switch handlerConf.Handler {
case EH_LogHandler:
h := &LogMessageEventHandler{Gw: gw}
err := h.Init(conf)
return h, err
case EH_WebHook:
h := &WebHookHandler{Gw: gw}
err := h.Init(conf)
return h, err
case EH_JSVMHandler:
// Load the globals and file here
if spec != nil {
h := &JSVMEventHandler{Spec: spec, Gw: gw}
err := h.Init(conf)
if err == nil {
gw.GlobalEventsJSVM.LoadJSPaths([]string{conf["path"].(string)}, "")
}
return h, err
}
case EH_CoProcessHandler:
if spec != nil {
dispatcher := loadedDrivers[spec.CustomMiddleware.Driver]
if dispatcher == nil {
return nil, errors.New("no plugin driver is available")
}
h := &CoProcessEventHandler{}
h.Spec = spec
err := h.Init(conf)
return h, err
}
}
return nil, errors.New("Handler not found")
}
Cognitive complexity: 19, Cyclomatic complexity: 10
func (*Gateway) FetchBundle
FetchBundle will fetch a given bundle, using the right BundleGetter. The first argument is the bundle name, the base bundle URL will be used as prefix.
func (gw *Gateway) FetchBundle(spec *APISpec) (Bundle, error) {
bundle := Bundle{Gw: gw}
var err error
if !gw.GetConfig().EnableBundleDownloader {
log.WithFields(logrus.Fields{
"prefix": "main",
}).Warning("Bundle downloader is disabled.")
err = errors.New("Bundle downloader is disabled")
return bundle, err
}
u, err := url.Parse(gw.GetConfig().BundleBaseURL)
if err != nil {
return bundle, err
}
u.Path = path.Join(u.Path, spec.CustomMiddlewareBundle)
bundleURL := u.String()
var getter BundleGetter
switch u.Scheme {
case "http":
getter = &HTTPBundleGetter{
URL: bundleURL,
InsecureSkipVerify: false,
}
case "https":
getter = &HTTPBundleGetter{
URL: bundleURL,
InsecureSkipVerify: gw.GetConfig().BundleInsecureSkipVerify,
}
case "file":
getter = &FileBundleGetter{
URL: bundleURL,
InsecureSkipVerify: gw.GetConfig().BundleInsecureSkipVerify,
}
default:
err = errors.New("Unknown URL scheme")
}
if err != nil {
return bundle, err
}
bundleData, err := pullBundle(getter, bundleBackoffMultiplier)
bundle.Name = spec.CustomMiddlewareBundle
bundle.Data = bundleData
bundle.Spec = spec
return bundle, err
}
Cognitive complexity: 16, Cyclomatic complexity: 8
func (*Gateway) FireSystemEvent
func (gw *Gateway) FireSystemEvent(name apidef.TykEvent, meta interface{}) {
fireEvent(name, meta, gw.GetConfig().GetEventTriggers())
}
Cognitive complexity: 1, Cyclomatic complexity: 1
func (*Gateway) GetApiSpecsFromAccessRights
GetApiSpecsFromAccessRights from the session.AccessRights returns the collection of api specs
func (gw *Gateway) GetApiSpecsFromAccessRights(sess *user.SessionState) []*APISpec {
var apis []*APISpec
if sess != nil && len(sess.AccessRights) > 0 {
for apiID := range sess.AccessRights {
spec := gw.getApiSpec(apiID)
if spec != nil {
apis = append(apis, spec)
}
}
}
return apis
}
Cognitive complexity: 7, Cyclomatic complexity: 5
func (*Gateway) GetCoProcessGrpcServerTargetURL
func (gw *Gateway) GetCoProcessGrpcServerTargetURL() (*url.URL, error) {
grpcURL, err := url.Parse(gw.GetConfig().CoProcessOptions.CoProcessGRPCServer)
if err != nil {
log.WithFields(logrus.Fields{
"prefix": "coprocess",
}).Error(err)
return nil, err
}
if grpcURL == nil || gw.GetConfig().CoProcessOptions.CoProcessGRPCServer == "" {
errString := "No gRPC URL is set!"
log.WithFields(logrus.Fields{
"prefix": "coprocess",
}).Error(errString)
return nil, err
}
return grpcURL, nil
}
Cognitive complexity: 6, Cyclomatic complexity: 4
func (*Gateway) GetConfig
func (gw *Gateway) GetConfig() config.Config {
return gw.config.Load().(config.Config)
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*Gateway) GetNodeID
GetNodeID reads NodeID safely.
func (gw *Gateway) GetNodeID() string {
gw.nodeIDMu.Lock()
defer gw.nodeIDMu.Unlock()
return gw.nodeID
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*Gateway) GetStorageForApi
func (gw *Gateway) GetStorageForApi(apiID string) (ExtendedOsinStorageInterface, int, error) {
apiSpec := gw.getApiSpec(apiID)
if apiSpec == nil {
log.WithFields(logrus.Fields{
"prefix": "api",
"apiID": apiID,
"status": "fail",
"err": "API not found",
}).Error("Failed to retrieve OAuth client list.")
return nil, http.StatusNotFound, errors.New(oAuthClientNotFound)
}
if apiSpec.OAuthManager == nil {
log.WithFields(logrus.Fields{
"prefix": "api",
"apiID": apiID,
"status": "fail",
"err": "API not found",
}).Error("Failed to revoke client tokens.")
return nil, http.StatusNotFound, errors.New(oAuthNotPropagatedErr)
}
return apiSpec.OAuthManager.Storage(), http.StatusOK, nil
}
Cognitive complexity: 6, Cyclomatic complexity: 3
func (*Gateway) InitHostCheckManager
func (gw *Gateway) InitHostCheckManager(ctx context.Context, store storage.Handler) {
// Already initialized
if gw.GlobalHostChecker != nil {
return
}
gw.GlobalHostChecker = &HostCheckerManager{Gw: gw}
gw.GlobalHostChecker.Init(store)
gw.GlobalHostChecker.Start(ctx)
}
Cognitive complexity: 3, Cyclomatic complexity: 2
func (*Gateway) LoadAPI
func (gw *Gateway) LoadAPI(specs ...*APISpec) (out []*APISpec) {
var err error
gwConf := gw.GetConfig()
oldPath := gwConf.AppPath
gwConf.AppPath, err = ioutil.TempDir("", "apps")
if err != nil {
log.WithError(err).Errorf("loadapi: failed to create temp dir")
}
gw.SetConfig(gwConf, true)
defer func() {
globalConf := gw.GetConfig()
os.RemoveAll(globalConf.AppPath)
globalConf.AppPath = oldPath
gw.SetConfig(globalConf, true)
}()
for i, spec := range specs {
if spec.Name == "" {
spec.Name = randStringBytes(15)
}
specBytes, err := json.Marshal(spec.APIDefinition)
if err != nil {
log.WithError(err).Errorf("API Spec Marshal failed: %+v", spec)
panic(err)
}
specFilePath := filepath.Join(gwConf.AppPath, spec.APIID+strconv.Itoa(i)+".json")
if err := ioutil.WriteFile(specFilePath, specBytes, 0644); err != nil {
panic(err)
}
oasSpecBytes, err := json.Marshal(&spec.OAS)
if err != nil {
log.WithError(err).Errorf("OAS Marshal failed: %+v", spec)
panic(err)
}
oasSpecFilePath := filepath.Join(gwConf.AppPath, spec.APIID+strconv.Itoa(i)+"-oas.json")
if err := ioutil.WriteFile(oasSpecFilePath, oasSpecBytes, 0644); err != nil {
panic(err)
}
}
gw.DoReload()
for _, spec := range specs {
out = append(out, gw.getApiSpec(spec.APIID))
}
return out
}
Cognitive complexity: 19, Cyclomatic complexity: 9
func (*Gateway) LoadDefinitionsFromRPCBackup
func (gw *Gateway) LoadDefinitionsFromRPCBackup() ([]*APISpec, error) {
tagList := getTagListAsString(gw.GetConfig().DBAppConfOptions.Tags)
checkKey := BackupApiKeyBase + tagList
store := &storage.RedisCluster{KeyPrefix: RPCKeyPrefix, ConnectionHandler: gw.StorageConnectionHandler}
connected := store.Connect()
log.Info("[RPC] --> Loading API definitions from backup")
if !connected {
return nil, errors.New("[RPC] --> RPC Backup recovery failed: redis connection failed")
}
secret := crypto.GetPaddedString(gw.GetConfig().Secret)
cryptoText, err := store.GetKey(checkKey)
if err != nil {
return nil, errors.New("[RPC] --> Failed to get node backup (" + checkKey + "): " + err.Error())
}
apiListAsString := crypto.Decrypt([]byte(secret), cryptoText)
a := APIDefinitionLoader{Gw: gw}
return a.processRPCDefinitions(apiListAsString, gw)
}
Cognitive complexity: 6, Cyclomatic complexity: 3
func (*Gateway) LoadPoliciesFromDashboard
LoadPoliciesFromDashboard will connect and download Policies from a Tyk Dashboard instance.
func (gw *Gateway) LoadPoliciesFromDashboard(endpoint, secret string, allowExplicit bool) (map[string]user.Policy, error) {
// Get the definitions
newRequest, err := http.NewRequest("GET", endpoint, nil)
if err != nil {
log.Error("Failed to create request: ", err)
return nil, err
}
newRequest.Header.Set("authorization", secret)
newRequest.Header.Set(header.XTykNodeID, gw.GetNodeID())
newRequest.Header.Set(header.XTykSessionID, gw.SessionID)
gw.ServiceNonceMutex.RLock()
newRequest.Header.Set("x-tyk-nonce", gw.ServiceNonce)
gw.ServiceNonceMutex.RUnlock()
log.WithFields(logrus.Fields{
"prefix": "policy",
}).Info("Mutex lock acquired... calling")
c := gw.initialiseClient()
log.WithFields(logrus.Fields{
"prefix": "policy",
}).Info("Calling dashboard service for policy list")
resp, err := c.Do(newRequest)
if err != nil {
log.Error("Policy request failed: ", err)
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, _ := ioutil.ReadAll(resp.Body)
log.Error("Policy request login failure, Response was: ", string(body))
return nil, ErrPoliciesFetchFailed
}
// Extract Policies
var list struct {
Message []DBPolicy
Nonce string
}
if err := json.NewDecoder(resp.Body).Decode(&list); err != nil {
log.Error("Failed to decode policy body: ", err)
return nil, err
}
gw.ServiceNonceMutex.Lock()
gw.ServiceNonce = list.Nonce
gw.ServiceNonceMutex.Unlock()
log.Debug("Loading Policies Finished: Nonce Set: ", list.Nonce)
policies := make(map[string]user.Policy, len(list.Message))
log.WithFields(logrus.Fields{
"prefix": "policy",
}).Info("Processing policy list")
for _, p := range list.Message {
id := p.MID.Hex()
if allowExplicit && p.ID != "" {
id = p.ID
}
p.ID = id
if _, ok := policies[id]; ok {
log.WithFields(logrus.Fields{
"prefix": "policy",
"policyID": p.ID,
"OrgID": p.OrgID,
}).Warning("--> Skipping policy, new item has a duplicate ID!")
continue
}
policies[id] = p.ToRegularPolicy()
}
return policies, err
}
Cognitive complexity: 20, Cyclomatic complexity: 9
func (*Gateway) LoadPoliciesFromRPC
func (gw *Gateway) LoadPoliciesFromRPC(store RPCDataLoader, orgId string, allowExplicit bool) (map[string]user.Policy, error) {
if rpc.IsEmergencyMode() {
return gw.LoadPoliciesFromRPCBackup()
}
if !store.Connect() {
return nil, errors.New("Policies backup: Failed connecting to database")
}
rpcPolicies := store.GetPolicies(orgId)
policies, err := parsePoliciesFromRPC(rpcPolicies, allowExplicit)
if err != nil {
log.WithFields(logrus.Fields{
"prefix": "policy",
}).Error("Failed decode: ", err, rpcPolicies)
return nil, err
}
if err := gw.saveRPCPoliciesBackup(rpcPolicies); err != nil {
log.Error(err)
}
return policies, nil
}
Cognitive complexity: 9, Cyclomatic complexity: 5
func (*Gateway) LoadPoliciesFromRPCBackup
func (gw *Gateway) LoadPoliciesFromRPCBackup() (map[string]user.Policy, error) {
tagList := getTagListAsString(gw.GetConfig().DBAppConfOptions.Tags)
checkKey := BackupPolicyKeyBase + tagList
store := &storage.RedisCluster{KeyPrefix: RPCKeyPrefix, ConnectionHandler: gw.StorageConnectionHandler}
connected := store.Connect()
log.Info("[RPC] Loading Policies from backup")
if !connected {
return nil, errors.New("[RPC] --> RPC Policy Backup recovery failed: redis connection failed")
}
secret := crypto.GetPaddedString(gw.GetConfig().Secret)
cryptoText, err := store.GetKey(checkKey)
listAsString := crypto.Decrypt([]byte(secret), cryptoText)
if err != nil {
return nil, errors.New("[RPC] --> Failed to get node policy backup (" + checkKey + "): " + err.Error())
}
if policies, err := parsePoliciesFromRPC(listAsString, gw.GetConfig().Policies.AllowExplicitPolicyID); err != nil {
log.WithFields(logrus.Fields{
"prefix": "policy",
}).Error("Failed decode: ", err)
return nil, err
} else {
return policies, nil
}
}
Cognitive complexity: 10, Cyclomatic complexity: 4
func (*Gateway) LoadSampleAPI
func (gw *Gateway) LoadSampleAPI(def string) (spec *APISpec) {
spec = gw.CreateDefinitionFromString(def)
gw.loadApps([]*APISpec{spec})
return
}
Cognitive complexity: 1, Cyclomatic complexity: 1
func (*Gateway) MarshalJSON
func (gw *Gateway) MarshalJSON() ([]byte, error) {
return json.Marshal(struct{}{})
}
Cognitive complexity: 2, Cyclomatic complexity: 1
func (*Gateway) MonitorApplicationInstrumentation
func (gw *Gateway) MonitorApplicationInstrumentation() {
log.Info("Starting application monitoring...")
go func() {
job := instrument.NewJob("GCActivity")
job_rl := instrument.NewJob("Load")
metadata := health.Kvs{"host": gw.hostDetails.Hostname}
applicationGCStats.PauseQuantiles = make([]time.Duration, 5)
for {
debug.ReadGCStats(&applicationGCStats)
job.GaugeKv("pauses_quantile_min", float64(applicationGCStats.PauseQuantiles[0].Nanoseconds()), metadata)
job.GaugeKv("pauses_quantile_25", float64(applicationGCStats.PauseQuantiles[1].Nanoseconds()), metadata)
job.GaugeKv("pauses_quantile_50", float64(applicationGCStats.PauseQuantiles[2].Nanoseconds()), metadata)
job.GaugeKv("pauses_quantile_75", float64(applicationGCStats.PauseQuantiles[3].Nanoseconds()), metadata)
job.GaugeKv("pauses_quantile_max", float64(applicationGCStats.PauseQuantiles[4].Nanoseconds()), metadata)
job_rl.GaugeKv("rps", float64(GlobalRate.Rate()), metadata)
time.Sleep(5 * time.Second)
}
}()
}
Cognitive complexity: 4, Cyclomatic complexity: 2
func (*Gateway) NewGRPCDispatcher
NewGRPCDispatcher wraps all the actions needed for this CP.
func (gw *Gateway) NewGRPCDispatcher() (coprocess.Dispatcher, error) {
if gw.GetConfig().CoProcessOptions.CoProcessGRPCServer == "" {
return nil, errors.New("No gRPC URL is set")
}
var err error
grpcUrl, err := gw.GetCoProcessGrpcServerTargetURL()
if err != nil {
return nil, err
}
dialOptions := []grpc.DialOption{gw.grpcCallOpts(), grpc.WithTransportCredentials(insecure.NewCredentials())}
authority := gw.GetConfig().CoProcessOptions.GRPCAuthority
if authority != "" {
dialOptions = append(dialOptions, grpc.WithAuthority(authority))
}
grpcConnection, err = grpc.NewClient(
GetCoProcessGrpcServerTargetUrlAsString(grpcUrl),
dialOptions...,
)
if err != nil {
log.WithFields(logrus.Fields{
"prefix": "coprocess",
}).Error(err)
return nil, err
}
grpcClient = coprocess.NewDispatcherClient(grpcConnection)
return &GRPCDispatcher{}, nil
}
Cognitive complexity: 11, Cyclomatic complexity: 5
func (*Gateway) NewMultiTargetProxy
func (gw *Gateway) NewMultiTargetProxy(spec *APISpec, logger *logrus.Entry) *MultiTargetProxy {
m := &MultiTargetProxy{}
m.versionProxies = make(map[string]*ReverseProxy)
m.specReference = spec
m.defaultProxy = gw.TykNewSingleHostReverseProxy(spec.target, spec, logger)
for vname, vdata := range spec.VersionData.Versions {
if vdata.OverrideTarget == "" {
log.WithFields(logrus.Fields{
"prefix": "multi-target",
}).Info("----> Version ", vname, " has no override target")
m.versionProxies[vname] = m.defaultProxy
continue
}
remote, err := url.Parse(vdata.OverrideTarget)
log.WithFields(logrus.Fields{
"prefix": "multi-target",
}).Info("----> Version ", vname, " has '", vdata.OverrideTarget, "' for override target")
log.WithFields(logrus.Fields{
"prefix": "multi-target",
}).Debug("Multi-target URL: ", vdata.OverrideTarget)
log.WithFields(logrus.Fields{
"prefix": "multi-target",
}).Debug("Multi-target URL (obj): ", remote)
if err != nil {
log.WithFields(logrus.Fields{
"prefix": "multi-target",
}).Error("Couldn't parse version target URL in MultiTarget: ", err)
}
m.versionProxies[vname] = gw.TykNewSingleHostReverseProxy(remote, spec, logger)
}
return m
}
Cognitive complexity: 13, Cyclomatic complexity: 4
func (*Gateway) NotifyCurrentServerStatus
func (gw *Gateway) NotifyCurrentServerStatus() {
if !gw.DRLManager.Ready() {
return
}
rate := GlobalRate.Rate()
if rate == 0 {
rate = 1
}
server := drl.Server{
HostName: gw.hostDetails.Hostname,
ID: gw.GetNodeID(),
LoadPerSec: rate,
TagHash: gw.getTagHash(),
}
asJson, err := json.Marshal(server)
if err != nil {
log.Error("Failed to encode payload: ", err)
return
}
n := Notification{
Command: NoticeGatewayDRLNotification,
Payload: string(asJson),
Gw: gw,
}
gw.MainNotifier.Notify(n)
}
Cognitive complexity: 8, Cyclomatic complexity: 4
func (*Gateway) PolicyByID
PolicyByID will return a Policy matching the passed Policy ID.
func (gw *Gateway) PolicyByID(id string) (user.Policy, bool) {
gw.policiesMu.RLock()
defer gw.policiesMu.RUnlock()
pol, ok := gw.policiesByID[id]
return pol, ok
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*Gateway) PolicyCount
PolicyCount will return the number of policies loaded in the gateway.
func (gw *Gateway) PolicyCount() int {
gw.policiesMu.RLock()
defer gw.policiesMu.RUnlock()
return len(gw.policiesByID)
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*Gateway) PolicyIDs
PolicyIDs returns a list of IDs for each policy loaded in the gateway.
func (gw *Gateway) PolicyIDs() []string {
gw.policiesMu.RLock()
defer gw.policiesMu.RUnlock()
result := make([]string, 0, len(gw.policiesByID))
for id := range gw.policiesByID {
result = append(result, id)
}
return result
}
Cognitive complexity: 3, Cyclomatic complexity: 2
func (*Gateway) ProcessOauthClientsOps
ProcessOauthClientsOps performs the appropriate action for the received clients it can be any of the Create,Update and Delete operations
func (gw *Gateway) ProcessOauthClientsOps(clients map[string]string) {
for clientInfo, action := range clients {
// clientInfo is: APIID.ClientID.OrgID
eventValues := strings.Split(clientInfo, ".")
apiId := eventValues[0]
oauthClientId := eventValues[1]
orgID := eventValues[2]
gw.ProcessSingleOauthClientEvent(apiId, oauthClientId, orgID, action)
}
}
Cognitive complexity: 3, Cyclomatic complexity: 2
func (*Gateway) ProcessSingleOauthClientEvent
func (gw *Gateway) ProcessSingleOauthClientEvent(apiId, oauthClientId, orgID, event string) {
store, _, err := gw.GetStorageForApi(apiId)
if err != nil {
log.Error("Could not get oauth storage for api")
return
}
switch event {
case OauthClientAdded:
// on add: pull from rpc and save it in local redis
client, err := store.GetClient(oauthClientId)
if err != nil {
log.WithError(err).Error("Could not retrieve new oauth client information")
return
}
err = store.SetClient(oauthClientId, orgID, client, false)
if err != nil {
log.WithError(err).Error("Could not save oauth client.")
return
}
log.Info("oauth client created successfully")
case OauthClientRemoved:
// on remove: remove from local redis
err := store.DeleteClient(oauthClientId, orgID, false)
if err != nil {
log.Errorf("Could not delete oauth client with id: %v", oauthClientId)
return
}
log.Infof("Oauth Client deleted successfully")
case OauthClientUpdated:
// on update: delete from local redis and pull again from rpc
_, err := store.GetClient(oauthClientId)
if err != nil {
log.WithError(err).Error("Could not retrieve oauth client information")
return
}
err = store.DeleteClient(oauthClientId, orgID, false)
if err != nil {
log.WithError(err).Error("Could not delete oauth client")
return
}
client, err := store.GetClient(oauthClientId)
if err != nil {
log.WithError(err).Error("Could not retrieve oauth client information")
return
}
err = store.SetClient(oauthClientId, orgID, client, false)
if err != nil {
log.WithError(err).Error("Could not save oauth client.")
return
}
log.Info("oauth client updated successfully")
default:
log.Warningf("Oauth client event not supported:%v", event)
}
}
Cognitive complexity: 21, Cyclomatic complexity: 13
func (*Gateway) ReplaceTykVariables
ReplaceTykVariables implements a variable replacement hook. It will replace
the template in. If escape is true, the values get escaped as a query
parameter for a HTTP request would. If no replacement has been made, in
is returned without modification.
func (gw *Gateway) ReplaceTykVariables(r *http.Request, in string, escape bool) string {
if strings.Contains(in, secretsConfLabel) {
contextData := ctxGetData(r)
vars := secretsConfMatch.FindAllString(in, -1)
in = gw.replaceVariables(in, vars, contextData, secretsConfLabel, escape)
}
if strings.Contains(in, envLabel) {
contextData := ctxGetData(r)
vars := envValueMatch.FindAllString(in, -1)
in = gw.replaceVariables(in, vars, contextData, envLabel, escape)
}
if strings.Contains(in, vaultLabel) {
contextData := ctxGetData(r)
vars := vaultMatch.FindAllString(in, -1)
in = gw.replaceVariables(in, vars, contextData, vaultLabel, escape)
}
if strings.Contains(in, consulLabel) {
contextData := ctxGetData(r)
vars := consulMatch.FindAllString(in, -1)
in = gw.replaceVariables(in, vars, contextData, consulLabel, escape)
}
if strings.Contains(in, contextLabel) {
contextData := ctxGetData(r)
vars := contextMatch.FindAllString(in, -1)
in = gw.replaceVariables(in, vars, contextData, contextLabel, escape)
}
if strings.Contains(in, metaLabel) {
vars := metaMatch.FindAllString(in, -1)
session := ctxGetSession(r)
if session == nil {
in = gw.replaceVariables(in, vars, nil, metaLabel, escape)
} else {
in = gw.replaceVariables(in, vars, session.MetaData, metaLabel, escape)
}
}
//todo add config_data
return in
}
Cognitive complexity: 16, Cyclomatic complexity: 8
func (*Gateway) RevokeAllTokensHandler
func (gw *Gateway) RevokeAllTokensHandler(w http.ResponseWriter, r *http.Request) {
err := r.ParseForm()
if err != nil {
doJSONWrite(w, http.StatusBadRequest, apiError("cannot parse form. Form malformed"))
return
}
clientId := r.PostFormValue("client_id")
clientSecret := r.PostFormValue("client_secret")
orgId := r.PostFormValue("org_id")
if clientId == "" {
doJSONWrite(w, http.StatusUnauthorized, apiError(oauthClientIdEmpty))
return
}
if clientSecret == "" {
doJSONWrite(w, http.StatusUnauthorized, apiError(oauthClientSecretEmpty))
return
}
apis := gw.getApisForOauthClientId(clientId, orgId)
if len(apis) == 0 {
//if api is 0 is because the client wasn't found
doJSONWrite(w, http.StatusNotFound, apiError("oauth client doesn't exist"))
return
}
tokens := []string{}
for _, apiId := range apis {
storage, _, err := gw.GetStorageForApi(apiId)
if err == nil {
_, tokensRevoked, _ := RevokeAllTokens(storage, clientId, clientSecret)
tokens = append(tokens, tokensRevoked...)
}
}
n := Notification{
Command: KeySpaceUpdateNotification,
Payload: strings.Join(tokens, ","),
Gw: gw,
}
gw.MainNotifier.Notify(n)
doJSONWrite(w, http.StatusOK, apiOk("tokens revoked successfully"))
}
Cognitive complexity: 15, Cyclomatic complexity: 7
func (*Gateway) RevokeTokenHandler
func (gw *Gateway) RevokeTokenHandler(w http.ResponseWriter, r *http.Request) {
err := r.ParseForm()
if err != nil {
doJSONWrite(w, http.StatusBadRequest, apiError("cannot parse form. Form malformed"))
return
}
tokenTypeHint := r.PostFormValue("token_type_hint")
token := r.PostFormValue("token")
clientID := r.PostFormValue("client_id")
orgID := r.PostFormValue("org_id")
if token == "" {
doJSONWrite(w, http.StatusBadRequest, apiError(oauthTokenEmpty))
return
}
if clientID == "" {
doJSONWrite(w, http.StatusBadRequest, apiError(oauthClientIdEmpty))
return
}
apis := gw.getApisForOauthClientId(clientID, orgID)
if len(apis) == 0 {
doJSONWrite(w, http.StatusBadRequest, apiError("oauth client doesn't exist"))
return
}
for _, apiID := range apis {
storage, _, err := gw.GetStorageForApi(apiID)
if err == nil {
RevokeToken(storage, token, tokenTypeHint)
}
}
doJSONWrite(w, http.StatusOK, apiOk("token revoked successfully"))
}
Cognitive complexity: 13, Cyclomatic complexity: 7
func (*Gateway) SetCheckerHostList
func (gw *Gateway) SetCheckerHostList() {
log.WithFields(logrus.Fields{
"prefix": "host-check-mgr",
}).Info("Loading uptime tests...")
hostList := []HostData{}
gw.apisMu.RLock()
for _, spec := range gw.apisByID {
gw.populateHostListByApiSpec(&hostList, spec)
}
gw.apisMu.RUnlock()
gw.GlobalHostChecker.UpdateTrackingList(hostList)
}
Cognitive complexity: 5, Cyclomatic complexity: 2
func (*Gateway) SetConfig
func (gw *Gateway) SetConfig(conf config.Config, skipReload ...bool) {
gw.configMu.Lock()
gw.config.Store(conf)
gw.configMu.Unlock()
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*Gateway) SetNodeID
SetNodeID writes NodeID safely.
func (gw *Gateway) SetNodeID(nodeID string) {
gw.nodeIDMu.Lock()
gw.nodeID = nodeID
gw.nodeIDMu.Unlock()
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*Gateway) SetPolicies
SetPolicies updates the internal policy map with a new policy map.
func (gw *Gateway) SetPolicies(pols map[string]user.Policy) {
gw.policiesMu.Lock()
defer gw.policiesMu.Unlock()
gw.policiesByID = pols
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*Gateway) SetPoliciesByID
SetPoliciesByID will update the internal policiesByID map with new policies. The key used will be the policy ID.
func (gw *Gateway) SetPoliciesByID(pols ...user.Policy) {
gw.policiesMu.Lock()
defer gw.policiesMu.Unlock()
for _, pol := range pols {
gw.policiesByID[pol.ID] = pol
}
}
Cognitive complexity: 3, Cyclomatic complexity: 2
func (*Gateway) SetupNewRelic
SetupNewRelic creates new newrelic.Application instance.
func (gw *Gateway) SetupNewRelic() (app *newrelic.Application) {
var (
err error
gwConfig = gw.GetConfig()
)
log := log.WithFields(logrus.Fields{"prefix": "newrelic"})
cfg := []newrelic.ConfigOption{
newrelic.ConfigAppName(gwConfig.NewRelic.AppName),
newrelic.ConfigLicense(gwConfig.NewRelic.LicenseKey),
newrelic.ConfigEnabled(gwConfig.NewRelic.AppName != ""),
newrelic.ConfigDistributedTracerEnabled(gwConfig.NewRelic.EnableDistributedTracing),
newrelic.ConfigLogger(newrelic.NewLogger(log)),
}
if app, err = newrelic.NewApplication(cfg...); err != nil {
log.Warn("Error initializing NewRelic, skipping... ", err)
return
}
instrument.AddSink(newrelic.NewSink(app))
return
}
Cognitive complexity: 4, Cyclomatic complexity: 2
func (*Gateway) SignatureVerifier
SignatureVerifier returns a verifier to use for validating signatures. It is configured with the PublicKeyPath value in gateway config.
func (gw *Gateway) SignatureVerifier() (goverify.Verifier, error) {
gwConfig := gw.GetConfig()
if gwConfig.PublicKeyPath == "" {
return nil, nil
}
cached := gw.signatureVerifier.Load()
if cached != nil {
return *cached, nil
}
log.Warnf("Creating new NotificationVerifier with pubkey: %q", gwConfig.PublicKeyPath)
verifier, err := goverify.LoadPublicKeyFromFile(gwConfig.PublicKeyPath)
if err != nil {
mainLog.WithError(err).Errorf("Failed loading public key from path: %s", err)
return nil, err
}
gw.signatureVerifier.Store(&verifier)
return verifier, nil
}
Cognitive complexity: 6, Cyclomatic complexity: 4
func (*Gateway) TykNewSingleHostReverseProxy
TykNewSingleHostReverseProxy returns a new ReverseProxy that rewrites URLs to the scheme, host, and base path provided in target. If the target's path is "/base" and the incoming request was for "/dir", the target request will be for /base/dir. This version modifies the stdlib version by also setting the host to the target, this allows us to work with heroku and other such providers
func (gw *Gateway) TykNewSingleHostReverseProxy(target *url.URL, spec *APISpec, logger *logrus.Entry) *ReverseProxy {
onceStartAllHostsDown.Do(func() {
handler := func(w http.ResponseWriter, r *http.Request) {
http.Error(w, "all hosts are down", http.StatusServiceUnavailable)
}
listener, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
panic(err)
}
server := &http.Server{
Handler: http.HandlerFunc(handler),
ReadTimeout: 1 * time.Second,
WriteTimeout: 1 * time.Second,
MaxHeaderBytes: 1 << 20,
}
allHostsDownURL = "http://" + listener.Addr().String()
go func() {
panic(server.Serve(listener))
}()
})
if logger == nil {
logger = logrus.NewEntry(log)
}
logger = logger.WithField("mw", "ReverseProxy")
targetQuery := target.RawQuery
director := func(req *http.Request) {
logger := logger
spec := spec
target := target
gw := gw
hostList := spec.Proxy.StructuredTargetList
switch {
case spec.Proxy.ServiceDiscovery.UseDiscoveryService:
var err error
hostList, err = urlFromService(spec, gw)
if err != nil {
logger.Error("[PROXY] [SERVICE DISCOVERY] Failed target lookup: ", err)
break
}
fallthrough // implies load balancing, with replaced host list
case spec.Proxy.EnableLoadBalancing:
host, err := gw.nextTarget(hostList, spec)
if err != nil {
logger.Error("[PROXY] [LOAD BALANCING] ", err)
host = allHostsDownURL
}
lbRemote, err := url.Parse(host)
if err != nil {
logger.Error("[PROXY] [LOAD BALANCING] Couldn't parse target URL:", err)
} else {
// Only replace target if everything is OK
target = lbRemote
targetQuery = target.RawQuery
}
}
targetToUse := target
if spec.URLRewriteEnabled && req.Context().Value(ctx.RetainHost) == true {
logger.Debug("Detected host rewrite, overriding target")
tmpTarget, err := url.Parse(req.URL.String())
if err != nil {
logger.Error("Failed to parse URL! Err: ", err)
} else {
// Specifically override with a URL rewrite
targetToUse = tmpTarget
}
}
// No override, and no load balancing? Use the existing target
// if this is false, there was an url rewrite, thus we
// don't want to do anything to the path - req.URL is
// already final.
if targetToUse == target {
req.URL.Scheme = targetToUse.Scheme
req.URL.Host = targetToUse.Host
req.URL.Path = singleJoiningSlash(targetToUse.Path, req.URL.Path, spec.Proxy.DisableStripSlash)
if req.URL.RawPath != "" {
req.URL.RawPath = singleJoiningSlash(targetToUse.Path, req.URL.RawPath, spec.Proxy.DisableStripSlash)
}
}
if !spec.Proxy.PreserveHostHeader {
req.Host = targetToUse.Host
}
if targetQuery == "" || req.URL.RawQuery == "" {
req.URL.RawQuery = targetQuery + req.URL.RawQuery
} else {
req.URL.RawQuery = targetQuery + "&" + req.URL.RawQuery
}
if _, ok := req.Header[header.UserAgent]; !ok {
// Set Tyk's own default user agent. Without
// this line, we would get the net/http default.
req.Header.Set(header.UserAgent, defaultUserAgent)
}
if spec.GlobalConfig.HttpServerOptions.SkipTargetPathEscaping {
// force RequestURI to skip escaping if API's proxy is set for this
// if we set opaque here it will force URL.RequestURI to skip escaping
if req.URL.RawPath != "" {
req.URL.Opaque = req.URL.RawPath
}
} else if req.URL.RawPath == req.URL.Path {
// this should force URL to do escaping
req.URL.RawPath = ""
}
switch req.URL.Scheme {
case "ws":
req.URL.Scheme = "http"
case "wss":
req.URL.Scheme = "https"
}
}
proxy := &ReverseProxy{
Director: director,
TykAPISpec: spec,
FlushInterval: time.Duration(spec.GlobalConfig.HttpServerOptions.FlushInterval) * time.Millisecond,
logger: logger,
wsUpgrader: websocket.Upgrader{
// CheckOrigin is not needed for the upgrader as tyk already provides
// its own middlewares for that.
CheckOrigin: func(r *http.Request) bool {
return true
},
},
sp: sync.Pool{
New: func() interface{} {
buffer := make([]byte, 32*1024)
return &buffer
},
},
Gw: gw,
}
proxy.ErrorHandler.BaseMiddleware = &BaseMiddleware{Spec: spec, Proxy: proxy, Gw: gw}
return proxy
}
Cognitive complexity: 12, Cyclomatic complexity: 2
func (*Gateway) TykOsinNewServer
TykOsinNewServer creates a new server instance, but uses an extended interface so we can SetClient() too.
func (gw *Gateway) TykOsinNewServer(config *osin.ServerConfig, storage ExtendedOsinStorageInterface) *TykOsinServer {
overrideServer := TykOsinServer{
Config: config,
Storage: storage,
AuthorizeTokenGen: &osin.AuthorizeTokenGenDefault{},
AccessTokenGen: accessTokenGen{gw},
}
overrideServer.Server.Config = config
overrideServer.Server.Storage = storage
overrideServer.Server.AuthorizeTokenGen = overrideServer.AuthorizeTokenGen
overrideServer.Server.AccessTokenGen = accessTokenGen{gw}
return &overrideServer
}
Cognitive complexity: 4, Cyclomatic complexity: 1
func (*Gateway) UnmarshalJSON
func (gw *Gateway) UnmarshalJSON(data []byte) error {
return nil
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*GoPluginMiddleware) EnabledForSpec
func (m *GoPluginMiddleware) EnabledForSpec() bool {
// global go plugins
if m.Path != "" && m.SymbolName != "" {
m.loadPlugin()
return true
}
// per path go plugins
for _, version := range m.Spec.VersionData.Versions {
for _, p := range version.ExtendedPaths.GoPlugin {
if !p.Disabled {
return true
}
}
}
return false
}
Cognitive complexity: 10, Cyclomatic complexity: 6
func (*GoPluginMiddleware) Name
func (m *GoPluginMiddleware) Name() string {
return "GoPluginMiddleware: " + m.Path + ":" + m.SymbolName
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*GoPluginMiddleware) ProcessRequest
func (m *GoPluginMiddleware) ProcessRequest(w http.ResponseWriter, r *http.Request, conf interface{}) (err error, respCode int) {
logger := m.logger
handler := m.handler
successHandler := m.successHandler
if !m.APILevel {
// if a Go plugin is found for this path, override the base handler and logger
if pluginMw, found := m.goPluginFromRequest(r); found {
logger = pluginMw.logger
handler = pluginMw.handler
successHandler = &SuccessHandler{BaseMiddleware: m.BaseMiddleware.Copy()}
} else {
return nil, http.StatusOK // next middleware
}
}
if handler == nil {
respCode = http.StatusInternalServerError
err = errors.New(http.StatusText(respCode))
return
}
// make sure tyk recover in case Go-plugin function panics
defer func() {
if e := recover(); e != nil {
err = fmt.Errorf("%v", e)
respCode = http.StatusInternalServerError
logger.WithError(err).Error("Recovered from panic while running Go-plugin middleware func")
}
}()
// prepare data to call Go-plugin function
// make sure request's body can be re-read again
nopCloseRequestBody(r)
// wrap ResponseWriter to check if response was sent
rw := &customResponseWriter{
ResponseWriter: w,
copyData: recordDetail(r, m.Spec),
}
// call Go-plugin function
t1 := time.Now()
// Inject definition into request context:
m.Spec.injectIntoReqContext(r)
handler(rw, r)
if session := ctxGetSession(r); session != nil {
if err := m.ApplyPolicies(session); err != nil {
m.Logger().WithError(err).Error("Could not apply policy to session")
return errors.New(http.StatusText(http.StatusInternalServerError)), http.StatusInternalServerError
}
}
// calculate latency
ms := DurationToMillisecond(time.Since(t1))
logger.WithField("ms", ms).Debug("Go-plugin request processing took")
// check if response was sent
if rw.responseSent {
// check if response code was an error one
switch {
case rw.statusCodeSent == http.StatusForbidden:
logger.WithError(err).Error("Authentication error in Go-plugin middleware func")
m.Base().FireEvent(EventAuthFailure, EventKeyFailureMeta{
EventMetaDefault: EventMetaDefault{Message: "Auth Failure", OriginatingRequest: EncodeRequestToEvent(r)},
Path: r.URL.Path,
Origin: request.RealIP(r),
Key: "n/a",
})
fallthrough
case rw.statusCodeSent >= http.StatusBadRequest:
// base middleware will report this error to analytics if needed
respCode = rw.statusCodeSent
err = fmt.Errorf("plugin function sent error response code: %d", rw.statusCodeSent)
logger.WithError(err).Error("Failed to process request with Go-plugin middleware func")
default:
// record 2XX to analytics
successHandler.RecordHit(r, analytics.Latency{Total: int64(ms)}, rw.statusCodeSent, rw.getHttpResponse(r), false)
// no need to continue passing this request down to reverse proxy
respCode = mwStatusRespond
}
} else {
respCode = http.StatusOK
}
return
}
Cognitive complexity: 29, Cyclomatic complexity: 11
func (*GranularAccessMiddleware) Name
func (m *GranularAccessMiddleware) Name() string {
return "GranularAccessMiddleware"
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*GranularAccessMiddleware) ProcessRequest
ProcessRequest will run any checks on the request on the way through the system, return an error to have the chain fail
func (m *GranularAccessMiddleware) ProcessRequest(w http.ResponseWriter, r *http.Request, _ interface{}) (error, int) {
if ctxGetRequestStatus(r) == StatusOkAndIgnore {
return nil, http.StatusOK
}
session := ctxGetSession(r)
sessionVersionData, foundAPI := session.AccessRights[m.Spec.APIID]
if !foundAPI {
return nil, http.StatusOK
}
if len(sessionVersionData.AllowedURLs) == 0 {
return nil, http.StatusOK
}
gwConfig := m.Gw.GetConfig()
// Hook per-api settings here (m.Spec...)
isPrefixMatch := gwConfig.HttpServerOptions.EnablePathPrefixMatching
isSuffixMatch := gwConfig.HttpServerOptions.EnablePathSuffixMatching
if isPrefixMatch {
urlPaths := []string{
m.Spec.StripListenPath(r.URL.Path),
r.URL.Path,
}
logger := m.Logger().WithField("paths", urlPaths)
for _, accessSpec := range sessionVersionData.AllowedURLs {
if !slices.Contains(accessSpec.Methods, r.Method) {
continue
}
// Append $ if so configured to match end of request path.
pattern := httputil.PreparePathRegexp(accessSpec.URL, isPrefixMatch, isSuffixMatch)
if isSuffixMatch && !strings.HasSuffix(pattern, "$") {
pattern += "$"
}
match, err := httputil.MatchPaths(pattern, urlPaths)
// unconditional log of err/match/url
// if loglevel is set to debug verbosity increases and all requests are logged,
// regardless if an error occured or not.
if gwConfig.LogLevel == "debug" || err != nil {
logger = logger.WithError(err).WithField("pattern", pattern).WithField("match", match)
if err != nil {
logger.Error("error matching endpoint")
} else {
logger.Debug("matching endpoint")
}
}
if err != nil || !match {
continue
}
return m.pass()
}
return m.block(logger)
}
logger := m.Logger().WithField("paths", []string{r.URL.Path})
// Legacy behaviour (5.5.0 and earlier), wildcard match against full request path.
// Fixed error handling in regex compilation to continue to next pattern (block).
urlPath := r.URL.Path
for _, accessSpec := range sessionVersionData.AllowedURLs {
if !slices.Contains(accessSpec.Methods, r.Method) {
continue
}
pattern := httputil.PreparePathRegexp(accessSpec.URL, false, isSuffixMatch)
logger.Debug("Checking: ", urlPath, " Against:", pattern)
// Wildcard match (user supplied, as-is)
asRegex, err := regexp.Compile(pattern)
if err != nil {
logger.WithError(err).Error("error compiling regex")
continue
}
match := asRegex.MatchString(r.URL.Path)
if match {
return m.pass()
}
}
return m.block(logger)
}
Cognitive complexity: 35, Cyclomatic complexity: 18
func (*GraphQLComplexityMiddleware) EnabledForSpec
func (m *GraphQLComplexityMiddleware) EnabledForSpec() bool {
return m.Spec.GraphQL.Enabled
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*GraphQLComplexityMiddleware) Name
func (m *GraphQLComplexityMiddleware) Name() string {
return "GraphQLComplexityMiddleware"
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*GraphQLComplexityMiddleware) ProcessRequest
ProcessRequest will run any checks on the request on the way through the system, return an error to have the chain fail
func (m *GraphQLComplexityMiddleware) ProcessRequest(w http.ResponseWriter, r *http.Request, _ interface{}) (error, int) {
accessDef, _, err := GetAccessDefinitionByAPIIDOrSession(ctxGetSession(r), m.Spec)
if err != nil {
m.Logger().Debugf("Error while calculating GraphQL complexity: '%s'", err)
return m.handleComplexityFailReason(ComplexityFailReasonInternalError)
}
graphEngineComplexityAccessDefinition := &graphengine.ComplexityAccessDefinition{
Limit: graphengine.ComplexityLimit{
MaxQueryDepth: accessDef.Limit.MaxQueryDepth,
},
FieldAccessRights: []graphengine.ComplexityFieldAccessDefinition{},
}
for _, fieldAccessRight := range accessDef.FieldAccessRights {
graphEngineComplexityAccessDefinition.FieldAccessRights = append(graphEngineComplexityAccessDefinition.FieldAccessRights, graphengine.ComplexityFieldAccessDefinition{
TypeName: fieldAccessRight.TypeName,
FieldName: fieldAccessRight.FieldName,
Limits: graphengine.ComplexityFieldLimits{
MaxQueryDepth: fieldAccessRight.Limits.MaxQueryDepth,
},
})
}
return m.Spec.GraphEngine.ProcessGraphQLComplexity(r, graphEngineComplexityAccessDefinition)
}
Cognitive complexity: 11, Cyclomatic complexity: 3
func (*GraphQLGranularAccessMiddleware) EnabledForSpec
func (m *GraphQLGranularAccessMiddleware) EnabledForSpec() bool {
return m.Spec.GraphQL.Enabled
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*GraphQLGranularAccessMiddleware) Name
func (m *GraphQLGranularAccessMiddleware) Name() string {
return "GraphQLGranularAccessMiddleware"
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*GraphQLGranularAccessMiddleware) ProcessRequest
ProcessRequest will run any checks on the request on the way through the system, return an error to have the chain fail
func (m *GraphQLGranularAccessMiddleware) ProcessRequest(w http.ResponseWriter, r *http.Request, _ interface{}) (error, int) {
if ctxGetRequestStatus(r) == StatusOkAndIgnore {
return nil, http.StatusOK
}
session := ctxGetSession(r)
accessDef, foundAPI := session.AccessRights[m.Spec.APIID]
if !foundAPI {
return nil, http.StatusOK
}
graphEngineGranularAccessDefinition := &graphengine.GranularAccessDefinition{
AllowedTypes: make([]graphengine.GranularAccessType, 0),
RestrictedTypes: make([]graphengine.GranularAccessType, 0),
DisableIntrospection: accessDef.DisableIntrospection,
}
for _, allowedType := range accessDef.AllowedTypes {
graphEngineGranularAccessDefinition.AllowedTypes = append(graphEngineGranularAccessDefinition.AllowedTypes, graphengine.GranularAccessType{
Name: allowedType.Name,
Fields: allowedType.Fields,
})
}
for _, restrictedType := range accessDef.RestrictedTypes {
graphEngineGranularAccessDefinition.RestrictedTypes = append(graphEngineGranularAccessDefinition.RestrictedTypes, graphengine.GranularAccessType{
Name: restrictedType.Name,
Fields: restrictedType.Fields,
})
}
return m.Spec.GraphEngine.ProcessGraphQLGranularAccess(w, r, graphEngineGranularAccessDefinition)
}
Cognitive complexity: 14, Cyclomatic complexity: 5
func (*GraphQLMiddleware) EnabledForSpec
func (m *GraphQLMiddleware) EnabledForSpec() bool {
return m.Spec.GraphQL.Enabled
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*GraphQLMiddleware) Init
func (m *GraphQLMiddleware) Init() {
schema, err := gql.NewSchemaFromString(m.Spec.GraphQL.Schema)
if err != nil {
log.Errorf("Error while creating schema from API definition: %v", err)
return
}
normalizationResult, err := schema.Normalize()
if err != nil {
log.Errorf("Error while normalizing schema from API definition: %v", err)
}
if !normalizationResult.Successful {
log.Errorf("Schema normalization was not successful. Reason: %v", normalizationResult.Errors)
}
reusableBodyReadCloser := func(buf io.ReadCloser) (io.ReadCloser, error) {
return newNopCloserBuffer(buf)
}
if m.Spec.GraphQL.Version == apidef.GraphQLConfigVersionNone || m.Spec.GraphQL.Version == apidef.GraphQLConfigVersion1 {
if m.Spec.GraphQL.Version == apidef.GraphQLConfigVersionNone {
log.Warn("GraphQL config version is not set, defaulting to version 1")
}
log.Info("GraphQL Config Version 1 is deprecated - Please consider migrating to version 2 or higher")
m.Spec.GraphEngine, err = graphengine.NewEngineV1(graphengine.EngineV1Options{
Logger: log,
ApiDefinition: m.Spec.APIDefinition,
Schema: schema,
HttpClient: &http.Client{
Transport: &http.Transport{TLSClientConfig: tlsClientConfig(m.Spec, nil)},
},
Injections: graphengine.EngineV1Injections{
PreSendHttpHook: preSendHttpHook{m},
PostReceiveHttpHook: postReceiveHttpHook{m},
ContextStoreRequest: ctxSetGraphQLRequest,
ContextRetrieveRequest: ctxGetGraphQLRequest,
NewReusableBodyReadCloser: reusableBodyReadCloser,
},
})
} else if m.Spec.GraphQL.Version == apidef.GraphQLConfigVersion2 {
httpClient := &http.Client{
Transport: &http.Transport{TLSClientConfig: tlsClientConfig(m.Spec, nil)},
}
m.Spec.GraphEngine, err = graphengine.NewEngineV2(graphengine.EngineV2Options{
Logger: log,
Schema: schema,
ApiDefinition: m.Spec.APIDefinition,
HttpClient: httpClient,
StreamingClient: httpClient,
OpenTelemetry: graphengine.EngineV2OTelConfig{
Enabled: m.Gw.GetConfig().OpenTelemetry.Enabled,
TracerProvider: m.Gw.TracerProvider,
},
Injections: graphengine.EngineV2Injections{
BeforeFetchHook: m,
AfterFetchHook: m,
WebsocketOnBeforeStart: m,
ContextStoreRequest: ctxSetGraphQLRequest,
ContextRetrieveRequest: ctxGetGraphQLRequest,
NewReusableBodyReadCloser: reusableBodyReadCloser,
SeekReadCloser: func(readCloser io.ReadCloser) (io.ReadCloser, error) {
body, ok := readCloser.(*nopCloserBuffer)
if !ok {
return nil, nil
}
_, err := body.Seek(0, io.SeekStart)
if err != nil {
return nil, err
}
return body, nil
},
TykVariableReplacer: m.Gw.ReplaceTykVariables,
},
})
} else if m.Spec.GraphQL.Version == apidef.GraphQLConfigVersion3Preview {
v2Schema, err := gqlv2.NewSchemaFromString(m.Spec.GraphQL.Schema)
if err != nil {
log.Errorf("Error while creating schema from API definition: %v", err)
return
}
engine, err := graphengine.NewEngineV3(graphengine.EngineV3Options{
Logger: log,
Schema: v2Schema,
ApiDefinition: m.Spec.APIDefinition,
OpenTelemetry: graphengine.EngineV2OTelConfig{
Enabled: m.Gw.GetConfig().OpenTelemetry.Enabled,
TracerProvider: m.Gw.TracerProvider,
},
HttpClient: &http.Client{
Transport: &http.Transport{TLSClientConfig: tlsClientConfig(m.Spec, nil)},
},
Injections: graphengine.EngineV3Injections{
ContextRetrieveRequest: ctxGetGraphQLRequestV2,
ContextStoreRequest: ctxSetGraphQLRequestV2,
// TODO use proper version or request for this
//WebsocketOnBeforeStart: m,
NewReusableBodyReadCloser: reusableBodyReadCloser,
SeekReadCloser: func(readCloser io.ReadCloser) (io.ReadCloser, error) {
body, ok := readCloser.(*nopCloserBuffer)
if !ok {
return nil, nil
}
_, err := body.Seek(0, io.SeekStart)
if err != nil {
return nil, err
}
return body, nil
},
TykVariableReplacer: m.Gw.ReplaceTykVariables,
},
})
if err != nil {
log.Errorf("Error creating enginev3: %v", err)
return
}
m.Spec.GraphEngine = engine
} else {
log.Errorf("Could not init GraphQL middleware: invalid config version provided: %s", m.Spec.GraphQL.Version)
}
}
Cognitive complexity: 47, Cyclomatic complexity: 15
func (*GraphQLMiddleware) Name
func (m *GraphQLMiddleware) Name() string {
return "GraphQLMiddleware"
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*GraphQLMiddleware) OnBeforeFetch
func (m *GraphQLMiddleware) OnBeforeFetch(ctx resolve.HookContext, input []byte) {
m.BaseMiddleware.Logger().
WithFields(
logrus.Fields{
"path": ctx.CurrentPath,
},
).Debugf("%s (beforeFetchHook): %s", ctx.CurrentPath, string(input))
}
Cognitive complexity: 1, Cyclomatic complexity: 1
func (*GraphQLMiddleware) OnBeforeStart
OnBeforeStart - is a graphql.WebsocketBeforeStartHook which allows to perform security checks for all operations over websocket connections
func (m *GraphQLMiddleware) OnBeforeStart(reqCtx context.Context, operation *gql.Request) error {
if m.Spec.UseKeylessAccess {
return nil
}
schema, err := graphengine.GetSchemaV1(m.Spec.GraphEngine)
if err != nil {
return err
}
v := reqCtx.Value(ctx.SessionData)
if v == nil {
m.Logger().Error("failed to get session in OnBeforeStart hook")
return errors.New("empty session")
}
session := v.(*user.SessionState)
accessDef, _, err := GetAccessDefinitionByAPIIDOrSession(session, m.Spec)
if err != nil {
m.Logger().Errorf("failed to get access definition in OnBeforeStart hook: '%s'", err)
return err
}
complexityCheck := &GraphqlComplexityChecker{logger: m.Logger()}
depthResult := complexityCheck.DepthLimitExceeded(operation, accessDef, schema)
switch depthResult {
case ComplexityFailReasonInternalError:
return ProxyingRequestFailedErr
case ComplexityFailReasonDepthLimitExceeded:
return GraphQLDepthLimitExceededErr
}
granularAccessCheck := &GraphqlGranularAccessChecker{}
result := granularAccessCheck.CheckGraphqlRequestFieldAllowance(operation, accessDef, schema)
switch result.failReason {
case GranularAccessFailReasonInternalError:
m.Logger().Errorf(RestrictedFieldValidationFailedLogMsg, result.internalErr)
return ProxyingRequestFailedErr
case GranularAccessFailReasonValidationError:
m.Logger().Debugf(RestrictedFieldValidationFailedLogMsg, result.validationResult.Errors)
return result.validationResult.Errors
}
return nil
}
Cognitive complexity: 18, Cyclomatic complexity: 11
func (*GraphQLMiddleware) OnData
func (m *GraphQLMiddleware) OnData(ctx resolve.HookContext, output []byte, singleFlight bool) {
m.BaseMiddleware.Logger().
WithFields(
logrus.Fields{
"path": ctx.CurrentPath,
"single_flight": singleFlight,
},
).Debugf("%s (afterFetchHook.OnData): %s", ctx.CurrentPath, string(output))
}
Cognitive complexity: 1, Cyclomatic complexity: 1
func (*GraphQLMiddleware) OnError
func (m *GraphQLMiddleware) OnError(ctx resolve.HookContext, output []byte, singleFlight bool) {
m.BaseMiddleware.Logger().
WithFields(
logrus.Fields{
"path": ctx.CurrentPath,
"single_flight": singleFlight,
},
).Debugf("%s (afterFetchHook.OnError): %s", ctx.CurrentPath, string(output))
}
Cognitive complexity: 1, Cyclomatic complexity: 1
func (*GraphQLMiddleware) ProcessRequest
func (m *GraphQLMiddleware) ProcessRequest(w http.ResponseWriter, r *http.Request, _ interface{}) (error, int) {
err := m.checkForUnsupportedUsage()
if err != nil {
m.Logger().WithError(err).Error("request could not be executed because of unsupported usage")
return ProxyingRequestFailedErr, http.StatusInternalServerError
}
if m.Spec.GraphEngine == nil {
m.Logger().Error("GraphEngine is not initialized")
return ProxyingRequestFailedErr, http.StatusInternalServerError
}
if !m.Spec.GraphEngine.HasSchema() {
m.Logger().Error("Schema is not created")
return ProxyingRequestFailedErr, http.StatusInternalServerError
}
if websocket.IsWebSocketUpgrade(r) {
if !m.websocketUpgradeAllowed() {
return errors.New("websockets are not allowed"), http.StatusUnprocessableEntity
}
if !m.websocketUpgradeUsesGraphQLProtocol(r) {
return errors.New("invalid websocket protocol for upgrading to a graphql websocket connection"), http.StatusBadRequest
}
ctxSetGraphQLIsWebSocketUpgrade(r, true)
return nil, http.StatusSwitchingProtocols
}
// With current in memory server approach we need body to be readable again
// as for proxy only API we are sending it as is
nopCloseRequestBody(r)
return m.Spec.GraphEngine.ProcessAndStoreGraphQLRequest(w, r)
}
Cognitive complexity: 13, Cyclomatic complexity: 7
func (*GraphqlComplexityChecker) DepthLimitEnabled
func (c *GraphqlComplexityChecker) DepthLimitEnabled(accessDef *user.AccessDefinition) bool {
// There is a possibility that depth limit is disabled on field level too,
// but we could not determine this without analyzing actual requested fields.
if len(accessDef.FieldAccessRights) > 0 {
return true
}
// If MaxQueryDepth is -1 or 0, it means unlimited and no need for depth limiting.
return accessDef.Limit.MaxQueryDepth > 0
}
Cognitive complexity: 2, Cyclomatic complexity: 2
func (*GraphqlComplexityChecker) DepthLimitExceeded
func (c *GraphqlComplexityChecker) DepthLimitExceeded(gqlRequest *graphql.Request, accessDef *user.AccessDefinition, schema *graphql.Schema) ComplexityFailReason {
if !c.DepthLimitEnabled(accessDef) {
return ComplexityFailReasonNone
}
isIntrospectionQuery, err := gqlRequest.IsIntrospectionQuery()
if err != nil {
c.logger.Debugf("Error while checking for introspection query: '%s'", err.Error())
return ComplexityFailReasonInternalError
}
if isIntrospectionQuery {
return ComplexityFailReasonNone
}
complexityRes, err := gqlRequest.CalculateComplexity(graphql.DefaultComplexityCalculator, schema)
if err != nil {
c.logger.Errorf("Error while calculating complexity of GraphQL request: '%s'", err)
return ComplexityFailReasonInternalError
}
if complexityRes.Errors != nil && complexityRes.Errors.Count() > 0 {
c.logger.Errorf("Error while calculating complexity of GraphQL request: '%s'", complexityRes.Errors.ErrorByIndex(0))
return ComplexityFailReasonInternalError
}
// do per query depth check
if len(accessDef.FieldAccessRights) == 0 {
if complexityRes.Depth > accessDef.Limit.MaxQueryDepth {
c.logger.Debugf("Complexity of the request is higher than the allowed limit '%d'", accessDef.Limit.MaxQueryDepth)
return ComplexityFailReasonDepthLimitExceeded
}
return ComplexityFailReasonNone
}
// do per query field depth check
for _, fieldComplexityRes := range complexityRes.PerRootField {
var (
fieldAccessDefinition user.FieldAccessDefinition
hasPerFieldLimits bool
)
for _, fieldAccessRight := range accessDef.FieldAccessRights {
if fieldComplexityRes.TypeName != fieldAccessRight.TypeName {
continue
}
if fieldComplexityRes.FieldName != fieldAccessRight.FieldName {
continue
}
fieldAccessDefinition = fieldAccessRight
hasPerFieldLimits = true
break
}
if hasPerFieldLimits {
if greaterThanInt(fieldComplexityRes.Depth, fieldAccessDefinition.Limits.MaxQueryDepth) {
c.logger.Debugf("Depth '%d' of the root field: %s.%s is higher than the allowed field limit '%d'",
fieldComplexityRes.Depth, fieldAccessDefinition.TypeName, fieldAccessDefinition.FieldName, fieldAccessDefinition.Limits.MaxQueryDepth)
return ComplexityFailReasonDepthLimitExceeded
}
continue
}
// favour global limit for query field
// have to increase resulting field depth by 1 to get a global depth
queryDepth := fieldComplexityRes.Depth + 1
if greaterThanInt(queryDepth, accessDef.Limit.MaxQueryDepth) {
c.logger.Debugf("Depth '%d' of the root field: %s.%s is higher than the allowed global limit '%d'",
queryDepth, fieldComplexityRes.TypeName, fieldComplexityRes.FieldName, accessDef.Limit.MaxQueryDepth)
return ComplexityFailReasonDepthLimitExceeded
}
}
return ComplexityFailReasonNone
}
Cognitive complexity: 30, Cyclomatic complexity: 16
func (*GraphqlGranularAccessChecker) CheckGraphqlRequestFieldAllowance
func (g *GraphqlGranularAccessChecker) CheckGraphqlRequestFieldAllowance(gqlRequest *graphql.Request, accessDef *user.AccessDefinition, schema *graphql.Schema) GraphqlGranularAccessResult {
if len(accessDef.AllowedTypes) != 0 {
fieldRestrictionList := graphql.FieldRestrictionList{
Kind: graphql.AllowList,
Types: accessDef.AllowedTypes,
}
return g.validateFieldRestrictions(gqlRequest, fieldRestrictionList, schema)
}
if len(accessDef.RestrictedTypes) != 0 {
fieldRestrictionList := graphql.FieldRestrictionList{
Kind: graphql.BlockList,
Types: accessDef.RestrictedTypes,
}
return g.validateFieldRestrictions(gqlRequest, fieldRestrictionList, schema)
}
// There are no restricted types. Every field is allowed access.
return GraphqlGranularAccessResult{failReason: GranularAccessFailReasonNone}
}
Cognitive complexity: 7, Cyclomatic complexity: 3
func (*HTTPBundleGetter) Get
Get performs an HTTP GET request.
func (g *HTTPBundleGetter) Get() ([]byte, error) {
tr := &(*http.DefaultTransport.(*http.Transport))
tr.TLSClientConfig = &tls.Config{
InsecureSkipVerify: g.InsecureSkipVerify,
MaxVersion: tls.VersionTLS12,
}
client := &http.Client{Transport: tr}
client.Timeout = 5 * time.Second
log.Infof("Attempting to download plugin bundle: %v", g.URL)
resp, err := client.Get(g.URL)
if err != nil {
return nil, fmt.Errorf("Error getting bundle: %w", err)
}
if resp.StatusCode != 200 {
httpError := fmt.Sprintf("HTTP Error, got status code %d", resp.StatusCode)
return nil, errors.New(httpError)
}
defer resp.Body.Close()
return ioutil.ReadAll(resp.Body)
}
Cognitive complexity: 6, Cyclomatic complexity: 3
func (*HTTPDashboardHandler) DeRegister
func (h *HTTPDashboardHandler) DeRegister() error {
req := h.newRequest(http.MethodDelete, h.DeRegistrationEndpoint)
req.Header.Set(header.XTykNodeID, h.Gw.GetNodeID())
h.Gw.ServiceNonceMutex.RLock()
req.Header.Set(header.XTykNonce, h.Gw.ServiceNonce)
h.Gw.ServiceNonceMutex.RUnlock()
c := h.Gw.initialiseClient()
resp, err := c.Do(req)
if err != nil {
return fmt.Errorf("deregister request failed with error %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("deregister request failed with status %d", resp.StatusCode)
}
val := NodeResponseOK{}
if err := json.NewDecoder(resp.Body).Decode(&val); err != nil {
return err
}
// Set the nonce
h.Gw.ServiceNonceMutex.Lock()
h.Gw.ServiceNonce = val.Nonce
h.Gw.ServiceNonceMutex.Unlock()
dashLog.Info("De-registered.")
return nil
}
Cognitive complexity: 7, Cyclomatic complexity: 4
func (*HTTPDashboardHandler) Init
func (h *HTTPDashboardHandler) Init() error {
h.RegistrationEndpoint = h.Gw.buildDashboardConnStr("/register/node")
h.DeRegistrationEndpoint = h.Gw.buildDashboardConnStr("/system/node")
h.HeartBeatEndpoint = h.Gw.buildDashboardConnStr("/register/ping")
h.KeyQuotaTriggerEndpoint = h.Gw.buildDashboardConnStr("/system/key/quota_trigger")
if h.Secret = h.Gw.GetConfig().NodeSecret; h.Secret == "" {
dashLog.Fatal("Node secret is not set, required for dashboard connection")
}
return nil
}
Cognitive complexity: 2, Cyclomatic complexity: 2
func (*HTTPDashboardHandler) NotifyDashboardOfEvent
NotifyDashboardOfEvent acts as a form of event which informs the dashboard of a key which has reached a certain usage quota
func (h *HTTPDashboardHandler) NotifyDashboardOfEvent(event interface{}) error {
meta, ok := event.(EventTriggerExceededMeta)
if !ok {
return errors.New("event type is currently not supported as a notification to the dashboard")
}
var b bytes.Buffer
if err := json.NewEncoder(&b).Encode(meta); err != nil {
log.Errorf("Could not decode event metadata :%v", err)
return err
}
req, err := http.NewRequest(http.MethodPost, h.KeyQuotaTriggerEndpoint, &b)
if err != nil {
log.Errorf("Could not create request.. %v", err)
return err
}
req.Header.Set("authorization", h.Secret)
req.Header.Set(header.XTykNodeID, h.Gw.GetNodeID())
h.Gw.ServiceNonceMutex.RLock()
req.Header.Set(header.XTykNonce, h.Gw.ServiceNonce)
h.Gw.ServiceNonceMutex.RUnlock()
c := h.Gw.initialiseClient()
resp, err := c.Do(req)
if err != nil {
log.Errorf("Request failed with error %v", err)
return err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
err := fmt.Errorf("unexpected status code while trying to notify dashboard of a key limit quota trigger.. Got %d", resp.StatusCode)
log.Error(err)
return err
}
val := NodeResponseOK{}
if err := json.NewDecoder(resp.Body).Decode(&val); err != nil {
return err
}
h.Gw.ServiceNonceMutex.Lock()
h.Gw.ServiceNonce = val.Nonce
h.Gw.ServiceNonceMutex.Unlock()
return nil
}
Cognitive complexity: 14, Cyclomatic complexity: 7
func (*HTTPDashboardHandler) Ping
func (h *HTTPDashboardHandler) Ping() error {
return h.sendHeartBeat(
h.newRequest(http.MethodGet, h.HeartBeatEndpoint),
h.Gw.initialiseClient())
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*HTTPDashboardHandler) Register
func (h *HTTPDashboardHandler) Register() error {
dashLog.Info("Registering gateway node with Dashboard")
req := h.newRequest(http.MethodGet, h.RegistrationEndpoint)
req.Header.Set(header.XTykSessionID, h.Gw.SessionID)
c := h.Gw.initialiseClient()
resp, err := c.Do(req)
if err != nil {
dashLog.Errorf("Request failed with error %v; retrying in 5s", err)
time.Sleep(time.Second * 5)
return h.Register()
} else if resp.StatusCode == http.StatusConflict {
dashLog.Debug("Node is already registered")
return nil
} else if resp != nil && resp.StatusCode != 200 {
dashLog.Errorf("Response failed with code %d; retrying in 5s", resp.StatusCode)
time.Sleep(time.Second * 5)
return h.Register()
}
defer resp.Body.Close()
val := NodeResponseOK{}
if err := json.NewDecoder(resp.Body).Decode(&val); err != nil {
return err
}
// Set the NodeID
var found bool
nodeID, found := val.Message["NodeID"]
h.Gw.SetNodeID(nodeID)
if !found {
dashLog.Error("Failed to register node, retrying in 5s")
time.Sleep(time.Second * 5)
return h.Register()
}
dashLog.WithField("id", h.Gw.GetNodeID()).Info("Node Registered")
// Set the nonce
h.Gw.ServiceNonceMutex.Lock()
h.Gw.ServiceNonce = val.Nonce
h.Gw.ServiceNonceMutex.Unlock()
dashLog.Debug("Registration Finished: Nonce Set: ", val.Nonce)
h.Gw.DoReload()
return nil
}
Cognitive complexity: 11, Cyclomatic complexity: 7
func (*HTTPDashboardHandler) StartBeating
func (h *HTTPDashboardHandler) StartBeating() error {
atomic.SwapInt32(&h.heartBeatStopSentinel, HeartBeatStarted)
req := h.newRequest(http.MethodGet, h.HeartBeatEndpoint)
client := h.Gw.initialiseClient()
for !h.isHeartBeatStopped() {
if err := h.sendHeartBeat(req, client); err != nil {
dashLog.Warning(err)
}
time.Sleep(time.Second * 2)
}
dashLog.Info("Stopped Heartbeat")
return nil
}
Cognitive complexity: 4, Cyclomatic complexity: 3
func (*HTTPDashboardHandler) StopBeating
func (h *HTTPDashboardHandler) StopBeating() {
atomic.SwapInt32(&h.heartBeatStopSentinel, HeartBeatStopped)
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*HTTPSignatureValidationMiddleware) EnabledForSpec
func (k *HTTPSignatureValidationMiddleware) EnabledForSpec() bool {
return k.Spec.EnableSignatureChecking
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*HTTPSignatureValidationMiddleware) Init
func (hm *HTTPSignatureValidationMiddleware) Init() {
hm.lowercasePattern = regexp.MustCompile(`%[a-f0-9][a-f0-9]`)
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*HTTPSignatureValidationMiddleware) Name
func (hm *HTTPSignatureValidationMiddleware) Name() string {
return "HTTPSignatureValidationMiddleware"
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*HTTPSignatureValidationMiddleware) ProcessRequest
func (hm *HTTPSignatureValidationMiddleware) ProcessRequest(w http.ResponseWriter, r *http.Request, _ interface{}) (error, int) {
if ctxGetRequestStatus(r) == StatusOkAndIgnore {
return nil, http.StatusOK
}
token, _ := hm.getAuthToken(hm.getAuthType(), r)
if token == "" {
return hm.authorizationError(r)
}
logger := hm.Logger().WithField("key", hm.Gw.obfuscateKey(token))
// Clean it
token = stripSignature(token)
// Separate out the field values
fieldValues, err := getFieldValues(token)
if err != nil {
logger.WithError(err).Error("Field extraction failed")
return hm.authorizationError(r)
}
// Generate a signature string
signatureString, err := generateHMACSignatureStringFromRequest(r, fieldValues.Headers, r.URL.Path)
if err != nil {
logger.WithError(err).WithField("signature_string", signatureString).Error("Signature string generation failed")
return hm.authorizationError(r)
}
if len(hm.Spec.HmacAllowedAlgorithms) > 0 {
algorithmAllowed := false
for _, alg := range hm.Spec.HmacAllowedAlgorithms {
if alg == fieldValues.Algorthm {
algorithmAllowed = true
break
}
}
if !algorithmAllowed {
logger.WithError(err).WithField("algorithm", fieldValues.Algorthm).Error("Algorithm not supported")
return hm.authorizationError(r)
}
}
var secret string
var rsaKey *rsa.PublicKey
var session user.SessionState
if strings.HasPrefix(fieldValues.Algorthm, "rsa") {
var certificateId string
certificateId, session, err = hm.getRSACertificateIdAndSessionForKeyID(r, fieldValues.KeyID)
if err != nil {
logger.WithError(err).WithFields(logrus.Fields{
"keyID": fieldValues.KeyID,
}).Error("Failed to fetch session/public key")
return hm.authorizationError(r)
}
publicKey := hm.Gw.CertificateManager.ListRawPublicKey(certificateId)
if publicKey == nil {
log.Error("Certificate not found")
return errors.New("Certificate not found"), http.StatusInternalServerError
}
var ok bool
rsaKey, ok = publicKey.(*rsa.PublicKey)
if !ok {
log.Error("Certificate doesn't contain RSA Public key")
return errors.New("Certificate doesn't contain RSA Public key"), http.StatusInternalServerError
}
} else {
// Get a session for the Key ID
secret, session, err = hm.getSecretAndSessionForKeyID(r, fieldValues.KeyID)
if err != nil {
logger.WithError(err).WithFields(logrus.Fields{
"keyID": fieldValues.KeyID,
}).Error("No HMAC secret for this key")
return hm.authorizationError(r)
}
}
var matchPass bool
if strings.HasPrefix(fieldValues.Algorthm, "rsa") {
matchPass, err = validateRSAEncodedSignature(signatureString, rsaKey, fieldValues.Algorthm, fieldValues.Signature)
if err != nil {
logger.WithError(err).Error("Signature validation failed.")
}
if !matchPass {
isLower, lowerList := hm.hasLowerCaseEscaped(fieldValues.Signature)
if isLower {
logger.Debug("--- Detected lower case encoding! ---")
upperedSignature := hm.replaceWithUpperCase(fieldValues.Signature, lowerList)
matchPass, err = validateRSAEncodedSignature(signatureString, rsaKey, fieldValues.Algorthm, upperedSignature)
if err != nil {
logger.WithError(err).Error("Signature validation failed.")
}
}
}
if !matchPass {
logger.WithFields(logrus.Fields{
"got": fieldValues.Signature,
}).Error("Signature string does not match!")
return hm.authorizationError(r)
}
} else {
// Create a signed string with the secret
encodedSignature, err := generateHMACEncodedSignature(signatureString, secret, fieldValues.Algorthm)
if err != nil {
logger.WithFields(logrus.Fields{
"error": err,
}).Error("Failed to validate signature")
return hm.authorizationError(r)
}
// Compare
matchPass = encodedSignature == fieldValues.Signature
// Check for lower case encoding (.Net issues, again)
if !matchPass {
isLower, lowerList := hm.hasLowerCaseEscaped(fieldValues.Signature)
if isLower {
logger.Debug("--- Detected lower case encoding! ---")
upperedSignature := hm.replaceWithUpperCase(fieldValues.Signature, lowerList)
if encodedSignature == upperedSignature {
matchPass = true
encodedSignature = upperedSignature
}
}
}
if !matchPass {
logger.WithFields(logrus.Fields{
"got": fieldValues.Signature,
}).Error("Signature string does not match!")
return hm.authorizationError(r)
}
}
// Check clock skew
_, dateVal := getDateHeader(r)
if !hm.checkClockSkew(dateVal) {
logger.Error("Clock skew outside of acceptable bounds")
return hm.authorizationError(r)
}
// Set session state on context, we will need it later
switch hm.Spec.BaseIdentityProvidedBy {
case apidef.HMACKey, apidef.UnsetAuth:
session.KeyID = fieldValues.KeyID
ctxSetSession(r, &session, false, hm.Gw.GetConfig().HashKeys)
hm.setContextVars(r, fieldValues.KeyID)
}
// Everything seems in order let the request through
return nil, http.StatusOK
}
Cognitive complexity: 64, Cyclomatic complexity: 28
func (*HeaderInjector) Base
func (h *HeaderInjector) Base() *BaseTykResponseHandler {
return &h.BaseTykResponseHandler
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*HeaderInjector) Enabled
func (h *HeaderInjector) Enabled() bool {
for _, version := range h.Spec.VersionData.Versions {
if version.GlobalResponseHeadersEnabled() {
return true
}
if version.HasEndpointResHeader() {
return true
}
}
return false
}
Cognitive complexity: 7, Cyclomatic complexity: 4
func (*HeaderInjector) HandleError
func (h *HeaderInjector) HandleError(rw http.ResponseWriter, req *http.Request) {
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*HeaderInjector) HandleResponse
func (h *HeaderInjector) HandleResponse(rw http.ResponseWriter, res *http.Response, req *http.Request, ses *user.SessionState) error {
// TODO: This should only target specific paths
ignoreCanonical := h.Gw.GetConfig().IgnoreCanonicalMIMEHeaderKey
vInfo, _ := h.Spec.Version(req)
versionPaths := h.Spec.RxPaths[vInfo.Name]
found, meta := h.Spec.CheckSpecMatchesStatus(req, versionPaths, HeaderInjectedResponse)
if found {
hmeta := meta.(*apidef.HeaderInjectionMeta)
for _, dKey := range hmeta.DeleteHeaders {
res.Header.Del(dKey)
}
for nKey, nVal := range hmeta.AddHeaders {
setCustomHeader(res.Header, nKey, h.Gw.ReplaceTykVariables(req, nVal, false), ignoreCanonical)
}
}
// Manage global response header options with versionInfo
if !vInfo.GlobalResponseHeadersDisabled {
for _, key := range vInfo.GlobalResponseHeadersRemove {
log.Debug("Removing: ", key)
res.Header.Del(key)
}
for key, val := range vInfo.GlobalResponseHeaders {
log.Debug("Adding: ", key)
setCustomHeader(res.Header, key, h.Gw.ReplaceTykVariables(req, val, false), ignoreCanonical)
}
// Manage global response header options with response_processors
for _, n := range h.config.RemoveHeaders {
res.Header.Del(n)
}
for header, v := range h.config.AddHeaders {
setCustomHeader(res.Header, header, h.Gw.ReplaceTykVariables(req, v, false), ignoreCanonical)
}
}
return nil
}
Cognitive complexity: 22, Cyclomatic complexity: 9
func (*HeaderInjector) Init
func (h *HeaderInjector) Init(c interface{}, spec *APISpec) error {
h.Spec = spec
return mapstructure.Decode(c, &h.config)
}
Cognitive complexity: 1, Cyclomatic complexity: 1
func (*HeaderInjector) Name
func (*HeaderInjector) Name() string {
return "HeaderInjector"
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*HeaderTransform) Base
func (h *HeaderTransform) Base() *BaseTykResponseHandler {
return &h.BaseTykResponseHandler
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*HeaderTransform) HandleError
func (h *HeaderTransform) HandleError(rw http.ResponseWriter, req *http.Request) {
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*HeaderTransform) HandleResponse
func (h *HeaderTransform) HandleResponse(rw http.ResponseWriter,
res *http.Response, req *http.Request, ses *user.SessionState) error {
// Parse target_host parameter from configuration
target_url, err := url.Parse(h.config.RevProxyTransform.Target_host)
if err != nil {
return err
}
ignoreCanonical := h.Gw.GetConfig().IgnoreCanonicalMIMEHeaderKey
for _, name := range h.config.RevProxyTransform.Headers {
// check if header is present and its value is not empty
val := res.Header.Get(name)
if val == "" {
continue
}
// Replace scheme
val = strings.Replace(val, h.Spec.target.Scheme, target_url.Scheme, -1)
// Replace host
val = strings.Replace(val, h.Spec.target.Host, target_url.Host, -1)
// Transform path
if h.Spec.Proxy.StripListenPath {
if len(h.Spec.target.Path) != 0 {
val = strings.Replace(val, h.Spec.target.Path,
h.Spec.Proxy.ListenPath, -1)
} else {
val = strings.Replace(val, req.URL.Path,
h.Spec.Proxy.ListenPath+req.URL.Path, -1)
}
} else {
if len(h.Spec.target.Path) != 0 {
val = strings.Replace(val, h.Spec.target.Path, "/", -1)
}
}
setCustomHeader(res.Header, name, val, ignoreCanonical)
}
return nil
}
Cognitive complexity: 17, Cyclomatic complexity: 7
func (*HeaderTransform) Init
func (h *HeaderTransform) Init(c interface{}, spec *APISpec) error {
if err := mapstructure.Decode(c, &h.config); err != nil {
return err
}
h.Spec = spec
return nil
}
Cognitive complexity: 3, Cyclomatic complexity: 2
func (*HeaderTransform) Name
func (h *HeaderTransform) Name() string {
return "HeaderTransform"
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*HostCheckerManager) AmIPolling
func (hc *HostCheckerManager) AmIPolling() bool {
if hc.store == nil {
log.WithFields(logrus.Fields{
"prefix": "host-check-mgr",
}).Error("No storage instance set for uptime tests! Disabling poller...")
return false
}
pollerCacheKey := PollerCacheKey
if hc.Gw.GetConfig().UptimeTests.PollerGroup != "" {
pollerCacheKey = pollerCacheKey + "." + hc.Gw.GetConfig().UptimeTests.PollerGroup
}
activeInstance, err := hc.store.GetKey(pollerCacheKey)
if err != nil {
log.WithFields(logrus.Fields{
"prefix": "host-check-mgr",
}).Debug("No Primary instance found, assuming control")
err := hc.store.SetKey(pollerCacheKey, hc.Id, 15)
if err != nil {
log.WithError(err).Error("cannot set key in pollerCacheKey")
}
return true
}
if activeInstance == hc.Id {
log.WithFields(logrus.Fields{
"prefix": "host-check-mgr",
}).Debug("Primary instance set, I am master")
err := hc.store.SetKey(pollerCacheKey, hc.Id, 15) // Reset TTL
if err != nil {
log.WithError(err).Error("could not reset TTL in polled cacheKey")
}
return true
}
log.WithFields(logrus.Fields{
"prefix": "host-check-mgr",
}).Debug("Active Instance is: ", activeInstance)
log.WithFields(logrus.Fields{
"prefix": "host-check-mgr",
}).Debug("--- I am: ", hc.Id)
return false
}
Cognitive complexity: 17, Cyclomatic complexity: 7
func (*HostCheckerManager) CheckActivePollerLoop
CheckActivePollerLoop manages the state of the HostCheckerManager UptimeTest polling loop, this will start the checking loop if it hasnt been started yet.
The check happens in a 10 seconds interval.
func (hc *HostCheckerManager) CheckActivePollerLoop(ctx context.Context) {
hc.checkPollerLoop(ctx)
tick := time.NewTicker(10 * time.Second)
defer func() {
tick.Stop()
log.WithFields(logrus.Fields{
"prefix": "host-check-mgr",
}).Debug("Stopping uptime tests")
}()
for {
select {
case <-ctx.Done():
return
case <-tick.C:
hc.checkPollerLoop(ctx)
}
}
}
Cognitive complexity: 7, Cyclomatic complexity: 4
func (*HostCheckerManager) DoServiceDiscoveryListUpdateForID
func (hc *HostCheckerManager) DoServiceDiscoveryListUpdateForID(apiID string) {
log.WithFields(logrus.Fields{
"prefix": "host-check-mgr",
}).Debug("[HOST CHECKER MANAGER] Getting data from service")
hostData, err := hc.ListFromService(apiID)
if err != nil {
return
}
log.WithFields(logrus.Fields{
"prefix": "host-check-mgr",
}).Debug("[HOST CHECKER MANAGER] Data was: \n", hostData)
log.WithFields(logrus.Fields{
"prefix": "host-check-mgr",
}).Info("[HOST CHECKER MANAGER] Refreshing uptime tests from service for API: ", apiID)
hc.UpdateTrackingListByAPIID(hostData, apiID)
}
Cognitive complexity: 5, Cyclomatic complexity: 2
func (*HostCheckerManager) GenerateCheckerId
func (hc *HostCheckerManager) GenerateCheckerId() {
hc.Id = uuid.New()
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*HostCheckerManager) HostDown
func (hc *HostCheckerManager) HostDown(urlStr string) bool {
u, err := url.Parse(urlStr)
if err != nil {
log.WithFields(logrus.Fields{
"prefix": "host-check-mgr",
}).Error(err)
}
log.WithFields(logrus.Fields{
"prefix": "host-check-mgr",
}).Debug("Key is: ", PoolerHostSentinelKeyPrefix+u.Host)
key := PoolerHostSentinelKeyPrefix + u.Host
// If the node doesn't perform any uptime checks, query the storage:
if hc.store != nil && !hc.pollerStarted {
v, _ := hc.store.GetKey(key)
return v == "1"
}
_, ok := hc.unhealthyHostList.Load(key)
// Found a key, the host is down
return ok
}
Cognitive complexity: 6, Cyclomatic complexity: 4
func (*HostCheckerManager) Init
func (hc *HostCheckerManager) Init(store storage.Handler) {
hc.store = store
hc.unhealthyHostList = new(sync.Map)
hc.resetsInitiated = make(map[string]bool)
// Generate a new ID for ourselves
hc.GenerateCheckerId()
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*HostCheckerManager) ListFromService
func (hc *HostCheckerManager) ListFromService(apiID string) ([]HostData, error) {
spec := hc.Gw.getApiSpec(apiID)
if spec == nil {
return nil, errors.New("API ID not found in register")
}
sd := ServiceDiscovery{}
sd.Init(&spec.UptimeTests.Config.ServiceDiscovery)
data, err := sd.Target(spec.UptimeTests.Config.ServiceDiscovery.QueryEndpoint)
if err != nil {
log.WithFields(logrus.Fields{
"prefix": "host-check-mgr",
}).Error("[HOST CHECKER MANAGER] Failed to retrieve host list: ", err)
return nil, err
}
// The returned data is a string, so lets unmarshal it:
checkTargets := make([]apidef.HostCheckObject, 0)
data0, _ := data.GetIndex(0)
if err := json.Unmarshal([]byte(data0), &checkTargets); err != nil {
log.WithFields(logrus.Fields{
"prefix": "host-check-mgr",
}).Error("[HOST CHECKER MANAGER] Decoder failed: ", err)
return nil, err
}
hostData := make([]HostData, len(checkTargets))
for i, target := range checkTargets {
newHostDoc, err := hc.Gw.GlobalHostChecker.PrepareTrackingHost(target, spec.APIID)
if err != nil {
log.WithFields(logrus.Fields{
"prefix": "host-check-mgr",
}).Error("[HOST CHECKER MANAGER] failed to convert to HostData", err)
} else {
hostData[i] = newHostDoc
}
}
return hostData, nil
}
Cognitive complexity: 17, Cyclomatic complexity: 6
func (*HostCheckerManager) OnHostBackUp
func (hc *HostCheckerManager) OnHostBackUp(ctx context.Context, report HostHealthReport) {
key := hc.getHostKey(report)
log.WithFields(logrus.Fields{
"prefix": "host-check-mgr",
}).Debug("Delete key: ", key)
hc.store.DeleteKey(key)
hc.unhealthyHostList.Delete(key)
spec := hc.Gw.getApiSpec(report.MetaData[UnHealthyHostMetaDataAPIKey])
if spec == nil {
log.WithFields(logrus.Fields{
"prefix": "host-check-mgr",
}).Warning("[HOST CHECKER MANAGER] Event can't fire for API that doesn't exist")
return
}
spec.FireEvent(EventHOSTUP, EventHostStatusMeta{
EventMetaDefault: EventMetaDefault{Message: "Uptime test succeeded"},
HostInfo: report,
})
log.WithFields(logrus.Fields{
"prefix": "host-check-mgr",
}).Warning("[HOST CHECKER MANAGER] Host is UP: ", report.CheckURL)
}
Cognitive complexity: 7, Cyclomatic complexity: 2
func (*HostCheckerManager) OnHostDown
func (hc *HostCheckerManager) OnHostDown(ctx context.Context, report HostHealthReport) {
key := hc.getHostKey(report)
log.WithFields(logrus.Fields{
"prefix": "host-check-mgr",
}).Debug("Update key: ", key)
err := hc.store.SetKey(key, "1", int64(hc.checker.checkTimeout*hc.checker.sampleTriggerLimit))
if err != nil {
log.WithError(err).Error("Host-Checker could not save key")
}
hc.unhealthyHostList.Store(key, 1)
spec := hc.Gw.getApiSpec(report.MetaData[UnHealthyHostMetaDataAPIKey])
if spec == nil {
log.WithFields(logrus.Fields{
"prefix": "host-check-mgr",
}).Warning("[HOST CHECKER MANAGER] Event can't fire for API that doesn't exist")
return
}
spec.FireEvent(EventHOSTDOWN, EventHostStatusMeta{
EventMetaDefault: EventMetaDefault{Message: "Uptime test failed"},
HostInfo: report,
})
log.WithFields(logrus.Fields{
"prefix": "host-check-mgr",
}).Warning("[HOST CHECKER MANAGER] Host is DOWN: ", report.CheckURL)
if spec.UptimeTests.Config.ServiceDiscovery.UseDiscoveryService {
apiID := spec.APIID
// only do this once
_, initiated := hc.resetsInitiated[apiID]
if !initiated {
hc.resetsInitiated[apiID] = true
// Lets re-check the uptime tests after x seconds
go func() {
log.WithFields(logrus.Fields{
"prefix": "host-check-mgr",
}).Printf("[HOST CHECKER MANAGER] Resetting test host list in %v seconds for API: %v", spec.UptimeTests.Config.RecheckWait, apiID)
time.Sleep(time.Duration(spec.UptimeTests.Config.RecheckWait) * time.Second)
hc.DoServiceDiscoveryListUpdateForID(apiID)
delete(hc.resetsInitiated, apiID)
}()
}
}
}
Cognitive complexity: 15, Cyclomatic complexity: 5
func (*HostCheckerManager) OnHostReport
func (hc *HostCheckerManager) OnHostReport(ctx context.Context, report HostHealthReport) {
if hc.Gw.GetConfig().UptimeTests.Config.EnableUptimeAnalytics {
go hc.RecordUptimeAnalytics(report)
}
}
Cognitive complexity: 2, Cyclomatic complexity: 2
func (*HostCheckerManager) PrepareTrackingHost
func (hc *HostCheckerManager) PrepareTrackingHost(checkObject apidef.HostCheckObject, apiID string) (HostData, error) {
// Build the check URL:
var hostData HostData
u, err := url.Parse(checkObject.CheckURL)
if err != nil {
log.WithFields(logrus.Fields{
"prefix": "host-check-mgr",
}).Error(err)
return hostData, err
}
var bodyData string
var bodyByteArr []byte
if len(checkObject.Body) > 0 {
bodyByteArr, err = base64.StdEncoding.DecodeString(checkObject.Body)
if err != nil {
log.WithFields(logrus.Fields{
"prefix": "host-check-mgr",
}).Error("Failed to load blob data: ", err)
return hostData, err
}
bodyData = string(bodyByteArr)
}
hostData = HostData{
CheckURL: checkObject.CheckURL,
MetaData: map[string]string{
UnHealthyHostMetaDataTargetKey: checkObject.CheckURL,
UnHealthyHostMetaDataAPIKey: apiID,
UnHealthyHostMetaDataHostKey: u.Host,
},
Method: checkObject.Method,
Protocol: checkObject.Protocol,
Timeout: checkObject.Timeout,
EnableProxyProtocol: checkObject.EnableProxyProtocol,
Commands: checkObject.Commands,
Headers: checkObject.Headers,
Body: bodyData,
}
return hostData, nil
}
Cognitive complexity: 10, Cyclomatic complexity: 4
func (*HostCheckerManager) RecordUptimeAnalytics
RecordHit will store an AnalyticsRecord in Redis
func (hc *HostCheckerManager) RecordUptimeAnalytics(report HostHealthReport) error {
// If we are obfuscating API Keys, store the hashed representation (config check handled in hashing function)
spec := hc.Gw.getApiSpec(report.MetaData[UnHealthyHostMetaDataAPIKey])
orgID := ""
if spec != nil {
orgID = spec.OrgID
}
t := time.Now()
var serverError bool
if report.ResponseCode > http.StatusOK {
serverError = true
}
newAnalyticsRecord := UptimeReportData{
URL: report.CheckURL,
RequestTime: int64(report.Latency),
ResponseCode: report.ResponseCode,
TCPError: report.IsTCPError,
ServerError: serverError,
Day: t.Day(),
Month: t.Month(),
Year: t.Year(),
Hour: t.Hour(),
Minute: t.Minute(),
TimeStamp: t,
APIID: report.MetaData[UnHealthyHostMetaDataAPIKey],
OrgID: orgID,
}
// For anlytics purposes, we need a code
if report.IsTCPError {
newAnalyticsRecord.ResponseCode = 521
}
newAnalyticsRecord.SetExpiry(spec.UptimeTests.Config.ExpireUptimeAnalyticsAfter)
encoded, err := msgpack.Marshal(newAnalyticsRecord)
if err != nil {
log.WithFields(logrus.Fields{
"prefix": "host-check-mgr",
}).Error("Error encoding uptime data:", err)
return err
}
log.WithFields(logrus.Fields{
"prefix": "host-check-mgr",
}).Debug("Recording uptime stat")
hc.store.AppendToSet(UptimeAnalytics_KEYNAME, string(encoded))
return nil
}
Cognitive complexity: 11, Cyclomatic complexity: 5
func (*HostCheckerManager) Start
func (hc *HostCheckerManager) Start(ctx context.Context) {
// Start loop to check if we are active instance
if hc != nil {
go hc.CheckActivePollerLoop(ctx)
}
}
Cognitive complexity: 2, Cyclomatic complexity: 2
func (*HostCheckerManager) StartPoller
func (hc *HostCheckerManager) StartPoller(ctx context.Context) {
log.WithFields(logrus.Fields{
"prefix": "host-check-mgr",
}).Debug("---> Initialising checker")
// If we are restarting, we want to retain the host list
hc.checkerMu.Lock()
if hc.checker == nil {
hc.checker = &HostUptimeChecker{Gw: hc.Gw}
}
hc.checker.Init(
hc.Gw.GetConfig().UptimeTests.Config.CheckerPoolSize,
hc.Gw.GetConfig().UptimeTests.Config.FailureTriggerSampleSize,
hc.Gw.GetConfig().UptimeTests.Config.TimeWait,
hc.currentHostList,
HostCheckCallBacks{
Up: hc.OnHostBackUp,
Fail: hc.OnHostDown,
Ping: hc.OnHostReport,
},
)
// Start the check loop
log.WithFields(logrus.Fields{
"prefix": "host-check-mgr",
}).Debug("---> Starting checker")
hc.checker.Start(ctx)
log.WithFields(logrus.Fields{
"prefix": "host-check-mgr",
}).Debug("---> Checker started.")
hc.checkerMu.Unlock()
}
Cognitive complexity: 7, Cyclomatic complexity: 2
func (*HostCheckerManager) StopPoller
func (hc *HostCheckerManager) StopPoller() {
if hc == nil {
return
}
hc.checkerMu.Lock()
hc.checker.Stop()
hc.checkerMu.Unlock()
}
Cognitive complexity: 2, Cyclomatic complexity: 2
func (*HostCheckerManager) UpdateTrackingList
func (hc *HostCheckerManager) UpdateTrackingList(hd []HostData) {
log.WithFields(logrus.Fields{
"prefix": "host-check-mgr",
}).Debug("--- Setting tracking list up")
newHostList := make(map[string]HostData)
for _, host := range hd {
newHostList[host.CheckURL] = host
}
hc.checkerMu.Lock()
hc.currentHostList = newHostList
if hc.checker != nil {
log.WithFields(logrus.Fields{
"prefix": "host-check-mgr",
}).Debug("Reset initiated")
hc.checker.ResetList(newHostList)
}
hc.checkerMu.Unlock()
}
Cognitive complexity: 7, Cyclomatic complexity: 3
func (*HostCheckerManager) UpdateTrackingListByAPIID
func (hc *HostCheckerManager) UpdateTrackingListByAPIID(hd []HostData, apiId string) {
log.WithFields(logrus.Fields{
"prefix": "host-check-mgr",
}).Debug("--- Setting tracking list up for ID: ", apiId)
newHostList := make(map[string]HostData)
hc.checkerMu.Lock()
for _, existingHost := range hc.currentHostList {
if existingHost.MetaData[UnHealthyHostMetaDataAPIKey] != apiId {
// Add the old check list that excludes this API
newHostList[existingHost.CheckURL] = existingHost
}
}
// Add the new list for this APIID:
for _, host := range hd {
newHostList[host.CheckURL] = host
}
hc.currentHostList = newHostList
if hc.checker != nil {
log.WithFields(logrus.Fields{
"prefix": "host-check-mgr",
}).Debug("Reset initiated")
hc.checker.ResetList(newHostList)
}
hc.checkerMu.Unlock()
log.WithFields(logrus.Fields{
"prefix": "host-check-mgr",
}).Info("--- Queued tracking list update for API: ", apiId)
}
Cognitive complexity: 13, Cyclomatic complexity: 5
func (*HostUptimeChecker) CheckHost
func (h *HostUptimeChecker) CheckHost(toCheck HostData) {
log.Debug("[HOST CHECKER] Checking: ", toCheck.CheckURL)
t1 := time.Now()
report := HostHealthReport{
HostData: toCheck,
}
switch toCheck.Protocol {
case "tcp", "tls":
host := toCheck.CheckURL
base := toCheck.Protocol + "://"
if !strings.HasPrefix(host, base) {
host = base + host
}
u, err := url.Parse(host)
if err != nil {
log.Error("Could not parse host: ", err)
return
}
var ls net.Conn
var d net.Dialer
d.Timeout = toCheck.Timeout
if toCheck.Protocol == "tls" {
ls, err = tls.DialWithDialer(&d, "tls", u.Host, nil)
} else {
ls, err = d.Dial("tcp", u.Host)
}
if err != nil {
log.Error("Could not connect to host: ", err)
report.IsTCPError = true
break
}
if toCheck.EnableProxyProtocol {
log.Debug("using proxy protocol")
ls = proxyproto.NewConn(ls)
}
defer ls.Close()
for _, cmd := range toCheck.Commands {
switch cmd.Name {
case "send":
log.Debugf("%s: sending %s", host, cmd.Message)
_, err = ls.Write([]byte(cmd.Message))
if err != nil {
log.Errorf("Failed to send %s :%v", cmd.Message, err)
report.IsTCPError = true
break
}
case "expect":
buf := make([]byte, len(cmd.Message))
_, err = ls.Read(buf)
if err != nil {
log.Errorf("Failed to read %s :%v", cmd.Message, err)
report.IsTCPError = true
break
}
g := string(buf)
if g != cmd.Message {
log.Errorf("Failed expectation expected %s got %s", cmd.Message, g)
report.IsTCPError = true
break
}
log.Debugf("%s: received %s", host, cmd.Message)
}
}
report.ResponseCode = http.StatusOK
default:
useMethod := toCheck.Method
if toCheck.Method == "" {
useMethod = http.MethodGet
}
req, err := http.NewRequest(useMethod, toCheck.CheckURL, strings.NewReader(toCheck.Body))
if err != nil {
log.Error("Could not create request: ", err)
return
}
ignoreCanonical := h.Gw.GetConfig().IgnoreCanonicalMIMEHeaderKey
for headerName, headerValue := range toCheck.Headers {
setCustomHeader(req.Header, headerName, headerValue, ignoreCanonical)
}
req.Header.Set("Connection", "close")
h.Gw.HostCheckerClient.Transport = &http.Transport{
TLSClientConfig: &tls.Config{
InsecureSkipVerify: h.Gw.GetConfig().ProxySSLInsecureSkipVerify,
MaxVersion: h.Gw.GetConfig().ProxySSLMaxVersion,
},
}
if toCheck.Timeout != 0 {
h.Gw.HostCheckerClient.Timeout = toCheck.Timeout
}
response, err := h.Gw.HostCheckerClient.Do(req)
if err != nil {
report.IsTCPError = true
break
}
response.Body.Close()
report.ResponseCode = response.StatusCode
}
millisec := DurationToMillisecond(time.Since(t1))
report.Latency = millisec
if report.IsTCPError {
h.errorChan <- report
return
}
if report.ResponseCode != http.StatusOK {
h.errorChan <- report
return
}
// host is healthy, report it
h.okChan <- report
}
Cognitive complexity: 14, Cyclomatic complexity: 7
func (*HostUptimeChecker) HostCheckLoop
func (h *HostUptimeChecker) HostCheckLoop(ctx context.Context) {
defer func() {
log.Info("[HOST CHECKER] Checker stopped")
}()
if h.Gw.isRunningTests() {
for {
select {
case <-ctx.Done():
return
case <-h.Gw.HostCheckTicker:
h.execCheck()
}
}
} else {
tick := time.NewTicker(h.getStaggeredTime())
defer tick.Stop()
for {
select {
case <-ctx.Done():
return
case <-tick.C:
h.execCheck()
}
}
}
}
Cognitive complexity: 15, Cyclomatic complexity: 8
func (*HostUptimeChecker) HostReporter
func (h *HostUptimeChecker) HostReporter(ctx context.Context) {
for {
select {
case <-ctx.Done():
h.Stop()
log.Debug("[HOST CHECKER] Received cancel signal")
return
case okHost := <-h.okChan:
// check if the the host url is in the sample map
if hostSample, found := h.samples.Load(okHost.CheckURL); found {
sample := hostSample.(HostSample)
//if it reached the h.sampleTriggerLimit, we're going to start decreasing the count value
if sample.reachedLimit {
newCount := sample.count - 1
if newCount <= 0 {
//if the count-1 is equals to zero, it means that the host is fully up.
h.samples.Delete(okHost.CheckURL)
log.Warning("[HOST CHECKER] [HOST UP]: ", okHost.CheckURL)
go h.cb.Up(ctx, okHost)
} else {
//in another case, we are one step closer. We just update the count number
sample.count = newCount
log.Warning("[HOST CHECKER] [HOST UP BUT NOT REACHED LIMIT]: ", okHost.CheckURL)
h.samples.Store(okHost.CheckURL, sample)
}
}
}
if h.cb.Ping != nil {
go h.cb.Ping(ctx, okHost)
}
case failedHost := <-h.errorChan:
sample := HostSample{
count: 1,
}
//If a host fails, we check if it has failed already
if hostSample, found := h.samples.Load(failedHost.CheckURL); found {
sample = hostSample.(HostSample)
// we add THIS failure to the count
sample.count = sample.count + 1
}
if sample.count >= h.sampleTriggerLimit {
// if it reached the h.sampleTriggerLimit, it means the host is down for us. We update the reachedLimit flag and store it in the sample map
log.Warning("[HOST CHECKER] [HOST DOWN]: ", failedHost.CheckURL)
//if this is the first time it reached the h.sampleTriggerLimit, the value of the reachedLimit flag is stored with the new count
if sample.reachedLimit == false {
sample.reachedLimit = true
h.samples.Store(failedHost.CheckURL, sample)
}
//we call the failureCallback to keep the redis key and the host checker manager updated
go h.cb.Fail(ctx, failedHost)
} else {
//if it failed but not reached the h.sampleTriggerLimit yet, we just add the counter to the map.
log.Warning("[HOST CHECKER] [HOST DOWN BUT NOT REACHED LIMIT]: ", failedHost.CheckURL)
h.samples.Store(failedHost.CheckURL, sample)
}
if h.cb.Ping != nil {
go h.cb.Ping(ctx, failedHost)
}
}
}
}
Cognitive complexity: 27, Cyclomatic complexity: 13
func (*HostUptimeChecker) Init
func (h *HostUptimeChecker) Init(workers, triggerLimit, timeout int, hostList map[string]HostData, cb HostCheckCallBacks) {
h.samples = new(sync.Map)
h.errorChan = make(chan HostHealthReport)
h.okChan = make(chan HostHealthReport)
h.HostList = hostList
h.unHealthyList = make(map[string]bool)
h.cb = cb
h.workerPoolSize = workers
if workers == 0 {
h.workerPoolSize = defaultWorkerPoolSize
}
h.sampleTriggerLimit = triggerLimit
if triggerLimit == 0 {
h.sampleTriggerLimit = defaultSampletTriggerLimit
}
h.checkTimeout = timeout
if timeout == 0 {
h.checkTimeout = defaultTimeout
}
log.Debug("[HOST CHECKER] Config:TriggerLimit: ", h.sampleTriggerLimit)
log.Debug("[HOST CHECKER] Config:Timeout: ~", h.checkTimeout)
log.Debug("[HOST CHECKER] Config:WorkerPool: ", h.workerPoolSize)
h.pool = tunny.NewFunc(h.workerPoolSize, func(hostData interface{}) interface{} {
input, _ := hostData.(HostData)
h.CheckHost(input)
return nil
})
log.Debug("[HOST CHECKER] Init complete")
}
Cognitive complexity: 9, Cyclomatic complexity: 4
func (*HostUptimeChecker) ResetList
func (h *HostUptimeChecker) ResetList(hostList map[string]HostData) {
h.resetListMu.Lock()
h.doResetList = true
h.newList = hostList
h.resetListMu.Unlock()
log.Debug("[HOST CHECKER] Checker reset queued!")
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*HostUptimeChecker) Start
func (h *HostUptimeChecker) Start(ctx context.Context) {
// Start the loop that checks for bum hosts
log.Debug("[HOST CHECKER] Starting...")
go h.HostCheckLoop(ctx)
log.Debug("[HOST CHECKER] Check loop started...")
go h.HostReporter(ctx)
log.Debug("[HOST CHECKER] Host reporter started...")
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*HostUptimeChecker) Stop
func (h *HostUptimeChecker) Stop() {
if h == nil {
return
}
was := atomic.SwapInt32(&h.isClosed, CLOSED)
if was == OPEN {
eraseSyncMap(h.samples)
log.Info("[HOST CHECKER] Stopping poller")
if h.pool != nil && h.pool.GetSize() > 0 {
h.pool.Close()
}
}
}
Cognitive complexity: 6, Cyclomatic complexity: 5
func (*IPBlackListMiddleware) EnabledForSpec
func (i *IPBlackListMiddleware) EnabledForSpec() bool {
enabled := !i.Spec.APIDefinition.IPAccessControlDisabled || i.Spec.APIDefinition.EnableIpBlacklisting
return enabled && len(i.Spec.APIDefinition.BlacklistedIPs) > 0
}
Cognitive complexity: 0, Cyclomatic complexity: 3
func (*IPBlackListMiddleware) Name
func (i *IPBlackListMiddleware) Name() string {
return "IPBlackListMiddleware"
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*IPBlackListMiddleware) ProcessRequest
ProcessRequest will run any checks on the request on the way through the system, return an error to have the chain fail
func (i *IPBlackListMiddleware) ProcessRequest(w http.ResponseWriter, r *http.Request, _ interface{}) (error, int) {
remoteIP := net.ParseIP(request.RealIP(r))
// Enabled, check incoming IP address
for _, ip := range i.Spec.BlacklistedIPs {
// Might be CIDR, try this one first then fallback to IP parsing later
blockedIP, blockedNet, err := net.ParseCIDR(ip)
if err != nil {
blockedIP = net.ParseIP(ip)
}
// Check CIDR if possible
if blockedNet != nil && blockedNet.Contains(remoteIP) {
return i.handleError(r, remoteIP.String())
}
// We parse the IP to manage IPv4 and IPv6 easily
if blockedIP.Equal(remoteIP) {
return i.handleError(r, remoteIP.String())
}
}
return nil, http.StatusOK
}
Cognitive complexity: 10, Cyclomatic complexity: 6
func (*IPWhiteListMiddleware) EnabledForSpec
func (i *IPWhiteListMiddleware) EnabledForSpec() bool {
enabled := !i.Spec.APIDefinition.IPAccessControlDisabled || i.Spec.APIDefinition.EnableIpWhiteListing
return enabled && len(i.Spec.APIDefinition.AllowedIPs) > 0
}
Cognitive complexity: 0, Cyclomatic complexity: 3
func (*IPWhiteListMiddleware) Name
func (i *IPWhiteListMiddleware) Name() string {
return "IPWhiteListMiddleware"
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*IPWhiteListMiddleware) ProcessRequest
ProcessRequest will run any checks on the request on the way through the system, return an error to have the chain fail
func (i *IPWhiteListMiddleware) ProcessRequest(w http.ResponseWriter, r *http.Request, _ interface{}) (error, int) {
remoteIP := net.ParseIP(request.RealIP(r))
// Enabled, check incoming IP address
for _, ip := range i.Spec.AllowedIPs {
// Might be CIDR, try this one first then fallback to IP parsing later
allowedIP, allowedNet, err := net.ParseCIDR(ip)
if err != nil {
allowedIP = net.ParseIP(ip)
}
// Check CIDR if possible
if allowedNet != nil && allowedNet.Contains(remoteIP) {
// matched, pass through
return nil, http.StatusOK
}
// We parse the IP to manage IPv4 and IPv6 easily
if allowedIP.Equal(remoteIP) {
// matched, pass through
return nil, http.StatusOK
}
}
// Fire Authfailed Event
AuthFailed(i, r, remoteIP.String())
// Report in health check
reportHealthValue(i.Spec, KeyFailure, "-1")
// Not matched, fail
return errors.New("access from this IP has been disallowed"), http.StatusForbidden
}
Cognitive complexity: 10, Cyclomatic complexity: 6
func (*JSVM) DeInit
func (j *JSVM) DeInit() {
j.Spec = nil
j.Log = nil
j.RawLog = nil
j.Gw = nil
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*JSVM) Init
Init creates the JSVM with the core library and sets up a default timeout.
func (j *JSVM) Init(spec *APISpec, logger *logrus.Entry, gw *Gateway) {
vm := otto.New()
j.Gw = gw
logger = logger.WithField("prefix", "jsvm")
// Init TykJS namespace, constructors etc.
if _, err := vm.Run(coreJS); err != nil {
logger.WithError(err).Error("Could not load TykJS")
return
}
// Load user's TykJS on top, if any
if path := gw.GetConfig().TykJSPath; path != "" {
f, err := os.Open(path)
if err == nil {
_, err = vm.Run(f)
f.Close()
if err != nil {
logger.WithError(err).Error("Could not load user's TykJS")
}
}
}
j.VM = vm
j.Spec = spec
// Add environment API
j.LoadTykJSApi()
if jsvmTimeout := gw.GetConfig().JSVMTimeout; jsvmTimeout <= 0 {
j.Timeout = time.Duration(defaultJSVMTimeout) * time.Second
logger.Debugf("Default JSVM timeout used: %v", j.Timeout)
} else {
j.Timeout = time.Duration(jsvmTimeout) * time.Second
logger.Debugf("Custom JSVM timeout: %v", j.Timeout)
}
j.Log = logger // use the global logger by default
j.RawLog = rawLog
}
Cognitive complexity: 12, Cyclomatic complexity: 6
func (*JSVM) LoadJSPaths
LoadJSPaths will load JS classes and functionality in to the VM by file
func (j *JSVM) LoadJSPaths(paths []string, prefix string) {
for _, mwPath := range paths {
if prefix != "" {
mwPath = filepath.Join(prefix, mwPath)
}
extension := filepath.Ext(mwPath)
if !strings.Contains(extension, ".js") {
j.Log.Errorf("Unsupported extension '%s' (%s)", extension, mwPath)
continue
}
j.Log.Info("Loading JS File: ", mwPath)
f, err := os.Open(mwPath)
if err != nil {
j.Log.WithError(err).Error("Failed to open JS middleware file")
continue
}
if _, err := j.VM.Run(f); err != nil {
j.Log.WithError(err).Error("Failed to load JS middleware")
}
f.Close()
}
}
Cognitive complexity: 11, Cyclomatic complexity: 6
func (*JSVM) LoadTykJSApi
func (j *JSVM) LoadTykJSApi() {
// Enable a log
j.VM.Set("log", func(call otto.FunctionCall) otto.Value {
j.Log.WithFields(logrus.Fields{
"type": "log-msg",
}).Info(call.Argument(0).String())
return otto.Value{}
})
j.VM.Set("rawlog", func(call otto.FunctionCall) otto.Value {
j.RawLog.Print(call.Argument(0).String() + "\n")
return otto.Value{}
})
// these two needed for non-utf8 bodies
j.VM.Set("b64dec", func(call otto.FunctionCall) otto.Value {
in := call.Argument(0).String()
out, err := base64.StdEncoding.DecodeString(in)
// Fallback to RawStdEncoding:
if err != nil {
out, err = base64.RawStdEncoding.DecodeString(in)
if err != nil {
j.Log.WithError(err).Error("Failed to base64 decode")
return otto.Value{}
}
}
returnVal, err := j.VM.ToValue(string(out))
if err != nil {
j.Log.WithError(err).Error("Failed to base64 decode")
return otto.Value{}
}
return returnVal
})
j.VM.Set("b64enc", func(call otto.FunctionCall) otto.Value {
in := []byte(call.Argument(0).String())
out := base64.StdEncoding.EncodeToString(in)
returnVal, err := j.VM.ToValue(out)
if err != nil {
j.Log.WithError(err).Error("Failed to base64 encode")
return otto.Value{}
}
return returnVal
})
j.VM.Set("rawb64dec", func(call otto.FunctionCall) otto.Value {
in := call.Argument(0).String()
out, err := base64.RawStdEncoding.DecodeString(in)
if err != nil {
j.Log.WithError(err).Error("Failed to base64 decode")
return otto.Value{}
}
returnVal, err := j.VM.ToValue(string(out))
if err != nil {
j.Log.WithError(err).Error("Failed to base64 decode")
return otto.Value{}
}
return returnVal
})
j.VM.Set("rawb64enc", func(call otto.FunctionCall) otto.Value {
in := []byte(call.Argument(0).String())
out := base64.RawStdEncoding.EncodeToString(in)
returnVal, err := j.VM.ToValue(out)
if err != nil {
j.Log.WithError(err).Error("Failed to base64 encode")
return otto.Value{}
}
return returnVal
})
ignoreCanonical := j.Gw.GetConfig().IgnoreCanonicalMIMEHeaderKey
// Enable the creation of HTTP Requsts
j.VM.Set("TykMakeHttpRequest", func(call otto.FunctionCall) otto.Value {
jsonHRO := call.Argument(0).String()
if jsonHRO == "undefined" {
// Nope, return nothing
return otto.Value{}
}
hro := TykJSHttpRequest{}
if err := json.Unmarshal([]byte(jsonHRO), &hro); err != nil {
j.Log.WithError(err).Error("JSVM: Failed to deserialise HTTP Request object")
return otto.Value{}
}
// Make the request
domain := hro.Domain
data := url.Values{}
for k, v := range hro.FormData {
data.Set(k, v)
}
u, _ := url.ParseRequestURI(domain + hro.Resource)
urlStr := u.String() // "https://api.com/user/"
var d string
if hro.Body != "" {
d = hro.Body
} else if len(hro.FormData) > 0 {
d = data.Encode()
}
r, _ := http.NewRequest(hro.Method, urlStr, nil)
if d != "" {
r, _ = http.NewRequest(hro.Method, urlStr, strings.NewReader(d))
}
for k, v := range hro.Headers {
setCustomHeader(r.Header, k, v, ignoreCanonical)
}
r.Close = true
maxSSLVersion := j.Gw.GetConfig().ProxySSLMaxVersion
if j.Spec.Proxy.Transport.SSLMaxVersion > 0 {
maxSSLVersion = j.Spec.Proxy.Transport.SSLMaxVersion
}
tr := &http.Transport{TLSClientConfig: &tls.Config{
MaxVersion: maxSSLVersion,
}}
if cert := j.Gw.getUpstreamCertificate(r.Host, j.Spec); cert != nil {
tr.TLSClientConfig.Certificates = []tls.Certificate{*cert}
}
if j.Gw.GetConfig().ProxySSLInsecureSkipVerify {
tr.TLSClientConfig.InsecureSkipVerify = true
}
if j.Spec.Proxy.Transport.SSLInsecureSkipVerify {
tr.TLSClientConfig.InsecureSkipVerify = true
}
tr.DialTLS = j.Gw.customDialTLSCheck(j.Spec, tr.TLSClientConfig)
tr.Proxy = proxyFromAPI(j.Spec)
// using new Client each time should be ok, since we closing connection every time
client := &http.Client{Transport: tr}
resp, err := client.Do(r)
if err != nil {
j.Log.WithError(err).Error("Request failed")
return otto.Value{}
}
body, _ := ioutil.ReadAll(resp.Body)
bodyStr := string(body)
tykResp := TykJSHttpResponse{
Code: resp.StatusCode,
Body: bodyStr,
Headers: resp.Header,
CodeComp: resp.StatusCode,
BodyComp: bodyStr,
HeadersComp: resp.Header,
}
retAsStr, _ := json.Marshal(tykResp)
returnVal, err := j.VM.ToValue(string(retAsStr))
if err != nil {
j.Log.WithError(err).Error("Failed to encode return value")
return otto.Value{}
}
return returnVal
})
// Expose Setters and Getters in the REST API for a key:
j.VM.Set("TykGetKeyData", func(call otto.FunctionCall) otto.Value {
apiKey := call.Argument(0).String()
apiId := call.Argument(1).String()
obj, _ := j.Gw.handleGetDetail(apiKey, apiId, "", false)
bs, _ := json.Marshal(obj)
returnVal, err := j.VM.ToValue(string(bs))
if err != nil {
j.Log.WithError(err).Error("Failed to encode return value")
return otto.Value{}
}
return returnVal
})
j.VM.Set("TykSetKeyData", func(call otto.FunctionCall) otto.Value {
apiKey := call.Argument(0).String()
encoddedSession := call.Argument(1).String()
suppressReset := call.Argument(2).String()
newSession := user.SessionState{}
err := json.Unmarshal([]byte(encoddedSession), &newSession)
if err != nil {
j.Log.WithError(err).Error("Failed to decode the sesison data")
return otto.Value{}
}
j.Gw.doAddOrUpdate(apiKey, &newSession, suppressReset == "1", false)
return otto.Value{}
})
// Batch request method
unsafeBatchHandler := BatchRequestHandler{Gw: j.Gw}
j.VM.Set("TykBatchRequest", func(call otto.FunctionCall) otto.Value {
requestSet := call.Argument(0).String()
j.Log.Debug("Batch input is: ", requestSet)
bs, err := unsafeBatchHandler.ManualBatchRequest([]byte(requestSet))
if err != nil {
j.Log.WithError(err).Error("Batch request error")
return otto.Value{}
}
returnVal, err := j.VM.ToValue(string(bs))
if err != nil {
j.Log.WithError(err).Error("Failed to encode return value")
return otto.Value{}
}
return returnVal
})
j.VM.Run(`function TykJsResponse(response, session_meta) {
return JSON.stringify({Response: response, SessionMeta: session_meta})
}`)
}
Cognitive complexity: 89, Cyclomatic complexity: 25
func (*JSVMEventHandler) HandleEvent
HandleEvent will be fired when the event handler instance is found in an APISpec EventPaths object during a request chain
func (l *JSVMEventHandler) HandleEvent(em config.EventMessage) {
// JSON-encode the event data object
msgAsJSON, err := json.Marshal(em)
if err != nil {
log.Error("Failed to encode event data: ", err)
return
}
// Execute the method name with the JSON object
_, err = l.Gw.GlobalEventsJSVM.VM.Run(l.conf.MethodName + `.DoProcessEvent(` + string(msgAsJSON) + `,` + l.SpecJSON + `);`)
if err != nil {
log.WithError(err).Error("executing JSVM method")
}
}
Cognitive complexity: 4, Cyclomatic complexity: 3
func (*JSVMEventHandler) Init
Init initializes the JSVMEventHandler by setting the method name and global values required for the JavaScript VM.
func (l *JSVMEventHandler) Init(handlerConf any) error {
var err error
if err = l.conf.Scan(handlerConf); err != nil {
log.WithFields(logrus.Fields{
"prefix": "jsvm_event_handler",
}).Error("Problem getting configuration, skipping. ", err)
return err
}
if l.conf.Disabled {
log.WithFields(logrus.Fields{
"prefix": "jsvm_event_handler",
}).Infof("skipping disabled jsvm event handler with method name %s at path %s", l.conf.MethodName, l.conf.Path)
return ErrEventHandlerDisabled
}
// Set the VM globals
globalVals := JSVMContextGlobal{
APIID: l.Spec.APIID,
OrgID: l.Spec.OrgID,
}
gValAsJSON, err := json.Marshal(globalVals)
if err != nil {
log.Error("Failed to marshal globals! ", err)
}
l.SpecJSON = string(gValAsJSON)
return nil
}
Cognitive complexity: 9, Cyclomatic complexity: 4
func (*JWTMiddleware) EnabledForSpec
func (k *JWTMiddleware) EnabledForSpec() bool {
return k.Spec.EnableJWT
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*JWTMiddleware) Name
func (k *JWTMiddleware) Name() string {
return "JWTMiddleware"
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*JWTMiddleware) ProcessRequest
func (k *JWTMiddleware) ProcessRequest(w http.ResponseWriter, r *http.Request, _ interface{}) (error, int) {
if ctxGetRequestStatus(r) == StatusOkAndIgnore {
return nil, http.StatusOK
}
logger := k.Logger()
var tykId string
rawJWT, config := k.getAuthToken(k.getAuthType(), r)
if rawJWT == "" {
// No header value, fail
logger.Info("Attempted access with malformed header, no JWT auth header found.")
log.Debug("Looked in: ", config.AuthHeaderName)
log.Debug("Raw data was: ", rawJWT)
log.Debug("Headers are: ", r.Header)
k.reportLoginFailure(tykId, r)
return errors.New("Authorization field missing"), http.StatusBadRequest
}
// enable bearer token format
rawJWT = stripBearer(rawJWT)
// Use own validation logic, see below
parser := jwt.NewParser(jwt.WithoutClaimsValidation())
// Verify the token
token, err := parser.Parse(rawJWT, func(token *jwt.Token) (interface{}, error) {
// Don't forget to validate the alg is what you expect:
if err := assertSigningMethod(k.Spec.JWTSigningMethod, token); err != nil {
return nil, err
}
val, err := k.getSecretToVerifySignature(r, token)
if err != nil {
k.Logger().WithError(err).Error("Couldn't get token")
return nil, err
}
return parseJWTKey(k.Spec.JWTSigningMethod, val)
})
if err == nil && token.Valid {
if jwtErr := k.timeValidateJWTClaims(token.Claims.(jwt.MapClaims)); jwtErr != nil {
return errors.New("Key not authorized: " + jwtErr.Error()), http.StatusUnauthorized
}
// Token is valid - let's move on
// Are we mapping to a central JWT Secret?
hasJWTSource := k.Spec.JWTSource != ""
hasJwksURIs := len(k.Spec.JWTJwksURIs) > 0
if hasJWTSource || hasJwksURIs {
return k.processCentralisedJWT(r, token)
}
// No, let's try one-to-one mapping
return k.processOneToOneTokenMap(r, token)
}
logger.Info("Attempted JWT access with non-existent key.")
k.reportLoginFailure(tykId, r)
if err != nil {
logger.WithError(err).Error("JWT validation error")
errorDetails := strings.Split(err.Error(), ":")
if errorDetails[0] == UnexpectedSigningMethod {
return errors.New(MsgKeyNotAuthorizedUnexpectedSigningMethod), http.StatusForbidden
}
}
return errors.New("Key not authorized"), http.StatusForbidden
}
Cognitive complexity: 21, Cyclomatic complexity: 12
func (*KeyExpired) Name
func (k *KeyExpired) Name() string {
return "KeyExpired"
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*KeyExpired) ProcessRequest
ProcessRequest will run any checks on the request on the way through the system, return an error to have the chain fail
func (k *KeyExpired) ProcessRequest(w http.ResponseWriter, r *http.Request, _ interface{}) (error, int) {
if ctxGetRequestStatus(r) == StatusOkAndIgnore {
return nil, http.StatusOK
}
logger := k.Logger()
session := ctxGetSession(r)
if session == nil {
return errors.New("Session state is missing or unset! Please make sure that auth headers are properly applied"), http.StatusBadRequest
}
token := ctxGetAuthToken(r)
if session.IsInactive {
logger.Info("Attempted access from inactive key.")
// Fire a key expired event
k.FireEvent(EventKeyExpired, EventKeyFailureMeta{
EventMetaDefault: EventMetaDefault{Message: "Attempted access from inactive key.", OriginatingRequest: EncodeRequestToEvent(r)},
Path: r.URL.Path,
Origin: request.RealIP(r),
Key: token,
})
// Report in health check
reportHealthValue(k.Spec, KeyFailure, "-1")
return errors.New("Key is inactive, please renew"), http.StatusForbidden
}
if !k.Spec.AuthManager.KeyExpired(session) {
return nil, http.StatusOK
}
logger.Info("Attempted access from expired key.")
k.FireEvent(EventKeyExpired, EventKeyFailureMeta{
EventMetaDefault: EventMetaDefault{Message: "Attempted access from expired key.", OriginatingRequest: EncodeRequestToEvent(r)},
Path: r.URL.Path,
Origin: request.RealIP(r),
Key: token,
})
// Report in health check
reportHealthValue(k.Spec, KeyFailure, "-1")
return errors.New("Key has expired, please renew"), http.StatusUnauthorized
}
Cognitive complexity: 13, Cyclomatic complexity: 5
func (*LDAPStorageHandler) Connect
func (l *LDAPStorageHandler) Connect() bool {
conn := ldap.NewLDAPConnection(l.LDAPServer, l.LDAPPort)
if err := conn.Connect(); err != nil {
log.Error("LDAP server connection failed: ", err)
return false
}
log.Info("LDAP: Connection established")
l.store = conn
return true
}
Cognitive complexity: 2, Cyclomatic complexity: 2
func (*LDAPStorageHandler) Decrement
func (l *LDAPStorageHandler) Decrement(keyName string) {
l.notifyReadOnly()
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*LDAPStorageHandler) DeleteAllKeys
func (r *LDAPStorageHandler) DeleteAllKeys() bool {
log.Warning("Not implementated")
return false
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*LDAPStorageHandler) DeleteKey
func (l *LDAPStorageHandler) DeleteKey(cn string) bool {
return l.notifyReadOnly()
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*LDAPStorageHandler) DeleteKeys
func (l *LDAPStorageHandler) DeleteKeys(keys []string) bool {
return l.notifyReadOnly()
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*LDAPStorageHandler) DeleteRawKey
func (l *LDAPStorageHandler) DeleteRawKey(cn string) bool {
return l.notifyReadOnly()
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*LDAPStorageHandler) DeleteRawKeys
func (l *LDAPStorageHandler) DeleteRawKeys([]string) bool { return l.notifyReadOnly() }
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*LDAPStorageHandler) GetExp
func (l *LDAPStorageHandler) GetExp(cn string) (int64, error) {
log.Warning("Not implementated")
return 0, nil
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*LDAPStorageHandler) GetKey
func (l *LDAPStorageHandler) GetKey(filter string) (string, error) {
log.Debug("Searching for filter: ", filter)
useFilter := strings.Replace(l.SearchString, "TYKKEYID", filter, 1)
log.Warning("Search filter is: ", useFilter)
search_request := ldap.NewSearchRequest(
l.BaseDN,
ldap.ScopeWholeSubtree, ldap.DerefAlways, 0, 0, false,
useFilter,
l.Attributes,
nil)
sr, err := l.store.Search(search_request)
if err != nil {
log.Debug("LDAP Key search failed: ", err)
return "", err
}
if len(sr.Entries) == 0 {
return "", nil
}
log.Debug("Found Key: ", sr.Entries[0])
entry := sr.Entries[0]
if entry.Attributes == nil {
log.Error("LDAP: No attributes found to check for session state. Failing")
return "", errors.New("Attributes for entry are empty")
}
for _, attr := range entry.Attributes {
if attr.Name == l.SessionAttributeName {
log.Debug("Found session data: ", attr.Values[0])
return attr.Values[0], nil
}
}
return "", nil
}
Cognitive complexity: 11, Cyclomatic complexity: 6
func (*LDAPStorageHandler) GetKeys
func (l *LDAPStorageHandler) GetKeys(filter string) []string {
log.Warning("Not implementated")
s := []string{}
return s
}
Cognitive complexity: 1, Cyclomatic complexity: 1
func (*LDAPStorageHandler) GetKeysAndValues
func (l *LDAPStorageHandler) GetKeysAndValues() map[string]string {
log.Warning("Not implementated")
s := map[string]string{}
return s
}
Cognitive complexity: 1, Cyclomatic complexity: 1
func (*LDAPStorageHandler) GetKeysAndValuesWithFilter
func (l *LDAPStorageHandler) GetKeysAndValuesWithFilter(filter string) map[string]string {
log.Warning("Not implementated")
s := map[string]string{}
return s
}
Cognitive complexity: 1, Cyclomatic complexity: 1
func (*LDAPStorageHandler) GetListRange
func (l *LDAPStorageHandler) GetListRange(keyName string, from, to int64) ([]string, error) {
log.Error("Not implemented")
return nil, nil
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*LDAPStorageHandler) GetMultiKey
func (r *LDAPStorageHandler) GetMultiKey(keyNames []string) ([]string, error) {
log.Warning("Not implementated")
return nil, nil
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*LDAPStorageHandler) GetRawKey
func (l *LDAPStorageHandler) GetRawKey(filter string) (string, error) {
log.Warning("Not implementated")
return "", nil
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*LDAPStorageHandler) GetRollingWindow
func (l *LDAPStorageHandler) GetRollingWindow(keyName string, per int64, pipeline bool) (int, []interface{}) {
log.Warning("Not Implemented!")
return 0, nil
}
Cognitive complexity: 1, Cyclomatic complexity: 1
func (*LDAPStorageHandler) IncrememntWithExpire
func (l *LDAPStorageHandler) IncrememntWithExpire(keyName string, timeout int64) int64 {
l.notifyReadOnly()
return 999
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*LDAPStorageHandler) LoadConfFromMeta
func (l *LDAPStorageHandler) LoadConfFromMeta(meta map[string]interface{}) {
l.LDAPServer = meta["ldap_server"].(string)
l.LDAPPort = uint16(meta["ldap_port"].(float64))
l.BaseDN = meta["base_dn"].(string)
attrArray := []string{}
for _, attr := range meta["attributes"].([]interface{}) {
val := attr.(string)
attrArray = append(attrArray, val)
}
l.Attributes = attrArray
l.SessionAttributeName = meta["session_attribute_name"].(string)
l.SearchString = meta["search_string"].(string)
}
Cognitive complexity: 6, Cyclomatic complexity: 2
func (*LDAPStorageHandler) SetExp
func (l *LDAPStorageHandler) SetExp(cn string, exp int64) error {
log.Warning("Not implementated")
return nil
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*LDAPStorageHandler) SetKey
func (l *LDAPStorageHandler) SetKey(cn, session string, timeout int64) error {
l.notifyReadOnly()
return nil
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*LDAPStorageHandler) SetRawKey
func (l *LDAPStorageHandler) SetRawKey(cn, session string, timeout int64) error {
l.notifyReadOnly()
return nil
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*LDAPStorageHandler) SetRollingWindow
func (l *LDAPStorageHandler) SetRollingWindow(keyName string, per int64, val string, pipeline bool) (int, []interface{}) {
log.Warning("Not Implemented!")
return 0, nil
}
Cognitive complexity: 1, Cyclomatic complexity: 1
func (*LogMessageEventHandler) HandleEvent
HandleEvent will be fired when the event handler instance is found in an APISpec EventPaths object during a request chain
func (l *LogMessageEventHandler) HandleEvent(em config.EventMessage) {
logMsg := l.conf.Prefix + ":" + string(em.Type)
logEventMessage, ok := em.Meta.(LogEventMessage)
if ok {
logMsg = logEventMessage.LogMessage(logMsg)
}
l.logger.Warning(logMsg)
}
Cognitive complexity: 2, Cyclomatic complexity: 2
func (*LogMessageEventHandler) Init
Init initializes the LogMessageEventHandler instance with the given configuration.
func (l *LogMessageEventHandler) Init(handlerConf any) error {
var err error
if err = l.conf.Scan(handlerConf); err != nil {
log.WithFields(logrus.Fields{
"prefix": "log_event_handler",
}).Error("Problem getting configuration, skipping. ", err)
return err
}
if l.conf.Disabled {
log.WithFields(logrus.Fields{
"prefix": "log_event_handler",
}).Infof("skipping disabled log event handler with prefix %s", l.conf.Prefix)
return ErrEventHandlerDisabled
}
l.logger = log
return nil
}
Cognitive complexity: 6, Cyclomatic complexity: 3
func (*MiddlewareContextVars) EnabledForSpec
func (m *MiddlewareContextVars) EnabledForSpec() bool {
return m.Spec.EnableContextVars
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*MiddlewareContextVars) Name
func (m *MiddlewareContextVars) Name() string {
return "MiddlewareContextVars"
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*MiddlewareContextVars) ProcessRequest
ProcessRequest will run any checks on the request on the way through the system, return an error to have the chain fail
func (m *MiddlewareContextVars) ProcessRequest(w http.ResponseWriter, r *http.Request, _ interface{}) (error, int) {
parseForm(r)
contextDataObject := map[string]interface{}{
"request_data": r.Form, // Form params (map[string][]string)
"headers": map[string][]string(r.Header),
"headers_Host": r.Host,
"path_parts": strings.Split(r.URL.Path, "/"), // Path parts
"path": r.URL.Path, // path data
"remote_addr": request.RealIP(r), // IP
"request_id": uuid.New(), //Correlation ID
}
for hname, vals := range r.Header {
n := "headers_" + strings.Replace(hname, "-", "_", -1)
contextDataObject[n] = vals[0]
}
for _, c := range r.Cookies() {
name := "cookies_" + strings.Replace(c.Name, "-", "_", -1)
contextDataObject[name] = c.Value
}
for key, vals := range r.Form {
name := "request_data_" + strings.Replace(key, "-", "_", -1)
if len(vals) > 0 {
contextDataObject[name] = vals[0]
}
}
ctxSetData(r, contextDataObject)
return nil, http.StatusOK
}
Cognitive complexity: 14, Cyclomatic complexity: 5
func (*MiniRequestObject) ReconstructParams
func (mr *MiniRequestObject) ReconstructParams(r *http.Request) {
updatedValues := r.URL.Query()
for _, k := range mr.DeleteParams {
updatedValues.Del(k)
}
for p, v := range mr.AddParams {
updatedValues.Set(p, v)
}
for p, v := range mr.ExtendedParams {
for _, val := range v {
updatedValues.Add(p, val)
}
}
if !reflect.DeepEqual(r.URL.Query(), updatedValues) {
r.URL.RawQuery = updatedValues.Encode()
}
}
Cognitive complexity: 14, Cyclomatic complexity: 6
func (*MockErrorReader) Read
func (e *MockErrorReader) Read(_ []byte) (n int, err error) {
return 0, e.ReturnError
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*MockReadCloser) Close
func (m *MockReadCloser) Close() error {
m.CloseCalled = true
return m.CloseError
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*MockReadCloser) Read
func (m *MockReadCloser) Read(p []byte) (n int, err error) {
return m.Reader.Read(p)
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*MultiTargetProxy) CopyResponse
func (m *MultiTargetProxy) CopyResponse(dst io.Writer, src io.Reader, flushInterval time.Duration) {
m.defaultProxy.CopyResponse(dst, src, flushInterval)
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*MultiTargetProxy) ServeHTTP
func (m *MultiTargetProxy) ServeHTTP(w http.ResponseWriter, r *http.Request) ProxyResponse {
log.WithFields(logrus.Fields{
"prefix": "multi-target",
}).Debug("Serving Multi-target...")
return m.proxyForRequest(r).ServeHTTP(w, r)
}
Cognitive complexity: 1, Cyclomatic complexity: 1
func (*MultiTargetProxy) ServeHTTPForCache
func (m *MultiTargetProxy) ServeHTTPForCache(w http.ResponseWriter, r *http.Request) ProxyResponse {
return m.proxyForRequest(r).ServeHTTPForCache(w, r)
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*Notification) Sign
func (n *Notification) Sign() {
n.SignatureAlgo = crypto.SHA256
hash := sha256.Sum256([]byte(string(n.Command) + n.Payload + n.Gw.GetConfig().NodeSecret))
n.Signature = hex.EncodeToString(hash[:])
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*OAuthClient) GetDescription
func (oc *OAuthClient) GetDescription() string {
return oc.Description
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*OAuthClient) GetId
func (oc *OAuthClient) GetId() string {
return oc.ClientID
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*OAuthClient) GetPolicyID
func (oc *OAuthClient) GetPolicyID() string {
return oc.PolicyID
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*OAuthClient) GetRedirectUri
func (oc *OAuthClient) GetRedirectUri() string {
return oc.ClientRedirectURI
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*OAuthClient) GetSecret
func (oc *OAuthClient) GetSecret() string {
return oc.ClientSecret
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*OAuthClient) GetUserData
func (oc *OAuthClient) GetUserData() interface{} {
return oc.MetaData
}
Cognitive complexity: 1, Cyclomatic complexity: 1
func (*OAuthHandlers) HandleAccessRequest
HandleAccessRequest handles the OAuth 2.0 token or refresh access request, and wraps Tyk's own and Osin's OAuth handlers, returns a response to the client and notifies the provider of the access request (in order to track identity against OAuth tokens without revealing tokens before they are requested).
func (o *OAuthHandlers) HandleAccessRequest(w http.ResponseWriter, r *http.Request) {
w.Header().Set(header.ContentType, header.ApplicationJSON)
// Handle response
resp := o.Manager.HandleAccess(r)
msg := o.generateOAuthOutputFromOsinResponse(resp)
if resp.IsError {
// Something went wrong, write out the error details and kill the response
w.WriteHeader(resp.ErrorStatusCode)
w.Write(msg)
return
}
// Ping endpoint with o_auth key and auth_key
authCode := r.FormValue("code")
oldRefreshToken := r.FormValue("refresh_token")
log.Debug("AUTH CODE: ", authCode)
newOauthToken := ""
if resp.Output["access_token"] != nil {
newOauthToken = resp.Output["access_token"].(string)
}
log.Debug("TOKEN: ", newOauthToken)
refreshToken := ""
if resp.Output["refresh_token"] != nil {
refreshToken = resp.Output["refresh_token"].(string)
}
log.Debug("REFRESH: ", refreshToken)
log.Debug("Old REFRESH: ", oldRefreshToken)
notificationType := newAccessToken
if oldRefreshToken != "" {
notificationType = refreshAccessToken
}
newNotification := NewOAuthNotification{
AuthCode: authCode,
NewOAuthToken: newOauthToken,
RefreshToken: refreshToken,
OldRefreshToken: oldRefreshToken,
NotificationType: notificationType,
}
o.notifyClientOfNewOauth(newNotification)
w.WriteHeader(http.StatusOK)
w.Write(msg)
}
Cognitive complexity: 9, Cyclomatic complexity: 5
func (*OAuthHandlers) HandleAuthorizePassthrough
HandleAuthorizePassthrough handles a Client Auth request, first it checks if the client is OK (otherwise it blocks the request), then it forwards on to the resource providers approval URI
func (o *OAuthHandlers) HandleAuthorizePassthrough(w http.ResponseWriter, r *http.Request) {
// Extract client data and check
resp := o.Manager.HandleAuthorisation(r, false, "")
if resp.IsError {
log.Error("[OAuth] There was an error with the request: ", resp)
// Something went wrong, write out the error details and kill the response
doJSONWrite(w, resp.ErrorStatusCode, apiError(resp.StatusText))
return
}
if r.Method == "GET" {
loginURL := fmt.Sprintf("%s?%s", o.Manager.API.Oauth2Meta.AuthorizeLoginRedirect, r.URL.RawQuery)
w.Header().Add("Location", loginURL)
} else {
w.Header().Add("Location", o.Manager.API.Oauth2Meta.AuthorizeLoginRedirect)
}
w.WriteHeader(307)
}
Cognitive complexity: 6, Cyclomatic complexity: 3
func (*OAuthHandlers) HandleGenerateAuthCodeData
HandleGenerateAuthCodeData handles a resource provider approving an OAuth request from a client
func (o *OAuthHandlers) HandleGenerateAuthCodeData(w http.ResponseWriter, r *http.Request) {
// On AUTH grab session state data and add to UserData (not validated, not good!)
sessionJSONData := r.FormValue("key_rules")
if sessionJSONData == "" {
log.Warning("Authorise request is missing key_rules in params, policy will be required!")
}
// Handle the authorisation and write the JSON output to the resource provider
resp := o.Manager.HandleAuthorisation(r, true, sessionJSONData)
code := http.StatusOK
msg := o.generateOAuthOutputFromOsinResponse(resp)
if resp.IsError {
code = resp.ErrorStatusCode
log.Error("[OAuth] OAuth response marked as error: ", resp)
}
w.WriteHeader(code)
w.Write(msg)
}
Cognitive complexity: 4, Cyclomatic complexity: 3
func (*OAuthHandlers) HandleRevokeAllTokens
func (o *OAuthHandlers) HandleRevokeAllTokens(w http.ResponseWriter, r *http.Request) {
err := r.ParseForm()
if err != nil {
doJSONWrite(w, http.StatusBadRequest, apiError("error parsing form. Form malformed"))
return
}
clientId := r.PostFormValue("client_id")
secret := r.PostFormValue("client_secret")
if clientId == "" {
doJSONWrite(w, http.StatusUnauthorized, apiError(oauthClientIdEmpty))
return
}
if secret == "" {
doJSONWrite(w, http.StatusUnauthorized, apiError(oauthClientSecretEmpty))
return
}
status, tokens, err := RevokeAllTokens(o.Manager.OsinServer.Storage, clientId, secret)
if err != nil {
doJSONWrite(w, status, apiError(err.Error()))
return
}
n := Notification{
Command: KeySpaceUpdateNotification,
Payload: strings.Join(tokens, ","),
Gw: o.Manager.Gw,
}
o.Manager.Gw.MainNotifier.Notify(n)
doJSONWrite(w, http.StatusOK, apiOk("tokens revoked successfully"))
}
Cognitive complexity: 9, Cyclomatic complexity: 5
func (*OAuthHandlers) HandleRevokeToken
in compliance with https://tools.ietf.org/html/rfc7009#section-2.1 ToDo: set an authentication mechanism
func (o *OAuthHandlers) HandleRevokeToken(w http.ResponseWriter, r *http.Request) {
err := r.ParseForm()
if err != nil {
doJSONWrite(w, http.StatusBadRequest, apiError("error parsing form. Form malformed"))
return
}
token := r.PostFormValue("token")
tokenTypeHint := r.PostFormValue("token_type_hint")
if token == "" {
doJSONWrite(w, http.StatusBadRequest, apiError(oauthTokenEmpty))
return
}
RevokeToken(o.Manager.OsinServer.Storage, token, tokenTypeHint)
doJSONWrite(w, http.StatusOK, apiOk("token revoked successfully"))
}
Cognitive complexity: 4, Cyclomatic complexity: 3
func (*OAuthManager) HandleAccess
HandleAccess wraps an access request with osin's primitives
func (o *OAuthManager) HandleAccess(r *http.Request) *osin.Response {
resp := o.OsinServer.NewResponse()
// we are intentionally ignoring errors, because this is called again by
// osin.We are only doing this to ensure r.From is properly initialized incase
// r.ParseForm was success
r.ParseForm()
if err := JSONToFormValues(r); err != nil {
log.Errorf("trying to set url values decoded from json body :%v", err)
}
var username string
if ar := o.OsinServer.HandleAccessRequest(resp, r); ar != nil {
var session *user.SessionState
if ar.Type == osin.PASSWORD {
username = r.Form.Get("username")
password := r.Form.Get("password")
searchKey := "apikey-" + storage.HashKey(o.API.OrgID+username, o.Gw.GetConfig().HashKeys)
log.Debug("Getting: ", searchKey)
var err error
session, err = o.OsinServer.Storage.GetUser(searchKey)
if err != nil {
log.Warning("Attempted access with non-existent user (OAuth password flow).")
} else {
var passMatch bool
if session.BasicAuthData.Hash == user.HashBCrypt {
err := bcrypt.CompareHashAndPassword([]byte(session.BasicAuthData.Password), []byte(password))
if err == nil {
passMatch = true
}
}
if session.BasicAuthData.Hash == user.HashPlainText &&
session.BasicAuthData.Password == password {
passMatch = true
}
if passMatch {
ar.Authorized = true
// not ideal, but we need to copy the session state across
pw := session.BasicAuthData.Password
hs := session.BasicAuthData.Hash
session.BasicAuthData.Password = ""
session.BasicAuthData.Hash = ""
asString, _ := json.Marshal(session)
ar.UserData = string(asString)
session.BasicAuthData.Password = pw
session.BasicAuthData.Hash = hs
//log.Warning("Old Keys: ", session.OauthKeys)
}
}
} else {
// Using a manual flow
ar.Authorized = true
}
// Does the user have an old OAuth token for this client?
if session != nil && session.OauthKeys != nil {
log.Debug("There's keys here bill...")
oldToken, foundKey := session.OauthKeys[ar.Client.GetId()]
if foundKey {
log.Info("Found old token, revoking: ", oldToken)
o.Gw.GlobalSessionManager.RemoveSession(o.API.OrgID, oldToken, false)
}
}
log.Debug("[OAuth] Finishing access request ")
o.OsinServer.FinishAccessRequest(resp, r, ar)
new_token, foundNewToken := resp.Output["access_token"]
if username != "" && foundNewToken {
log.Debug("Updating token data in key")
if session.OauthKeys == nil {
session.OauthKeys = make(map[string]string)
}
session.OauthKeys[ar.Client.GetId()] = new_token.(string)
log.Debug("New token: ", new_token.(string))
log.Debug("Keys: ", session.OauthKeys)
// add oauth-client user_fields to session's meta
if userData := ar.Client.GetUserData(); userData != nil {
metadata, ok := userData.(map[string]interface{})
if !ok {
log.WithField("oauthClientID", ar.Client.GetId()).
Error("Could not set session meta_data from oauth-client fields, type mismatch")
} else {
session.MetaData = metadata
// set session alias to developer email as we do it for regular API keys created for developer
if devEmail, found := session.MetaData[keyDataDeveloperEmail].(string); found {
session.Alias = devEmail
// we don't need it in meta-data as we set it to alias
delete(session.MetaData, keyDataDeveloperEmail)
}
}
}
keyName := o.Gw.generateToken(o.API.OrgID, username)
log.Debug("Updating user:", keyName)
err := o.Gw.GlobalSessionManager.UpdateSession(keyName, session, session.Lifetime(o.API.GetSessionLifetimeRespectsKeyExpiration(), o.API.SessionLifetime, o.Gw.GetConfig().ForceGlobalSessionLifetime, o.Gw.GetConfig().GlobalSessionLifetime), false)
if err != nil {
log.Error(err)
}
}
}
if resp.IsError {
clientId := r.Form.Get("client_id")
log.WithFields(logrus.Fields{
"org_id": o.API.OrgID,
"client_id": clientId,
"response error": resp.StatusText,
"response code": resp.ErrorStatusCode,
"RemoteAddr": request.RealIP(r), //r.RemoteAddr,
}).Error("[OAuth] OAuth response marked as error")
}
return resp
}
Cognitive complexity: 42, Cyclomatic complexity: 21
func (*OAuthManager) HandleAuthorisation
HandleAuthorisation creates the authorisation data for the request
func (o *OAuthManager) HandleAuthorisation(r *http.Request, complete bool, session string) *osin.Response {
resp := o.OsinServer.NewResponse()
if ar := o.OsinServer.HandleAuthorizeRequest(resp, r); ar != nil {
// Since this is called by the Reource provider (proxied API), we assume it has been approved
ar.Authorized = true
if complete {
ar.UserData = session
o.OsinServer.FinishAuthorizeRequest(resp, r, ar)
}
}
if resp.IsError && resp.InternalError != nil {
log.Error(resp.InternalError)
}
return resp
}
Cognitive complexity: 6, Cyclomatic complexity: 5
func (*OAuthManager) Storage
func (o *OAuthManager) Storage() ExtendedOsinStorageInterface {
return o.OsinServer.Storage
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*Oauth2KeyExists) EnabledForSpec
func (k *Oauth2KeyExists) EnabledForSpec() bool {
return k.Spec.UseOauth2
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*Oauth2KeyExists) Name
func (k *Oauth2KeyExists) Name() string {
return "Oauth2KeyExists"
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*Oauth2KeyExists) ProcessRequest
ProcessRequest will run any checks on the request on the way through the system, return an error to have the chain fail
func (k *Oauth2KeyExists) ProcessRequest(w http.ResponseWriter, r *http.Request, _ interface{}) (error, int) {
if ctxGetRequestStatus(r) == StatusOkAndIgnore {
return nil, http.StatusOK
}
logger := k.Logger()
// We're using OAuth, start checking for access keys
token, _ := k.getAuthToken(k.getAuthType(), r)
parts := strings.Split(token, " ")
if len(parts) < 2 {
logger.Info("Attempted access with malformed header, no auth header found.")
return errorAndStatusCode(ErrOAuthAuthorizationFieldMissing)
}
if strings.ToLower(parts[0]) != "bearer" {
logger.Info("Bearer token malformed")
return errorAndStatusCode(ErrOAuthAuthorizationFieldMalformed)
}
accessToken := parts[1]
logger = logger.WithField("key", k.Gw.obfuscateKey(accessToken))
// get session for the given oauth token
session, keyExists := k.CheckSessionAndIdentityForValidKey(accessToken, r)
accessToken = session.KeyID
if !keyExists {
logger.Warning("Attempted access with non-existent key.")
// Fire Authfailed Event
AuthFailed(k, r, accessToken)
// Report in health check
reportHealthValue(k.Spec, KeyFailure, "-1")
return errorAndStatusCode(ErrOAuthKeyNotFound)
}
// Make sure OAuth-client is still present
oauthClientDeletedKey := "oauth-del-" + k.Spec.APIID + session.OauthClientID
oauthClientDeleted := false
// check if that oauth client was deleted with using memory cache first
if val, found := k.Gw.UtilCache.Get(oauthClientDeletedKey); found {
oauthClientDeleted = val.(bool)
} else {
// if not cached in memory then hit Redis to get oauth-client from there
if _, err := k.Spec.OAuthManager.Storage().GetClient(session.OauthClientID); err != nil {
// set this oauth client as deleted in memory cache for the next N sec
k.Gw.UtilCache.Set(oauthClientDeletedKey, true, checkOAuthClientDeletedInterval)
oauthClientDeleted = true
} else {
// set this oauth client as NOT deleted in memory cache for next N sec
k.Gw.UtilCache.Set(oauthClientDeletedKey, false, checkOAuthClientDeletedInterval)
}
}
if oauthClientDeleted {
logger.WithField("oauthClientID", session.OauthClientID).Warning("Attempted access for deleted OAuth client.")
return errorAndStatusCode(ErrOAuthClientDeleted)
}
// Set session state on context, we will need it later
switch k.Spec.BaseIdentityProvidedBy {
case apidef.OAuthKey, apidef.UnsetAuth:
hashKeys := k.Gw.GetConfig().HashKeys
ctxSetSession(r, &session, false, hashKeys)
if hashKeys {
ctxSetSpanAttributes(r, k.Name(), otel.OAuthClientIDAttribute(session.KeyHash()))
}
}
// Request is valid, carry on
return nil, http.StatusOK
}
Cognitive complexity: 24, Cyclomatic complexity: 11
func (*OpenIDMW) EnabledForSpec
func (k *OpenIDMW) EnabledForSpec() bool {
if k.Spec.UseOpenID {
log.Warn("Support for OpenID Connect Middleware will be deprecated starting from 5.7.0. To avoid any disruptions, we recommend that you use JSON Web Token (JWT) instead, as explained in https://tyk.io/docs/basic-config-and-security/security/authentication-authorization/openid-connect/")
}
return k.Spec.UseOpenID
}
Cognitive complexity: 3, Cyclomatic complexity: 3
func (*OpenIDMW) Init
func (k *OpenIDMW) Init() {
k.provider_client_policymap = make(map[string]map[string]string)
// Create an OpenID Configuration and store
var err error
k.providerConfiguration, err = openid.NewConfiguration(openid.ProvidersGetter(k.getProviders),
openid.ErrorHandler(k.dummyErrorHandler))
if err != nil {
k.Logger().WithError(err).Error("OpenID configuration error")
}
}
Cognitive complexity: 2, Cyclomatic complexity: 2
func (*OpenIDMW) Name
func (k *OpenIDMW) Name() string {
return "OpenIDMW"
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*OpenIDMW) ProcessRequest
func (k *OpenIDMW) ProcessRequest(w http.ResponseWriter, r *http.Request, _ interface{}) (error, int) {
if ctxGetRequestStatus(r) == StatusOkAndIgnore {
return nil, http.StatusOK
}
k.providerConfiguration.IDTokenGetter = func(r *http.Request) (token string, err error) {
token, _ = k.getAuthToken(k.getAuthType(), r)
return openid.CheckAndSplitHeader(token)
}
logger := k.Logger()
// 1. Validate the JWT
ouser, token, halt := openid.AuthenticateOIDWithUser(k.providerConfiguration, w, r)
// 2. Generate the internal representation for the key
if halt {
// Fire Authfailed Event
k.reportLoginFailure("[JWT]", r)
return errors.New("Key not authorised"), http.StatusUnauthorized
}
// 3. Create or set the session to match
iss, found := token.Claims.(jwt.MapClaims)["iss"]
clients, cfound := token.Claims.(jwt.MapClaims)["aud"]
if !found && !cfound {
logger.Error("No issuer or audiences found!")
k.reportLoginFailure("[NOT GENERATED]", r)
return errors.New("Key not authorised"), http.StatusUnauthorized
}
// decide if we use policy ID from provider client settings or list of policies from scope-policy mapping
useScope := len(k.Spec.GetScopeToPolicyMapping()) != 0
k.lock.RLock()
clientSet, foundIssuer := k.provider_client_policymap[iss.(string)]
k.lock.RUnlock()
if !foundIssuer {
logger.Error("No issuer or audiences found!")
k.reportLoginFailure("[NOT GENERATED]", r)
return errors.New("Key not authorised"), http.StatusUnauthorized
}
policyID := ""
clientID := ""
switch v := clients.(type) {
case string:
k.lock.RLock()
policyID = clientSet[v]
k.lock.RUnlock()
clientID = v
case []interface{}:
for _, audVal := range v {
k.lock.RLock()
policy, foundPolicy := clientSet[audVal.(string)]
k.lock.RUnlock()
if foundPolicy {
clientID = audVal.(string)
policyID = policy
break
}
}
}
if !useScope && policyID == "" {
logger.Error("No matching policy found!")
k.reportLoginFailure("[NOT GENERATED]", r)
return errors.New("Key not authorised"), http.StatusUnauthorized
}
data := []byte(ouser.ID)
keyID := fmt.Sprintf("%x", md5.Sum(data))
sessionID := k.Gw.generateToken(k.Spec.OrgID, keyID)
if k.Spec.OpenIDOptions.SegregateByClient {
// We are segregating by client, so use it as part of the internal token
logger.Debug("Client ID:", clientID)
sessionID = k.Gw.generateToken(k.Spec.OrgID, fmt.Sprintf("%x", md5.Sum([]byte(clientID)))+keyID)
}
logger.Debug("Generated Session ID: ", sessionID)
var policiesToApply []string
if !useScope {
policiesToApply = append(policiesToApply, policyID)
} else {
scopeClaimName := k.Spec.GetScopeClaimName()
if scopeClaimName == "" {
scopeClaimName = "scope"
}
if scope := getScopeFromClaim(token.Claims.(jwt.MapClaims), scopeClaimName); scope != nil {
// add all policies matched from scope-policy mapping
policiesToApply = mapScopeToPolicies(k.Spec.GetScopeToPolicyMapping(), scope)
}
}
session, exists := k.CheckSessionAndIdentityForValidKey(sessionID, r)
sessionID = session.KeyID
if !exists {
// Create it
logger.Debug("Key does not exist, creating")
session = user.SessionState{}
if !useScope {
// We need a base policy as a template, either get it from the token itself OR a proxy client ID within Tyk
newSession, err := k.Gw.generateSessionFromPolicy(policyID,
k.Spec.OrgID,
true)
if err != nil {
k.reportLoginFailure(sessionID, r)
logger.Error("Could not find a valid policy to apply to this token!")
return errors.New("Key not authorized: no matching policy"), http.StatusForbidden
}
session = newSession.Clone()
}
session.OrgID = k.Spec.OrgID
session.MetaData = map[string]interface{}{"TykJWTSessionID": sessionID, "ClientID": clientID}
session.Alias = clientID + ":" + ouser.ID
session.KeyID = sessionID
// Update the session in the session manager in case it gets called again
logger.Debug("Policy applied to key")
}
// apply new policy to session if any and update session
session.SetPolicies(policiesToApply...)
if err := k.ApplyPolicies(&session); err != nil {
k.Logger().WithError(err).Error("Could not apply new policy from OIDC client to session")
return errors.New("Key not authorized: could not apply new policy"), http.StatusForbidden
}
// 4. Set session state on context, we will need it later
switch k.Spec.BaseIdentityProvidedBy {
case apidef.OIDCUser, apidef.UnsetAuth:
ctxSetSession(r, &session, true, k.Gw.GetConfig().HashKeys)
}
ctxSetJWTContextVars(k.Spec, r, token)
return nil, http.StatusOK
}
Cognitive complexity: 46, Cyclomatic complexity: 23
func (*OrganizationMonitor) AllowAccessNext
func (k *OrganizationMonitor) AllowAccessNext(
orgChan chan bool,
path string,
IP string,
r *http.Request,
session *user.SessionState) {
// Is it active?
logEntry := k.Gw.getExplicitLogEntryForRequest(k.Logger(), path, IP, k.Spec.OrgID, nil)
if session.IsInactive {
logEntry.Warning("Organisation access is disabled.")
orgChan <- false
return
}
customQuotaKey := ""
// We found a session, apply the quota and rate limiter
reason := k.Gw.SessionLimiter.ForwardMessage(
r,
session,
k.Spec.OrgID,
customQuotaKey,
k.Spec.OrgSessionManager.Store(),
session.Per > 0 && session.Rate > 0,
true,
k.Spec,
false,
)
sessionLifeTime := session.Lifetime(k.Spec.GetSessionLifetimeRespectsKeyExpiration(), k.Spec.SessionLifetime, k.Gw.GetConfig().ForceGlobalSessionLifetime, k.Gw.GetConfig().GlobalSessionLifetime)
if err := k.Spec.OrgSessionManager.UpdateSession(k.Spec.OrgID, session, sessionLifeTime, false); err == nil {
// update in-app cache if needed
if !k.Spec.GlobalConfig.LocalSessionCache.DisableCacheSessionState {
k.Gw.SessionCache.Set(k.Spec.OrgID, session.Clone(), sessionLifeTime)
}
} else {
logEntry.WithError(err).WithField("orgID", k.Spec.OrgID).Error("Could not update org session")
}
isExceeded := false
switch reason {
case sessionFailNone:
// all good, keep org active
case sessionFailQuota:
isExceeded = true
logEntry.Warning("Organisation quota has been exceeded.")
// Fire a quota exceeded event
k.FireEvent(
EventOrgQuotaExceeded,
EventKeyFailureMeta{
EventMetaDefault: EventMetaDefault{
Message: "Organisation quota has been exceeded",
},
Path: path,
Origin: IP,
Key: k.Spec.OrgID,
},
)
case sessionFailRateLimit:
isExceeded = true
logEntry.Warning("Organisation rate limit has been exceeded.")
// Fire a rate limit exceeded event
k.FireEvent(
EventOrgRateLimitExceeded,
EventKeyFailureMeta{
EventMetaDefault: EventMetaDefault{
Message: "Organisation rate limit has been exceeded",
},
Path: path,
Origin: IP,
Key: k.Spec.OrgID,
},
)
}
if k.Spec.GlobalConfig.Monitor.MonitorOrgKeys {
// Run the trigger monitor
k.mon.Check(session, "")
}
if isExceeded {
orgChan <- false
return
}
orgChan <- true
}
Cognitive complexity: 21, Cyclomatic complexity: 11
func (*OrganizationMonitor) EnabledForSpec
func (k *OrganizationMonitor) EnabledForSpec() bool {
// If false, we aren't enforcing quotas so skip this mw
// altogether
return k.Spec.GlobalConfig.EnforceOrgQuotas
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*OrganizationMonitor) Name
func (k *OrganizationMonitor) Name() string {
return "OrganizationMonitor"
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*OrganizationMonitor) ProcessRequest
func (k *OrganizationMonitor) ProcessRequest(w http.ResponseWriter, r *http.Request, _ interface{}) (error, int) {
// Skip rate limiting and quotas for looping
if !ctxCheckLimits(r) {
return nil, http.StatusOK
}
// short path for specs which have organization limiter enabled but organization has no session
if k.getOrgHasNoSession() {
return nil, http.StatusOK
}
var orgSession user.SessionState
var found bool
// try to check in in-app cache 1st
if !k.Spec.GlobalConfig.LocalSessionCache.DisableCacheSessionState {
var cachedSession interface{}
if cachedSession, found = k.Gw.SessionCache.Get(k.Spec.OrgID); found {
sess := cachedSession.(user.SessionState)
orgSession = sess.Clone()
}
}
// try to get from Redis
if !found {
// not found in in-app cache, let's read from Redis
orgSession, found = k.OrgSession(k.Spec.OrgID)
if !found {
// prevent reads from in-app cache and from Redis for next runs
k.setOrgHasNoSession(true)
// No organisation session has not been created, should not be a pre-requisite in site setups, so we pass the request on
return nil, http.StatusOK
}
}
clone := orgSession.Clone()
if k.Spec.GlobalConfig.ExperimentalProcessOrgOffThread {
// Make a copy of request before before sending to goroutine
r2 := r.WithContext(r.Context())
return k.ProcessRequestOffThread(r2, &clone)
}
return k.ProcessRequestLive(r, &clone)
}
Cognitive complexity: 16, Cyclomatic complexity: 8
func (*OrganizationMonitor) ProcessRequestLive
ProcessRequest will run any checks on the request on the way through the system, return an error to have the chain fail
func (k *OrganizationMonitor) ProcessRequestLive(r *http.Request, orgSession *user.SessionState) (error, int) {
logger := k.Logger()
if orgSession.IsInactive {
logger.Warning("Organisation access is disabled.")
return errors.New("this organisation access has been disabled, please contact your API administrator"), http.StatusForbidden
}
customQuotaKey := ""
// We found a session, apply the quota and rate limiter
reason := k.Gw.SessionLimiter.ForwardMessage(
r,
orgSession,
k.Spec.OrgID,
customQuotaKey,
k.Spec.OrgSessionManager.Store(),
orgSession.Per > 0 && orgSession.Rate > 0,
true,
k.Spec,
false,
)
sessionLifeTime := orgSession.Lifetime(k.Spec.GetSessionLifetimeRespectsKeyExpiration(), k.Spec.SessionLifetime, k.Gw.GetConfig().ForceGlobalSessionLifetime, k.Gw.GetConfig().GlobalSessionLifetime)
if err := k.Spec.OrgSessionManager.UpdateSession(k.Spec.OrgID, orgSession, sessionLifeTime, false); err == nil {
// update in-app cache if needed
if !k.Spec.GlobalConfig.LocalSessionCache.DisableCacheSessionState {
k.Gw.SessionCache.Set(k.Spec.OrgID, orgSession.Clone(), sessionLifeTime)
}
} else {
logger.WithError(err).Error("Could not update org session")
}
switch reason {
case sessionFailNone:
// all good, keep org active
case sessionFailQuota:
logger.Warning("Organisation quota has been exceeded.", k.Spec.OrgID)
// Fire a quota exceeded event
k.FireEvent(
EventOrgQuotaExceeded,
EventKeyFailureMeta{
EventMetaDefault: EventMetaDefault{
Message: "Organisation quota has been exceeded",
OriginatingRequest: EncodeRequestToEvent(r),
},
Path: r.URL.Path,
Origin: request.RealIP(r),
Key: k.Spec.OrgID,
})
return errors.New("This organisation quota has been exceeded, please contact your API administrator"), http.StatusForbidden
case sessionFailRateLimit:
logger.Warning("Organisation rate limit has been exceeded.", k.Spec.OrgID)
// Fire a rate limit exceeded event
k.FireEvent(
EventOrgRateLimitExceeded,
EventKeyFailureMeta{
EventMetaDefault: EventMetaDefault{
Message: "Organisation rate limit has been exceeded",
OriginatingRequest: EncodeRequestToEvent(r),
},
Path: r.URL.Path,
Origin: request.RealIP(r),
Key: k.Spec.OrgID,
},
)
return errors.New("This organisation rate limit has been exceeded, please contact your API administrator"), http.StatusForbidden
}
if k.Spec.GlobalConfig.Monitor.MonitorOrgKeys {
// Run the trigger monitor
k.mon.Check(orgSession, "")
}
// Lets keep a reference of the org
setCtxValue(r, ctx.OrgSessionContext, orgSession)
// Request is valid, carry on
return nil, http.StatusOK
}
Cognitive complexity: 19, Cyclomatic complexity: 10
func (*OrganizationMonitor) ProcessRequestOffThread
func (k *OrganizationMonitor) ProcessRequestOffThread(r *http.Request, orgSession *user.SessionState) (error, int) {
orgChanMap.Lock()
orgChan, ok := orgChanMap.channels[k.Spec.OrgID]
if !ok {
orgChan = make(chan bool)
orgChanMap.channels[k.Spec.OrgID] = orgChan
go k.SetOrgSentinel(orgChan, k.Spec.OrgID)
}
orgChanMap.Unlock()
active, found := orgActiveMap.Load(k.Spec.OrgID)
// Lets keep a reference of the org
// session might be updated by go-routine AllowAccessNext and we loose those changes here
// but it is OK as we need it in context for detailed org logging
clone := orgSession.Clone()
setCtxValue(r, ctx.OrgSessionContext, &clone)
orgSessionCopy := orgSession.Clone()
go k.AllowAccessNext(
orgChan,
r.URL.Path,
request.RealIP(r),
r,
&orgSessionCopy,
)
if found && !active.(bool) {
k.Logger().Debug("Is not active")
return errors.New("This organization access has been disabled or quota/rate limit is exceeded, please contact your API administrator"), http.StatusForbidden
}
// Request is valid, carry on
return nil, http.StatusOK
}
Cognitive complexity: 4, Cyclomatic complexity: 4
func (*OrganizationMonitor) SetOrgSentinel
func (k *OrganizationMonitor) SetOrgSentinel(orgChan chan bool, orgId string) {
for isActive := range orgChan {
k.Logger().Debug("Chan got:", isActive)
orgActiveMap.Store(orgId, isActive)
}
}
Cognitive complexity: 3, Cyclomatic complexity: 2
func (*PersistGraphQLOperationMiddleware) EnabledForSpec
func (i *PersistGraphQLOperationMiddleware) EnabledForSpec() bool {
for _, v := range i.Spec.VersionData.Versions {
if len(v.ExtendedPaths.PersistGraphQL) > 0 {
return true
}
}
return false
}
Cognitive complexity: 5, Cyclomatic complexity: 3
func (*PersistGraphQLOperationMiddleware) Name
func (i *PersistGraphQLOperationMiddleware) Name() string {
return "PersistGraphQLOperationMiddleware"
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*PersistGraphQLOperationMiddleware) ProcessRequest
ProcessRequest will run any checks on the request on the way through the system, return an error to have the chain fail
func (i *PersistGraphQLOperationMiddleware) ProcessRequest(w http.ResponseWriter, r *http.Request, _ interface{}) (error, int) {
vInfo, _ := i.Spec.Version(r)
versionPaths := i.Spec.RxPaths[vInfo.Name]
found, meta := i.Spec.CheckSpecMatchesStatus(r, versionPaths, PersistGraphQL)
if !found {
// PersistGraphQLOperationMiddleware not enabled for this endpoint
return nil, http.StatusOK
}
mwSpec, _ := meta.(*apidef.PersistGraphQLMeta)
ctxSetRequestMethod(r, r.Method)
r.Method = http.MethodPost
_, err := io.ReadAll(r.Body)
if err != nil {
i.Logger().WithError(err).Error("error reading request")
return errors.New("error reading the request"), http.StatusBadRequest
}
defer r.Body.Close()
replacers := make(map[string]int)
fullPath := fmt.Sprintf("%s/%s", strings.TrimRight(i.Spec.Proxy.ListenPath, "/"), strings.TrimLeft(mwSpec.Path, "/"))
paths := strings.Split(fullPath, "/")
for i, part := range paths {
if strings.HasPrefix(part, "{") && strings.HasSuffix(part, "}") {
key := "$path." + strings.Replace(part, "{", "", -1)
key = strings.Replace(key, "}", "", -1)
replacers[key] = i
}
}
varBytes, err := json.Marshal(mwSpec.Variables)
if err != nil {
i.Logger().WithError(err).Error("error proxying request")
return ProxyingRequestFailedErr, http.StatusInternalServerError
}
variablesStr := i.Gw.ReplaceTykVariables(r, string(varBytes), false)
requestPathParts := strings.Split(r.RequestURI, "/")
for replacer, pathIndex := range replacers {
variablesStr = strings.ReplaceAll(variablesStr, replacer, requestPathParts[pathIndex])
}
graphqlQuery := GraphQLRequest{
Query: mwSpec.Operation,
Variables: []byte(variablesStr),
}
graphQLQueryBytes, err := json.Marshal(graphqlQuery)
if err != nil {
i.Logger().WithError(err).Error("error proxying request")
return ProxyingRequestFailedErr, http.StatusInternalServerError
}
newBuf := bytes.NewBuffer(graphQLQueryBytes)
r.Body = io.NopCloser(newBuf)
r.ContentLength = int64(newBuf.Len())
nopCloseRequestBody(r)
r.Header.Set("Content-Type", "application/json")
ctxSetUrlRewritePath(r, r.URL.Path)
r.URL.Path = "/"
return nil, http.StatusOK
}
Cognitive complexity: 18, Cyclomatic complexity: 9
func (*RPCStorageHandler) AddToSortedSet
func (r *RPCStorageHandler) AddToSortedSet(keyName, value string, score float64) {
r.Gw.handleGlobalAddToSortedSet(keyName, value, score)
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*RPCStorageHandler) AppendToSet
func (r *RPCStorageHandler) AppendToSet(keyName, value string) {
ibd := model.InboundData{
KeyName: keyName,
Value: value,
}
_, err := rpc.FuncClientSingleton("AppendToSet", ibd)
if err != nil {
rpc.EmitErrorEventKv(
rpc.FuncClientSingletonCall,
"AppendToSet",
err,
map[string]string{
"keyName": keyName,
},
)
}
if r.IsRetriableError(err) {
if rpc.Login() {
r.AppendToSet(keyName, value)
}
}
}
Cognitive complexity: 8, Cyclomatic complexity: 4
func (*RPCStorageHandler) CheckForKeyspaceChanges
CheckForKeyspaceChanges will poll for keysace changes
func (r *RPCStorageHandler) CheckForKeyspaceChanges(orgId string) {
log.Debug("Checking for keyspace changes...")
var keys interface{}
var err error
var funcName string
var req interface{}
reqData := map[string]string{}
if groupID := r.Gw.GetConfig().SlaveOptions.GroupID; groupID == "" {
funcName = "GetKeySpaceUpdate"
req = orgId
reqData["orgId"] = orgId
} else {
funcName = "GetGroupKeySpaceUpdate"
req = model.GroupKeySpaceRequest{
OrgID: orgId,
GroupID: groupID,
}
reqData["orgId"] = orgId
reqData["GroupID"] = groupID
}
keys, err = rpc.FuncClientSingleton(funcName, req)
if err != nil {
rpc.EmitErrorEventKv(
rpc.FuncClientSingletonCall,
funcName,
err,
reqData,
)
if r.IsRetriableError(err) {
if rpc.Login() {
r.CheckForKeyspaceChanges(orgId)
}
}
log.Warning("Keyspace warning: ", err)
return
}
if keys == nil {
log.Info("Keys returned nil object, skipping check")
return
}
if len(keys.([]string)) > 0 {
log.Info("Keyspace changes detected, updating local cache")
go r.ProcessKeySpaceChanges(keys.([]string), orgId)
}
}
Cognitive complexity: 18, Cyclomatic complexity: 7
func (*RPCStorageHandler) CheckForReload
CheckForReload will start a long poll
func (r *RPCStorageHandler) CheckForReload(orgId string) bool {
select {
case <-r.Gw.ctx.Done():
return false
default:
}
log.Debug("[RPC STORE] Check Reload called...")
reload, err := rpc.FuncClientSingleton("CheckReload", orgId)
if err != nil {
rpc.EmitErrorEventKv(
rpc.FuncClientSingletonCall,
"CheckReload",
err,
map[string]string{
"orgId": orgId,
},
)
if r.IsRetriableError(err) {
log.Warning("[RPC STORE] CheckReload: Not logged in")
if rpc.Login() {
r.CheckForReload(orgId)
}
} else if !strings.Contains(err.Error(), "Cannot obtain response during") {
forcer := rpc.NewSyncForcer(r.Gw.StorageConnectionHandler, r.buildNodeInfo)
forcer.SetFirstConnection(true)
log.Warning("[RPC STORE] RPC Reload Checker encountered unexpected error: ", err)
}
time.Sleep(1 * time.Second)
} else if reload == true {
// Do the reload!
log.Warning("[RPC STORE] Received Reload instruction!")
go func() {
r.Gw.MainNotifier.Notify(Notification{Command: NoticeGroupReload, Gw: r.Gw})
}()
}
return true
}
Cognitive complexity: 15, Cyclomatic complexity: 7
func (*RPCStorageHandler) Connect
Connect will establish a connection to the RPC
func (r *RPCStorageHandler) Connect() bool {
slaveOptions := r.Gw.GetConfig().SlaveOptions
rpcConfig := rpc.Config{
UseSSL: slaveOptions.UseSSL,
SSLInsecureSkipVerify: slaveOptions.SSLInsecureSkipVerify,
SSLMinVersion: r.Gw.GetConfig().HttpServerOptions.MinVersion,
SSLMaxVersion: r.Gw.GetConfig().HttpServerOptions.MaxVersion,
ConnectionString: slaveOptions.ConnectionString,
RPCKey: slaveOptions.RPCKey,
APIKey: slaveOptions.APIKey,
GroupID: slaveOptions.GroupID,
CallTimeout: slaveOptions.CallTimeout,
PingTimeout: slaveOptions.PingTimeout,
RPCPoolSize: slaveOptions.RPCPoolSize,
}
return rpc.Connect(
rpcConfig,
r.SuppressRegister,
dispatcherFuncs,
r.getGroupLoginCallback(r.Gw.GetConfig().SlaveOptions.SynchroniserEnabled),
func() {
r.Gw.reloadURLStructure(nil)
},
r.DoReload,
)
}
Cognitive complexity: 2, Cyclomatic complexity: 1
func (*RPCStorageHandler) Decrement
Decrement will decrement a key in redis
func (r *RPCStorageHandler) Decrement(keyName string) {
log.Warning("Decrement called")
_, err := rpc.FuncClientSingleton("Decrement", keyName)
if err != nil {
rpc.EmitErrorEventKv(
rpc.FuncClientSingletonCall,
"Decrement",
err,
map[string]string{
"keyName": keyName,
},
)
}
if r.IsRetriableError(err) {
if rpc.Login() {
r.Decrement(keyName)
return
}
}
}
Cognitive complexity: 7, Cyclomatic complexity: 4
func (*RPCStorageHandler) DeleteAllKeys
func (r *RPCStorageHandler) DeleteAllKeys() bool {
log.Warning("Not implementated")
return false
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*RPCStorageHandler) DeleteKey
DeleteKey will remove a key from the database
func (r *RPCStorageHandler) DeleteKey(keyName string) bool {
log.Debug("DEL Key was: ", r.Gw.obfuscateKey(keyName))
log.Debug("DEL Key became: ", r.Gw.obfuscateKey(r.fixKey(keyName)))
ok, err := rpc.FuncClientSingleton("DeleteKey", r.fixKey(keyName))
if err != nil {
rpc.EmitErrorEventKv(
rpc.FuncClientSingletonCall,
"DeleteKey",
err,
map[string]string{
"keyName": keyName,
"fixedKeyName": r.fixKey(keyName),
},
)
if r.IsRetriableError(err) {
if rpc.Login() {
return r.DeleteKey(keyName)
}
}
}
return ok == true
}
Cognitive complexity: 7, Cyclomatic complexity: 4
func (*RPCStorageHandler) DeleteKeys
DeleteKeys will remove a group of keys in bulk
func (r *RPCStorageHandler) DeleteKeys(keys []string) bool {
if len(keys) > 0 {
asInterface := make([]string, len(keys))
for i, v := range keys {
asInterface[i] = r.fixKey(v)
}
log.Debug("Deleting: ", asInterface)
ok, err := rpc.FuncClientSingleton("DeleteKeys", asInterface)
if err != nil {
rpc.EmitErrorEventKv(
rpc.FuncClientSingletonCall,
"DeleteKeys",
err,
map[string]string{
"keys": strings.Join(keys, ","),
"asInterface": strings.Join(asInterface, ","),
},
)
if r.IsRetriableError(err) {
if rpc.Login() {
return r.DeleteKeys(keys)
}
}
}
return ok == true
}
log.Debug("RPCStorageHandler called DEL - Nothing to delete")
return true
}
Cognitive complexity: 12, Cyclomatic complexity: 6
func (*RPCStorageHandler) DeleteRawKey
DeleteKey will remove a key from the database without prefixing, assumes user knows what they are doing
func (r *RPCStorageHandler) DeleteRawKey(keyName string) bool {
ok, err := rpc.FuncClientSingleton("DeleteRawKey", keyName)
if err != nil {
rpc.EmitErrorEventKv(
rpc.FuncClientSingletonCall,
"DeleteRawKey",
err,
map[string]string{
"keyName": keyName,
},
)
if r.IsRetriableError(err) {
if rpc.Login() {
return r.DeleteRawKey(keyName)
}
}
}
return ok == true
}
Cognitive complexity: 7, Cyclomatic complexity: 4
func (*RPCStorageHandler) DeleteRawKeys
func (r *RPCStorageHandler) DeleteRawKeys(keys []string) bool {
ret, err := rpc.FuncClientSingleton("DeleteRawKeys", keys)
if err != nil {
rpc.EmitErrorEventKv(
rpc.FuncClientSingletonCall,
"DeleteKey",
err,
nil,
)
if r.IsRetriableError(err) {
if rpc.Login() {
return r.DeleteRawKeys(keys)
}
}
}
success, ok := ret.(bool)
return success && ok
}
Cognitive complexity: 6, Cyclomatic complexity: 5
func (*RPCStorageHandler) DeleteScanMatch
func (r *RPCStorageHandler) DeleteScanMatch(pattern string) bool {
log.Error("RPCStorageHandler.DeleteScanMatch - Not implemented")
return false
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*RPCStorageHandler) Disconnect
func (r *RPCStorageHandler) Disconnect() error {
request := model.GroupLoginRequest{
UserKey: r.Gw.GetConfig().SlaveOptions.APIKey,
GroupID: r.Gw.GetConfig().SlaveOptions.GroupID,
Node: r.buildNodeInfo(),
}
_, err := rpc.FuncClientSingleton("Disconnect", request)
return err
}
Cognitive complexity: 1, Cyclomatic complexity: 1
func (*RPCStorageHandler) Exists
func (r *RPCStorageHandler) Exists(keyName string) (bool, error) {
log.Error("Not implemented")
return false, nil
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*RPCStorageHandler) GetAndDeleteSet
func (r *RPCStorageHandler) GetAndDeleteSet(keyName string) []interface{} {
log.Error("RPCStorageHandler.GetAndDeleteSet - Not implemented, please disable your purger")
return nil
}
Cognitive complexity: 1, Cyclomatic complexity: 1
func (*RPCStorageHandler) GetApiDefinitions
GetAPIDefinitions will pull API definitions from the RPC server
func (r *RPCStorageHandler) GetApiDefinitions(orgId string, tags []string) string {
dr := model.DefRequest{
OrgId: orgId,
Tags: tags,
LoadOAS: true,
}
defString, err := rpc.FuncClientSingleton("GetApiDefinitions", dr)
if err != nil {
rpc.EmitErrorEventKv(
rpc.FuncClientSingletonCall,
"GetApiDefinitions",
err,
map[string]string{
"orgId": orgId,
"tags": strings.Join(tags, ","),
},
)
if r.IsRetriableError(err) {
if rpc.Login() {
return r.GetApiDefinitions(orgId, tags)
}
}
return ""
}
log.Debug("API Definitions retrieved")
if defString == nil {
log.Warning("RPC Handler: GetApiDefinitions() returned nil, returning empty string")
return ""
}
return defString.(string)
}
Cognitive complexity: 10, Cyclomatic complexity: 5
func (*RPCStorageHandler) GetExp
func (r *RPCStorageHandler) GetExp(keyName string) (int64, error) {
log.Debug("GetExp called")
value, err := rpc.FuncClientSingleton("GetExp", r.fixKey(keyName))
if err != nil {
rpc.EmitErrorEventKv(
rpc.FuncClientSingletonCall,
"GetExp",
err,
map[string]string{
"keyName": keyName,
"fixedKeyName": r.fixKey(keyName),
},
)
if r.IsRetriableError(err) {
if rpc.Login() {
return r.GetExp(keyName)
}
}
log.Error("Error trying to get TTL: ", err)
return 0, storage.ErrKeyNotFound
}
return value.(int64), nil
}
Cognitive complexity: 7, Cyclomatic complexity: 4
func (*RPCStorageHandler) GetKey
GetKey will retrieve a key from the database
func (r *RPCStorageHandler) GetKey(keyName string) (string, error) {
start := time.Now() // get current time
// log.Debug("[STORE] Getting WAS: ", keyName)
// log.Debug("[STORE] Getting: ", r.fixKey(keyName))
value, err := r.GetRawKey(r.fixKey(keyName))
elapsed := time.Since(start)
log.Debug("GetKey took ", elapsed)
return value, err
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*RPCStorageHandler) GetKeyPrefix
func (r *RPCStorageHandler) GetKeyPrefix() string {
log.Error("RPCStorageHandler.GetKeyPrefix - Not implemented")
return ""
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*RPCStorageHandler) GetKeys
GetKeys will return all keys according to the filter (filter is a prefix - e.g. tyk.keys.*)
func (r *RPCStorageHandler) GetKeys(filter string) []string {
log.Error("RPCStorageHandler.GetKeys - Not Implemented")
return nil
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*RPCStorageHandler) GetKeysAndValues
GetKeysAndValues will return all keys and their values - not to be used lightly
func (r *RPCStorageHandler) GetKeysAndValues() map[string]string {
searchStr := r.KeyPrefix + "*"
kvPair, err := rpc.FuncClientSingleton("GetKeysAndValues", searchStr)
if err != nil {
rpc.EmitErrorEvent(rpc.FuncClientSingletonCall, "GetKeysAndValues", err)
if r.IsRetriableError(err) {
if rpc.Login() {
return r.GetKeysAndValues()
}
}
return nil
}
returnValues := make(map[string]string)
for i, v := range kvPair.(*model.KeysValuesPair).Keys {
returnValues[r.cleanKey(v)] = kvPair.(*model.KeysValuesPair).Values[i]
}
return returnValues
}
Cognitive complexity: 9, Cyclomatic complexity: 5
func (*RPCStorageHandler) GetKeysAndValuesWithFilter
GetKeysAndValuesWithFilter will return all keys and their values with a filter
func (r *RPCStorageHandler) GetKeysAndValuesWithFilter(filter string) map[string]string {
searchStr := r.KeyPrefix + r.hashKey(filter) + "*"
log.Debug("[STORE] Getting list by: ", searchStr)
kvPair, err := rpc.FuncClientSingleton("GetKeysAndValuesWithFilter", searchStr)
if err != nil {
rpc.EmitErrorEventKv(
rpc.FuncClientSingletonCall,
"GetKeysAndValuesWithFilter",
err,
map[string]string{
"searchStr": searchStr,
},
)
if r.IsRetriableError(err) {
if rpc.Login() {
return r.GetKeysAndValuesWithFilter(filter)
}
}
return nil
}
returnValues := make(map[string]string)
for i, v := range kvPair.(*model.KeysValuesPair).Keys {
returnValues[r.cleanKey(v)] = kvPair.(*model.KeysValuesPair).Values[i]
}
return returnValues
}
Cognitive complexity: 10, Cyclomatic complexity: 5
func (*RPCStorageHandler) GetListRange
func (r *RPCStorageHandler) GetListRange(keyName string, from, to int64) ([]string, error) {
log.Error("Not implemented")
return nil, nil
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*RPCStorageHandler) GetMultiKey
func (r *RPCStorageHandler) GetMultiKey(keyNames []string) ([]string, error) {
var err error
var value string
for _, key := range keyNames {
value, err = r.GetKey(key)
if err == nil {
return []string{value}, nil
}
}
return nil, err
}
Cognitive complexity: 6, Cyclomatic complexity: 3
func (*RPCStorageHandler) GetPolicies
GetPolicies will pull Policies from the RPC server
func (r *RPCStorageHandler) GetPolicies(orgId string) string {
defString, err := rpc.FuncClientSingleton("GetPolicies", orgId)
if err != nil {
rpc.EmitErrorEventKv(
rpc.FuncClientSingletonCall,
"GetPolicies",
err,
map[string]string{
"orgId": orgId,
},
)
if r.IsRetriableError(err) {
if rpc.Login() {
return r.GetPolicies(orgId)
}
}
return ""
}
if defString != nil {
return defString.(string)
}
return ""
}
Cognitive complexity: 9, Cyclomatic complexity: 5
func (*RPCStorageHandler) GetRawKey
func (r *RPCStorageHandler) GetRawKey(keyName string) (string, error) {
cacheEnabled := r.Gw.GetConfig().SlaveOptions.EnableRPCCache
var cacheStore cache.Repository
if cacheEnabled {
cacheStore = r.Gw.RPCGlobalCache
if strings.Contains(keyName, "cert-") {
cacheStore = r.Gw.RPCCertCache
}
if cachedVal, found := cacheStore.Get(keyName); found {
switch typedVal := cachedVal.(type) {
case string:
return typedVal, nil
case error:
return "", typedVal
}
}
}
if rpc.IsEmergencyMode() {
return "", storage.ErrMDCBConnectionLost
}
value, err := rpc.FuncClientSingleton("GetKey", keyName)
if err != nil {
rpc.EmitErrorEventKv(
rpc.FuncClientSingletonCall,
"GetKey",
err,
map[string]string{"keyName": keyName},
)
if r.IsRetriableError(err) && rpc.Login() {
return r.GetRawKey(keyName)
}
if cacheEnabled {
// Errors, and key not found, should be cached for a small amount of time
cacheStore.Set(keyName, storage.ErrKeyNotFound, 1)
}
return "", storage.ErrKeyNotFound
}
if cacheEnabled {
cacheStore.Set(keyName, value, cache.DefaultExpiration)
}
return value.(string), nil
}
Cognitive complexity: 21, Cyclomatic complexity: 13
func (*RPCStorageHandler) GetRollingWindow
func (r *RPCStorageHandler) GetRollingWindow(keyName string, per int64, pipeline bool) (int, []interface{}) {
log.Warning("Not Implemented!")
return 0, nil
}
Cognitive complexity: 1, Cyclomatic complexity: 1
func (*RPCStorageHandler) GetSortedSetRange
func (r *RPCStorageHandler) GetSortedSetRange(keyName, scoreFrom, scoreTo string) ([]string, []float64, error) {
return r.Gw.handleGetSortedSetRange(keyName, scoreFrom, scoreTo)
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*RPCStorageHandler) IncrememntWithExpire
IncrementWithExpire will increment a key in redis
func (r *RPCStorageHandler) IncrememntWithExpire(keyName string, expire int64) int64 {
ibd := model.InboundData{
KeyName: keyName,
Expire: expire,
}
val, err := rpc.FuncClientSingleton("IncrememntWithExpire", ibd)
if err != nil {
rpc.EmitErrorEventKv(
rpc.FuncClientSingletonCall,
"IncrememntWithExpire",
err,
map[string]string{
"keyName": keyName,
},
)
}
if r.IsRetriableError(err) {
if rpc.Login() {
return r.IncrememntWithExpire(keyName, expire)
}
}
if val == nil {
log.Warning("RPC increment returned nil value, returning 0")
return 0
}
return val.(int64)
}
Cognitive complexity: 10, Cyclomatic complexity: 5
func (*RPCStorageHandler) ProcessKeySpaceChanges
ProcessKeySpaceChanges receives an array of keys to be processed, those keys are considered changes in the keyspace in the management layer, they could be: regular keys (hashed, unhashed), revoke oauth client, revoke single oauth token, certificates (added, removed), oauth client (added, updated, removed), user key events (reset)
func (r *RPCStorageHandler) ProcessKeySpaceChanges(keys []string, orgId string) {
keysToReset := map[string]bool{}
TokensToBeRevoked := map[string]string{}
ClientsToBeRevoked := map[string]string{}
notRegularKeys := map[string]bool{}
CertificatesToRemove := map[string]string{}
CertificatesToAdd := map[string]string{}
OauthClients := map[string]string{}
apiIDsToDeleteCache := make([]string, 0)
userKeyResets := make(map[string]string)
for _, key := range keys {
splitKeys := strings.Split(key, ":")
if len(splitKeys) > 1 {
action := splitKeys[len(splitKeys)-1]
switch action {
case ResetQuota:
keysToReset[splitKeys[0]] = true
case CertificateRemoved:
CertificatesToRemove[key] = splitKeys[0]
notRegularKeys[key] = true
case CertificateAdded:
CertificatesToAdd[key] = splitKeys[0]
notRegularKeys[key] = true
case OAuthRevokeToken, OAuthRevokeAccessToken, OAuthRevokeRefreshToken:
TokensToBeRevoked[splitKeys[0]] = key
notRegularKeys[key] = true
case OAuthRevokeAllTokens:
ClientsToBeRevoked[splitKeys[1]] = key
notRegularKeys[key] = true
case OauthClientAdded, OauthClientUpdated, OauthClientRemoved:
OauthClients[splitKeys[0]] = action
notRegularKeys[key] = true
case NoticeDeleteAPICache.String():
apiIDsToDeleteCache = append(apiIDsToDeleteCache, splitKeys[0])
notRegularKeys[key] = true
case NoticeUserKeyReset.String():
keyParts := strings.Split(splitKeys[0], ".")
if len(keyParts) != 2 {
log.Error("Invalid user key reset format")
continue
}
userKeyResets[keyParts[0]] = keyParts[1]
default:
log.Debug("ignoring processing of action:", action)
}
}
}
for oldKey, newKey := range userKeyResets {
if r.Gw.GetConfig().SlaveOptions.APIKey == oldKey {
config := r.Gw.GetConfig()
// Updating the key in the KV store if we are using one
r.Gw.updateKeyInStore(config.Private.EdgeOriginalAPIKeyPath, newKey)
config.SlaveOptions.APIKey = newKey
r.Gw.SetConfig(config)
connected := r.Connect()
if !connected {
log.Error("Failed to reconnect to RPC storage")
continue
}
}
ok := r.Gw.MainNotifier.Notify(Notification{
Command: NoticeUserKeyReset,
Payload: fmt.Sprintf("%s.%s:%s", oldKey, newKey, NoticeUserKeyReset),
Gw: r.Gw,
})
if !ok {
log.Error("Failed to notify other gateways about user key reset")
}
}
// Process OAuth clients
r.Gw.ProcessOauthClientsOps(OauthClients)
for clientId, key := range ClientsToBeRevoked {
splitKeys := strings.Split(key, ":")
apiId := splitKeys[0]
clientSecret := splitKeys[2]
storage, _, err := r.Gw.GetStorageForApi(apiId)
if err != nil {
continue
}
_, tokens, _ := RevokeAllTokens(storage, clientId, clientSecret)
keys = append(keys, tokens...)
}
//single and specific tokens
for token, key := range TokensToBeRevoked {
//key formed as: token:apiId:tokenActionTypeHint
//but hashed as: token#hashed:apiId:tokenActionTypeHint
splitKeys := strings.Split(key, ":")
apiId := splitKeys[1]
tokenActionTypeHint := splitKeys[2]
hashedKey := strings.Contains(token, "#hashed")
if !hashedKey {
storage, _, err := r.Gw.GetStorageForApi(apiId)
if err != nil {
continue
}
var tokenTypeHint string
switch tokenActionTypeHint {
case OAuthRevokeAccessToken:
tokenTypeHint = "access_token"
case OAuthRevokeRefreshToken:
tokenTypeHint = "refresh_token"
}
RevokeToken(storage, token, tokenTypeHint)
} else {
token = strings.Split(token, "#")[0]
r.Gw.handleDeleteHashedKey(token, orgId, apiId, false)
}
r.Gw.SessionCache.Delete(token)
r.Gw.RPCGlobalCache.Delete(r.KeyPrefix + token)
}
// remove certs
for _, certId := range CertificatesToRemove {
log.Debugf("Removing certificate: %v", certId)
r.Gw.CertificateManager.Delete(certId, orgId)
r.Gw.RPCCertCache.Delete("cert-raw-" + certId)
}
for _, certId := range CertificatesToAdd {
log.Debugf("Adding certificate: %v", certId)
//If we are in a slave node, MDCB Storage GetRaw should get the certificate from MDCB and cache it locally
content, err := r.Gw.CertificateManager.GetRaw(certId)
if content == "" && err != nil {
log.Debugf("Error getting certificate content")
}
}
synchronizerEnabled := r.Gw.GetConfig().SlaveOptions.SynchroniserEnabled
for _, key := range keys {
// Skip keys that are user keys to be reset
splitKeys := strings.Split(key, ":")
if len(splitKeys) > 1 {
userKeys := strings.Split(splitKeys[0], ".")
if len(userKeys) == 2 {
_, ok := userKeyResets[userKeys[0]]
if ok {
continue
}
}
}
_, isOauthTokenKey := notRegularKeys[key]
if !isOauthTokenKey {
splitKeys := strings.Split(key, ":")
_, resetQuota := keysToReset[splitKeys[0]]
isHashed := len(splitKeys) > 1 && splitKeys[1] == "hashed"
var status int
var err error
if isHashed {
log.Info("--> removing cached (hashed) key: ", splitKeys[0])
key = splitKeys[0]
_, status = r.Gw.handleDeleteHashedKey(key, orgId, "", resetQuota)
} else {
log.Info("--> removing cached key: ", r.Gw.obfuscateKey(key))
// in case it's a username (basic auth) or custom-key then generate the token
if storage.TokenOrg(key) == "" {
key = r.Gw.generateToken(orgId, key)
}
_, status = r.Gw.handleDeleteKey(key, orgId, "-1", resetQuota)
// check if we must remove the key by custom key id
status, err = r.deleteUsingTokenID(key, orgId, resetQuota, status)
if err != nil {
log.Debugf("cannot remove key:%v status: %v", key, status)
}
}
// if key not found locally and synchroniser disabled then we should not pull it from management layer
if status == http.StatusNotFound && !synchronizerEnabled {
continue
}
r.Gw.getSessionAndCreate(key, r, isHashed, orgId)
r.Gw.SessionCache.Delete(key)
r.Gw.RPCGlobalCache.Delete(r.KeyPrefix + key)
}
}
for _, apiID := range apiIDsToDeleteCache {
if r.Gw.invalidateAPICache(apiID) {
log.WithField("apiID", apiID).Info("cache invalidated")
continue
}
log.WithField("apiID", apiID).Error("cache invalidation failed")
}
// Notify rest of gateways in cluster to flush cache
n := Notification{
Command: KeySpaceUpdateNotification,
Payload: strings.Join(keys, ","),
Gw: r.Gw,
}
r.Gw.MainNotifier.Notify(n)
}
Cognitive complexity: 87, Cyclomatic complexity: 42
func (*RPCStorageHandler) Publish
func (r *RPCStorageHandler) Publish(channel, message string) error {
log.Warning("RPCStorageHandler.Publish - NO PUBSUB DEFINED")
return nil
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*RPCStorageHandler) RemoveFromList
func (r *RPCStorageHandler) RemoveFromList(keyName, value string) error {
log.Error("Not implemented")
return nil
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*RPCStorageHandler) RemoveSortedSetRange
func (r *RPCStorageHandler) RemoveSortedSetRange(keyName, scoreFrom, scoreTo string) error {
return r.Gw.handleRemoveSortedSetRange(keyName, scoreFrom, scoreTo)
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*RPCStorageHandler) SetExp
func (r *RPCStorageHandler) SetExp(keyName string, timeout int64) error {
log.Error("RPCStorageHandler.SetExp - Not Implemented")
return nil
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*RPCStorageHandler) SetKey
SetKey will create (or update) a key value in the store
func (r *RPCStorageHandler) SetKey(keyName, session string, timeout int64) error {
start := time.Now() // get current time
ibd := model.InboundData{
KeyName: r.fixKey(keyName),
SessionState: session,
Timeout: timeout,
}
_, err := rpc.FuncClientSingleton("SetKey", ibd)
if err != nil {
rpc.EmitErrorEventKv(
rpc.FuncClientSingletonCall,
"SetKey",
err,
map[string]string{
"keyName": keyName,
"fixedKeyName": ibd.KeyName,
},
)
if r.IsRetriableError(err) {
if rpc.Login() {
return r.SetKey(keyName, session, timeout)
}
}
log.Debug("Error trying to set value:", err)
return err
}
elapsed := time.Since(start)
log.Debug("SetKey took ", elapsed)
return nil
}
Cognitive complexity: 8, Cyclomatic complexity: 4
func (*RPCStorageHandler) SetRawKey
func (r *RPCStorageHandler) SetRawKey(keyName, session string, timeout int64) error {
return nil
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*RPCStorageHandler) SetRollingWindow
SetScrollingWindow is used in the rate limiter to handle rate limits fairly.
func (r *RPCStorageHandler) SetRollingWindow(keyName string, per int64, val string, pipeline bool) (int, []interface{}) {
start := time.Now() // get current time
ibd := model.InboundData{
KeyName: keyName,
Per: per,
Expire: -1,
}
intVal, err := rpc.FuncClientSingleton("SetRollingWindow", ibd)
if err != nil {
rpc.EmitErrorEventKv(
rpc.FuncClientSingletonCall,
"SetRollingWindow",
err,
map[string]string{
"keyName": keyName,
"per": strconv.Itoa(int(per)),
},
)
if r.IsRetriableError(err) {
if rpc.Login() {
return r.SetRollingWindow(keyName, per, val, false)
}
}
}
elapsed := time.Since(start)
log.Debug("SetRollingWindow took ", elapsed)
if intVal == nil {
log.Warning("RPC Handler: SetRollingWindow() returned nil, returning 0")
return 0, nil
}
return intVal.(int), nil
}
Cognitive complexity: 11, Cyclomatic complexity: 5
func (*RPCStorageHandler) StartPubSubHandler
StartPubSubHandler will listen for a signal and run the callback with the message
func (r *RPCStorageHandler) StartPubSubHandler(_ string, _ func(*temporalmodel.Message)) error {
log.Warning("RPCStorageHandler.StartPubSubHandler - NO PUBSUB DEFINED")
return nil
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*RPCStorageHandler) StartRPCKeepaliveWatcher
func (r *RPCStorageHandler) StartRPCKeepaliveWatcher() {
log.WithFields(logrus.Fields{
"prefix": "RPC Conn Mgr",
}).Info("[RPC Conn Mgr] Starting keepalive watcher...")
for {
select {
case <-r.Gw.ctx.Done():
return
default:
}
if err := r.SetKey("0000", "0000", 10); err != nil {
log.WithError(err).WithFields(logrus.Fields{
"prefix": "RPC Conn Mgr",
}).Warning("Can't connect to RPC layer")
if r.IsRetriableError(err) {
if rpc.Login() {
continue
}
}
if strings.Contains(err.Error(), "Cannot obtain response during timeout") {
continue
}
}
time.Sleep(10 * time.Second)
}
}
Cognitive complexity: 14, Cyclomatic complexity: 7
func (*RPCStorageHandler) StartRPCLoopCheck
func (r *RPCStorageHandler) StartRPCLoopCheck(orgId string) {
if r.Gw.GetConfig().SlaveOptions.DisableKeySpaceSync {
return
}
log.Info("[RPC] Starting keyspace poller")
for {
seconds := r.Gw.GetConfig().SlaveOptions.KeySpaceSyncInterval
r.CheckForKeyspaceChanges(orgId)
time.Sleep(time.Duration(seconds) * time.Second)
}
}
Cognitive complexity: 4, Cyclomatic complexity: 3
func (*RateCheckMW) Name
func (m *RateCheckMW) Name() string {
return "RateCheckMW"
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*RateCheckMW) ProcessRequest
func (m *RateCheckMW) ProcessRequest(w http.ResponseWriter, r *http.Request, _ interface{}) (error, int) {
// Let's track r/ps
GlobalRate.Incr(1)
return nil, http.StatusOK
}
Cognitive complexity: 1, Cyclomatic complexity: 1
func (*RateLimitAndQuotaCheck) EnabledForSpec
func (k *RateLimitAndQuotaCheck) EnabledForSpec() bool {
return !k.Spec.DisableRateLimit || !k.Spec.DisableQuota
}
Cognitive complexity: 0, Cyclomatic complexity: 2
func (*RateLimitAndQuotaCheck) Name
func (k *RateLimitAndQuotaCheck) Name() string {
return "RateLimitAndQuotaCheck"
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*RateLimitAndQuotaCheck) ProcessRequest
ProcessRequest will run any checks on the request on the way through the system, return an error to have the chain fail
func (k *RateLimitAndQuotaCheck) ProcessRequest(w http.ResponseWriter, r *http.Request, _ interface{}) (error, int) {
if ctxGetRequestStatus(r) == StatusOkAndIgnore {
return nil, http.StatusOK
}
// Skip rate limiting and quotas for looping
if !ctxCheckLimits(r) {
return nil, http.StatusOK
}
session := ctxGetSession(r)
rateLimitKey := ctxGetAuthToken(r)
quotaKey := ""
if pattern, found := session.MetaData["rate_limit_pattern"]; found {
if patternString, ok := pattern.(string); ok && patternString != "" {
if customKeyValue := k.Gw.ReplaceTykVariables(r, patternString, false); customKeyValue != "" {
rateLimitKey = customKeyValue
quotaKey = customKeyValue
}
}
}
storeRef := k.Gw.GlobalSessionManager.Store()
reason := k.Gw.SessionLimiter.ForwardMessage(
r,
session,
rateLimitKey,
quotaKey,
storeRef,
!k.Spec.DisableRateLimit,
!k.Spec.DisableQuota,
k.Spec,
false,
)
throttleRetryLimit := session.ThrottleRetryLimit
throttleInterval := session.ThrottleInterval
if len(session.AccessRights) > 0 {
if rights, ok := session.AccessRights[k.Spec.APIID]; ok {
if !rights.Limit.IsEmpty() {
throttleInterval = rights.Limit.ThrottleInterval
throttleRetryLimit = rights.Limit.ThrottleRetryLimit
}
}
}
k.emitRateLimitEvents(r, rateLimitKey)
switch reason {
case sessionFailNone:
case sessionFailRateLimit:
err, errCode := k.handleRateLimitFailure(r, event.RateLimitExceeded, "Rate Limit Exceeded", rateLimitKey)
if throttleRetryLimit > 0 {
for {
ctxIncThrottleLevel(r, throttleRetryLimit)
time.Sleep(time.Duration(throttleInterval * float64(time.Second)))
reason = k.Gw.SessionLimiter.ForwardMessage(
r,
session,
rateLimitKey,
quotaKey,
storeRef,
!k.Spec.DisableRateLimit,
!k.Spec.DisableQuota,
k.Spec,
true,
)
log.WithFields(logrus.Fields{
"middleware": "RateLimitAndQuotaCheck",
"func": "ProcessRequest",
}).Debugf("after dry-run (reason: '%s')", reason)
if ctxThrottleLevel(r) > throttleRetryLimit {
break
}
if reason == sessionFailNone {
return k.ProcessRequest(w, r, nil)
}
}
}
return err, errCode
case sessionFailQuota:
return k.handleQuotaFailure(r, rateLimitKey)
case sessionFailInternalServerError:
return ProxyingRequestFailedErr, http.StatusInternalServerError
default:
// Other reason? Still not allowed
return errors.New("Access denied"), http.StatusForbidden
}
// Run the trigger monitor
if k.Spec.GlobalConfig.Monitor.MonitorUserKeys {
k.Gw.SessionMonitor.Check(session, rateLimitKey)
}
// Request is valid, carry on
return nil, http.StatusOK
}
Cognitive complexity: 34, Cyclomatic complexity: 20
func (*RateLimitForAPI) EnabledForSpec
func (k *RateLimitForAPI) EnabledForSpec() bool {
if !k.shouldEnable() {
return false
}
// We'll init here
k.keyName = "apilimiter-" + k.Spec.OrgID + k.Spec.APIID
// Set last updated on each load to ensure we always use a new rate limit bucket
k.apiSess = &user.SessionState{
Rate: k.Spec.GlobalRateLimit.Rate,
Per: k.Spec.GlobalRateLimit.Per,
LastUpdated: strconv.Itoa(int(time.Now().UnixNano())),
}
k.apiSess.SetKeyHash(storage.HashKey(k.keyName, k.Gw.GetConfig().HashKeys))
return true
}
Cognitive complexity: 3, Cyclomatic complexity: 2
func (*RateLimitForAPI) Name
func (k *RateLimitForAPI) Name() string {
return "RateLimitForAPI"
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*RateLimitForAPI) ProcessRequest
ProcessRequest will run any checks on the request on the way through the system, return an error to have the chain fail
func (k *RateLimitForAPI) ProcessRequest(w http.ResponseWriter, r *http.Request, _ interface{}) (error, int) {
// Skip rate limiting and quotas for looping
if !ctxCheckLimits(r) {
return nil, http.StatusOK
}
storeRef := k.Gw.GlobalSessionManager.Store()
customQuotaKey := ""
reason := k.Gw.SessionLimiter.ForwardMessage(
r,
k.getSession(r),
k.keyName,
customQuotaKey,
storeRef,
true,
false,
k.Spec,
false,
)
k.emitRateLimitEvents(r, k.keyName)
if reason == sessionFailRateLimit {
return k.handleRateLimitFailure(r, event.RateLimitExceeded, "API Rate Limit Exceeded", k.keyName)
}
// Request is valid, carry on
return nil, http.StatusOK
}
Cognitive complexity: 5, Cyclomatic complexity: 3
func (*RedisAnalyticsHandler) Flush
Flush will stop the analytics processing and empty the analytics buffer and then re-init the workers again
func (r *RedisAnalyticsHandler) Flush() {
r.Stop()
r.Start()
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*RedisAnalyticsHandler) Init
func (r *RedisAnalyticsHandler) Init() {
r.globalConf = r.Gw.GetConfig()
if r.globalConf.AnalyticsConfig.EnableGeoIP {
if db, err := maxminddb.Open(r.globalConf.AnalyticsConfig.GeoIPDBLocation); err != nil {
log.Error("Failed to init GeoIP Database: ", err)
} else {
r.GeoIPDB = db
}
}
r.Store.Connect()
ps := r.Gw.GetConfig().AnalyticsConfig.PoolSize
recordsBufferSize := r.globalConf.AnalyticsConfig.RecordsBufferSize
r.workerBufferSize = recordsBufferSize / uint64(ps)
log.WithField("workerBufferSize", r.workerBufferSize).Debug("Analytics pool worker buffer size")
r.enableMultipleAnalyticsKeys = r.globalConf.AnalyticsConfig.EnableMultipleAnalyticsKeys
r.analyticsSerializer = serializer.NewAnalyticsSerializer(r.globalConf.AnalyticsConfig.SerializerType)
r.Start()
}
Cognitive complexity: 6, Cyclomatic complexity: 3
func (*RedisAnalyticsHandler) RecordHit
RecordHit will store an analytics.Record in Redis
func (r *RedisAnalyticsHandler) RecordHit(record *analytics.AnalyticsRecord) error {
if r.mockEnabled {
r.mockRecordHit(record)
return nil
}
// check if we should stop sending records 1st
if atomic.LoadUint32(&r.shouldStop) > 0 {
return nil
}
// just send record to channel consumed by pool of workers
// leave all data crunching and Redis I/O work for pool workers
r.mu.Lock()
r.recordsChan <- record
r.mu.Unlock()
return nil
}
Cognitive complexity: 4, Cyclomatic complexity: 3
func (*RedisAnalyticsHandler) Start
Start initialize the records channel and spawn the record workers
func (r *RedisAnalyticsHandler) Start() {
r.recordsChan = make(chan *analytics.AnalyticsRecord, r.globalConf.AnalyticsConfig.RecordsBufferSize)
atomic.SwapUint32(&r.shouldStop, 0)
for i := 0; i < r.Gw.GetConfig().AnalyticsConfig.PoolSize; i++ {
r.poolWg.Add(1)
go r.recordWorker()
}
}
Cognitive complexity: 2, Cyclomatic complexity: 2
func (*RedisAnalyticsHandler) Stop
Stop stops the analytics processing
func (r *RedisAnalyticsHandler) Stop() {
// flag to stop sending records into channel
atomic.SwapUint32(&r.shouldStop, 1)
// close channel to stop workers
r.mu.Lock()
close(r.recordsChan)
r.mu.Unlock()
// wait for all workers to be done
r.poolWg.Wait()
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*RedisCacheMiddleware) CreateCheckSum
func (m *RedisCacheMiddleware) CreateCheckSum(req *http.Request, keyName string, regex string, additionalKeyFromHeaders string) (string, error) {
h := md5.New()
// Compose key into string
key := req.Method + "-" + req.URL.String()
if additionalKeyFromHeaders != "" {
key = key + "-" + additionalKeyFromHeaders
}
_, err := io.WriteString(h, key)
if err != nil {
return "", err
}
if e := addBodyHash(req, regex, h); e != nil {
return "", e
}
reqChecksum := hex.EncodeToString(h.Sum(nil))
return m.Spec.APIID + keyName + reqChecksum, nil
}
Cognitive complexity: 6, Cyclomatic complexity: 4
func (*RedisCacheMiddleware) EnabledForSpec
func (m *RedisCacheMiddleware) EnabledForSpec() bool {
return m.Spec.CacheOptions.EnableCache
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*RedisCacheMiddleware) Init
func (m *RedisCacheMiddleware) Init() {
m.sh = SuccessHandler{m.BaseMiddleware}
}
Cognitive complexity: 1, Cyclomatic complexity: 1
func (*RedisCacheMiddleware) Name
func (m *RedisCacheMiddleware) Name() string {
return "RedisCacheMiddleware"
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*RedisCacheMiddleware) ProcessRequest
ProcessRequest will run any checks on the request on the way through the system, return an error to have the chain fail
func (m *RedisCacheMiddleware) ProcessRequest(w http.ResponseWriter, r *http.Request, _ interface{}) (error, int) {
t1 := time.Now()
var stat RequestStatus
var cacheKeyRegex string
var cacheMeta *EndPointCacheMeta
version, _ := m.Spec.Version(r)
versionPaths := m.Spec.RxPaths[version.Name]
// Lets see if we can throw a sledgehammer at this
if m.Spec.CacheOptions.CacheAllSafeRequests && isSafeMethod(r.Method) {
stat = StatusCached
}
if stat != StatusCached {
// New request checker, more targeted, less likely to fail
found, meta := m.Spec.CheckSpecMatchesStatus(r, versionPaths, Cached)
if found {
cacheMeta = meta.(*EndPointCacheMeta)
stat = StatusCached
cacheKeyRegex = cacheMeta.CacheKeyRegex
}
}
// Cached route matched, let go
if stat != StatusCached {
m.Logger().Debug("Not a cached path")
return nil, http.StatusOK
}
token := ctxGetAuthToken(r)
// No authentication data? use the IP.
if token == "" {
token = request.RealIP(r)
}
var retBlob string
key, err := m.CreateCheckSum(r, token, cacheKeyRegex, m.getCacheKeyFromHeaders(r))
if err != nil {
m.Logger().Debug("Error creating checksum. Skipping cache check")
return nil, http.StatusOK
}
cacheOnlyResponseCodes := m.Spec.CacheOptions.CacheOnlyResponseCodes
timeout := m.Spec.CacheOptions.CacheTimeout
if cacheMeta != nil {
// override api level CacheOnlyResponseCodes by endpoint level if provided
if len(cacheMeta.CacheOnlyResponseCodes) > 0 {
cacheOnlyResponseCodes = cacheMeta.CacheOnlyResponseCodes
}
// override api level Timout by endpoint level if provided
if cacheMeta.Timeout > 0 {
timeout = cacheMeta.Timeout
}
}
ctxSetCacheOptions(r, &cacheOptions{
key: key,
cacheOnlyResponseCodes: cacheOnlyResponseCodes,
timeout: timeout,
})
retBlob, err = m.store.GetKey(key)
if err != nil {
// Record not found, continue with the middleware chain
return nil, http.StatusOK
}
cachedData, timestamp, err := m.decodePayload(retBlob)
if err != nil {
// Tere was an issue with this cache entry - lets remove it:
m.store.DeleteKey(key)
return nil, http.StatusOK
}
if m.isTimeStampExpired(timestamp) || len(cachedData) == 0 {
m.store.DeleteKey(key)
return nil, http.StatusOK
}
bufData := bufio.NewReader(strings.NewReader(cachedData))
newRes, err := http.ReadResponse(bufData, r)
if err != nil {
m.Logger().WithError(err).Error("Could not create response object")
m.store.DeleteKey(key)
return nil, http.StatusOK
}
nopCloseResponseBody(newRes)
defer newRes.Body.Close()
for _, h := range hopHeaders {
newRes.Header.Del(h)
}
session := ctxGetSession(r)
// Only add ratelimit data to keyed sessions
if session != nil {
quotaMax, quotaRemaining, _, quotaRenews := session.GetQuotaLimitByAPIID(m.Spec.APIID)
newRes.Header.Set(header.XRateLimitLimit, strconv.Itoa(int(quotaMax)))
newRes.Header.Set(header.XRateLimitRemaining, strconv.Itoa(int(quotaRemaining)))
newRes.Header.Set(header.XRateLimitReset, strconv.Itoa(int(quotaRenews)))
}
newRes.Header.Set(cachedResponseHeader, "1")
copyHeader(w.Header(), newRes.Header, m.Gw.GetConfig().IgnoreCanonicalMIMEHeaderKey)
if reqEtag := r.Header.Get("If-None-Match"); reqEtag != "" {
if respEtag := newRes.Header.Get("Etag"); respEtag != "" {
if strings.Contains(reqEtag, respEtag) {
newRes.StatusCode = http.StatusNotModified
}
}
}
w.WriteHeader(newRes.StatusCode)
if newRes.StatusCode != http.StatusNotModified {
m.Proxy.CopyResponse(w, newRes.Body, 0)
}
// Record analytics
if !m.Spec.DoNotTrack {
ms := DurationToMillisecond(time.Since(t1))
m.sh.RecordHit(r, analytics.Latency{Total: int64(ms)}, newRes.StatusCode, newRes, true)
}
// Stop any further execution after we wrote cache out
return nil, mwStatusRespond
}
Cognitive complexity: 44, Cyclomatic complexity: 23
func (*RedisNotifier) Notify
Notify will send a notification to a channel
func (r *RedisNotifier) Notify(notif interface{}) bool {
if n, ok := notif.(Notification); ok {
n.Sign()
notif = n
}
toSend, err := json.Marshal(notif)
if err != nil {
pubSubLog.Error("Problem marshalling notification: ", err)
return false
}
// pubSubLog.Debug("Sending notification", notif)
if err := r.store.Publish(r.channel, string(toSend)); err != nil {
if !errors.Is(err, storage.ErrRedisIsDown) {
pubSubLog.Error("Could not send notification: ", err)
}
return false
}
return true
}
Cognitive complexity: 9, Cyclomatic complexity: 5
func (*RedisOsinStorageInterface) Clone
func (r *RedisOsinStorageInterface) Clone() osin.Storage {
return r
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*RedisOsinStorageInterface) Close
func (r *RedisOsinStorageInterface) Close() {}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*RedisOsinStorageInterface) DeleteClient
DeleteClient Removes a client from the system
func (r *RedisOsinStorageInterface) DeleteClient(id string, orgID string, ignorePrefix bool) error {
key := prefixClient + id
if ignorePrefix {
key = id
}
// Get the raw vals:
clientJSON, err := r.store.GetKey(key)
keyForSet := prefixClientset + prefixClient // Org ID
if err == nil {
log.Debug("Removing from set")
r.store.RemoveFromSet(keyForSet, clientJSON)
}
r.store.DeleteKey(key)
indexKey := prefixClientIndexList + orgID
// delete from oauth client
r.store.RemoveFromList(indexKey, key)
// delete list of tokens for this client
r.store.DeleteKey(prefixClientTokens + id)
if r.Gw.GetConfig().SlaveOptions.UseRPC {
r.redisStore.RemoveFromList(indexKey, key)
r.redisStore.DeleteKey(prefixClientTokens + id)
r.redisStore.RemoveFromSet(keyForSet, clientJSON)
}
return nil
}
Cognitive complexity: 6, Cyclomatic complexity: 4
func (*RedisOsinStorageInterface) GetClient
GetClient will retrieve client data
func (r *RedisOsinStorageInterface) GetClient(id string) (osin.Client, error) {
key := prefixClient + id
log.Debug("Getting client ID:", id)
clientJSON, err := r.store.GetKey(key)
if err != nil {
log.Debugf("Failure retrieving client ID key %q: %v", key, err)
return nil, err
}
client := new(OAuthClient)
if err := json.Unmarshal([]byte(clientJSON), &client); err != nil {
log.Debug("Couldn't unmarshal OAuth client object: ", err)
}
return client, nil
}
Cognitive complexity: 4, Cyclomatic complexity: 3
func (*RedisOsinStorageInterface) GetClientNoPrefix
GetClientNoPrefix will retrieve client data, but not assign a prefix - this is an unfortunate hack, but we don't want to change the signature in Osin for GetClient to support the odd Redis prefixing
func (r *RedisOsinStorageInterface) GetClientNoPrefix(id string) (osin.Client, error) {
key := id
clientJSON, err := r.store.GetKey(key)
if err != nil {
log.Error("Failure retrieving client ID key: ", err)
return nil, err
}
client := new(OAuthClient)
if err := json.Unmarshal([]byte(clientJSON), client); err != nil {
log.Error("Couldn't unmarshal OAuth client object: ", err)
}
return client, nil
}
Cognitive complexity: 4, Cyclomatic complexity: 3
func (*RedisOsinStorageInterface) GetClientTokens
func (r *RedisOsinStorageInterface) GetClientTokens(id string) ([]OAuthClientToken, error) {
key := prefixClientTokens + id
// use current timestamp as a start score so all expired tokens won't be picked
nowTs := time.Now().Unix()
startScore := strconv.FormatInt(nowTs, 10)
log.Info("Getting client tokens sorted list:", key)
tokens, scores, err := r.redisStore.GetSortedSetRange(key, startScore, "+inf")
if err != nil {
return nil, err
}
// clean up expired tokens in sorted set (remove all tokens with score up to current timestamp minus retention)
if r.Gw.GetConfig().OauthTokenExpiredRetainPeriod > 0 {
cleanupStartScore := strconv.FormatInt(nowTs-int64(r.Gw.GetConfig().OauthTokenExpiredRetainPeriod), 10)
go r.redisStore.RemoveSortedSetRange(key, "-inf", cleanupStartScore)
}
if len(tokens) == 0 {
return []OAuthClientToken{}, nil
}
// convert sorted set data and scores into reply struct
tokensData := make([]OAuthClientToken, len(tokens))
for i := range tokens {
tokensData[i] = OAuthClientToken{
Token: tokens[i],
Expires: int64(scores[i]), // we store expire timestamp as a score
}
}
return tokensData, nil
}
Cognitive complexity: 11, Cyclomatic complexity: 5
func (*RedisOsinStorageInterface) GetClients
GetClients will retrieve a list of clients for a prefix
func (r *RedisOsinStorageInterface) GetClients(filter string, orgID string, ignorePrefix bool) ([]ExtendedOsinClientInterface, error) {
key := prefixClient + filter
if ignorePrefix {
key = filter
}
indexKey := prefixClientIndexList + orgID
var clientJSON map[string]string
if !r.Gw.GetConfig().Storage.EnableCluster {
exists, _ := r.store.Exists(indexKey)
if exists {
keys, err := r.store.GetListRange(indexKey, 0, -1)
if err != nil {
log.Error("Couldn't get OAuth client index list: ", err)
return nil, err
}
keyVals, err := r.store.GetMultiKey(keys)
if err != nil {
log.Error("Couldn't get OAuth client index list values: ", err)
return nil, err
}
clientJSON = make(map[string]string)
for i, key := range keys {
clientJSON[key] = keyVals[i]
}
} else {
clientJSON = r.store.GetKeysAndValuesWithFilter(key)
for key := range clientJSON {
r.store.AppendToSet(indexKey, key)
}
}
} else {
keyForSet := prefixClientset + prefixClient // Org ID
var err error
if clientJSON, err = r.store.GetSet(keyForSet); err != nil {
return nil, err
}
}
theseClients := []ExtendedOsinClientInterface{}
for _, clientJSON := range clientJSON {
client := new(OAuthClient)
if err := json.Unmarshal([]byte(clientJSON), &client); err != nil {
log.Error("Couldn't unmarshal OAuth client object: ", err)
return nil, err
}
theseClients = append(theseClients, client)
}
return theseClients, nil
}
Cognitive complexity: 28, Cyclomatic complexity: 11
func (*RedisOsinStorageInterface) GetExtendedClient
func (r *RedisOsinStorageInterface) GetExtendedClient(id string) (ExtendedOsinClientInterface, error) {
osinClient, err := r.GetClient(id)
if err != nil {
log.WithError(err).Error("Failure retrieving client ID key")
return nil, err
}
return osinClient.(*OAuthClient), err
}
Cognitive complexity: 2, Cyclomatic complexity: 2
func (*RedisOsinStorageInterface) GetExtendedClientNoPrefix
GetExtendedClientNoPrefix custom getter to handle prefixing issues in Redis,
func (r *RedisOsinStorageInterface) GetExtendedClientNoPrefix(id string) (ExtendedOsinClientInterface, error) {
osinClient, err := r.GetClientNoPrefix(id)
if err != nil {
log.WithError(err).Error("Failure retrieving client ID key")
return nil, err
}
return osinClient.(*OAuthClient), err
}
Cognitive complexity: 2, Cyclomatic complexity: 2
func (*RedisOsinStorageInterface) GetPaginatedClientTokens
GetPaginatedClientTokens returns all tokens associated with the given id. It returns the tokens, the total number of pages of the tokens after pagination and an error if any
func (r *RedisOsinStorageInterface) GetPaginatedClientTokens(id string, page int) ([]OAuthClientToken, int, error) {
key := prefixClientTokens + id
// use current timestamp as a start score so all expired tokens won't be picked
nowTs := time.Now().Unix()
startScore := strconv.FormatInt(nowTs, 10)
log.Info("Getting client tokens sorted list:", key)
tokens, scores, err := r.store.GetSortedSetRange(key, startScore, "+inf")
if err != nil {
return nil, 0, err
}
// clean up expired tokens in sorted set (remove all tokens with score up to current timestamp minus retention)
if r.Gw.GetConfig().OauthTokenExpiredRetainPeriod > 0 {
cleanupStartScore := strconv.FormatInt(nowTs-int64(r.Gw.GetConfig().OauthTokenExpiredRetainPeriod), 10)
go r.store.RemoveSortedSetRange(key, "-inf", cleanupStartScore)
}
itemsPerPage := 100
tokenNumber := len(tokens)
if tokenNumber == 0 {
return []OAuthClientToken{}, 0, nil
}
startIdx := (page - 1) * itemsPerPage
endIdx := startIdx + itemsPerPage
if tokenNumber < startIdx {
startIdx = tokenNumber
}
if tokenNumber < endIdx {
endIdx = tokenNumber
}
totalPages := int(math.Ceil(float64(len(tokens)) / float64(itemsPerPage)))
tokens = tokens[startIdx:endIdx]
// convert sorted set data and scores into reply struct
tokensData := make([]OAuthClientToken, len(tokens))
for i := range tokens {
tokensData[i] = OAuthClientToken{
Token: tokens[i],
Expires: int64(scores[i]), // we store expire timestamp as a score
}
}
return tokensData, totalPages, nil
}
Cognitive complexity: 15, Cyclomatic complexity: 7
func (*RedisOsinStorageInterface) GetUser
LoadRefresh will load access data from Redis
func (r *RedisOsinStorageInterface) GetUser(username string) (*user.SessionState, error) {
key := username
log.Debug("Loading User key: ", key)
accessJSON, err := r.store.GetRawKey(key)
if err != nil {
log.Error("Failure retreiving access token by key: ", err)
return nil, err
}
// new interface means having to make this nested... ick.
session := &user.SessionState{}
if err := json.Unmarshal([]byte(accessJSON), session); err != nil {
log.Error("Couldn't unmarshal OAuth auth data object (LoadRefresh): ", err,
"; Decoding: ", accessJSON)
return nil, err
}
return session, nil
}
Cognitive complexity: 5, Cyclomatic complexity: 3
func (*RedisOsinStorageInterface) LoadAccess
LoadAccess will load access data from redis
func (r *RedisOsinStorageInterface) LoadAccess(token string) (*osin.AccessData, error) {
key := prefixAccess + storage.HashKey(token, r.Gw.GetConfig().HashKeys)
log.Debug("Loading ACCESS key: ", key)
accessJSON, err := r.store.GetKey(key)
if err != nil {
// Fallback to unhashed value for backward compatibility
key = prefixAccess + token
accessJSON, err = r.store.GetKey(key)
if err != nil {
log.Error("Failure retreiving access token by key: ", err)
return nil, err
}
}
accessData := osin.AccessData{Client: new(OAuthClient)}
if err := json.Unmarshal([]byte(accessJSON), &accessData); err != nil {
log.Error("Couldn't unmarshal OAuth auth data object (LoadAccess): ", err)
return nil, err
}
return &accessData, nil
}
Cognitive complexity: 7, Cyclomatic complexity: 4
func (*RedisOsinStorageInterface) LoadAuthorize
LoadAuthorize loads auth data from redis
func (r *RedisOsinStorageInterface) LoadAuthorize(code string) (*osin.AuthorizeData, error) {
key := prefixAuth + code
log.Debug("Loading auth code: ", key)
authJSON, err := r.store.GetKey(key)
if err != nil {
log.Error("Failure retreiving auth code key: ", err)
return nil, err
}
authData := osin.AuthorizeData{Client: new(OAuthClient)}
if err := json.Unmarshal([]byte(authJSON), &authData); err != nil {
log.Error("Couldn't unmarshal OAuth auth data object (LoadAuthorize): ", err)
return nil, err
}
return &authData, nil
}
Cognitive complexity: 5, Cyclomatic complexity: 3
func (*RedisOsinStorageInterface) LoadRefresh
LoadRefresh will load access data from Redis
func (r *RedisOsinStorageInterface) LoadRefresh(token string) (*osin.AccessData, error) {
key := prefixRefresh + token
log.Debug("Loading REFRESH key: ", key)
accessJSON, err := r.store.GetKey(key)
if err != nil {
log.Error("Failure retreiving access token by key: ", err)
return nil, err
}
// new interface means having to make this nested... ick.
accessData := osin.AccessData{Client: new(OAuthClient)}
if err := json.Unmarshal([]byte(accessJSON), &accessData); err != nil {
log.Error("Couldn't unmarshal OAuth auth data object (LoadRefresh): ", err,
"; Decoding: ", accessJSON)
return nil, err
}
return &accessData, nil
}
Cognitive complexity: 5, Cyclomatic complexity: 3
func (*RedisOsinStorageInterface) RemoveAccess
RemoveAccess will remove access data from Redis
func (r *RedisOsinStorageInterface) RemoveAccess(token string) error {
access, err := r.LoadAccess(token)
if err == nil {
key := prefixClientTokens + access.Client.GetId()
//remove from set oauth.client-tokens
log.Info("removing token from oauth client tokens list")
limit := strconv.FormatFloat(float64(access.ExpireAt().Unix()), 'f', 0, 64)
r.redisStore.RemoveSortedSetRange(key, limit, limit)
} else {
log.Warning("Cannot load access token:", token)
}
key := prefixAccess + storage.HashKey(token, r.Gw.GetConfig().HashKeys)
r.store.DeleteKey(key)
// remove the access token from central storage too
r.sessionManager.RemoveSession(r.orgID, token, false)
return nil
}
Cognitive complexity: 4, Cyclomatic complexity: 2
func (*RedisOsinStorageInterface) RemoveAuthorize
RemoveAuthorize removes authorisation keys from redis
func (r *RedisOsinStorageInterface) RemoveAuthorize(code string) error {
key := prefixAuth + code
r.store.DeleteKey(key)
return nil
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*RedisOsinStorageInterface) RemoveRefresh
RemoveRefresh will remove a refresh token from redis
func (r *RedisOsinStorageInterface) RemoveRefresh(token string) error {
log.Debug("is going to revoke refresh token: ", token)
key := prefixRefresh + token
r.store.DeleteKey(key)
return nil
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*RedisOsinStorageInterface) SaveAccess
SaveAccess will save a token and it's access data to redis
func (r *RedisOsinStorageInterface) SaveAccess(accessData *osin.AccessData) error {
authDataJSON, err := json.Marshal(accessData)
if err != nil {
return err
}
key := prefixAccess + storage.HashKey(accessData.AccessToken, r.Gw.GetConfig().HashKeys)
log.Debug("Saving ACCESS key: ", key)
// Overide default ExpiresIn:
if oauthTokenExpire := r.Gw.GetConfig().OauthTokenExpire; oauthTokenExpire != 0 {
accessData.ExpiresIn = oauthTokenExpire
}
err = r.store.SetKey(key, string(authDataJSON), int64(accessData.ExpiresIn))
if err != nil {
log.WithError(err).Error("could not save access data")
}
// add code to list of tokens for this client
sortedListKey := prefixClientTokens + accessData.Client.GetId()
log.Debug("Adding ACCESS key to sorted list: ", sortedListKey)
r.redisStore.AddToSortedSet(
sortedListKey,
storage.HashKey(accessData.AccessToken, r.Gw.GetConfig().HashKeys),
float64(accessData.CreatedAt.Unix()+int64(accessData.ExpiresIn)), // set score as token expire timestamp
)
// Create a user.SessionState object and register it with the authmanager
newSession := user.NewSessionState()
// ------
checkPolicy := true
if accessData.UserData != nil {
checkPolicy = false
err := json.Unmarshal([]byte(accessData.UserData.(string)), newSession)
if err != nil {
log.Info("Couldn't decode user.SessionState from UserData, checking policy: ", err)
checkPolicy = true
}
}
if checkPolicy {
// defined in JWT middleware
sessionFromPolicy, err := r.Gw.generateSessionFromPolicy(accessData.Client.GetPolicyID(), "", false)
if err != nil {
return errors.New("Couldn't use policy or key rules to create token, failing")
}
newSession = &sessionFromPolicy
}
// ------
// Set the client ID for analytics
newSession.OauthClientID = accessData.Client.GetId()
// Override timeouts so that we can be in sync with Osin
newSession.Expires = time.Now().Unix() + int64(accessData.ExpiresIn)
c, ok := accessData.Client.(*OAuthClient)
if ok && c.MetaData != nil {
if newSession.MetaData == nil {
newSession.MetaData = make(map[string]interface{})
}
// Allow session inherit and *override* client values
for k, v := range c.MetaData.(map[string]interface{}) {
if _, found := newSession.MetaData[k]; !found {
newSession.MetaData[k] = v
}
}
}
sessionLifetime := r.Gw.ApplyLifetime(newSession)
// Use the default session expiry here as this is OAuth
r.sessionManager.UpdateSession(accessData.AccessToken, newSession, sessionLifetime, false)
// Store the refresh token too
if accessData.RefreshToken != "" {
accessDataJSON, err := json.Marshal(accessData)
if err != nil {
return err
}
key := prefixRefresh + accessData.RefreshToken
refreshExpire := int64(1209600) // 14 days
if oauthRefreshExpire := r.Gw.GetConfig().OauthRefreshExpire; oauthRefreshExpire != 0 {
refreshExpire = oauthRefreshExpire
}
log.Debug("STORING ACCESS DATA: ", string(accessDataJSON))
err = r.store.SetKey(key, string(accessDataJSON), refreshExpire)
if err != nil {
log.WithError(err).Error("could not save access data")
}
return err
}
return nil
}
Cognitive complexity: 33, Cyclomatic complexity: 17
func (*RedisOsinStorageInterface) SaveAuthorize
SaveAuthorize saves authorisation data to Redis
func (r *RedisOsinStorageInterface) SaveAuthorize(authData *osin.AuthorizeData) error {
authDataJSON, err := json.Marshal(&authData)
if err != nil {
return err
}
key := prefixAuth + authData.Code
log.Debug("Saving auth code: ", key)
err = r.store.SetKey(key, string(authDataJSON), int64(authData.ExpiresIn))
return nil
}
Cognitive complexity: 2, Cyclomatic complexity: 2
func (*RedisOsinStorageInterface) SetClient
SetClient creates client data
func (r *RedisOsinStorageInterface) SetClient(id string, orgID string, client osin.Client, ignorePrefix bool) error {
clientDataJSON, err := json.Marshal(client)
if err != nil {
log.Error("Couldn't marshal client data: ", err)
return err
}
key := prefixClient + id
if ignorePrefix {
key = id
}
log.Debug("CREATING: ", key)
err = r.store.SetKey(key, string(clientDataJSON), 0)
if err != nil {
log.WithError(err).Error("could not save oauth client data")
}
keyForSet := prefixClientset + prefixClient // Org ID
indexKey := prefixClientIndexList + orgID
//check if the indexKey exists
exists, err := r.store.Exists(indexKey)
if err != nil {
return err
}
// if it exists, delete it to avoid duplicity in the client index list
if exists {
r.store.RemoveFromList(indexKey, key)
}
// append to oauth client index list
r.store.AppendToSet(indexKey, key)
// In set, there is no option for update so the existing client should be removed before adding new one.
set, _ := r.store.GetSet(keyForSet)
for _, v := range set {
if strings.Contains(v, client.GetId()) {
r.store.RemoveFromSet(keyForSet, v)
}
}
r.store.AddToSet(keyForSet, string(clientDataJSON))
return nil
}
Cognitive complexity: 15, Cyclomatic complexity: 8
func (*RedisOsinStorageInterface) SetUser
func (r *RedisOsinStorageInterface) SetUser(username string, session *user.SessionState, timeout int64) error {
key := username
authDataJSON, err := json.Marshal(session)
if err != nil {
return err
}
if err := r.store.SetRawKey(key, string(authDataJSON), timeout); err != nil {
log.Error("Failure setting user token by key: ", err)
return err
}
return nil
}
Cognitive complexity: 4, Cyclomatic complexity: 3
func (*RedisPurger) PurgeCache
func (r *RedisPurger) PurgeCache() {
expireAfter := r.Gw.GetConfig().AnalyticsConfig.StorageExpirationTime
if expireAfter == -1 {
return
}
if expireAfter == 0 {
expireAfter = 60 // 1 minute
}
for i := -1; i < 10; i++ {
var analyticsKey string
if i == -1 {
//if it's the first iteration, we look for tyk-system-analytics to maintain backwards compatibility or if analytics_config.enable_multiple_analytics_keys is disabled in the gateway
analyticsKey = analyticsKeyName
} else {
analyticsKey = fmt.Sprintf("%v_%v", analyticsKeyName, i)
}
exp, _ := r.Store.GetExp(analyticsKey)
if exp == -1 {
r.Store.SetExp(analyticsKey, int64(expireAfter))
}
}
}
Cognitive complexity: 12, Cyclomatic complexity: 6
func (*RegexExtractor) ExtractAndCheck
func (e *RegexExtractor) ExtractAndCheck(r *http.Request) (SessionID string, returnOverrides ReturnOverrides) {
if e.IDExtractorConfig.RegexExpression == "" {
returnOverrides = e.Error(r, nil, "RegexExtractor expects an expression")
return SessionID, returnOverrides
}
var err error
if e.compiledExpr == nil {
e.compiledExpr, err = regexp.Compile(e.IDExtractorConfig.RegexExpression)
if err != nil {
returnOverrides = e.Error(r, nil, "RegexExtractor found an invalid expression")
return SessionID, returnOverrides
}
}
var extractorOutput string
switch e.Config.ExtractFrom {
case apidef.HeaderSource:
extractorOutput, err = e.ExtractHeader(r)
case apidef.BodySource:
extractorOutput, err = e.ExtractBody(r)
case apidef.FormSource:
extractorOutput, err = e.ExtractForm(r, e.IDExtractorConfig.FormParamName)
}
if err != nil {
returnOverrides = e.Error(r, err, "RegexExtractor error")
return SessionID, returnOverrides
}
regexOutput := e.compiledExpr.FindAllString(extractorOutput, -1)
if e.IDExtractorConfig.RegexMatchIndex > len(regexOutput)-1 {
returnOverrides = e.Error(r, fmt.Errorf("can't find regexp match group"), "RegexExtractor error")
return SessionID, returnOverrides
}
SessionID = e.GenerateSessionID(regexOutput[e.IDExtractorConfig.RegexMatchIndex], e.BaseMiddleware)
previousSession, keyExists := e.BaseMiddleware.CheckSessionAndIdentityForValidKey(SessionID, r)
SessionID = previousSession.KeyID
if keyExists {
if previousSession.IdExtractorDeadline > time.Now().Unix() {
ctxSetSession(r, &previousSession, true, e.Gw.GetConfig().HashKeys)
returnOverrides = ReturnOverrides{
ResponseCode: 200,
}
}
}
return SessionID, returnOverrides
}
Cognitive complexity: 20, Cyclomatic complexity: 12
func (*ReloadMachinery) Disable
Disable turns off tracking of reload cycles and queues
func (r *ReloadMachinery) Disable() {
r.mu.Lock()
defer r.mu.Unlock()
r.run = true
r.count = 0
r.cycles = 0
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*ReloadMachinery) Enable
Enable when callled it will allow r to keep track of reload cycles and queues
func (r *ReloadMachinery) Enable() {
r.mu.Lock()
defer r.mu.Unlock()
r.run = true
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*ReloadMachinery) EnsureQueued
EnsureQueued this will block until any queue happens. It will timeout after 100ms
func (r *ReloadMachinery) EnsureQueued(t *testing.T) {
t.Helper()
tick := time.NewTicker(time.Millisecond)
defer tick.Stop()
for {
timeout := time.NewTimer(100 * time.Millisecond)
select {
case <-timeout.C:
t.Fatal("Timedout waiting for reload to be queued")
case <-tick.C:
if !timeout.Stop() {
<-timeout.C
}
if r.Queued() {
return
}
}
}
}
Cognitive complexity: 9, Cyclomatic complexity: 6
func (*ReloadMachinery) EnsureReloaded
EnsureReloaded this will block until any reload happens. It will timeout after 200ms
func (r *ReloadMachinery) EnsureReloaded(t *testing.T) {
t.Helper()
tick := time.NewTicker(time.Millisecond)
defer tick.Stop()
for {
timeout := time.NewTimer(200 * time.Millisecond)
select {
case <-timeout.C:
t.Fatal("Timedout waiting for reload to be queued")
case <-tick.C:
if !timeout.Stop() {
<-timeout.C
}
if r.Reloaded() {
return
}
}
}
}
Cognitive complexity: 9, Cyclomatic complexity: 6
func (*ReloadMachinery) OnQueued
OnQueued is called when a reload has been queued. This increments the queue count
func (r *ReloadMachinery) OnQueued() {
r.mu.Lock()
defer r.mu.Unlock()
if r.run {
r.count++
}
}
Cognitive complexity: 2, Cyclomatic complexity: 2
func (*ReloadMachinery) OnReload
OnReload is called when a reload has been completed. This increments the reload cycles count.
func (r *ReloadMachinery) OnReload() {
r.mu.Lock()
defer r.mu.Unlock()
if r.run {
r.cycles++
}
}
Cognitive complexity: 2, Cyclomatic complexity: 2
func (*ReloadMachinery) Queued
Queued returns true if any queue happened
func (r *ReloadMachinery) Queued() bool {
r.mu.RLock()
defer r.mu.RUnlock()
return r.count > 0
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*ReloadMachinery) ReloadTicker
func (r *ReloadMachinery) ReloadTicker() <-chan time.Time {
return r.reloadTick
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*ReloadMachinery) Reloaded
Reloaded returns true if a read has occurred since r was enabled
func (r *ReloadMachinery) Reloaded() bool {
r.mu.RLock()
defer r.mu.RUnlock()
return r.cycles > 0
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*ReloadMachinery) Reset
Reset sets reloads counts and queues to 0
func (r *ReloadMachinery) Reset() {
r.mu.Lock()
defer r.mu.Unlock()
r.count = 0
r.cycles = 0
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*ReloadMachinery) StartTicker
func (r *ReloadMachinery) StartTicker() {
r.stop = make(chan struct{})
r.started = true
go func() {
for {
select {
case <-r.stop:
return
default:
r.Tick()
}
}
}()
}
Cognitive complexity: 6, Cyclomatic complexity: 3
func (*ReloadMachinery) StopTicker
func (r *ReloadMachinery) StopTicker() {
if r.started {
close(r.stop)
r.started = false
}
}
Cognitive complexity: 2, Cyclomatic complexity: 2
func (*ReloadMachinery) Tick
Tick triggers reload
func (r *ReloadMachinery) Tick() {
r.reloadTick <- time.Time{}
}
Cognitive complexity: 1, Cyclomatic complexity: 1
func (*ReloadMachinery) TickOk
TickOk triggers a reload and ensures a queue happened and a reload cycle happens. This will block until all the cases are met.
func (r *ReloadMachinery) TickOk(t *testing.T) {
t.Helper()
r.EnsureQueued(t)
r.Tick()
r.EnsureReloaded(t)
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*RequestSigning) EnabledForSpec
func (s *RequestSigning) EnabledForSpec() bool {
return s.Spec.RequestSigning.IsEnabled
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*RequestSigning) Name
func (s *RequestSigning) Name() string {
return "RequestSigning"
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*RequestSigning) ProcessRequest
func (s *RequestSigning) ProcessRequest(w http.ResponseWriter, r *http.Request, _ interface{}) (error, int) {
if !s.isRequestSigningConfigValid() {
log.Error("Fields required for signing the request are missing")
return errors.New("Fields required for signing the request are missing"), http.StatusInternalServerError
}
var algoList []string
if len(s.Spec.HmacAllowedAlgorithms) > 0 {
algoList = s.Spec.HmacAllowedAlgorithms
} else {
algoList = supportedAlgorithms
}
algorithmAllowed := false
for _, alg := range algoList {
if alg == s.Spec.RequestSigning.Algorithm {
algorithmAllowed = true
break
}
}
if !algorithmAllowed {
log.WithField("algorithm", s.Spec.RequestSigning.Algorithm).Error("Algorithm not supported")
return errors.New("Request signing algorithm is not supported"), http.StatusInternalServerError
}
headers := generateHeaderList(r, s.Spec.RequestSigning.HeaderList)
path := s.getRequestPath(r)
signatureString, err := generateHMACSignatureStringFromRequest(r, headers, path)
if err != nil {
log.Error(err)
return err, http.StatusInternalServerError
}
strHeaders := strings.Join(headers, " ")
var encodedSignature string
if strings.HasPrefix(s.Spec.RequestSigning.Algorithm, "rsa") {
if s.Spec.RequestSigning.CertificateId == "" {
log.Error("CertificateID is empty")
return errors.New("CertificateID is empty"), http.StatusInternalServerError
}
certList := s.Gw.CertificateManager.List([]string{s.Spec.RequestSigning.CertificateId}, certs.CertificatePrivate)
if len(certList) == 0 || certList[0] == nil {
log.Error("Certificate not found")
return errors.New("Certificate not found"), http.StatusInternalServerError
}
cert := certList[0]
rsaKey, ok := cert.PrivateKey.(*rsa.PrivateKey)
if !ok {
log.Error("Certificate does not contain RSA private key")
return errors.New("Certificate does not contain RSA private key"), http.StatusInternalServerError
}
encodedSignature, err = generateRSAEncodedSignature(signatureString, rsaKey, s.Spec.RequestSigning.Algorithm)
if err != nil {
log.Error("Error while generating signature:", err)
return err, http.StatusInternalServerError
}
} else {
var err error
encodedSignature, err = generateHMACEncodedSignature(signatureString, s.Spec.RequestSigning.Secret, s.Spec.RequestSigning.Algorithm)
if err != nil {
return err, http.StatusInternalServerError
}
}
//Generate Authorization header
authHeader := "Signature "
//Append keyId
authHeader += "keyId=\"" + s.Spec.RequestSigning.KeyId + "\","
//Append algorithm
authHeader += "algorithm=\"" + s.Spec.RequestSigning.Algorithm + "\","
//Append Headers
authHeader += "headers=\"" + strHeaders + "\","
//Append signature
authHeader += "signature=\"" + encodedSignature + "\""
if s.Spec.RequestSigning.SignatureHeader != "" {
r.Header.Set(s.Spec.RequestSigning.SignatureHeader, authHeader)
log.Debugf("Setting %s headers as =%s", s.Spec.RequestSigning.SignatureHeader, authHeader)
} else {
r.Header.Set("Authorization", authHeader)
log.Debug("Setting Authorization headers as =", authHeader)
}
return nil, http.StatusOK
}
Cognitive complexity: 35, Cyclomatic complexity: 15
func (*RequestSizeLimitMiddleware) EnabledForSpec
func (t *RequestSizeLimitMiddleware) EnabledForSpec() bool {
for _, version := range t.Spec.VersionData.Versions {
if len(version.ExtendedPaths.SizeLimit) > 0 ||
(!version.GlobalSizeLimitDisabled && version.GlobalSizeLimit > 0) {
return true
}
}
return false
}
Cognitive complexity: 5, Cyclomatic complexity: 5
func (*RequestSizeLimitMiddleware) Name
func (t *RequestSizeLimitMiddleware) Name() string {
return "RequestSizeLimitMiddleware"
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*RequestSizeLimitMiddleware) ProcessRequest
RequestSizeLimit will check a request for maximum request size, this can be a global limit or a matched limit.
func (t *RequestSizeLimitMiddleware) ProcessRequest(w http.ResponseWriter, r *http.Request, _ interface{}) (error, int) {
if _, ok := skippedMethods[r.Method]; ok {
return nil, http.StatusOK
}
logger := t.Logger()
logger.Debug("Request size limiter active")
vInfo, _ := t.Spec.Version(r)
logger.Debug("Global limit is: ", vInfo.GlobalSizeLimit)
// Manage global headers first
if vInfo.GlobalSizeLimit > 0 {
logger.Debug("Checking global limit")
err, code := t.checkRequestLimit(r, vInfo.GlobalSizeLimit)
// If not OK, block
if code != http.StatusOK {
return err, code
}
}
// if there's no paths at all path check
if len(vInfo.ExtendedPaths.SizeLimit) == 0 {
return nil, http.StatusOK
}
versionPaths := t.Spec.RxPaths[vInfo.Name]
// If there's a potential match, try to match
found, meta := t.Spec.CheckSpecMatchesStatus(r, versionPaths, RequestSizeLimit)
if found {
logger.Debug("Request size limit matched for this URL, checking...")
rmeta := meta.(*apidef.RequestSizeMeta)
return t.checkRequestLimit(r, rmeta.SizeLimit)
}
return nil, http.StatusOK
}
Cognitive complexity: 11, Cyclomatic complexity: 6
func (*ResponseCacheMiddleware) Base
func (m *ResponseCacheMiddleware) Base() *BaseTykResponseHandler {
return &m.BaseTykResponseHandler
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*ResponseCacheMiddleware) EnabledForSpec
func (m *ResponseCacheMiddleware) EnabledForSpec() bool {
return m.Spec.CacheOptions.EnableCache
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*ResponseCacheMiddleware) HandleError
func (h *ResponseCacheMiddleware) HandleError(rw http.ResponseWriter, req *http.Request) {
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*ResponseCacheMiddleware) HandleResponse
HandleResponse checks if the http.Response argument can be cached and caches it for future requests.
func (m *ResponseCacheMiddleware) HandleResponse(w http.ResponseWriter, res *http.Response, r *http.Request, ses *user.SessionState) error {
// No cache of empty responses
if res == nil {
m.Logger().Warning("Upstream request must have failed, response is empty")
return nil
}
// Skip caching the request if cache disabled
if !m.EnabledForSpec() {
return nil
}
// Has cache been enabled on the request?
options := ctxGetCacheOptions(r)
if options == nil {
m.Logger().Debug("Request is not cacheable")
return nil
}
cacheThisRequest := true
cacheTTL := options.timeout
// make sure the status codes match if specified
if len(options.cacheOnlyResponseCodes) > 0 {
foundCode := false
for _, code := range options.cacheOnlyResponseCodes {
if code == res.StatusCode {
foundCode = true
break
}
}
cacheThisRequest = foundCode
}
// Are we using upstream cache control?
if m.Spec.CacheOptions.EnableUpstreamCacheControl {
// Do we enable cache for this response?
if res.Header.Get(upstreamCacheHeader) != "" {
cacheThisRequest = true
}
// Read custom or default cache TTL header name
cacheTTLHeader := upstreamCacheTTLHeader
if m.Spec.CacheOptions.CacheControlTTLHeader != "" {
cacheTTLHeader = m.Spec.CacheOptions.CacheControlTTLHeader
}
// Get cache TTL from header
ttl := res.Header.Get(cacheTTLHeader)
if ttl != "" {
if cacheAsInt, err := strconv.Atoi(ttl); err == nil {
cacheTTL = int64(cacheAsInt)
}
}
}
var toStore string
var err error
if cacheThisRequest {
res.Body, err = newNopCloserBuffer(res.Body)
if err != nil {
m.Logger().WithError(err).Error("error reading cache body")
return nil
}
var wireFormatReq bytes.Buffer
if err := res.Write(&wireFormatReq); err != nil {
m.Logger().WithError(err).Error("error encoding cache")
return nil
}
ts := m.getTimeTTL(cacheTTL)
toStore = m.encodePayload(wireFormatReq.String(), ts)
go func() {
err := m.store.SetKey(options.key, toStore, cacheTTL)
if err != nil {
m.Logger().WithError(err).Error("could not save key in cache store")
}
}()
}
/*
m.Logger().
WithError(err).
WithField("cache", cacheThisRequest).
WithField("stored", toStore).
WithField("key", options.key).
Debug("Done response cache")
*/
return nil
}
Cognitive complexity: 32, Cyclomatic complexity: 16
func (*ResponseCacheMiddleware) Init
func (h *ResponseCacheMiddleware) Init(c interface{}, spec *APISpec) error {
h.Spec = spec
return nil
}
Cognitive complexity: 1, Cyclomatic complexity: 1
func (*ResponseCacheMiddleware) Logger
func (m *ResponseCacheMiddleware) Logger() *logrus.Entry {
return log.WithField("mw", m.Name())
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*ResponseCacheMiddleware) Name
func (m *ResponseCacheMiddleware) Name() string {
return "ResponseCacheMiddleware"
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*ResponseGoPluginMiddleware) HandleError
func (h *ResponseGoPluginMiddleware) HandleError(rw http.ResponseWriter, req *http.Request) {
//noop
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*ResponseGoPluginMiddleware) HandleGoPluginResponse
func (h *ResponseGoPluginMiddleware) HandleGoPluginResponse(w http.ResponseWriter, res *http.Response, req *http.Request) error {
// make sure tyk recover in case Go-plugin function panics
defer func() {
if e := recover(); e != nil {
err := fmt.Errorf("%v", e)
w.WriteHeader(http.StatusInternalServerError)
h.logger.WithError(err).Error("Recovered from panic while running Go-plugin middleware func")
}
}()
// Inject definition into response context
h.Spec.injectIntoReqContext(req)
// wrap ResponseWriter to check if response was sent
rw := &customResponseWriter{
ResponseWriter: w,
copyData: recordDetail(req, h.Spec),
}
// call Go-plugin function
t1 := time.Now()
h.ResHandler(rw, res, req)
// calculate latency
ms := DurationToMillisecond(time.Since(t1))
h.logger.WithField("ms", ms).Debug("Go-plugin response processing took")
// check if response was sent
if rw.responseSent {
// check if response code was an error one
if rw.statusCodeSent >= http.StatusBadRequest {
// base middleware will report this error to analytics if needed
w.WriteHeader(rw.statusCodeSent)
err := fmt.Errorf("plugin function sent error response code: %d", rw.statusCodeSent)
h.logger.WithError(err).Error("Returned error code while processing response with Go-plugin middleware func")
return err
}
}
return nil
}
Cognitive complexity: 8, Cyclomatic complexity: 4
func (*ResponseGoPluginMiddleware) HandleResponse
func (h *ResponseGoPluginMiddleware) HandleResponse(w http.ResponseWriter, res *http.Response, req *http.Request, ses *user.SessionState) error {
err := h.HandleGoPluginResponse(w, res, req)
if err != nil {
return err
}
return nil
}
Cognitive complexity: 2, Cyclomatic complexity: 2
func (*ResponseGoPluginMiddleware) Init
func (h *ResponseGoPluginMiddleware) Init(c interface{}, spec *APISpec) error {
h.Spec = spec
h.Path = c.(apidef.MiddlewareDefinition).Path
h.SymbolName = c.(apidef.MiddlewareDefinition).Name
h.logger = log.WithFields(logrus.Fields{
"mwPath": h.Path,
"mwSymbolName": h.SymbolName,
})
if h.ResHandler != nil {
h.logger.Info("Go-plugin middleware is already initialized")
// noop
return nil
}
newPath, err := goplugin.GetPluginFileNameToLoad(goplugin.FileSystemStorage{}, h.Path)
if err != nil {
h.logger.WithError(err).Error("Could not load Go-plugin. File was not found")
return err
}
if h.Path != newPath {
h.Path = newPath
}
// try to load plugin
if h.ResHandler, err = goplugin.GetResponseHandler(h.Path, h.SymbolName); err != nil {
h.logger.WithError(err).Error("Could not load Go-plugin")
return err
}
h.logger.Infof("Loaded Go response plugin: %s", h.SymbolName)
return nil
}
Cognitive complexity: 11, Cyclomatic complexity: 5
func (*ResponseTransformJQMiddleware) HandleError
func (h *ResponseTransformJQMiddleware) HandleError(rw http.ResponseWriter, req *http.Request) {
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*ResponseTransformJQMiddleware) HandleResponse
func (h *ResponseTransformJQMiddleware) HandleResponse(rw http.ResponseWriter, res *http.Response, req *http.Request, ses *user.SessionState) error {
log.Warning("JQ transforms not supported")
return nil
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*ResponseTransformJQMiddleware) Init
func (h *ResponseTransformJQMiddleware) Init(c interface{}, spec *APISpec) error {
h.Spec = spec
return nil
}
Cognitive complexity: 1, Cyclomatic complexity: 1
func (*ResponseTransformMiddleware) Base
func (r *ResponseTransformMiddleware) Base() *BaseTykResponseHandler {
return &r.BaseTykResponseHandler
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*ResponseTransformMiddleware) Enabled
func (r *ResponseTransformMiddleware) Enabled() bool {
for _, version := range r.Spec.VersionData.Versions {
if len(version.ExtendedPaths.TransformResponse) > 0 {
for _, transformResponse := range version.ExtendedPaths.TransformResponse {
if !transformResponse.Disabled {
return true
}
}
}
}
return false
}
Cognitive complexity: 10, Cyclomatic complexity: 5
func (*ResponseTransformMiddleware) HandleError
func (r *ResponseTransformMiddleware) HandleError(rw http.ResponseWriter, req *http.Request) {
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*ResponseTransformMiddleware) HandleResponse
func (r *ResponseTransformMiddleware) HandleResponse(rw http.ResponseWriter, res *http.Response, req *http.Request, ses *user.SessionState) error {
logger := log.WithFields(logrus.Fields{
"prefix": "outbound-transform",
"server_name": r.Spec.Proxy.TargetURL,
"api_id": r.Spec.APIID,
"path": req.URL.Path,
})
versionInfo, _ := r.Spec.Version(req)
versionPaths := r.Spec.RxPaths[versionInfo.Name]
found, meta := r.Spec.CheckSpecMatchesStatus(req, versionPaths, TransformedResponse)
if !found {
return nil
}
tmeta := meta.(*TransformSpec)
respBody := respBodyReader(req, res)
body, _ := ioutil.ReadAll(respBody)
defer respBody.Close()
// Put into an interface:
bodyData := make(map[string]interface{})
switch tmeta.TemplateData.Input {
case apidef.RequestXML:
if len(body) == 0 {
body = []byte("<_/>")
}
mxj.XmlCharsetReader = WrappedCharsetReader
var err error
xmlMap, err := mxj.NewMapXml(body) // unmarshal
if err != nil {
logger.WithError(err).Error("Error unmarshalling XML")
//todo return error
break
}
for k, v := range xmlMap {
bodyData[k] = v
}
default: // apidef.RequestJSON
if len(body) == 0 {
body = []byte("{}")
}
var tempBody interface{}
if err := json.Unmarshal(body, &tempBody); err != nil {
logger.WithError(err).Error("Error unmarshalling JSON")
//todo return error
break
}
switch tempBody.(type) {
case []interface{}:
bodyData["array"] = tempBody
case map[string]interface{}:
bodyData = tempBody.(map[string]interface{})
}
}
if r.Spec.EnableContextVars {
bodyData["_tyk_context"] = ctxGetData(req)
}
if tmeta.TemplateData.EnableSession {
if session := ctxGetSession(req); session != nil {
bodyData["_tyk_meta"] = session.MetaData
} else {
logger.Error("Session context was enabled but not found.")
}
}
// Apply to template
var bodyBuffer bytes.Buffer
if err := tmeta.Template.Execute(&bodyBuffer, bodyData); err != nil {
logger.WithError(err).Error("Failed to apply template to request")
}
// Re-compress if original upstream response was compressed
encoding := res.Header.Get("Content-Encoding")
bodyBuffer = compressBuffer(bodyBuffer, encoding)
res.ContentLength = int64(bodyBuffer.Len())
res.Header.Set("Content-Length", strconv.Itoa(bodyBuffer.Len()))
res.Body = ioutil.NopCloser(&bodyBuffer)
return nil
}
Cognitive complexity: 36, Cyclomatic complexity: 16
func (*ResponseTransformMiddleware) Init
func (r *ResponseTransformMiddleware) Init(c interface{}, spec *APISpec) error {
r.Spec = spec
return nil
}
Cognitive complexity: 1, Cyclomatic complexity: 1
func (*ResponseTransformMiddleware) Name
func (r *ResponseTransformMiddleware) Name() string {
return "ResponseTransformMiddleware"
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*ReverseProxy) CheckCircuitBreakerEnforced
func (p *ReverseProxy) CheckCircuitBreakerEnforced(spec *APISpec, req *http.Request) (bool, *ExtendedCircuitBreakerMeta) {
if !spec.CircuitBreakerEnabled {
return false, nil
}
versionInfo, _ := spec.Version(req)
versionPaths := spec.RxPaths[versionInfo.Name]
found, meta := spec.CheckSpecMatchesStatus(req, versionPaths, CircuitBreaker)
if found {
exMeta := meta.(*ExtendedCircuitBreakerMeta)
p.logger.Debug("CB Enforced for path: ", *exMeta)
return true, exMeta
}
return false, nil
}
Cognitive complexity: 4, Cyclomatic complexity: 3
func (*ReverseProxy) CheckHardTimeoutEnforced
CheckHardTimeoutEnforced checks APISpec versions for a fine grained timeout value. The value is defined in seconds, but we're using float64 to enable sub-second durations for tests. Changing to int would break that behaviour.
func (p *ReverseProxy) CheckHardTimeoutEnforced(spec *APISpec, req *http.Request) (bool, float64) {
if !spec.EnforcedTimeoutEnabled {
return false, 0
}
vInfo, _ := spec.Version(req)
versionPaths := spec.RxPaths[vInfo.Name]
found, meta := spec.CheckSpecMatchesStatus(req, versionPaths, HardTimeout)
if found {
intMeta, ok := meta.(*int)
if ok && *intMeta > 0 {
p.logger.Debug("HARD TIMEOUT ENFORCED: ", *intMeta)
return true, float64(*intMeta)
}
}
return false, 0
}
Cognitive complexity: 6, Cyclomatic complexity: 5
func (*ReverseProxy) CheckHeaderInRemoveList
func (p *ReverseProxy) CheckHeaderInRemoveList(hdr string, spec *APISpec, req *http.Request) bool {
vInfo, _ := spec.Version(req)
versionPaths := spec.RxPaths[vInfo.Name]
for _, gdKey := range vInfo.GlobalHeadersRemove {
if strings.ToLower(gdKey) == strings.ToLower(hdr) {
return true
}
}
// Check path config
if found, meta := spec.CheckSpecMatchesStatus(req, versionPaths, HeaderInjected); found {
hmeta := meta.(*apidef.HeaderInjectionMeta)
for _, gdKey := range hmeta.DeleteHeaders {
if strings.ToLower(gdKey) == strings.ToLower(hdr) {
return true
}
}
}
return false
}
Cognitive complexity: 12, Cyclomatic complexity: 6
func (*ReverseProxy) CopyResponse
func (p *ReverseProxy) CopyResponse(dst io.Writer, src io.Reader, flushInterval time.Duration) {
if flushInterval != 0 {
if wf, ok := dst.(writeFlusher); ok {
mlw := &maxLatencyWriter{
dst: wf,
latency: flushInterval,
}
defer mlw.stop()
// set up initial timer so headers get flushed even if body writes are delayed
mlw.flushPending = true
mlw.t = time.AfterFunc(flushInterval, mlw.delayedFlush)
dst = mlw
}
}
p.copyBuffer(dst, src)
}
Cognitive complexity: 5, Cyclomatic complexity: 3
func (*ReverseProxy) HandleResponse
func (p *ReverseProxy) HandleResponse(rw http.ResponseWriter, res *http.Response, ses *user.SessionState) error {
// Remove hop-by-hop headers listed in the
// "Connection" header of the response.
if c := res.Header.Get(header.Connection); c != "" {
for _, f := range strings.Split(c, ",") {
if f = strings.TrimSpace(f); f != "" {
res.Header.Del(f)
}
}
}
for _, h := range hopHeaders {
res.Header.Del(h)
}
defer res.Body.Close()
// Close connections
if p.Gw.GetConfig().CloseConnections {
res.Header.Set(header.Connection, "close")
}
// Add resource headers
if ses != nil {
// We have found a session, lets report back
quotaMax, quotaRemaining, _, quotaRenews := ses.GetQuotaLimitByAPIID(p.TykAPISpec.APIID)
res.Header.Set(header.XRateLimitLimit, strconv.Itoa(int(quotaMax)))
res.Header.Set(header.XRateLimitRemaining, strconv.Itoa(int(quotaRemaining)))
res.Header.Set(header.XRateLimitReset, strconv.Itoa(int(quotaRenews)))
}
copyHeader(rw.Header(), res.Header, p.Gw.GetConfig().IgnoreCanonicalMIMEHeaderKey)
announcedTrailers := len(res.Trailer)
if announcedTrailers > 0 {
trailerKeys := make([]string, 0, len(res.Trailer))
for k := range res.Trailer {
trailerKeys = append(trailerKeys, k)
}
rw.Header().Add("Trailer", strings.Join(trailerKeys, ", "))
}
// do not write on a hijacked connection
if res.StatusCode != http.StatusSwitchingProtocols {
rw.WriteHeader(res.StatusCode)
}
if len(res.Trailer) > 0 {
// Force chunking if we saw a response trailer.
// This prevents net/http from calculating the length for short
// bodies and adding a Content-Length.
if fl, ok := rw.(http.Flusher); ok {
fl.Flush()
}
}
p.CopyResponse(rw, res.Body, p.flushInterval(res))
if len(res.Trailer) == announcedTrailers {
copyHeader(rw.Header(), res.Trailer, p.Gw.GetConfig().IgnoreCanonicalMIMEHeaderKey)
return nil
}
for k, vv := range res.Trailer {
k = http.TrailerPrefix + k
for _, v := range vv {
rw.Header().Add(k, v)
}
}
return nil
}
Cognitive complexity: 33, Cyclomatic complexity: 15
func (*ReverseProxy) IsUpgrade
IsUpgrade will return the upgrade header value and true if present for the request. It requires EnableWebSockets to be enabled in the gateway HTTP server config.
func (p *ReverseProxy) IsUpgrade(req *http.Request) (string, bool) {
if !p.Gw.GetConfig().HttpServerOptions.EnableWebSockets {
return "", false
}
return httputil.IsUpgrade(req)
}
Cognitive complexity: 2, Cyclomatic complexity: 2
func (*ReverseProxy) ServeHTTP
func (p *ReverseProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) ProxyResponse {
startTime := time.Now()
p.logger.WithField("ts", startTime.UnixNano()).Debug("Started")
resp := p.WrappedServeHTTP(rw, req, recordDetail(req, p.TykAPISpec))
finishTime := time.Since(startTime)
p.logger.WithField("ns", finishTime.Nanoseconds()).Debug("Finished")
// make response body to be nopCloser and re-readable before serve it through chain of middlewares
nopCloseResponseBody(resp.Response)
return resp
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*ReverseProxy) ServeHTTPForCache
func (p *ReverseProxy) ServeHTTPForCache(rw http.ResponseWriter, req *http.Request) ProxyResponse {
startTime := time.Now()
p.logger.WithField("ts", startTime.UnixNano()).Debug("Started")
resp := p.WrappedServeHTTP(rw, req, true)
nopCloseResponseBody(resp.Response)
finishTime := time.Since(startTime)
p.logger.WithField("ns", finishTime.Nanoseconds()).Debug("Finished")
return resp
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*ReverseProxy) WrappedServeHTTP
func (p *ReverseProxy) WrappedServeHTTP(rw http.ResponseWriter, req *http.Request, withCache bool) ProxyResponse {
if trace.IsEnabled() {
span, ctx := trace.Span(req.Context(), req.URL.Path)
defer span.Finish()
ext.SpanKindRPCClient.Set(span)
req = req.WithContext(ctx)
}
var roundTripper *TykRoundTripper
reqCtx := req.Context()
if cn, ok := rw.(http.CloseNotifier); ok {
var cancel context.CancelFunc
reqCtx, cancel = context.WithCancel(reqCtx)
defer cancel()
notifyChan := cn.CloseNotify()
go func() {
select {
case <-notifyChan:
cancel()
case <-reqCtx.Done():
}
}()
}
// Do this before we make a shallow copy
session := ctxGetSession(req)
outreq := new(http.Request)
logreq := new(http.Request)
*outreq = *req // includes shallow copies of maps, but okay
*logreq = *req
deepCopyErr := deepCopyBody(req, outreq)
if deepCopyErr != nil {
p.logger.Debug("Unable to create deep copy of request, err: ", deepCopyErr)
p.ErrorHandler.HandleError(rw, logreq, "There was a problem with reading Body of the Request.",
http.StatusInternalServerError, true)
return ProxyResponse{}
}
// remove context data from the copies
setContext(outreq, context.Background())
setContext(logreq, context.Background())
p.logger.Debug("Upstream request URL: ", req.URL)
// We need to double set the context for the outbound request to reprocess the target
if p.TykAPISpec.URLRewriteEnabled && req.Context().Value(ctx.RetainHost) == true {
p.logger.Debug("Detected host rewrite, notifying director")
setCtxValue(outreq, ctx.RetainHost, true)
}
if req.ContentLength == 0 {
outreq.Body = nil // Issue 16036: nil Body for http.Transport retries
}
outreq = outreq.WithContext(reqCtx)
setContext(logreq, outreq.Context())
outreq.Header = cloneHeader(req.Header)
if trace.IsEnabled() {
span := opentracing.SpanFromContext(req.Context())
trace.Inject(p.TykAPISpec.Name, span, outreq.Header)
}
p.Director(outreq)
outreq.Close = false
p.logger.Debug("Outbound request URL: ", outreq.URL.String())
reqUpType, outReqUpgrade := p.IsUpgrade(req)
// See RFC 2616, section 14.10.
if c := outreq.Header.Get("Connection"); c != "" {
for _, f := range strings.Split(c, ",") {
if f = strings.TrimSpace(f); f != "" {
outreq.Header.Del(f)
}
}
}
// Remove other hop-by-hop headers to the backend. Especially
// important is "Connection" because we want a persistent
// connection, regardless of what the client sent to us.
for _, h := range hopHeaders {
hv := outreq.Header.Get(h)
if hv == "" {
continue
}
if h == "Te" && hv == "trailers" {
continue
}
outreq.Header.Del(h)
logreq.Header.Del(h)
}
if outReqUpgrade {
outreq.Header.Set("Connection", "Upgrade")
logreq.Header.Set("Connection", "Upgrade")
outreq.Header.Set("Upgrade", reqUpType)
logreq.Header.Set("Upgrade", reqUpType)
}
addrs := requestIPHops(req)
if !p.CheckHeaderInRemoveList(header.XForwardFor, p.TykAPISpec, req) {
outreq.Header.Set(header.XForwardFor, addrs)
}
// Circuit breaker
breakerEnforced, breakerConf := p.CheckCircuitBreakerEnforced(p.TykAPISpec, req)
// set up TLS certificates for upstream if needed
var tlsCertificates []tls.Certificate
if cert := p.Gw.getUpstreamCertificate(outreq.URL.Host, p.TykAPISpec); cert != nil {
p.logger.Debug("Found upstream mutual TLS certificate")
tlsCertificates = []tls.Certificate{*cert}
}
p.TykAPISpec.Lock()
isTimeoutEnforced, enforcedTimeout := p.CheckHardTimeoutEnforced(p.TykAPISpec, outreq)
// limit request time with context timeout
if isTimeoutEnforced {
timeoutContext, cancel := context.WithTimeout(outreq.Context(), time.Duration(enforcedTimeout)*time.Second)
defer cancel()
outreq = outreq.WithContext(timeoutContext)
}
// create HTTP transport
createTransport := p.TykAPISpec.HTTPTransport == nil
// Check if timeouts are set for this endpoint
if !createTransport && p.Gw.GetConfig().MaxConnTime != 0 {
createTransport = time.Since(p.TykAPISpec.HTTPTransportCreated) > time.Duration(p.Gw.GetConfig().MaxConnTime)*time.Second
}
if createTransport {
var oldTransport *http.Transport
if p.TykAPISpec.HTTPTransport != nil {
oldTransport = p.TykAPISpec.HTTPTransport.transport
// Prevent new idle connections to be generated.
oldTransport.DisableKeepAlives = true
}
timeout := proxyTimeout(p.TykAPISpec)
// If an enforced timeout is configured for this API endpoint, use it instead
// of the global default timeout, as it should take precedence
if isTimeoutEnforced {
timeout = enforcedTimeout
}
p.TykAPISpec.HTTPTransport = p.httpTransport(timeout, rw, req, outreq)
p.TykAPISpec.HTTPTransportCreated = time.Now()
if oldTransport != nil {
oldTransport.CloseIdleConnections()
}
}
roundTripper = p.TykAPISpec.HTTPTransport
if roundTripper.transport != nil {
roundTripper.transport.TLSClientConfig.Certificates = tlsCertificates
}
p.TykAPISpec.Unlock()
if outreq.URL.Scheme == "h2c" {
outreq.URL.Scheme = "http"
}
if p.TykAPISpec.Proxy.Transport.SSLForceCommonNameCheck || p.Gw.GetConfig().SSLForceCommonNameCheck {
// if proxy is enabled, add CommonName verification in verifyPeerCertificate
// DialTLS is not executed if proxy is used
httpTransport := roundTripper.transport
p.logger.Debug("Using forced SSL CN check")
if proxyURL, _ := httpTransport.Proxy(req); proxyURL != nil {
p.logger.Debug("Detected proxy: " + proxyURL.String())
tlsConfig := httpTransport.TLSClientConfig
host, _, _ := net.SplitHostPort(outreq.Host)
p.setCommonNameVerifyPeerCertificate(tlsConfig, host)
}
}
p.addAuthInfo(outreq, req)
// do request round trip
var (
res *http.Response
isHijacked bool
upstreamLatency time.Duration
err error
)
if breakerEnforced {
if !breakerConf.CB.Ready() {
p.logger.Debug("ON REQUEST: Circuit Breaker is in OPEN state")
p.ErrorHandler.HandleError(rw, logreq, "Service temporarily unavailable.", 503, true)
return ProxyResponse{}
}
p.logger.Debug("ON REQUEST: Circuit Breaker is in CLOSED or HALF-OPEN state")
res, isHijacked, upstreamLatency, err = p.handleOutboundRequest(roundTripper, outreq, rw)
if err != nil || res.StatusCode/100 == 5 {
breakerConf.CB.Fail()
} else {
breakerConf.CB.Success()
}
} else {
res, isHijacked, upstreamLatency, err = p.handleOutboundRequest(roundTripper, outreq, rw)
}
if err != nil {
token := ctxGetAuthToken(req)
var alias string
if session != nil {
alias = session.Alias
}
p.logger.WithFields(logrus.Fields{
"prefix": "proxy",
"user_ip": addrs,
"server_name": outreq.Host,
"user_id": p.Gw.obfuscateKey(token),
"user_name": alias,
"org_id": p.TykAPISpec.OrgID,
"api_id": p.TykAPISpec.APIID,
}).Error("http: proxy error: ", err)
if strings.HasPrefix(err.Error(), "mock:") {
p.ErrorHandler.HandleError(rw, logreq, err.Error(), res.StatusCode, true)
return ProxyResponse{UpstreamLatency: upstreamLatency}
}
if strings.Contains(err.Error(), "timeout awaiting response headers") || strings.Contains(err.Error(), "context deadline exceeded") {
p.ErrorHandler.HandleError(rw, logreq, "Upstream service reached hard timeout.", http.StatusGatewayTimeout, true)
if p.TykAPISpec.Proxy.ServiceDiscovery.UseDiscoveryService {
p.logger.Debug("[PROXY] [SERVICE DISCOVERY] Upstream host failed, refreshing host list")
p.Gw.ServiceCache.Delete(p.TykAPISpec.APIID)
}
return ProxyResponse{UpstreamLatency: upstreamLatency}
}
if strings.Contains(err.Error(), "context canceled") {
p.ErrorHandler.HandleError(rw, logreq, "Client closed request", 499, true)
return ProxyResponse{UpstreamLatency: upstreamLatency}
}
if strings.Contains(err.Error(), "no such host") {
p.ErrorHandler.HandleError(rw, logreq, "Upstream host lookup failed", http.StatusInternalServerError, true)
return ProxyResponse{UpstreamLatency: upstreamLatency}
}
p.ErrorHandler.HandleError(rw, logreq, "There was a problem proxying the request", http.StatusInternalServerError, true)
return ProxyResponse{UpstreamLatency: upstreamLatency}
}
if isHijacked {
return ProxyResponse{UpstreamLatency: upstreamLatency}
}
_, upgrade := p.IsUpgrade(req)
// Deal with 101 Switching Protocols responses: (WebSocket, h2c, etc)
if upgrade && res.StatusCode == 101 {
if err := p.handleUpgradeResponse(rw, outreq, res); err != nil {
p.ErrorHandler.HandleError(rw, logreq, err.Error(), http.StatusInternalServerError, true)
return ProxyResponse{UpstreamLatency: upstreamLatency}
}
}
ses := new(user.SessionState)
if session != nil {
ses = session
}
// Middleware chain handling here - very simple, but should do
// the trick. Chain can be empty, in which case this is a no-op.
// abortRequest is set to true when a response hook fails
// For reference see "HandleError" in coprocess.go
abortRequest, err := handleResponseChain(p.TykAPISpec.ResponseChain, rw, res, req, ses)
if abortRequest {
return ProxyResponse{UpstreamLatency: upstreamLatency}
}
if err != nil {
p.logger.Error("Response chain failed! ", err)
}
inres := new(http.Response)
if httputil.IsStreamingRequest(req) || httputil.IsStreamingResponse(res) {
withCache = false
}
if withCache {
*inres = *res // includes shallow copies of maps, but okay
if !upgrade {
defer res.Body.Close()
// Buffer body data
var bodyBuffer bytes.Buffer
bodyBuffer2 := new(bytes.Buffer)
p.CopyResponse(&bodyBuffer, res.Body, p.flushInterval(res))
*bodyBuffer2 = bodyBuffer
// Create new ReadClosers so we can split output
res.Body = ioutil.NopCloser(&bodyBuffer)
inres.Body = ioutil.NopCloser(bodyBuffer2)
}
}
// We should at least copy the status code in
inres.StatusCode = res.StatusCode
inres.ContentLength = res.ContentLength
p.HandleResponse(rw, res, ses)
return ProxyResponse{UpstreamLatency: upstreamLatency, Response: inres}
}
Cognitive complexity: 111, Cyclomatic complexity: 55
func (*RoundRobin) WithLen
func (r *RoundRobin) WithLen(len int) int {
if len < 1 {
return 0
}
// -1 to start at 0, not 1
cur := atomic.AddUint32(&r.pos, 1) - 1
return int(cur) % len
}
Cognitive complexity: 2, Cyclomatic complexity: 2
func (*ServiceDiscovery) Hostname
func (s *ServiceDiscovery) Hostname(item *gabs.Container) string {
var hostname string
// Get a nested object
if s.isNested {
hostname = s.NestedObject(item)
} else {
hostname = s.Object(item)
}
return hostname
}
Cognitive complexity: 4, Cyclomatic complexity: 2
func (*ServiceDiscovery) Init
func (s *ServiceDiscovery) Init(spec *apidef.ServiceDiscoveryConfiguration) {
s.spec = spec
s.isNested = spec.UseNestedQuery
s.isTargetList = spec.UseTargetList
s.endpointReturnsList = spec.EndpointReturnsList
s.targetPath = spec.TargetPath
if spec.PortDataPath != "" {
s.portSeperate = true
s.portPath = spec.PortDataPath
}
if spec.ParentDataPath != "" {
s.parentPath = spec.ParentDataPath
}
s.dataPath = spec.DataPath
}
Cognitive complexity: 4, Cyclomatic complexity: 3
func (*ServiceDiscovery) NestedObject
func (s *ServiceDiscovery) NestedObject(item *gabs.Container) string {
parentData := s.decodeToNameSpace(s.parentPath, item)
// Get the data path from the decoded object
subContainer := gabs.Container{}
switch x := parentData.(type) {
case string:
s.ParseObject(x, &subContainer)
default:
log.Debug("Get Nested Object: parentData is not a string")
return ""
}
return s.Object(&subContainer)
}
Cognitive complexity: 4, Cyclomatic complexity: 3
func (*ServiceDiscovery) Object
func (s *ServiceDiscovery) Object(item *gabs.Container) string {
hostnameData := s.decodeToNameSpace(s.dataPath, item)
if str, ok := hostnameData.(string); ok {
// Get the port
str = s.addPortFromObject(str, item)
return str
}
log.Warning("Get Object: hostname is not a string")
return ""
}
Cognitive complexity: 2, Cyclomatic complexity: 2
func (*ServiceDiscovery) ParseObject
func (s *ServiceDiscovery) ParseObject(contents string, jsonParsed *gabs.Container) error {
log.Debug("Parsing raw data: ", contents)
jp, err := gabs.ParseJSON([]byte(contents))
if err != nil {
log.Error(err)
return err
}
*jsonParsed = *jp
log.Debug("Got:", jsonParsed)
return nil
}
Cognitive complexity: 2, Cyclomatic complexity: 2
func (*ServiceDiscovery) ProcessRawData
func (s *ServiceDiscovery) ProcessRawData(rawData string) (*apidef.HostList, error) {
var jsonParsed gabs.Container
hostlist := apidef.NewHostList()
if s.endpointReturnsList {
// Convert to an object
jsonData := s.rawListToObj(rawData)
if err := s.ParseObject(jsonData, &jsonParsed); err != nil {
log.Error("Parse object failed: ", err)
return nil, err
}
log.Debug("Parsed object list: ", jsonParsed)
// Treat JSON as a list and then apply the data path
if s.isTargetList {
// Get all values
asList := s.SubObjectFromList(&jsonParsed)
log.Debug("Host list:", asList)
hostlist.Set(asList)
return hostlist, nil
}
// Get the top value
list := s.SubObjectFromList(&jsonParsed)
var host string
for _, v := range list {
host = v
break
}
hostlist.Set([]string{host})
return hostlist, nil
}
// It's an object
s.ParseObject(rawData, &jsonParsed)
if s.isTargetList {
// It's a list object
log.Debug("It's a target list - getting sub object from list")
log.Debug("Passing in: ", jsonParsed)
asList := s.SubObjectFromList(&jsonParsed)
hostlist.Set(asList)
log.Debug("Got from object: ", hostlist)
return hostlist, nil
}
// It's a single object
host := s.SubObject(&jsonParsed)
hostlist.Set([]string{host})
return hostlist, nil
}
Cognitive complexity: 13, Cyclomatic complexity: 6
func (*ServiceDiscovery) SubObject
func (s *ServiceDiscovery) SubObject(obj *gabs.Container) string {
return s.Hostname(obj) + s.targetPath
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*ServiceDiscovery) SubObjectFromList
func (s *ServiceDiscovery) SubObjectFromList(objList *gabs.Container) []string {
hostList := []string{}
var hostname string
var set []*gabs.Container
if s.endpointReturnsList {
// pre-process the object since we've nested it
set = s.decodeToNameSpaceAsArray(arrayName, objList)
log.Debug("set: ", set)
} else if s.isNested { // It's an object, but the value may be nested
// Get the actual raw string object
parentData := s.decodeToNameSpace(s.parentPath, objList)
// Get the data path from the decoded object
subContainer := gabs.Container{}
// Now check if this string is a list
nestedString, ok := parentData.(string)
if !ok {
log.Debug("parentData is not a string")
return hostList
}
if s.isList(nestedString) {
log.Debug("Yup, it's a list")
jsonData := s.rawListToObj(nestedString)
s.ParseObject(jsonData, &subContainer)
set = s.decodeToNameSpaceAsArray(arrayName, &subContainer)
// Hijack this here because we need to use a non-nested get
for _, item := range set {
log.Debug("Child in list: ", item)
hostname = s.Object(item) + s.targetPath
// Add to list
hostList = append(hostList, hostname)
}
return hostList
}
log.Debug("Not a list")
s.ParseObject(nestedString, &subContainer)
set = s.decodeToNameSpaceAsArray(s.dataPath, objList)
log.Debug("set (object list): ", objList)
} else if s.parentPath != "" {
set = s.decodeToNameSpaceAsArray(s.parentPath, objList)
}
for _, item := range set {
log.Debug("Child in list: ", item)
hostname = s.Hostname(item) + s.targetPath
// Add to list
hostList = append(hostList, hostname)
}
return hostList
}
Cognitive complexity: 18, Cyclomatic complexity: 8
func (*ServiceDiscovery) Target
func (s *ServiceDiscovery) Target(serviceURL string) (*apidef.HostList, error) {
// Get the data
rawData, err := s.getServiceData(serviceURL)
if err != nil {
return nil, err
}
return s.ProcessRawData(rawData)
}
Cognitive complexity: 2, Cyclomatic complexity: 2
func (*SessionLimiter) Context
func (l *SessionLimiter) Context() context.Context {
return l.ctx
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*SessionLimiter) ForwardMessage
ForwardMessage will enforce rate limiting, returning a non-zero sessionFailReason if session limits have been exceeded. Key values to manage rate are Rate and Per, e.g. Rate of 10 messages Per 10 seconds
func (l *SessionLimiter) ForwardMessage(r *http.Request, session *user.SessionState, rateLimitKey string, quotaKey string, store storage.Handler, enableRL, enableQ bool, api *APISpec, dryRun bool) sessionFailReason {
// check for limit on API level (set to session by ApplyPolicies)
accessDef, allowanceScope, err := GetAccessDefinitionByAPIIDOrSession(session, api)
if err != nil {
log.WithField("apiID", api.APIID).Debugf("[RATE] %s", err.Error())
return sessionFailRateLimit
}
var (
apiLimit = accessDef.Limit.Clone()
endpointRLKeySuffix = ""
)
endpointRLInfo, doEndpointRL := l.RateLimitInfo(r, api, accessDef.Endpoints)
if doEndpointRL {
apiLimit.Rate = endpointRLInfo.Rate
apiLimit.Per = endpointRLInfo.Per
endpointRLKeySuffix = endpointRLInfo.KeySuffix
}
// If quotaKey is not set then the default ratelimit keys should be used.
useCustomKey := quotaKey != ""
// If rate is -1 or 0, it means unlimited and no need for rate limiting.
if enableRL && apiLimit.Rate > 0 {
log.Debug("[RATELIMIT] Inbound raw key is: ", rateLimitKey)
// This limiter key should be used consistently here out.
limiterKey := rate.LimiterKey(session, allowanceScope, rateLimitKey, useCustomKey)
if endpointRLKeySuffix != "" {
log.Debugf("[RATELIMIT] applying endpoint rate limit key suffix: %s: %s", limiterKey, endpointRLKeySuffix)
limiterKey = rate.Prefix(limiterKey, endpointRLKeySuffix)
}
log.Debug("[RATELIMIT] Rate limiter key is: ", limiterKey)
limiter := rate.Limiter(l.config, l.limiterStorage)
switch {
case limiter != nil:
err := limiter(r.Context(), limiterKey, apiLimit.Rate, apiLimit.Per)
if errors.Is(err, rate.ErrLimitExhausted) {
return sessionFailRateLimit
}
case l.config.EnableSentinelRateLimiter:
if l.limitSentinel(r, session, limiterKey, apiLimit, dryRun) {
return sessionFailRateLimit
}
case l.config.EnableRedisRollingLimiter:
if l.limitRedis(r, session, limiterKey, apiLimit, dryRun) {
return sessionFailRateLimit
}
default:
var n float64
if l.drlManager.Servers != nil {
n = float64(l.drlManager.Servers.Count())
}
cost := apiLimit.Rate / apiLimit.Per
c := l.config.DRLThreshold
if c == 0 {
// defaults to 5
c = 5
}
if n <= 1 || n*c < cost {
// If we have 1 server, there is no need to strain redis at all the leaky
// bucket algorithm will suffice.
bucketKey := limiterKey + ":" + session.LastUpdated
if useCustomKey {
bucketKey = limiterKey
}
if l.limitDRL(bucketKey, apiLimit, dryRun) {
return sessionFailRateLimit
}
} else {
if l.limitRedis(r, session, limiterKey, apiLimit, dryRun) {
return sessionFailRateLimit
}
}
}
}
if enableQ {
if l.config.LegacyEnableAllowanceCountdown {
session.Allowance = session.Allowance - 1
}
if l.RedisQuotaExceeded(r, session, quotaKey, allowanceScope, apiLimit, store, l.config.HashKeys) {
return sessionFailQuota
}
}
return sessionFailNone
}
Cognitive complexity: 39, Cyclomatic complexity: 23
func (*SessionLimiter) RateLimitInfo
func (l *SessionLimiter) RateLimitInfo(r *http.Request, api *APISpec, endpoints user.Endpoints) (*user.EndpointRateLimitInfo, bool) {
// Hook per-api settings here (m.Spec...)
isPrefixMatch := l.config.HttpServerOptions.EnablePathPrefixMatching
isSuffixMatch := l.config.HttpServerOptions.EnablePathSuffixMatching
urlPaths := []string{
api.StripListenPath(r.URL.Path),
r.URL.Path,
}
for _, endpoint := range endpoints {
if !endpoint.Methods.Contains(r.Method) {
continue
}
pattern := httputil.PreparePathRegexp(endpoint.Path, isPrefixMatch, isSuffixMatch)
asRegex, err := regexp.Compile(pattern)
if err != nil {
log.WithError(err).Error("endpoint rate limit: error compiling regex")
continue
}
for _, urlPath := range urlPaths {
match := asRegex.MatchString(urlPath)
if !match {
break
}
for _, endpointMethod := range endpoint.Methods {
if !strings.EqualFold(endpointMethod.Name, r.Method) {
continue
}
return &user.EndpointRateLimitInfo{
KeySuffix: storage.HashStr(fmt.Sprintf("%s:%s", endpointMethod.Name, endpoint.Path)),
Rate: endpointMethod.Limit.Rate,
Per: endpointMethod.Limit.Per,
}, true
}
}
}
return nil, false
}
Cognitive complexity: 19, Cyclomatic complexity: 8
func (*SessionLimiter) RedisQuotaExceeded
RedisQuotaExceeded returns true if the request should be blocked as over quota.
func (l *SessionLimiter) RedisQuotaExceeded(r *http.Request, session *user.SessionState, quotaKey, scope string, limit *user.APILimit, store storage.Handler, hashKeys bool) bool {
logger := log.WithFields(logrus.Fields{
"quotaMax": limit.QuotaMax,
"quotaRenewalRate": limit.QuotaRenewalRate,
})
if limit.QuotaMax <= 0 {
return false
}
// don't use the requests cancellation context
ctx := context.Background()
quotaScope := ""
if scope != "" {
quotaScope = scope + "-"
}
key := session.KeyID
if hashKeys {
key = storage.HashStr(session.KeyID)
}
if quotaKey != "" {
key = quotaKey
}
now := time.Now()
// rawKey is the redis key for quota
rawKey := QuotaKeyPrefix + quotaScope + key
var quotaRenewalRate time.Duration
if limit.QuotaRenewalRate > 0 {
quotaRenewalRate = time.Second * time.Duration(limit.QuotaRenewalRate)
}
conn := l.limiterStorage
var expired, exists bool
var expiredAt time.Time
dur, err := conn.PTTL(ctx, rawKey).Result()
if err != nil && !errors.Is(err, redis.Nil) {
logger.WithError(err).Error("error getting key TTL, blocking")
return true
}
// The command returns -2 if the key does not exist.
// The command returns -1 if the key exists but has no associated expire.
expired = dur < 0
exists = dur != -2
expiredAt = now.Add(dur)
logger = logger.WithFields(logrus.Fields{
"exists": exists,
"expired": expired,
"rawKey": rawKey,
})
increment := func() bool {
var res *redis.IntCmd
_, err := conn.Pipelined(ctx, func(pipe redis.Pipeliner) error {
res = pipe.Incr(ctx, rawKey)
if res.Val() == 1 && quotaRenewalRate > 0 {
pipe.Expire(ctx, rawKey, quotaRenewalRate)
}
return nil
})
if err != nil {
logger.WithError(err).Error("error incrementing quota key")
return true
}
quota := res.Val()
blocked := quota-1 >= limit.QuotaMax
remaining := limit.QuotaMax - quota
if blocked {
remaining = 0
}
logger = logger.WithField("quota", quota-1)
logger = logger.WithField("blocked", blocked)
logger = logger.WithField("remaining", remaining)
logger.Debug("[QUOTA] Update quota key")
l.updateSessionQuota(session, scope, remaining, expiredAt.Unix())
return blocked
}
// If exists and not expired, just increment it.
if exists && !expired {
return increment()
}
// if key is expired and can't renew, update the counter and
// block traffic going forward.
if limit.QuotaRenewalRate <= 0 {
return increment()
}
// First, ensure a distributed lock
locker := limiter.NewLimiter(conn).Locker(rawKey)
// Lock the key
if err := locker.Lock(ctx); err != nil {
// Increment the key if lock fails
return increment()
}
// Unlock the key when done
defer func() {
if err := locker.Unlock(ctx); err != nil {
logger.WithError(err).Error("error unlocking quota key")
}
}()
// locked: reset quota + increment
conn.Set(ctx, rawKey, 0, quotaRenewalRate)
return increment()
}
Cognitive complexity: 31, Cyclomatic complexity: 17
func (*StatsDSink) Drain
func (s *StatsDSink) Drain() {
s.cmdChan <- statsdEmitCmd{Kind: statsdCmdKindDrain}
<-s.drainDoneChan
}
Cognitive complexity: 1, Cyclomatic complexity: 1
func (*StatsDSink) EmitComplete
func (s *StatsDSink) EmitComplete(job string, status health.CompletionStatus, nanos int64, kvs map[string]string) {
s.cmdChan <- statsdEmitCmd{Kind: statsdCmdKindComplete, Job: job, Status: status, Nanos: nanos}
}
Cognitive complexity: 1, Cyclomatic complexity: 1
func (*StatsDSink) EmitEvent
func (s *StatsDSink) EmitEvent(job, event string, kvs map[string]string) {
s.cmdChan <- statsdEmitCmd{Kind: statsdCmdKindEvent, Job: job, Event: event}
}
Cognitive complexity: 1, Cyclomatic complexity: 1
func (*StatsDSink) EmitEventErr
func (s *StatsDSink) EmitEventErr(job, event string, inputErr error, kvs map[string]string) {
s.cmdChan <- statsdEmitCmd{Kind: statsdCmdKindEventErr, Job: job, Event: event}
}
Cognitive complexity: 1, Cyclomatic complexity: 1
func (*StatsDSink) EmitGauge
func (s *StatsDSink) EmitGauge(job, event string, value float64, kvs map[string]string) {
s.cmdChan <- statsdEmitCmd{Kind: statsdCmdKindGauge, Job: job, Event: event, Value: value}
}
Cognitive complexity: 1, Cyclomatic complexity: 1
func (*StatsDSink) EmitTiming
func (s *StatsDSink) EmitTiming(job, event string, nanos int64, kvs map[string]string) {
s.cmdChan <- statsdEmitCmd{Kind: statsdCmdKindTiming, Job: job, Event: event, Nanos: nanos}
}
Cognitive complexity: 1, Cyclomatic complexity: 1
func (*StatsDSink) Stop
func (s *StatsDSink) Stop() {
s.cmdChan <- statsdEmitCmd{Kind: statsdCmdKindStop}
<-s.stopDoneChan
}
Cognitive complexity: 1, Cyclomatic complexity: 1
func (*StripAuth) EnabledForSpec
func (sa *StripAuth) EnabledForSpec() bool {
return sa.Spec.StripAuthData
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*StripAuth) Name
func (sa *StripAuth) Name() string {
return "StripAuth"
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*StripAuth) ProcessRequest
func (sa *StripAuth) ProcessRequest(w http.ResponseWriter, r *http.Request, _ interface{}) (error, int) {
strip := func(typ string, config *apidef.AuthConfig) {
log.WithFields(logrus.Fields{
"prefix": sa.Name(),
}).Debugf("%s: %+v\n", typ, config)
if config.UseParam {
sa.stripFromParams(r, config)
}
sa.stripFromHeaders(r, config)
}
for typ, config := range sa.Spec.AuthConfigs {
strip(typ, &config)
}
// For backward compatibility
if len(sa.Spec.AuthConfigs) == 0 {
strip(apidef.AuthTokenType, &sa.Spec.Auth)
}
return nil, http.StatusOK
}
Cognitive complexity: 10, Cyclomatic complexity: 4
func (*SuccessHandler) RecordHit
func (s *SuccessHandler) RecordHit(r *http.Request, timing analytics.Latency, code int, responseCopy *http.Response, cached bool) {
if s.Spec.DoNotTrack || ctxGetDoNotTrack(r) {
return
}
ip := request.RealIP(r)
if s.Spec.GlobalConfig.StoreAnalytics(ip) {
t := time.Now()
// Track the key ID if it exists
token := ctxGetAuthToken(r)
// Track version data
version := s.Spec.getVersionFromRequest(r)
if version == "" {
version = "Non Versioned"
}
// If OAuth, we need to grab it from the session, which may or may not exist
oauthClientID := ""
var alias string
session := ctxGetSession(r)
tags := make([]string, 0, estimateTagsCapacity(session, s.Spec))
if session != nil {
oauthClientID = session.OauthClientID
tags = append(tags, getSessionTags(session)...)
alias = session.Alias
}
if len(s.Spec.TagHeaders) > 0 {
tags = tagHeaders(r, s.Spec.TagHeaders, tags)
}
if len(s.Spec.Tags) > 0 {
tags = append(tags, s.Spec.Tags...)
}
if cached {
tags = append(tags, "cached-response")
}
rawRequest := ""
rawResponse := ""
if recordDetail(r, s.Spec) {
// Get the wire format representation
var wireFormatReq bytes.Buffer
r.Write(&wireFormatReq)
rawRequest = base64.StdEncoding.EncodeToString(wireFormatReq.Bytes())
// responseCopy, unlike requestCopy, can be nil
// here - if the response was cached in
// mw_redis_cache, RecordHit gets passed a nil
// response copy.
// TODO: pass a copy of the cached response in
// mw_redis_cache instead? is there a reason not
// to include that in the analytics?
if responseCopy != nil && responseCopy.Body != nil {
// we need to delete the chunked transfer encoding header to avoid malformed body in our rawResponse
httputil.RemoveResponseTransferEncoding(responseCopy, "chunked")
responseContent, err := io.ReadAll(responseCopy.Body)
if err != nil {
log.Error("Couldn't read response body", err)
}
responseCopy.Body = respBodyReader(r, responseCopy)
// Get the wire format representation
var wireFormatRes bytes.Buffer
responseCopy.Write(&wireFormatRes)
responseCopy.Body = ioutil.NopCloser(bytes.NewBuffer(responseContent))
rawResponse = base64.StdEncoding.EncodeToString(wireFormatRes.Bytes())
}
}
trackEP := false
trackedPath := r.URL.Path
if p := ctxGetTrackedPath(r); p != "" {
trackEP = true
trackedPath = p
}
host := r.URL.Host
if host == "" && s.Spec.target != nil {
host = s.Spec.target.Host
}
record := analytics.AnalyticsRecord{
Method: r.Method,
Host: host,
Path: trackedPath,
RawPath: r.URL.Path,
ContentLength: r.ContentLength,
UserAgent: r.Header.Get(header.UserAgent),
Day: t.Day(),
Month: t.Month(),
Year: t.Year(),
Hour: t.Hour(),
ResponseCode: code,
APIKey: token,
TimeStamp: t,
APIVersion: version,
APIName: s.Spec.Name,
APIID: s.Spec.APIID,
OrgID: s.Spec.OrgID,
OauthID: oauthClientID,
RequestTime: timing.Total,
RawRequest: rawRequest,
RawResponse: rawResponse,
IPAddress: ip,
Geo: analytics.GeoData{},
Network: analytics.NetworkStats{},
Latency: timing,
Tags: tags,
Alias: alias,
TrackPath: trackEP,
ExpireAt: t,
}
if s.Spec.GlobalConfig.AnalyticsConfig.EnableGeoIP {
record.GetGeo(ip, s.Gw.Analytics.GeoIPDB)
}
recordGraphDetails(&record, r, responseCopy, s.Spec)
// skip tagging subgraph requests for graphpump, it only handles generated supergraph requests
if s.Spec.GraphQL.Enabled && s.Spec.GraphQL.ExecutionMode != apidef.GraphQLExecutionModeSubgraph {
record.Tags = append(record.Tags, "tyk-graph-analytics")
record.ApiSchema = base64.StdEncoding.EncodeToString([]byte(s.Spec.GraphQL.Schema))
}
expiresAfter := s.Spec.ExpireAnalyticsAfter
if s.Spec.GlobalConfig.EnforceOrgDataAge {
orgExpireDataTime := s.OrgSessionExpiry(s.Spec.OrgID)
if orgExpireDataTime > 0 {
expiresAfter = orgExpireDataTime
}
}
record.SetExpiry(expiresAfter)
if s.Spec.GlobalConfig.AnalyticsConfig.NormaliseUrls.Enabled {
NormalisePath(&record, &s.Spec.GlobalConfig)
}
if s.Spec.AnalyticsPlugin.Enabled {
//send to plugin
_ = s.Spec.AnalyticsPluginConfig.processRecord(&record)
}
err := s.Gw.Analytics.RecordHit(&record)
if err != nil {
log.WithError(err).Error("could not store analytic record")
}
}
// Report in health check
reportHealthValue(s.Spec, RequestLog, strconv.FormatInt(timing.Total, 10))
}
Cognitive complexity: 41, Cyclomatic complexity: 24
func (*SuccessHandler) ServeHTTP
ServeHTTP will store the request details in the analytics store if necessary and proxy the request to it's final destination, this is invoked by the ProxyHandler or right at the start of a request chain if the URL Spec states the path is Ignored
func (s *SuccessHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) *http.Response {
log.Debug("Started proxy")
defer s.Base().UpdateRequestSession(r)
// Make sure we get the correct target URL
s.Spec.SanitizeProxyPaths(r)
addVersionHeader(w, r, s.Spec.GlobalConfig)
t1 := time.Now()
resp := s.Proxy.ServeHTTP(w, r)
millisec := DurationToMillisecond(time.Since(t1))
log.Debug("Upstream request took (ms): ", millisec)
if resp.Response != nil {
latency := analytics.Latency{
Total: int64(millisec),
Upstream: int64(DurationToMillisecond(resp.UpstreamLatency)),
}
s.RecordHit(r, latency, resp.Response.StatusCode, resp.Response, false)
s.RecordAccessLog(r, resp.Response, latency)
}
log.Debug("Done proxy")
return nil
}
Cognitive complexity: 3, Cyclomatic complexity: 2
func (*SuccessHandler) ServeHTTPWithCache
ServeHTTPWithCache will store the request details in the analytics store if necessary and proxy the request to it's final destination, this is invoked by the ProxyHandler or right at the start of a request chain if the URL Spec states the path is Ignored Itwill also return a response object for the cache
func (s *SuccessHandler) ServeHTTPWithCache(w http.ResponseWriter, r *http.Request) ProxyResponse {
// Make sure we get the correct target URL
s.Spec.SanitizeProxyPaths(r)
t1 := time.Now()
inRes := s.Proxy.ServeHTTPForCache(w, r)
millisec := DurationToMillisecond(time.Since(t1))
addVersionHeader(w, r, s.Spec.GlobalConfig)
log.Debug("Upstream request took (ms): ", millisec)
if inRes.Response != nil {
latency := analytics.Latency{
Total: int64(millisec),
Upstream: int64(DurationToMillisecond(inRes.UpstreamLatency)),
}
s.RecordHit(r, latency, inRes.Response.StatusCode, inRes.Response, false)
}
return inRes
}
Cognitive complexity: 3, Cyclomatic complexity: 2
func (*Test) AddDynamicHandler
func (s *Test) AddDynamicHandler(path string, handlerFunc http.HandlerFunc) {
path = strings.Trim(path, "/")
s.dynamicHandlers[path] = handlerFunc
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*Test) BundleHandleFunc
func (s *Test) BundleHandleFunc(w http.ResponseWriter, r *http.Request) {
s.Gw.TestBundleMu.Lock()
defer s.Gw.TestBundleMu.Unlock()
bundleName := strings.Replace(r.URL.Path, "/bundles/", "", -1)
bundle, exists := s.Gw.TestBundles[bundleName]
if !exists {
log.Warning(s.Gw.TestBundles)
http.Error(w, "Bundle not found", http.StatusNotFound)
return
}
w.Header().Set("Content-Type", "application/zip")
z := zip.NewWriter(w)
for name, content := range bundle {
f, _ := z.Create(name)
f.Write([]byte(content))
}
z.Close()
}
Cognitive complexity: 5, Cyclomatic complexity: 3
func (*Test) Close
func (s *Test) Close() {
defer s.cancel()
for _, p := range s.Gw.DefaultProxyMux.proxies {
if p.listener != nil {
p.listener.Close()
}
}
gwConfig := s.Gw.GetConfig()
s.Gw.DefaultProxyMux.swap(&proxyMux{}, s.Gw)
if s.config.SeparateControlAPI {
gwConfig.ControlAPIPort = 0
s.Gw.SetConfig(gwConfig)
}
// if jsvm enabled we need to unmount to prevent high memory consumption
if s.Gw.GetConfig().EnableJSVM {
s.Gw.GlobalEventsJSVM.VM = nil
}
ctxShutDown, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
err := s.HttpHandler.Shutdown(ctxShutDown)
if err != nil {
log.WithError(err).Error("shutting down the http handler")
} else {
log.Info("server exited properly")
}
s.Gw.Analytics.Stop()
s.Gw.ReloadTestCase.StopTicker()
s.Gw.GlobalHostChecker.StopPoller()
s.Gw.NewRelicApplication.Shutdown(5 * time.Second)
err = s.RemoveApis()
if err != nil {
log.Error("could not remove apis")
}
s.Gw.cacheClose()
}
Cognitive complexity: 16, Cyclomatic complexity: 7
func (*Test) CreatePolicy
func (s *Test) CreatePolicy(pGen ...func(p *user.Policy)) string {
pID := s.Gw.keyGen.GenerateAuthKey("")
pol := CreateStandardPolicy()
pol.ID = pID
if len(pGen) > 0 {
pGen[0](pol)
}
s.Gw.policiesMu.Lock()
s.Gw.policiesByID[pol.ID] = *pol
s.Gw.policiesMu.Unlock()
return pol.ID
}
Cognitive complexity: 2, Cyclomatic complexity: 2
func (*Test) CreateSession
func (s *Test) CreateSession(sGen ...func(s *user.SessionState)) (*user.SessionState, string) {
session := CreateStandardSession()
if len(sGen) > 0 {
sGen[0](session)
}
client := GetTLSClient(nil, nil)
resp, err := s.Do(test.TestCase{
Method: http.MethodPost,
Path: "/tyk/keys/create",
Data: session,
Client: client,
AdminAuth: true,
})
if err != nil {
log.Fatal("Error while creating session:", err)
return nil, ""
}
keySuccess := apiModifyKeySuccess{}
err = json.NewDecoder(resp.Body).Decode(&keySuccess)
if err != nil {
log.Fatal("Error while decoding session response:", err)
return nil, ""
}
createdSession, _ := s.Gw.GlobalSessionManager.SessionDetail(session.OrgID, keySuccess.Key, false)
return &createdSession, keySuccess.Key
}
Cognitive complexity: 8, Cyclomatic complexity: 4
func (*Test) DeletePolicy
func (s *Test) DeletePolicy(policyID string) {
s.Gw.policiesMu.Lock()
delete(s.Gw.policiesByID, policyID)
s.Gw.policiesMu.Unlock()
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*Test) Do
func (s *Test) Do(tc test.TestCase) (*http.Response, error) {
req, _ := s.testRunner.RequestBuilder(&tc)
return s.testRunner.Do(req, &tc)
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*Test) EnablePort
func (s *Test) EnablePort(port int, protocol string) {
c := s.Gw.GetConfig()
if c.PortWhiteList == nil {
c.PortWhiteList = map[string]config.PortWhiteList{
protocol: {
Ports: []int{port},
},
}
} else {
m, ok := c.PortWhiteList[protocol]
if !ok {
m = config.PortWhiteList{
Ports: []int{port},
}
} else {
m.Ports = append(m.Ports, port)
}
c.PortWhiteList[protocol] = m
}
s.Gw.SetConfig(c)
}
Cognitive complexity: 13, Cyclomatic complexity: 3
func (*Test) GetApiById
func (s *Test) GetApiById(apiId string) *APISpec {
return s.Gw.getApiSpec(apiId)
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*Test) GetPolicyById
func (s *Test) GetPolicyById(policyId string) (user.Policy, bool) {
s.Gw.policiesMu.Lock()
defer s.Gw.policiesMu.Unlock()
pol, found := s.Gw.policiesByID[policyId]
return pol, found
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*Test) RegisterBundle
func (s *Test) RegisterBundle(name string, files map[string]string) string {
s.Gw.TestBundleMu.Lock()
defer s.Gw.TestBundleMu.Unlock()
bundleID := name + "-" + uuid.NewHex() + ".zip"
s.Gw.TestBundles[bundleID] = files
return bundleID
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*Test) RegisterJSFileMiddleware
func (s *Test) RegisterJSFileMiddleware(apiid string, files map[string]string) {
gwConfig := s.Gw.GetConfig()
err := os.MkdirAll(gwConfig.MiddlewarePath+"/"+apiid+"/post", 0755)
if err != nil {
log.WithError(err).Error("creating directory in middleware post path")
}
err = os.MkdirAll(gwConfig.MiddlewarePath+"/"+apiid+"/pre", 0755)
if err != nil {
log.WithError(err).Error("creating directory in middleware pre path")
}
for file, content := range files {
err = ioutil.WriteFile(gwConfig.MiddlewarePath+"/"+apiid+"/"+file, []byte(content), 0755)
if err != nil {
log.WithError(err).Error("writing in file")
}
}
}
Cognitive complexity: 9, Cyclomatic complexity: 5
func (*Test) ReloadGatewayProxy
func (s *Test) ReloadGatewayProxy() {
s.Gw.DefaultProxyMux.swap(&proxyMux{}, s.Gw)
s.Gw.startServer()
}
Cognitive complexity: 1, Cyclomatic complexity: 1
func (*Test) RemoveApis
RemoveApis clean all the apis from a living gw
func (s *Test) RemoveApis() error {
s.Gw.apisMu.Lock()
defer func() {
s.Gw.apiSpecs = []*APISpec{}
s.Gw.apisByID = map[string]*APISpec{}
s.Gw.apisMu.Unlock()
}()
// clear bundle caches
for _, spec := range s.Gw.apiSpecs {
destPath := s.Gw.getBundleDestPath(spec)
if _, err := os.Stat(destPath); err == nil {
err = os.RemoveAll(destPath)
log.WithError(err).Infof("Clearing bundle cache: %s", destPath)
}
}
err := os.RemoveAll(s.Gw.GetConfig().AppPath)
if err != nil {
log.WithError(err).Error("removing apis from gw")
}
return err
}
Cognitive complexity: 10, Cyclomatic complexity: 4
func (*Test) ResetTestConfig
Deprecated: ResetTestConfig resets the config for the global gateway.
The function does nothing, the correct way is to reuse StartTest(), filling the config from the provided callback. Usage impacts a few tests (small). See TestCustomDomain for an updated test.
func (s *Test) ResetTestConfig() {
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*Test) Run
func (s *Test) Run(t testing.TB, testCases ...test.TestCase) (*http.Response, error) {
t.Helper()
return s.testRunner.Run(t, testCases...)
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*Test) RunExt
TODO:(gernest) when hot reload is supported enable this.
func (s *Test) RunExt(t testing.TB, testCases ...test.TestCase) {
t.Helper()
s.Run(t, testCases...)
var testMatrix = []struct {
goagain bool
overrideDefaults bool
}{
{false, false},
{false, true},
{true, true},
{true, false},
}
for _, m := range testMatrix {
s.config.HotReload = m.goagain
s.config.overrideDefaults = m.overrideDefaults
title := fmt.Sprintf("hotReload: %v, overrideDefaults: %v", m.goagain, m.overrideDefaults)
t.(*testing.T).Run(title, func(t *testing.T) {
s.Run(t, testCases...)
})
}
}
Cognitive complexity: 10, Cyclomatic complexity: 2
func (*Test) StopRPCClient
func (s *Test) StopRPCClient() {
rpc.Reset()
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*TrackEndpointMiddleware) EnabledForSpec
func (t *TrackEndpointMiddleware) EnabledForSpec() bool {
if !t.Spec.GlobalConfig.EnableAnalytics || t.Spec.DoNotTrack {
return false
}
for _, version := range t.Spec.VersionData.Versions {
if len(version.ExtendedPaths.TrackEndpoints) > 0 || len(version.ExtendedPaths.DoNotTrackEndpoints) > 0 {
return true
}
}
return false
}
Cognitive complexity: 7, Cyclomatic complexity: 6
func (*TrackEndpointMiddleware) Name
func (t *TrackEndpointMiddleware) Name() string {
return "TrackEndpointMiddleware"
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*TrackEndpointMiddleware) ProcessRequest
ProcessRequest will run any checks on the request on the way through the system, return an error to have the chain fail
func (t *TrackEndpointMiddleware) ProcessRequest(w http.ResponseWriter, r *http.Request, _ interface{}) (error, int) {
vInfo, _ := t.Spec.Version(r)
versionPaths := t.Spec.RxPaths[vInfo.Name]
foundTracked, metaTrack := t.Spec.CheckSpecMatchesStatus(r, versionPaths, RequestTracked)
if foundTracked {
ctxSetTrackedPath(r, metaTrack.(*apidef.TrackEndpointMeta).Path)
}
foundDnTrack, _ := t.Spec.CheckSpecMatchesStatus(r, versionPaths, RequestNotTracked)
if foundDnTrack {
ctxSetDoNotTrack(r, true)
}
return nil, http.StatusOK
}
Cognitive complexity: 5, Cyclomatic complexity: 3
func (*TransformHeaders) EnabledForSpec
func (t *TransformHeaders) EnabledForSpec() bool {
for _, version := range t.Spec.VersionData.Versions {
if version.GlobalHeadersEnabled() {
return true
}
if version.HasEndpointReqHeader() {
return true
}
}
return false
}
Cognitive complexity: 7, Cyclomatic complexity: 4
func (*TransformHeaders) Name
func (t *TransformHeaders) Name() string {
return "TransformHeaders"
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*TransformHeaders) ProcessRequest
ProcessRequest will run any checks on the request on the way through the system, return an error to have the chain fail
func (t *TransformHeaders) ProcessRequest(w http.ResponseWriter, r *http.Request, _ interface{}) (error, int) {
vInfo, _ := t.Spec.Version(r)
ignoreCanonical := t.Gw.GetConfig().IgnoreCanonicalMIMEHeaderKey
// Manage global headers first - remove
if !vInfo.GlobalHeadersDisabled {
for _, gdKey := range vInfo.GlobalHeadersRemove {
t.Logger().Debug("Removing: ", gdKey)
r.Header.Del(gdKey)
}
// Add
for nKey, nVal := range vInfo.GlobalHeaders {
t.Logger().Debug("Adding: ", nKey)
setCustomHeader(r.Header, nKey, t.Gw.ReplaceTykVariables(r, nVal, false), ignoreCanonical)
}
}
versionPaths := t.Spec.RxPaths[vInfo.Name]
found, meta := t.Spec.CheckSpecMatchesStatus(r, versionPaths, HeaderInjected)
if found {
hmeta := meta.(*apidef.HeaderInjectionMeta)
for _, dKey := range hmeta.DeleteHeaders {
r.Header.Del(dKey)
}
for nKey, nVal := range hmeta.AddHeaders {
setCustomHeader(r.Header, nKey, t.Gw.ReplaceTykVariables(r, nVal, false), ignoreCanonical)
}
}
return nil, http.StatusOK
}
Cognitive complexity: 17, Cyclomatic complexity: 7
func (*TransformJQMiddleware) EnabledForSpec
func (t *TransformJQMiddleware) EnabledForSpec() bool {
for _, version := range t.Spec.VersionData.Versions {
if len(version.ExtendedPaths.TransformJQ) > 0 {
log.Warning("JQ transform not supported.")
return false
}
}
return false
}
Cognitive complexity: 5, Cyclomatic complexity: 3
func (*TransformJQMiddleware) Name
func (t *TransformJQMiddleware) Name() string {
return "TransformJQMiddleware"
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*TransformJQMiddleware) ProcessRequest
ProcessRequest will run any checks on the request on the way through the system, return an error to have the chain fail
func (t *TransformJQMiddleware) ProcessRequest(w http.ResponseWriter, r *http.Request, _ interface{}) (error, int) {
return nil, 200
}
Cognitive complexity: 1, Cyclomatic complexity: 1
func (*TransformMethod) EnabledForSpec
func (t *TransformMethod) EnabledForSpec() bool {
for _, version := range t.Spec.VersionData.Versions {
if len(version.ExtendedPaths.MethodTransforms) > 0 {
return true
}
}
return false
}
Cognitive complexity: 5, Cyclomatic complexity: 3
func (*TransformMethod) Name
func (t *TransformMethod) Name() string {
return "TransformMethod"
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*TransformMethod) ProcessRequest
ProcessRequest will run any checks on the request on the way through the system, return an error to have the chain fail
func (t *TransformMethod) ProcessRequest(w http.ResponseWriter, r *http.Request, _ interface{}) (error, int) {
version, _ := t.Spec.Version(r)
versionPaths := t.Spec.RxPaths[version.Name]
found, meta := t.Spec.CheckSpecMatchesStatus(r, versionPaths, MethodTransformed)
if !found {
return nil, http.StatusOK
}
mmeta := meta.(*apidef.MethodTransformMeta)
toMethod := strings.ToUpper(mmeta.ToMethod)
ctxSetRequestMethod(r, r.Method)
switch strings.ToUpper(mmeta.ToMethod) {
case "GET", "POST", "PUT", "DELETE", "OPTIONS", "PATCH":
ctxSetTransformRequestMethod(r, toMethod)
default:
return errors.New("Method not allowed"), http.StatusMethodNotAllowed
}
return nil, http.StatusOK
}
Cognitive complexity: 6, Cyclomatic complexity: 4
func (*TransformMiddleware) EnabledForSpec
func (t *TransformMiddleware) EnabledForSpec() bool {
for _, version := range t.Spec.VersionData.Versions {
if len(version.ExtendedPaths.Transform) > 0 {
return true
}
}
return false
}
Cognitive complexity: 5, Cyclomatic complexity: 3
func (*TransformMiddleware) Name
func (t *TransformMiddleware) Name() string {
return "TransformMiddleware"
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*TransformMiddleware) ProcessRequest
ProcessRequest will run any checks on the request on the way through the system, return an error to have the chain fail
func (t *TransformMiddleware) ProcessRequest(w http.ResponseWriter, r *http.Request, _ interface{}) (error, int) {
vInfo, _ := t.Spec.Version(r)
versionPaths := t.Spec.RxPaths[vInfo.Name]
found, meta := t.Spec.CheckSpecMatchesStatus(r, versionPaths, Transformed)
if !found {
return nil, http.StatusOK
}
err := transformBody(r, meta.(*TransformSpec), t)
if err != nil {
t.Logger().WithError(err).Error("Body transform failure")
}
return nil, http.StatusOK
}
Cognitive complexity: 5, Cyclomatic complexity: 3
func (*TykRoundTripper) RoundTrip
func (rt *TykRoundTripper) RoundTrip(r *http.Request) (*http.Response, error) {
hasInternalHeader := r.Header.Get(apidef.TykInternalApiHeader) != ""
if r.URL.Scheme == "tyk" || hasInternalHeader {
if hasInternalHeader {
r.Header.Del(apidef.TykInternalApiHeader)
}
handler, _, found := rt.Gw.findInternalHttpHandlerByNameOrID(r.Host)
if !found {
rt.logger.WithField("looping_url", "tyk://"+r.Host).Error("Couldn't detect target")
return nil, errors.New("handler could")
}
rt.logger.WithField("looping_url", "tyk://"+r.Host).Debug("Executing request on internal route")
return handleInMemoryLoop(handler, r)
}
if rt.Gw.GetConfig().OpenTelemetry.Enabled {
var baseRoundTripper http.RoundTripper = rt.transport
if rt.h2ctransport != nil {
baseRoundTripper = rt.h2ctransport
}
tr := otel.HTTPRoundTripper(baseRoundTripper)
return tr.RoundTrip(r)
}
if rt.h2ctransport != nil {
return rt.h2ctransport.RoundTrip(r)
}
return rt.transport.RoundTrip(r)
}
Cognitive complexity: 12, Cyclomatic complexity: 8
func (*URLRewriteMiddleware) CheckHostRewrite
func (m *URLRewriteMiddleware) CheckHostRewrite(oldPath, newTarget string, r *http.Request) error {
oldAsURL, errParseOld := url.Parse(oldPath)
if errParseOld != nil {
return errParseOld
}
newAsURL, errParseNew := url.Parse(newTarget)
if errParseNew != nil {
return errParseNew
}
if shouldRewriteHost(oldAsURL, newAsURL) {
log.Debug("Detected a host rewrite in pattern!")
setCtxValue(r, ctx.RetainHost, true)
}
return nil
}
Cognitive complexity: 6, Cyclomatic complexity: 4
func (*URLRewriteMiddleware) EnabledForSpec
func (m *URLRewriteMiddleware) EnabledForSpec() bool {
if m.InitTriggerRx() {
m.Spec.URLRewriteEnabled = true
return true
}
return false
}
Cognitive complexity: 2, Cyclomatic complexity: 2
func (*URLRewriteMiddleware) InitTriggerRx
InitTriggerRx will go over all defined URLRewrite triggers and initialize them. It will skip disabled triggers, returning true if at least one trigger is enabled.
func (m *URLRewriteMiddleware) InitTriggerRx() (enabled bool) {
// Generate regexp for each special match parameter
for verKey := range m.Spec.VersionData.Versions {
for pathKey := range m.Spec.VersionData.Versions[verKey].ExtendedPaths.URLRewrite {
rewrite := m.Spec.VersionData.Versions[verKey].ExtendedPaths.URLRewrite[pathKey]
if rewrite.Disabled {
continue
}
enabled = true
for trKey := range rewrite.Triggers {
tr := rewrite.Triggers[trKey]
for key, h := range tr.Options.HeaderMatches {
h.Init()
tr.Options.HeaderMatches[key] = h
}
for key, q := range tr.Options.QueryValMatches {
q.Init()
tr.Options.QueryValMatches[key] = q
}
for key, h := range tr.Options.SessionMetaMatches {
h.Init()
tr.Options.SessionMetaMatches[key] = h
}
for key, h := range tr.Options.RequestContextMatches {
h.Init()
tr.Options.RequestContextMatches[key] = h
}
for key, h := range tr.Options.PathPartMatches {
h.Init()
tr.Options.PathPartMatches[key] = h
}
if tr.Options.PayloadMatches.MatchPattern != "" {
tr.Options.PayloadMatches.Init()
}
rewrite.Triggers[trKey] = tr
}
m.Spec.VersionData.Versions[verKey].ExtendedPaths.URLRewrite[pathKey] = rewrite
}
}
return
}
Cognitive complexity: 28, Cyclomatic complexity: 11
func (*URLRewriteMiddleware) Name
func (m *URLRewriteMiddleware) Name() string {
return "URLRewriteMiddleware"
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*URLRewriteMiddleware) ProcessRequest
ProcessRequest will run any checks on the request on the way through the system, return an error to have the chain fail
func (m *URLRewriteMiddleware) ProcessRequest(w http.ResponseWriter, r *http.Request, _ interface{}) (error, int) {
vInfo, _ := m.Spec.Version(r)
found, meta := m.Spec.CheckSpecMatchesStatus(r, m.Spec.RxPaths[vInfo.Name], URLRewrite)
if !found {
return nil, http.StatusOK
}
//Used for looping feature
//To get host and query parameters
ctxSetOrigRequestURL(r, r.URL)
log.Debug("Rewriter active")
umeta := meta.(*apidef.URLRewriteMeta)
log.Debug(r.URL)
oldPath := r.URL.String()
p, err := m.Gw.urlRewrite(umeta, r)
if err != nil {
log.Error(err)
return err, http.StatusInternalServerError
}
// During looping target can be API name
// Need make it compatible with URL parser
if strings.HasPrefix(p, LoopScheme) {
p = LoopHostRE.ReplaceAllStringFunc(p, func(match string) string {
host := strings.TrimPrefix(match, LoopScheme+"://")
return LoopingUrl(host)
})
}
if err = m.CheckHostRewrite(oldPath, p, r); err != nil {
log.WithError(err).WithField("from", oldPath).WithField("to", p).Error("Checking Host rewrite: error parsing URL")
}
newURL, err := url.Parse(p)
if err != nil {
log.Error("URL Rewrite failed, could not parse: ", p)
} else {
//Setting new path here breaks request middleware
//New path is set in DummyProxyHandler/Cache middleware
ctxSetURLRewriteTarget(r, newURL)
}
return nil, http.StatusOK
}
Cognitive complexity: 10, Cyclomatic complexity: 4
func (*UptimeReportData) SetExpiry
func (u *UptimeReportData) SetExpiry(expiresInSeconds int64) {
expiry := time.Duration(expiresInSeconds) * time.Second
if expiresInSeconds == 0 {
// Expiry is set to 100 years
expiry = (24 * time.Hour) * (365 * 100)
}
t := time.Now()
t2 := t.Add(expiry)
u.ExpireAt = t2
}
Cognitive complexity: 2, Cyclomatic complexity: 2
func (*ValidateJSON) EnabledForSpec
func (k *ValidateJSON) EnabledForSpec() bool {
for _, v := range k.Spec.VersionData.Versions {
if len(v.ExtendedPaths.ValidateJSON) > 0 {
return true
}
}
return false
}
Cognitive complexity: 5, Cyclomatic complexity: 3
func (*ValidateJSON) Name
func (k *ValidateJSON) Name() string {
return "ValidateJSON"
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*ValidateJSON) ProcessRequest
ProcessRequest will run any checks on the request on the way through the system, return an error to have the chain fail
func (k *ValidateJSON) ProcessRequest(w http.ResponseWriter, r *http.Request, _ interface{}) (error, int) {
versionInfo, _ := k.Spec.Version(r)
versionPaths := k.Spec.RxPaths[versionInfo.Name]
found, meta := k.Spec.CheckSpecMatchesStatus(r, versionPaths, ValidateJSONRequest)
if !found {
return nil, http.StatusOK
}
vPathMeta := meta.(*apidef.ValidatePathMeta)
if vPathMeta.Schema == nil {
return errors.New("no schemas to validate against"), http.StatusInternalServerError
}
nopCloseRequestBody(r)
// Load input body into gojsonschema
bodyBytes, err := io.ReadAll(r.Body)
if err != nil {
return err, http.StatusBadRequest
}
r.Body = io.NopCloser(bytes.NewBuffer(bodyBytes))
defer r.Body.Close()
inputLoader := gojsonschema.NewBytesLoader(bodyBytes)
// Perform validation
result, err := gojsonschema.Validate(vPathMeta.SchemaCache, inputLoader)
if err != nil {
return fmt.Errorf("JSON parsing error: %w", err), http.StatusBadRequest
}
// Handle Failure
if !result.Valid() {
if vPathMeta.ErrorResponseCode == 0 {
vPathMeta.ErrorResponseCode = http.StatusUnprocessableEntity
}
return k.formatError(result.Errors()), vPathMeta.ErrorResponseCode
}
// Handle Success
return nil, http.StatusOK
}
Cognitive complexity: 13, Cyclomatic complexity: 7
func (*ValidateRequest) EnabledForSpec
func (k *ValidateRequest) EnabledForSpec() bool {
if !k.Spec.IsOAS {
return false
}
middleware := k.Spec.OAS.GetTykExtension().Middleware
if middleware == nil {
return false
}
if len(middleware.Operations) == 0 {
return false
}
for _, operation := range middleware.Operations {
if operation.ValidateRequest == nil {
continue
}
if operation.ValidateRequest.Enabled {
k.Spec.HasValidateRequest = true
return true
}
}
return false
}
Cognitive complexity: 13, Cyclomatic complexity: 7
func (*ValidateRequest) Name
func (k *ValidateRequest) Name() string {
return "ValidateRequest"
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*ValidateRequest) ProcessRequest
ProcessRequest will run any checks on the request on the way through the system, return an error to have the chain fail
func (k *ValidateRequest) ProcessRequest(w http.ResponseWriter, r *http.Request, _ interface{}) (error, int) {
operation := ctxGetOperation(r)
if operation == nil {
return nil, http.StatusOK
}
validateRequest := operation.ValidateRequest
if validateRequest == nil || !validateRequest.Enabled {
return nil, http.StatusOK
}
errResponseCode := http.StatusUnprocessableEntity
if validateRequest.ErrorResponseCode != 0 {
errResponseCode = validateRequest.ErrorResponseCode
}
// Validate request
requestValidationInput := &openapi3filter.RequestValidationInput{
Request: r,
PathParams: operation.pathParams,
Route: operation.route,
Options: &openapi3filter.Options{
AuthenticationFunc: func(ctx context.Context, input *openapi3filter.AuthenticationInput) error {
return nil
},
},
}
err := openapi3filter.ValidateRequest(r.Context(), requestValidationInput)
if err != nil {
return fmt.Errorf("request validation error: %w", err), errResponseCode
}
// Handle Success
return nil, http.StatusOK
}
Cognitive complexity: 12, Cyclomatic complexity: 6
func (*ValueExtractor) Extract
func (e *ValueExtractor) Extract(input interface{}) string {
headerValue := input.(string)
return headerValue
}
Cognitive complexity: 1, Cyclomatic complexity: 1
func (*ValueExtractor) ExtractAndCheck
func (e *ValueExtractor) ExtractAndCheck(r *http.Request) (sessionID string, returnOverrides ReturnOverrides) {
var extractorOutput string
var err error
switch e.Config.ExtractFrom {
case apidef.HeaderSource:
extractorOutput, err = e.ExtractHeader(r)
case apidef.FormSource:
extractorOutput, err = e.ExtractForm(r, e.IDExtractorConfig.FormParamName)
}
if err != nil {
returnOverrides = e.Error(r, err, "ValueExtractor error")
return sessionID, returnOverrides
}
sessionID = e.GenerateSessionID(extractorOutput, e.BaseMiddleware)
previousSession, keyExists := e.CheckSessionAndIdentityForValidKey(sessionID, r)
sessionID = previousSession.KeyID
if keyExists {
if previousSession.IdExtractorDeadline > time.Now().Unix() {
ctxSetSession(r, &previousSession, true, e.Gw.GetConfig().HashKeys)
returnOverrides = ReturnOverrides{
ResponseCode: 200,
}
}
}
return sessionID, returnOverrides
}
Cognitive complexity: 11, Cyclomatic complexity: 7
func (*VersionCheck) DoMockReply
func (v *VersionCheck) DoMockReply(w http.ResponseWriter, meta apidef.MockResponseMeta) {
responseMessage := []byte(meta.Body)
for header, value := range meta.Headers {
w.Header().Add(header, value)
}
w.WriteHeader(meta.Code)
w.Write(responseMessage)
}
Cognitive complexity: 3, Cyclomatic complexity: 2
func (*VersionCheck) Init
func (v *VersionCheck) Init() {
v.sh = SuccessHandler{v.BaseMiddleware}
}
Cognitive complexity: 1, Cyclomatic complexity: 1
func (*VersionCheck) Name
func (v *VersionCheck) Name() string {
return "VersionCheck"
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*VersionCheck) ProcessRequest
ProcessRequest will run any checks on the request on the way through the system, return an error to have the chain fail
func (v *VersionCheck) ProcessRequest(w http.ResponseWriter, r *http.Request, _ interface{}) (error, int) {
targetVersion := v.Spec.getVersionFromRequest(r)
if targetVersion == "" {
targetVersion = v.Spec.VersionDefinition.Default
}
ctxSetSpanAttributes(r, v.Name(), otel.APIVersionAttribute(targetVersion))
isBase := func(vName string) bool {
return vName == apidef.Self || vName == v.Spec.VersionDefinition.Name
}
if v.Spec.VersionDefinition.Enabled && !isBase(targetVersion) {
if targetVersion == "" {
return errors.New(string(VersionNotFound)), http.StatusForbidden
}
subVersionID := v.Spec.VersionDefinition.Versions[targetVersion]
handler, _, found := v.Gw.findInternalHttpHandlerByNameOrID(subVersionID)
if !found {
if !v.Spec.VersionDefinition.FallbackToDefault {
return errors.New(string(VersionDoesNotExist)), http.StatusNotFound
}
if isBase(v.Spec.VersionDefinition.Default) {
goto outside
}
targetID, ok := v.Spec.VersionDefinition.Versions[v.Spec.VersionDefinition.Default]
if !ok {
log.Errorf("fallback to default but %s is not in the versions list", v.Spec.VersionDefinition.Default)
return errors.New(http.StatusText(http.StatusInternalServerError)), http.StatusInternalServerError
}
handler, _, found = v.Gw.findInternalHttpHandlerByNameOrID(targetID)
if !found {
log.Errorf("fallback to default but there is no such API found with the id: %s", targetID)
return errors.New(http.StatusText(http.StatusInternalServerError)), http.StatusInternalServerError
}
}
v.Spec.SanitizeProxyPaths(r)
handler.ServeHTTP(w, r)
return nil, mwStatusRespond
}
outside:
// For OAS route matching
if v.Spec.HasMock || v.Spec.HasValidateRequest {
findRouteAndOperation(v.Spec, r)
}
// Check versioning, blacklist, whitelist and ignored status
requestValid, stat := v.Spec.RequestValid(r)
if !requestValid {
// Fire a versioning failure event
v.FireEvent(EventVersionFailure, EventVersionFailureMeta{
EventMetaDefault: EventMetaDefault{
Message: "Attempted access to disallowed version / path.",
OriginatingRequest: EncodeRequestToEvent(r),
},
Path: r.URL.Path,
Origin: request.RealIP(r),
Reason: string(stat),
})
return errors.New(string(stat)), http.StatusForbidden
}
versionInfo, _ := v.Spec.Version(r)
versionPaths := v.Spec.RxPaths[versionInfo.Name]
whiteListStatus := v.Spec.WhiteListEnabled[versionInfo.Name]
// We handle redirects before ignores in case we aren't using a whitelist
if stat == StatusRedirectFlowByReply {
_, meta := v.Spec.URLAllowedAndIgnored(r, versionPaths, whiteListStatus)
var mockMeta apidef.MockResponseMeta
var ok bool
if mockMeta, ok = meta.(apidef.MockResponseMeta); !ok {
endpointMethodMeta := meta.(*apidef.EndpointMethodMeta)
mockMeta.Body = endpointMethodMeta.Data
mockMeta.Headers = endpointMethodMeta.Headers
mockMeta.Code = endpointMethodMeta.Code
}
v.DoMockReply(w, mockMeta)
return nil, mwStatusRespond
}
if !v.Spec.ExpirationTs.IsZero() {
w.Header().Set(XTykAPIExpires, v.Spec.ExpirationTs.Format(time.RFC1123))
} else if expTime := versionInfo.ExpiryTime(); !expTime.IsZero() { // Deprecated
w.Header().Set(XTykAPIExpires, expTime.Format(time.RFC1123))
}
if stat == StatusOkAndIgnore {
ctxSetRequestStatus(r, stat)
}
return nil, http.StatusOK
}
Cognitive complexity: 34, Cyclomatic complexity: 19
func (*VersionsHandler) ServeHTTP
func (h *VersionsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
apiID := mux.Vars(r)["apiID"]
searchText := strings.ToLower(r.URL.Query().Get("searchText"))
justInternal := r.URL.Query().Get("accessType") == "internal"
justExternal := r.URL.Query().Get("accessType") == "external"
canInclude := func(api *apidef.APIDefinition, name string) bool {
if justInternal && !api.Internal {
return false
}
if justExternal && api.Internal {
return false
}
if searchText == "" {
return true
}
return strings.Contains(strings.ToLower(name), searchText)
}
baseAPI, err := h.getApiDef(apiID)
if err != nil {
doJSONWrite(w, http.StatusNotFound, apiError(err.Error()))
return
}
var versionMetas VersionMetas
for name, id := range baseAPI.VersionDefinition.Versions {
currentAPI, err := h.getApiDef(id)
if err != nil {
log.WithError(err).Errorf("Could not retrieve API version detail for id: %s", id)
continue
}
if !canInclude(currentAPI, name) {
continue
}
versionMetas.Metas = append(versionMetas.Metas, VersionMeta{
ID: id,
Name: currentAPI.Name,
VersionName: name,
Internal: currentAPI.Internal,
ExpirationDate: currentAPI.Expiration,
IsDefaultVersion: baseAPI.VersionDefinition.Default == name,
})
}
sort.Slice(versionMetas.Metas, func(i, j int) bool {
return versionMetas.Metas[i].VersionName < versionMetas.Metas[j].VersionName
})
if canInclude(baseAPI, baseAPI.VersionDefinition.Name) {
versionMetas.Metas = append([]VersionMeta{{
ID: baseAPI.APIID,
Name: baseAPI.Name,
VersionName: baseAPI.VersionDefinition.Name,
Internal: baseAPI.Internal,
ExpirationDate: baseAPI.Expiration,
IsDefaultVersion: baseAPI.VersionDefinition.Default == baseAPI.VersionDefinition.Name,
}}, versionMetas.Metas...)
}
versionMetas.Status = "success"
doJSONWrite(w, http.StatusOK, versionMetas)
}
Cognitive complexity: 22, Cyclomatic complexity: 11
func (*VirtualEndpoint) EnabledForSpec
func (d *VirtualEndpoint) EnabledForSpec() bool {
if !d.Spec.GlobalConfig.EnableJSVM {
return false
}
return d.Spec.hasVirtualEndpoint()
}
Cognitive complexity: 2, Cyclomatic complexity: 2
func (*VirtualEndpoint) HandleResponse
func (d *VirtualEndpoint) HandleResponse(rw http.ResponseWriter, res *http.Response, ses *user.SessionState) {
// Externalising this from the MW so we can re-use it elsewhere
d.Gw.handleForcedResponse(rw, res, ses, d.Spec)
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*VirtualEndpoint) Init
func (d *VirtualEndpoint) Init() {
d.sh = SuccessHandler{d.BaseMiddleware}
}
Cognitive complexity: 1, Cyclomatic complexity: 1
func (*VirtualEndpoint) Name
func (d *VirtualEndpoint) Name() string {
return "VirtualEndpoint"
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*VirtualEndpoint) ProcessRequest
ProcessRequest will run any checks on the request on the way through the system, return an error to have the chain fail
func (d *VirtualEndpoint) ProcessRequest(w http.ResponseWriter, r *http.Request, _ interface{}) (error, int) {
vmeta := d.getMetaFromRequest(r)
if vmeta == nil {
// nothing can be done here, reply with 200 to allow proxy to target
return nil, http.StatusOK
}
if _, err := d.ServeHTTPForCache(w, r, vmeta); err != nil {
message := "Error during virtual endpoint execution. Contact Administrator for more details."
d.Logger().WithError(err).WithField("vmeta", vmeta).Error(message)
if vmeta.ProxyOnError {
return nil, http.StatusOK
}
return errors.New(message), http.StatusInternalServerError
}
return nil, mwStatusRespond
}
Cognitive complexity: 7, Cyclomatic complexity: 4
func (*VirtualEndpoint) ServeHTTPForCache
func (d *VirtualEndpoint) ServeHTTPForCache(w http.ResponseWriter, r *http.Request, vmeta *apidef.VirtualMeta) (*http.Response, error) {
t1 := time.Now()
if vmeta == nil {
if vmeta = d.getMetaFromRequest(r); vmeta == nil {
return nil, errors.New("No request info")
}
}
// Create the proxy object
originalBody, err := ioutil.ReadAll(r.Body)
if err != nil {
return nil, fmt.Errorf("failed to read request body: %w", err)
}
defer r.Body.Close()
scheme := "http"
if r.TLS != nil {
scheme = "https"
}
requestData := RequestObject{
Headers: r.Header,
Body: string(originalBody),
URL: r.URL.String(),
Scheme: scheme,
}
// We need to copy the body _back_ for the decode
r.Body = ioutil.NopCloser(bytes.NewReader(originalBody))
parseForm(r)
requestData.Params = r.Form
requestAsJson, err := json.Marshal(requestData)
if err != nil {
return nil, fmt.Errorf("failed to encode request object for virtual endpoint: %w", err)
}
// Encode the configuration data too
specAsJson := specToJson(d.Spec)
session := new(user.SessionState)
// Encode the session object (if not a pre-process)
if vmeta.UseSession {
session = ctxGetSession(r)
}
sessionAsJson, err := json.Marshal(session)
if err != nil {
return nil, fmt.Errorf("failed to encode session for VM: %w", err)
}
// Run the middleware
vm := d.Spec.JSVM.VM.Copy()
vm.Interrupt = make(chan func(), 1)
d.Logger().Debug("Running: ", vmeta.ResponseFunctionName)
// buffered, leaving no chance of a goroutine leak since the
// spawned goroutine will send 0 or 1 values.
ret := make(chan otto.Value, 1)
errRet := make(chan error, 1)
go func() {
defer func() {
// the VM executes the panic func that gets it
// to stop, so we must recover here to not crash
// the whole Go program.
recover()
}()
returnRaw, err := vm.Run(vmeta.ResponseFunctionName + `(` + string(requestAsJson) + `, ` + string(sessionAsJson) + `, ` + specAsJson + `);`)
ret <- returnRaw
errRet <- err
}()
var returnRaw otto.Value
t := time.NewTimer(d.Spec.JSVM.Timeout)
select {
case returnRaw = <-ret:
if err := <-errRet; err != nil {
return nil, fmt.Errorf("Failed to run JS middleware: %w", err)
}
t.Stop()
case <-t.C:
t.Stop()
d.Logger().Error("JS middleware timed out after ", d.Spec.JSVM.Timeout)
vm.Interrupt <- func() {
// only way to stop the VM is to send it a func
// that panics.
panic("stop")
}
return nil, fmt.Errorf("JS middleware timed out after %s", d.Spec.JSVM.Timeout)
}
returnDataStr, _ := returnRaw.ToString()
// Decode the return object
newResponseData := VMResponseObject{}
if err := json.Unmarshal([]byte(returnDataStr), &newResponseData); err != nil {
d.Logger().WithError(err).WithField("return_data", returnDataStr).Errorf("Failed to decode virtual endpoint response data on return from VM")
return nil, fmt.Errorf("Failed to decode virtual endpoint response: %w", err)
}
// Save the sesison data (if modified)
if vmeta.UseSession {
newMeta := newResponseData.SessionMeta
if !reflect.DeepEqual(session.MetaData, newMeta) {
session.MetaData = newMeta
ctxSetSession(r, session, true, d.Gw.GetConfig().HashKeys)
}
}
copiedResponse := d.Gw.forceResponse(w, r, &newResponseData, d.Spec, session, false, d.Logger())
ms := DurationToMillisecond(time.Since(t1))
d.Logger().Debug("JSVM Virtual Endpoint execution took: (ms) ", ms)
if copiedResponse != nil {
d.sh.RecordHit(r, analytics.Latency{Total: int64(ms)}, copiedResponse.StatusCode, copiedResponse, false)
}
return copiedResponse, nil
}
Cognitive complexity: 33, Cyclomatic complexity: 15
func (*WebHookHandler) BuildRequest
func (w *WebHookHandler) BuildRequest(reqBody string) (*http.Request, error) {
req, err := http.NewRequest(string(w.getRequestMethod(w.conf.Method)), w.conf.TargetPath, strings.NewReader(reqBody))
if err != nil {
log.WithFields(logrus.Fields{
"prefix": "webhooks",
}).Error("Failed to create request object: ", err)
return nil, err
}
req.Header.Set(header.UserAgent, header.TykHookshot)
ignoreCanonical := w.Gw.GetConfig().IgnoreCanonicalMIMEHeaderKey
for key, val := range w.conf.HeaderList {
setCustomHeader(req.Header, key, val, ignoreCanonical)
}
if req.Header.Get(header.ContentType) == "" {
req.Header.Set(header.ContentType, w.contentType)
}
return req, nil
}
Cognitive complexity: 8, Cyclomatic complexity: 4
func (*WebHookHandler) Checksum
func (w *WebHookHandler) Checksum(reqBody string) (string, error) {
// We do this twice because fuck it.
localRequest, _ := http.NewRequest(string(w.getRequestMethod(w.conf.Method)), w.conf.TargetPath, strings.NewReader(reqBody))
h := md5.New()
localRequest.Write(h)
return hex.EncodeToString(h.Sum(nil)), nil
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*WebHookHandler) CreateBody
CreateBody will render the webhook event message template and return it as a string. If an error occurs, an empty string will be returned alongside an error.
func (w *WebHookHandler) CreateBody(em config.EventMessage) (string, error) {
var reqBody bytes.Buffer
err := w.template.Execute(&reqBody, em)
if err != nil {
return "", err
}
return reqBody.String(), err
}
Cognitive complexity: 2, Cyclomatic complexity: 2
func (*WebHookHandler) HandleEvent
HandleEvent will be fired when the event handler instance is found in an APISpec EventPaths object during a request chain
func (w *WebHookHandler) HandleEvent(em config.EventMessage) {
// Inject event message into template, render to string
reqBody, err := w.CreateBody(em)
if err != nil {
// We're just logging the template rendering issue here
// but we're passing on the partial rendered contents
log.WithError(err).WithFields(logrus.Fields{
"prefix": "webhooks",
}).Error("Webhook template rendering error")
return
}
// Construct request (method, body, params)
req, err := w.BuildRequest(reqBody)
if err != nil {
return
}
// Generate signature for request
reqChecksum, _ := w.Checksum(reqBody)
// Check request velocity for this hook (wasHookFired())
if w.WasHookFired(reqChecksum) {
return
}
cli := &http.Client{Timeout: 30 * time.Second}
resp, err := cli.Do(req)
if err != nil {
log.WithFields(logrus.Fields{
"prefix": "webhooks",
}).Error("Webhook request failed: ", err)
} else {
defer resp.Body.Close()
if resp.StatusCode >= 200 && resp.StatusCode < 300 {
content, err := ioutil.ReadAll(resp.Body)
if err == nil {
log.WithFields(logrus.Fields{
"prefix": "webhooks",
"responseCode": resp.StatusCode,
}).Debug(string(content))
} else {
log.WithFields(logrus.Fields{
"prefix": "webhooks",
}).Error(err)
}
} else {
log.WithFields(logrus.Fields{
"prefix": "webhooks",
"responseCode": resp.StatusCode,
}).Error("Request to webhook failed")
}
}
if w.dashboardService != nil && em.Type == EventTriggerExceeded {
w.dashboardService.NotifyDashboardOfEvent(em.Meta)
}
w.setHookFired(reqChecksum)
}
Cognitive complexity: 26, Cyclomatic complexity: 10
func (*WebHookHandler) Init
Init enables the init of event handler instances when they are created on ApiSpec creation
func (w *WebHookHandler) Init(handlerConf interface{}) error {
var err error
if err = w.conf.Scan(handlerConf); err != nil {
log.WithFields(logrus.Fields{
"prefix": "webhooks",
}).Error("Problem getting configuration, skipping. ", err)
return err
}
if w.conf.Disabled {
log.WithFields(logrus.Fields{
"prefix": "webhooks",
}).Infof("skipping disabled webhook %s", w.conf.Name)
return ErrEventHandlerDisabled
}
w.store = &storage.RedisCluster{KeyPrefix: "webhook.cache.", ConnectionHandler: w.Gw.StorageConnectionHandler}
w.store.Connect()
// Pre-load template on init
if w.conf.TemplatePath != "" {
w.template, err = htmltemplate.ParseFiles(w.conf.TemplatePath)
if err != nil {
log.WithFields(logrus.Fields{
"prefix": "webhooks",
"target": w.conf.TargetPath,
}).Warning("Custom template load failure, using default: ", err)
}
if strings.HasSuffix(w.conf.TemplatePath, ".json") {
w.contentType = header.ApplicationJSON
}
}
// We use the default if TemplatePath was empty or if we failed
// to load it.
if w.template == nil {
log.WithFields(logrus.Fields{
"prefix": "webhooks",
"target": w.conf.TargetPath,
}).Info("Loading default template.")
defaultPath := filepath.Join(w.Gw.GetConfig().TemplatePath, "default_webhook.json")
w.template, err = htmltemplate.ParseFiles(defaultPath)
if err != nil {
log.WithFields(logrus.Fields{
"prefix": "webhooks",
}).Error("Could not load the default template: ", err)
return err
}
w.contentType = header.ApplicationJSON
}
log.WithFields(logrus.Fields{
"prefix": "webhooks",
}).Debug("Timeout set to: ", w.conf.EventTimeout)
if !w.checkURL(w.conf.TargetPath) {
log.WithFields(logrus.Fields{
"prefix": "webhooks",
}).Error("Init failed for this webhook, invalid URL, URL must be absolute")
}
if w.Gw.GetConfig().UseDBAppConfigs {
dashboardServiceInit(w.Gw)
w.dashboardService = w.Gw.DashService
}
return nil
}
Cognitive complexity: 27, Cyclomatic complexity: 10
func (*WebHookHandler) WasHookFired
hookFired checks if an event has been fired within the EventTimeout setting
func (w *WebHookHandler) WasHookFired(checksum string) bool {
if _, err := w.store.GetKey(checksum); err != nil {
// Key not found, so hook is in limit
log.WithFields(logrus.Fields{
"prefix": "webhooks",
}).Debug("Event can fire, no duplicates found")
return false
}
return true
}
Cognitive complexity: 3, Cyclomatic complexity: 2
func (*XPathExtractor) ExtractAndCheck
func (e *XPathExtractor) ExtractAndCheck(r *http.Request) (SessionID string, returnOverrides ReturnOverrides) {
var err error
if e.IDExtractorConfig.XPathExpression == "" {
returnOverrides = e.Error(r, err, "XPathExtractor: no expression set")
return SessionID, returnOverrides
}
if e.path == nil {
expressionString := e.IDExtractorConfig.XPathExpression
e.path, err = xmlpath.Compile(expressionString)
if err != nil {
returnOverrides = e.Error(r, err, "XPathExtractor: bad expression")
return SessionID, returnOverrides
}
}
var extractorOutput string
switch e.Config.ExtractFrom {
case apidef.HeaderSource:
extractorOutput, err = e.ExtractHeader(r)
case apidef.BodySource:
extractorOutput, err = e.ExtractBody(r)
case apidef.FormSource:
extractorOutput, err = e.ExtractForm(r, e.IDExtractorConfig.FormParamName)
}
if err != nil {
returnOverrides = e.Error(r, err, "XPathExtractor error")
return SessionID, returnOverrides
}
extractedXml, err := xmlpath.Parse(strings.NewReader(extractorOutput))
if err != nil {
returnOverrides = e.Error(r, err, "XPathExtractor: couldn't parse input")
return SessionID, returnOverrides
}
output, ok := e.path.String(extractedXml)
if !ok {
returnOverrides = e.Error(r, err, "XPathExtractor: no input")
return SessionID, returnOverrides
}
SessionID = e.GenerateSessionID(output, e.BaseMiddleware)
previousSession, keyExists := e.BaseMiddleware.CheckSessionAndIdentityForValidKey(SessionID, r)
SessionID = previousSession.KeyID
if keyExists {
if previousSession.IdExtractorDeadline > time.Now().Unix() {
ctxSetSession(r, &previousSession, true, e.Gw.GetConfig().HashKeys)
returnOverrides = ReturnOverrides{
ResponseCode: 200,
}
}
}
return SessionID, returnOverrides
}
Cognitive complexity: 22, Cyclomatic complexity: 13
func (*customResponseWriter) Flush
func (w *customResponseWriter) Flush() {
if flusher, ok := w.ResponseWriter.(http.Flusher); ok {
flusher.Flush()
}
}
Cognitive complexity: 2, Cyclomatic complexity: 2
func (*customResponseWriter) Write
func (w *customResponseWriter) Write(b []byte) (int, error) {
w.responseSent = true
if w.statusCodeSent == 0 {
w.statusCodeSent = http.StatusOK // no WriteHeader was called so it will be set to StatusOK in actual ResponseWriter
}
// send actual data
num, err := w.ResponseWriter.Write(b)
// copy data sent
if w.copyData {
if w.data == nil {
w.data = make([]byte, num)
copy(w.data, b[:num])
} else {
w.data = append(w.data, b[:num]...)
}
}
// count how many bytes we sent
w.dataLength += int64(num)
return num, err
}
Cognitive complexity: 8, Cyclomatic complexity: 4
func (*customResponseWriter) WriteHeader
func (w *customResponseWriter) WriteHeader(statusCode int) {
w.responseSent = true
w.statusCodeSent = statusCode
w.ResponseWriter.WriteHeader(statusCode)
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*dummyStreamingMiddleware) EnabledForSpec
func (d *dummyStreamingMiddleware) EnabledForSpec() bool {
streamingConfig := d.Gw.GetConfig().Streaming
if streamingConfig.Enabled && d.Spec.isStreamingAPI() {
d.Logger().Warnf("Warning: %s", MessageStreamingOnlySupportedInEE)
return true
}
return false
}
Cognitive complexity: 2, Cyclomatic complexity: 3
func (*dummyStreamingMiddleware) Name
func (d *dummyStreamingMiddleware) Name() string {
return "StreamingMiddleware"
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*dummyStreamingMiddleware) ProcessRequest
func (d *dummyStreamingMiddleware) ProcessRequest(w http.ResponseWriter, r *http.Request, _ interface{}) (error, int) {
failHttpCode := http.StatusForbidden
d.Logger().WithField("status_code", failHttpCode).Errorf("Error: %s", MessageStreamingOnlySupportedInEE)
return ee.ErrActionNotAllowed, failHttpCode
}
Cognitive complexity: 1, Cyclomatic complexity: 1
func (*explicitRouteHandler) ServeHTTP
func (h *explicitRouteHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == h.prefix || strings.HasPrefix(r.URL.Path, h.prefix+"/") {
h.handler.ServeHTTP(w, r)
return
}
w.WriteHeader(http.StatusNotFound)
_, _ = fmt.Fprint(w, http.StatusText(http.StatusNotFound))
}
Cognitive complexity: 2, Cyclomatic complexity: 3
func (*h2cWrapper) ServeHTTP
func (h *h2cWrapper) ServeHTTP(w http.ResponseWriter, r *http.Request) {
h.h.ServeHTTP(w, r)
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*handleWrapper) ServeHTTP
func (h *handleWrapper) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if r.Body != nil {
if !h.handleRequestLimits(w, r) {
return
}
}
if h.maxRequestBodySize > 0 {
// this greedily reads in the request body and
// make request body to be nopCloser and re-readable
// before serve it through chain of middlewares
if err := nopCloseRequestBodyErr(r); err != nil {
if err.Error() == "http: request body too large" {
httputil.EntityTooLarge(w, r)
return
}
httputil.InternalServerError(w, r)
return
}
} else {
// this leaves the body on lazy read as before
if _, err := copyRequest(r); err != nil {
log.WithError(err).Error("Error reading request body")
httputil.InternalServerError(w, r)
}
}
// Test don't provide a router
if h.router == nil {
return
}
h.router.ServeHTTP(w, r)
}
Cognitive complexity: 16, Cyclomatic complexity: 8
func (*httpProxyHandler) Stop
func (p *httpProxyHandler) Stop(s *Test) error {
return p.server.Close()
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*introspectionCache) GetRes
func (c *introspectionCache) GetRes(token string) (jwt.MapClaims, bool) {
var claims jwt.MapClaims
claimsStr, err := c.GetKey(token)
if err != nil {
return nil, false
}
err = json.Unmarshal([]byte(claimsStr), &claims)
if err != nil {
return nil, false
}
return claims, true
}
Cognitive complexity: 4, Cyclomatic complexity: 3
func (*introspectionCache) SetRes
func (c *introspectionCache) SetRes(token string, res jwt.MapClaims, timeout int64) error {
claimsInBytes, err := json.Marshal(res)
if err != nil {
return err
}
return c.SetKey(token, string(claimsInBytes), timeout)
}
Cognitive complexity: 2, Cyclomatic complexity: 2
func (*maxLatencyWriter) Write
func (m *maxLatencyWriter) Write(p []byte) (n int, err error) {
m.mu.Lock()
defer m.mu.Unlock()
n, err = m.dst.Write(p)
if m.latency < 0 {
m.dst.Flush()
return
}
if m.flushPending {
return
}
if m.t == nil {
m.t = time.AfterFunc(m.latency, m.delayedFlush)
} else {
m.t.Reset(m.latency)
}
m.flushPending = true
return
}
Cognitive complexity: 8, Cyclomatic complexity: 4
func (*noopUpstreamBasicAuth) EnabledForSpec
EnabledForSpec will always return false for noopUpstreamBasicAuth.
func (d *noopUpstreamBasicAuth) EnabledForSpec() bool {
if d.Spec.UpstreamAuth.BasicAuth.Enabled {
d.Logger().Error("Upstream basic auth is supported only in Tyk Enterprise Edition")
}
return false
}
Cognitive complexity: 2, Cyclomatic complexity: 2
func (*noopUpstreamBasicAuth) Name
Name returns the name of the mw.
func (d *noopUpstreamBasicAuth) Name() string {
return "NooPUpstreamBasicAuth"
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*noopUpstreamBasicAuth) ProcessRequest
ProcessRequest is noop implementation for upstream basic auth mw.
func (d *noopUpstreamBasicAuth) ProcessRequest(_ http.ResponseWriter, r *http.Request, _ interface{}) (error, int) {
return nil, http.StatusOK
}
Cognitive complexity: 1, Cyclomatic complexity: 1
func (*noopUpstreamOAuth) EnabledForSpec
EnabledForSpec will always return false for noopUpstreamOAuth.
func (d *noopUpstreamOAuth) EnabledForSpec() bool {
if d.Spec.UpstreamAuth.OAuth.Enabled {
d.Logger().Error("Upstream OAuth is supported only in Tyk Enterprise Edition")
}
return false
}
Cognitive complexity: 2, Cyclomatic complexity: 2
func (*noopUpstreamOAuth) Name
Name returns the name of the mw.
func (d *noopUpstreamOAuth) Name() string {
return "NooPUpstreamOAuth"
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*noopUpstreamOAuth) ProcessRequest
ProcessRequest is noop implementation for upstream OAuth mw.
func (d *noopUpstreamOAuth) ProcessRequest(_ http.ResponseWriter, _ *http.Request, _ interface{}) (error, int) {
return nil, http.StatusOK
}
Cognitive complexity: 1, Cyclomatic complexity: 1
func (*nopCloserBuffer) Close
Close is a no-op Close
func (n *nopCloserBuffer) Close() error {
return nil
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*nopCloserBuffer) Read
Read just a wrapper around real Read which also moves position to the start if we get EOF to have it ready for next read-cycle
func (n *nopCloserBuffer) Read(p []byte) (int, error) {
if err := n.copy(); err != nil {
return 0, err
}
idx := n.position
num, err := bytes.NewBuffer(n.buf.Bytes()[idx:]).Read(p)
if err == nil {
cnt := int64(n.buf.Len())
if idx+int64(len(p)) < cnt {
n.position += int64(len(p))
} else {
n.position = cnt
}
}
// move to start to have it ready for next read cycle
if errors.Is(err, io.EOF) {
_, seekErr := n.Seek(0, io.SeekStart)
if seekErr != nil {
log.WithError(seekErr).Error("can't rewind nopCloserBuffer")
}
}
return num, err
}
Cognitive complexity: 12, Cyclomatic complexity: 6
func (*nopCloserBuffer) Seek
Seek seeks within the buffer
func (n *nopCloserBuffer) Seek(offset int64, whence int) (int64, error) {
if whence != io.SeekStart {
return 0, errors.New("invalid seek method, only supporting SeekStart")
}
if offset == 0 && n.position == 0 {
return 0, nil
}
if err := n.copy(); err != nil {
return 0, err
}
cnt := int64(n.buf.Len())
if offset >= cnt || offset < 0 {
return 0, errors.New("invalid seek offset")
}
n.position = offset
return offset, nil
}
Cognitive complexity: 8, Cyclomatic complexity: 7
func (*redisChannelHook) Fire
func (hook *redisChannelHook) Fire(entry *logrus.Entry) error {
orgId, found := entry.Data["org_id"]
if !found {
return nil
}
newEntry, err := hook.formatter.Format(entry)
if err != nil {
log.Error(err)
return nil
}
msg := string(newEntry)
n := InterfaceNotification{
Type: "gateway-log",
Message: msg,
OrgID: orgId.(string),
Timestamp: time.Now(),
}
go hook.notifier.Notify(n)
return nil
}
Cognitive complexity: 5, Cyclomatic complexity: 3
func (*redisChannelHook) Levels
func (hook *redisChannelHook) Levels() []logrus.Level {
return []logrus.Level{
logrus.InfoLevel,
logrus.ErrorLevel,
logrus.FatalLevel,
logrus.PanicLevel,
}
}
Cognitive complexity: 1, Cyclomatic complexity: 1
func (*testMessageAdapter) Channel
Channel returns the channel the message was received on.
func (m *testMessageAdapter) Channel() (string, error) {
return "", nil
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*testMessageAdapter) Payload
Payload returns the message payload.
func (m *testMessageAdapter) Payload() (string, error) {
return m.Msg, nil
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*testMessageAdapter) Type
Type returns the message type.
func (m *testMessageAdapter) Type() string {
return "message"
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*variableReplaceRoundTripper) RoundTrip
func (d *variableReplaceRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
for key := range req.Header {
val := d.gw.ReplaceTykVariables(d.outReq, req.Header.Get(key), false)
req.Header.Set(key, val)
}
return d.next.RoundTrip(req)
}
Cognitive complexity: 3, Cyclomatic complexity: 2
func (*wrapMiddleware) Base
func (w *wrapMiddleware) Base() *BaseMiddleware {
return w.BaseMiddleware
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*wrapMiddleware) Config
func (w *wrapMiddleware) Config() (interface{}, error) {
return w.BaseMiddleware.Config()
}
Cognitive complexity: 1, Cyclomatic complexity: 1
func (*wrapMiddleware) EnabledForSpec
func (w *wrapMiddleware) EnabledForSpec() bool {
return w.mw.EnabledForSpec()
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*wrapMiddleware) Init
func (w *wrapMiddleware) Init() {
w.mw.Init()
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*wrapMiddleware) Logger
func (w *wrapMiddleware) Logger() *logrus.Entry {
return w.mw.Logger()
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*wrapMiddleware) Name
func (w *wrapMiddleware) Name() string {
return w.mw.Name()
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*wrapMiddleware) ProcessRequest
func (w *wrapMiddleware) ProcessRequest(rw http.ResponseWriter, r *http.Request, data interface{}) (error, int) {
return w.mw.ProcessRequest(rw, r, data)
}
Cognitive complexity: 1, Cyclomatic complexity: 1
func (*wrapMiddleware) Unload
func (w *wrapMiddleware) Unload() {
w.mw.Unload()
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (APIDefinitionLoader) FromDashboardService
FromDashboardService will connect and download ApiDefintions from a Tyk Dashboard instance.
func (a APIDefinitionLoader) FromDashboardService(endpoint string) ([]*APISpec, error) {
// Get the definitions
log.Debug("Calling: ", endpoint)
newRequest, err := http.NewRequest("GET", endpoint, nil)
if err != nil {
log.Error("Failed to create request: ", err)
}
gwConfig := a.Gw.GetConfig()
newRequest.Header.Set("authorization", gwConfig.NodeSecret)
log.Debug("Using: NodeID: ", a.Gw.GetNodeID())
newRequest.Header.Set(header.XTykNodeID, a.Gw.GetNodeID())
a.Gw.ServiceNonceMutex.RLock()
newRequest.Header.Set(header.XTykNonce, a.Gw.ServiceNonce)
a.Gw.ServiceNonceMutex.RUnlock()
newRequest.Header.Set(header.XTykSessionID, a.Gw.SessionID)
c := a.Gw.initialiseClient()
resp, err := c.Do(newRequest)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode == http.StatusForbidden {
body, _ := ioutil.ReadAll(resp.Body)
return nil, fmt.Errorf("login failure, Response was: %v", string(body))
}
if resp.StatusCode != http.StatusOK {
body, _ := ioutil.ReadAll(resp.Body)
return nil, fmt.Errorf("dashboard API error, response was: %v", string(body))
}
// Extract tagged APIs#
list := model.NewMergedAPIList()
inBytes, err := io.ReadAll(resp.Body)
if err != nil {
log.Error("Couldn't read api definition list")
return nil, err
}
inBytes = a.replaceSecrets(inBytes)
err = json.Unmarshal(inBytes, &list)
if err != nil {
log.Error("Couldn't unmarshal api definition list")
return nil, err
}
// Extract tagged entries only
apiDefs := list.Filter(gwConfig.DBAppConfOptions.NodeIsSegmented, gwConfig.DBAppConfOptions.Tags...)
// Process
specs := a.prepareSpecs(apiDefs, gwConfig, false)
// Set the nonce
a.Gw.ServiceNonceMutex.Lock()
a.Gw.ServiceNonce = list.Nonce
a.Gw.ServiceNonceMutex.Unlock()
log.Debug("Loading APIS Finished: Nonce Set: ", list.Nonce)
return specs, nil
}
Cognitive complexity: 12, Cyclomatic complexity: 7
func (APIDefinitionLoader) FromDir
FromDir will load APIDefinitions from a directory on the filesystem. Definitions need to be the JSON representation of APIDefinition object
func (a APIDefinitionLoader) FromDir(dir string) []*APISpec {
var specs []*APISpec
// Grab json files from directory
paths, _ := filepath.Glob(filepath.Join(dir, "*.json"))
for _, path := range paths {
if strings.HasSuffix(path, "-oas.json") {
continue
}
spec, err := a.loadDefFromFilePath(path)
if err != nil {
continue
}
specs = append(specs, spec)
}
return specs
}
Cognitive complexity: 7, Cyclomatic complexity: 4
func (APIDefinitionLoader) FromRPC
FromCloud will connect and download ApiDefintions from a Mongo DB instance.
func (a APIDefinitionLoader) FromRPC(store RPCDataLoader, orgId string, gw *Gateway) ([]*APISpec, error) {
if rpc.IsEmergencyMode() {
return gw.LoadDefinitionsFromRPCBackup()
}
if !store.Connect() {
return nil, errors.New("Can't connect RPC layer")
}
// enable segments
var tags []string
if gw.GetConfig().DBAppConfOptions.NodeIsSegmented {
log.Info("Segmented node, loading: ", gw.GetConfig().DBAppConfOptions.Tags)
tags = gw.GetConfig().DBAppConfOptions.Tags
}
apiCollection := store.GetApiDefinitions(orgId, tags)
apiCollection = string(a.replaceSecrets([]byte(apiCollection)))
//store.Disconnect()
if rpc.LoadCount() > 0 {
if err := gw.saveRPCDefinitionsBackup(apiCollection); err != nil {
log.Error(err)
}
}
return a.processRPCDefinitions(apiCollection, gw)
}
Cognitive complexity: 10, Cyclomatic complexity: 6
func (APIDefinitionLoader) GetOASFilepath
func (a APIDefinitionLoader) GetOASFilepath(path string) string {
return strings.TrimSuffix(path, ".json") + "-oas.json"
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (APIDefinitionLoader) MakeSpec
MakeSpec will generate a flattened URLSpec from and APIDefinitions' VersionInfo data. paths are keyed to the Api version name, which is determined during routing to speed up lookups
func (a APIDefinitionLoader) MakeSpec(def *model.MergedAPI, logger *logrus.Entry) (*APISpec, error) {
if logger == nil {
logger = logrus.NewEntry(log).WithFields(logrus.Fields{
"api_id": def.APIID,
"org_id": def.OrgID,
"name": def.Name,
})
}
spec := &APISpec{}
apiString, err := json.Marshal(def)
if err != nil {
logger.WithError(err).Error("Failed to JSON marshal API definition")
return nil, err
}
sha256hash := sha256.Sum256(apiString)
// Unique API content ID, to check if we already have if it changed from previous sync
spec.Checksum = base64.URLEncoding.EncodeToString(sha256hash[:])
spec.APIDefinition = def.APIDefinition
if currSpec := a.Gw.getApiSpec(def.APIID); !shouldReloadSpec(currSpec, spec) {
return currSpec, nil
}
// new expiration feature
if def.Expiration != "" {
if t, err := time.Parse(apidef.ExpirationTimeFormat, def.Expiration); err != nil {
logger.WithError(err).WithField("Expiration", def.Expiration).Error("Could not parse expiration date for API")
} else {
def.ExpirationTs = t
}
}
// Deprecated
// parse version expiration time stamps
for key, ver := range def.VersionData.Versions {
if ver.Expires == "" || ver.Expires == "-1" {
continue
}
// calculate the time
if t, err := time.Parse(apidef.ExpirationTimeFormat, ver.Expires); err != nil {
logger.WithError(err).WithField("expires", ver.Expires).Error("Could not parse expiry date for API")
} else {
ver.ExpiresTs = t
def.VersionData.Versions[key] = ver
}
}
// We'll push the default HealthChecker:
spec.Health = &DefaultHealthChecker{
Gw: a.Gw,
APIID: spec.APIID,
}
// Add any new session managers or auth handlers here
spec.AuthManager = &DefaultSessionManager{Gw: a.Gw}
spec.OrgSessionManager = &DefaultSessionManager{
orgID: spec.OrgID,
Gw: a.Gw,
}
spec.GlobalConfig = a.Gw.GetConfig()
if err = a.Gw.loadBundle(spec); err != nil {
logger.WithError(err).Error("Couldn't load bundle")
return nil, err
}
if a.Gw.GetConfig().EnableJSVM && (spec.hasVirtualEndpoint() || spec.CustomMiddleware.Driver == apidef.OttoDriver) {
logger.Debug("Initializing JSVM")
spec.JSVM.Init(spec, logger, a.Gw)
}
// Set up Event Handlers
if len(def.EventHandlers.Events) > 0 {
logger.Debug("Initializing event handlers")
}
spec.EventPaths = make(map[apidef.TykEvent][]config.TykEventHandler)
for eventName, eventHandlerConfs := range def.EventHandlers.Events {
logger.Debug("FOUND EVENTS TO INIT")
for _, handlerConf := range eventHandlerConfs {
logger.Debug("CREATING EVENT HANDLERS")
eventHandlerInstance, err := a.Gw.EventHandlerByName(handlerConf, spec)
if err != nil {
logger.Error("Failed to init event handler: ", err)
} else {
logger.Debug("Init Event Handler: ", eventName)
spec.EventPaths[eventName] = append(spec.EventPaths[eventName], eventHandlerInstance)
}
}
}
spec.RxPaths = make(map[string][]URLSpec, len(def.VersionData.Versions))
spec.WhiteListEnabled = make(map[string]bool, len(def.VersionData.Versions))
for _, v := range def.VersionData.Versions {
var pathSpecs []URLSpec
var whiteListSpecs bool
// If we have transitioned to extended path specifications, we should use these now
if v.UseExtendedPaths {
pathSpecs, whiteListSpecs = a.getExtendedPathSpecs(v, spec, a.Gw.GetConfig())
} else {
logger.Warning("Legacy path detected! Upgrade to extended.")
pathSpecs, whiteListSpecs = a.getPathSpecs(v, a.Gw.GetConfig())
}
spec.RxPaths[v.Name] = pathSpecs
spec.WhiteListEnabled[v.Name] = whiteListSpecs
}
if spec.IsOAS && def.OAS != nil {
loader := openapi3.NewLoader()
if err := loader.ResolveRefsIn(&def.OAS.T, nil); err != nil {
logger.WithError(err).Errorf("Dashboard loaded API's OAS reference resolve failed: %s", def.APIID)
}
spec.OAS = *def.OAS
}
if err := httputil.ValidatePath(spec.Proxy.ListenPath); err != nil {
logger.WithError(err).Error("Invalid listen path when creating router")
return nil, err
}
oasSpec := spec.OAS.T
oasSpec.Servers = openapi3.Servers{
{URL: spec.Proxy.ListenPath},
}
spec.OASRouter, err = gorillamux.NewRouter(&oasSpec)
if err != nil {
logger.WithError(err).Error("Could not create OAS router")
}
spec.setHasMock()
return spec, nil
}
Cognitive complexity: 59, Cyclomatic complexity: 25
func (APIDefinitionLoader) ParseDefinition
func (a APIDefinitionLoader) ParseDefinition(r io.Reader) (api apidef.APIDefinition) {
if err := json.NewDecoder(r).Decode(&api); err != nil {
log.Error("Couldn't unmarshal api configuration: ", err)
}
return
}
Cognitive complexity: 2, Cyclomatic complexity: 2
func (APIDefinitionLoader) ParseOAS
func (a APIDefinitionLoader) ParseOAS(r io.Reader) (oas oas.OAS) {
if err := json.NewDecoder(r).Decode(&oas); err != nil {
log.Error("Couldn't unmarshal oas configuration: ", err)
}
return
}
Cognitive complexity: 2, Cyclomatic complexity: 2
func (CustomMiddlewareResponseHook) Base
func (h CustomMiddlewareResponseHook) Base() *BaseTykResponseHandler {
return &h.BaseTykResponseHandler
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (DefaultKeyGenerator) GenerateAuthKey
GenerateAuthKey is a utility function for generating new auth keys. Returns the storage key name and the actual key
func (d DefaultKeyGenerator) GenerateAuthKey(orgID string) string {
return d.Gw.generateToken(orgID, "")
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (DefaultKeyGenerator) GenerateHMACSecret
GenerateHMACSecret is a utility function for generating new auth keys. Returns the storage key name and the actual key
func (DefaultKeyGenerator) GenerateHMACSecret() string {
return base64.StdEncoding.EncodeToString([]byte(uuid.NewHex()))
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (LDAPStorageHandler) AddToSet
func (l LDAPStorageHandler) AddToSet(keyName, value string) {
log.Error("Not implemented")
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (LDAPStorageHandler) AddToSortedSet
func (l LDAPStorageHandler) AddToSortedSet(keyName, value string, score float64) {
log.Error("Not implemented")
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (LDAPStorageHandler) AppendToSet
func (l LDAPStorageHandler) AppendToSet(keyName, value string) {
log.Error("Not implemented")
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (LDAPStorageHandler) DeleteScanMatch
func (l LDAPStorageHandler) DeleteScanMatch(pattern string) bool {
log.Error("Not implemented")
return false
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (LDAPStorageHandler) Exists
func (l LDAPStorageHandler) Exists(keyName string) (bool, error) {
log.Error("Not implemented")
return false, nil
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (LDAPStorageHandler) GetAndDeleteSet
func (l LDAPStorageHandler) GetAndDeleteSet(keyName string) []interface{} {
log.Error("Not implemented")
return nil
}
Cognitive complexity: 1, Cyclomatic complexity: 1
func (LDAPStorageHandler) GetKeyPrefix
func (l LDAPStorageHandler) GetKeyPrefix() string {
log.Error("Not implemented")
return ""
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (LDAPStorageHandler) GetSet
func (l LDAPStorageHandler) GetSet(keyName string) (map[string]string, error) {
log.Error("Not implemented")
return nil, nil
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (LDAPStorageHandler) GetSortedSetRange
func (l LDAPStorageHandler) GetSortedSetRange(keyName, scoreFrom, scoreTo string) ([]string, []float64, error) {
log.Error("Not implemented")
return nil, nil, nil
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (LDAPStorageHandler) RemoveFromList
func (l LDAPStorageHandler) RemoveFromList(keyName, value string) error {
log.Error("Not implemented")
return nil
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (LDAPStorageHandler) RemoveFromSet
func (l LDAPStorageHandler) RemoveFromSet(keyName, value string) {
log.Error("Not implemented")
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (LDAPStorageHandler) RemoveSortedSetRange
func (l LDAPStorageHandler) RemoveSortedSetRange(keyName, scoreFrom, scoreTo string) error {
log.Error("Not implemented")
return nil
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (MethodNotAllowedHandler) ServeHTTP
func (m MethodNotAllowedHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
doJSONWrite(w, http.StatusMethodNotAllowed, apiError("Method not supported"))
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (Monitor) Check
func (m Monitor) Check(sessionData *user.SessionState, key string) {
if !m.Enabled() {
return
}
if m.checkLimit(sessionData, key, sessionData.QuotaMax, sessionData.QuotaRemaining, sessionData.QuotaRenews) {
return
}
for _, ac := range sessionData.AccessRights {
if ac.Limit.IsEmpty() {
continue
}
if m.checkLimit(sessionData, key, ac.Limit.QuotaMax, ac.Limit.QuotaRemaining, ac.Limit.QuotaRenews) {
return
}
}
}
Cognitive complexity: 11, Cyclomatic complexity: 6
func (Monitor) Enabled
func (m Monitor) Enabled() bool {
return m.Gw.GetConfig().Monitor.EnableTriggerMonitors
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (Monitor) Fire
func (m Monitor) Fire(sessionData *user.SessionState, key string, triggerLimit, usagePercentage float64) {
em := config.EventMessage{
Type: EventTriggerExceeded,
Meta: EventTriggerExceededMeta{
EventMetaDefault: EventMetaDefault{Message: "Quota trigger reached"},
OrgID: sessionData.OrgID,
Key: key,
TriggerLimit: int64(triggerLimit),
UsagePercentage: int64(usagePercentage),
},
TimeStamp: time.Now().String(),
}
go m.Gw.MonitoringHandler.HandleEvent(em)
}
Cognitive complexity: 3, Cyclomatic complexity: 1
func (NotificationCommand) String
func (n NotificationCommand) String() string {
return string(n)
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (RPCStorageHandler) AddToSet
func (r RPCStorageHandler) AddToSet(keyName, value string) {
log.Error("RPCStorageHandler.AddToSet - Not implemented")
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (RPCStorageHandler) GetSet
func (r RPCStorageHandler) GetSet(keyName string) (map[string]string, error) {
log.Error("RPCStorageHandler.GetSet - Not implemented")
return nil, nil
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (RPCStorageHandler) IsRetriableError
func (r RPCStorageHandler) IsRetriableError(err error) bool {
if err != nil {
return err.Error() == "Access Denied"
}
return false
}
Cognitive complexity: 2, Cyclomatic complexity: 2
func (RPCStorageHandler) RemoveFromSet
func (r RPCStorageHandler) RemoveFromSet(keyName, value string) {
log.Error("RPCStorageHandler.RemoveFromSet - Not implemented")
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (RedisPurger) PurgeLoop
func (r RedisPurger) PurgeLoop(ctx context.Context) {
tick := time.NewTicker(time.Second)
defer tick.Stop()
for {
select {
case <-ctx.Done():
return
case <-tick.C:
r.PurgeCache()
}
}
}
Cognitive complexity: 5, Cyclomatic complexity: 4
func (ResponseGoPluginMiddleware) Base
func (h ResponseGoPluginMiddleware) Base() *BaseTykResponseHandler {
return &h.BaseTykResponseHandler
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (ResponseGoPluginMiddleware) Name
func (ResponseGoPluginMiddleware) Name() string {
return "ResponseGoPluginMiddleware"
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (ResponseTransformJQMiddleware) Base
func (h ResponseTransformJQMiddleware) Base() *BaseTykResponseHandler {
return &h.BaseTykResponseHandler
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (ResponseTransformJQMiddleware) Name
func (ResponseTransformJQMiddleware) Name() string {
return "ResponseTransformJQMiddleware"
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (TraceMiddleware) ProcessRequest
func (tr TraceMiddleware) ProcessRequest(w http.ResponseWriter, r *http.Request, conf interface{}) (error, int) {
if trace.IsEnabled() {
span, ctx := trace.Span(r.Context(),
tr.Name(),
)
defer span.Finish()
setContext(r, ctx)
return tr.TykMiddleware.ProcessRequest(w, r, conf)
}
if baseMw := tr.Base(); baseMw != nil {
cfg := baseMw.Gw.GetConfig()
if cfg.OpenTelemetry.Enabled {
otel.AddTraceID(r.Context(), w)
var span otel.Span
if baseMw.Spec.DetailedTracing {
var ctx context.Context
ctx, span = baseMw.Gw.TracerProvider.Tracer().Start(r.Context(), tr.Name())
defer span.End()
setContext(r, ctx)
} else {
span = otel.SpanFromContext(r.Context())
}
err, i := tr.TykMiddleware.ProcessRequest(w, r, conf)
if err != nil {
span.SetStatus(otel.SPAN_STATUS_ERROR, err.Error())
}
attrs := ctxGetSpanAttributes(r, tr.TykMiddleware.Name())
if len(attrs) > 0 {
span.SetAttributes(attrs...)
}
return err, i
}
}
return tr.TykMiddleware.ProcessRequest(w, r, conf)
}
Cognitive complexity: 15, Cyclomatic complexity: 7
func (ZipBundleSaver) Save
Save implements the main method of the BundleSaver interface. It makes use of archive/zip.
func (ZipBundleSaver) Save(bundle *Bundle, bundlePath string, spec *APISpec) error {
buf := bytes.NewReader(bundle.Data)
reader, err := zip.NewReader(buf, int64(len(bundle.Data)))
if err != nil {
return err
}
for _, f := range reader.File {
destPath := filepath.Join(bundlePath, f.Name)
if f.FileHeader.Mode().IsDir() {
if err := os.Mkdir(destPath, 0700); err != nil {
return err
}
continue
}
rc, err := f.Open()
if err != nil {
return err
}
newFile, err := os.Create(destPath)
if err != nil {
return err
}
if _, err = io.Copy(newFile, rc); err != nil {
return err
}
rc.Close()
if err := newFile.Close(); err != nil {
return err
}
}
return nil
}
Cognitive complexity: 17, Cyclomatic complexity: 9
func (accessTokenGen) GenerateAccessToken
GenerateAccessToken generates base64-encoded UUID access and refresh tokens
func (a accessTokenGen) GenerateAccessToken(data *osin.AccessData, generaterefresh bool) (accesstoken, refreshtoken string, err error) {
log.Info("[OAuth] Generating new token")
var newSession user.SessionState
checkPolicy := true
if data.UserData != nil {
checkPolicy = false
err := json.Unmarshal([]byte(data.UserData.(string)), &newSession)
if err != nil {
log.Info("[GenerateAccessToken] Couldn't decode user.SessionState from UserData, checking policy: ", err)
checkPolicy = true
}
}
if checkPolicy {
// defined in JWT middleware
sessionFromPolicy, err := a.Gw.generateSessionFromPolicy(data.Client.GetPolicyID(), "", false)
if err != nil {
return "", "", errors.New("Couldn't use policy or key rules to create token, failing")
}
newSession = sessionFromPolicy.Clone()
}
accesstoken = a.Gw.keyGen.GenerateAuthKey(newSession.OrgID)
if generaterefresh {
refreshtoken = base64.StdEncoding.EncodeToString([]byte(uuid.New()))
}
return
}
Cognitive complexity: 10, Cyclomatic complexity: 6
func (nopCloser) Close
Close is a no-op Close
func (n nopCloser) Close() error {
return nil
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (nopCloser) Read
Read just a wrapper around real Read which also moves position to the start if we get EOF to have it ready for next read-cycle
func (n nopCloser) Read(p []byte) (int, error) {
num, err := n.ReadSeeker.Read(p)
if errors.Is(err, io.EOF) { // move to start to have it ready for next read cycle
_, seekErr := n.Seek(0, io.SeekStart)
if seekErr != nil {
log.WithError(seekErr).Error("can't rewind nopCloser")
}
}
return num, err
}
Cognitive complexity: 4, Cyclomatic complexity: 3
func (postReceiveHttpHook) Execute
func (p postReceiveHttpHook) Execute(ctx datasource.HookContext, resp *http.Response, body []byte) {
p.m.BaseMiddleware.Logger().
WithFields(
logrus.Fields{
"typename": ctx.TypeName,
"fieldname": ctx.FieldName,
"response_body": string(body),
"status_code": resp.StatusCode,
},
).Debugf("%s.%s: postReceiveHttpHook executed", ctx.TypeName, ctx.FieldName)
}
Cognitive complexity: 1, Cyclomatic complexity: 1
func (preSendHttpHook) Execute
func (p preSendHttpHook) Execute(ctx datasource.HookContext, req *http.Request) {
p.m.BaseMiddleware.Logger().
WithFields(
logrus.Fields{
"typename": ctx.TypeName,
"fieldname": ctx.FieldName,
"upstream_url": req.URL.String(),
},
).Debugf("%s.%s: preSendHttpHook executed", ctx.TypeName, ctx.FieldName)
}
Cognitive complexity: 1, Cyclomatic complexity: 1
func (proxy) String
func (p proxy) String() string {
ls := ""
if p.listener != nil {
ls = p.listener.Addr().String()
}
return fmt.Sprintf("[proxy] :%d %s", p.port, ls)
}
Cognitive complexity: 2, Cyclomatic complexity: 2
func (sessionFailReason) String
func (sfr sessionFailReason) String() string {
switch sfr {
case sessionFailNone:
return "sessionFailNone"
case sessionFailRateLimit:
return "sessionFailRateLimit"
case sessionFailQuota:
return "sessionFailQuota"
default:
return fmt.Sprintf("%d", uint(sfr))
}
}
Cognitive complexity: 5, Cyclomatic complexity: 5
Private functions
func addBodyHash
addBodyHash (req *http.Request, regex string, h hash.Hash) error
References: hex.EncodeToString, io.WriteString, murmur3.New128, regexp.Compile.
func addCustomHeader
addCustomHeader (h http.Header, key string, value []string, ignoreCanonical bool)
func addGroupsToContextData
addGroupsToContextData (cd map[string]interface{}, keyPrefix string, groups []string)
References: strconv.Itoa, strings.Join.
func addMascotHeaders
addMascotHeaders (w http.ResponseWriter)
References: fmt.Sprintf, strings.Split.
func addMatchToContextData
addMatchToContextData (cd map[string]interface{}, match []string, trNum int, trName string, indices ...int)
func addSecureAndCacheHeaders
addSecureAndCacheHeaders (next http.HandlerFunc) http.HandlerFunc
References: http.Request, http.ResponseWriter.
func addVersionHeader
addVersionHeader (w http.ResponseWriter, r *http.Request, globalConf config.Config)
func allowMethods
allowMethods (next http.HandlerFunc, methods ...string) http.HandlerFunc
References: http.Request, http.ResponseWriter, http.StatusMethodNotAllowed.
func apiError
apiError (msg string) apiStatusMessage
func apiOk
apiOk (msg string) apiStatusMessage
func appendIfMissing
appendIfMissing ensures dest slice is unique with new items.
appendIfMissing (src []string, in ...string) []string
func areMapsEqual
check if 2 maps are the same
areMapsEqual (a,b map[string]string) bool
func assertSigningMethod
assertSigningMethod asserts the provided signing method with that of jwt.
assertSigningMethod (signingMethod string, token *jwt.Token) error
References: fmt.Errorf.
func buildTriggerKey
buildTriggerKey (num int, name string, indices ...int) string
References: strconv.Itoa, strings.Join.
func bundleError
bundleError is a log helper.
bundleError (spec *APISpec, err error, message string) error
References: errors.New, fmt.Sprintf, logrus.Fields.
func cgoCString
cgoCString (in string) *_Ctype_char
func cgoCint
cgoCint (in int) _Ctype_int
func cgoGoString
cgoGoString (in *_Ctype_char) string
func checkContextTrigger
checkContextTrigger (r *http.Request, options map[string]apidef.StringRegexMap, any bool, triggernum int) bool
func checkHeaderTrigger
checkHeaderTrigger (r *http.Request, options map[string]apidef.StringRegexMap, any bool, triggernum int) bool
References: textproto.CanonicalMIMEHeaderKey.
func checkPathParts
checkPathParts (r *http.Request, options map[string]apidef.StringRegexMap, any bool, triggernum int) bool
References: strings.Split.
func checkPayload
checkPayload (r *http.Request, options apidef.StringRegexMap, triggernum int) bool
References: bytes.NewBuffer, io.NopCloser, io.ReadAll.
func checkQueryString
checkQueryString (r *http.Request, options map[string]apidef.StringRegexMap, any bool, triggernum int) bool
func checkSessionTrigger
checkSessionTrigger (r *http.Request, sess *user.SessionState, options map[string]apidef.StringRegexMap, any bool, triggernum int) bool
func chunkedResponseHandler
chunkedResponseHandler (w http.ResponseWriter, r *http.Request)
References: http.Flusher.
func cleanIdleMemConnProviders
cleanIdleMemConnProviders checks memconn.Provider instances periodically and deletes idle ones.
cleanIdleMemConnProviders (ctx context.Context)
References: time.NewTicker, time.Now.
func cleanIdleMemConnProvidersEagerly
cleanIdleMemConnProvidersEagerly deletes idle memconn.Provider instances and closes the underlying listener to free resources.
cleanIdleMemConnProvidersEagerly (pointInTime time.Time)
func cloneHeader
cloneHeader (h http.Header) http.Header
References: http.Header.
func compressBuffer
compressBuffer (in bytes.Buffer, encoding string) bytes.Buffer
References: flate.NewWriter, gzip.NewWriter.
func compressedResponseHandler
compressedResponseHandler (w http.ResponseWriter, r *http.Request)
References: gzip.NewWriter, json.NewEncoder.
func contains
contains checks whether the given slice contains the given item.
contains (s []string, i string) bool
func containsEscapedChars
checks if a string contains escaped characters
containsEscapedChars (str string) bool
References: url.PathUnescape.
func coprocessAuthEnabled
coprocessAuthEnabled (spec *APISpec) bool
func copyAllowedURLs
copyAllowedURLs (input []user.AccessSpec) []user.AccessSpec
References: user.AccessSpec.
func copyBody
copyBody (body io.ReadCloser, greedy bool) (io.ReadCloser, error)
References: io.SeekStart.
func copyHeader
copyHeader (dst,src http.Header, ignoreCanonical bool)
func copyRequest
copyRequest (r *http.Request) (*http.Request, error)
func copyResponse
copyResponse (r *http.Response) (*http.Response, error)
References: http.StatusSwitchingProtocols.
func countApisByListenHash
countApisByListenHash (specs []*APISpec) map[string]int
References: logrus.Fields.
func createConnectionStringFromDashboardObject
createConnectionStringFromDashboardObject (config dashboardConfigPayload) string
References: strconv.Itoa, strings.TrimRight.
func createJWKTokenHMAC
createJWKTokenHMAC (jGen ...func(*jwt.Token)) string
func createMemConnProviderIfNeeded
createMemConnProviderIfNeeded creates a new memconn.Provider and net.Listener for the given host.
createMemConnProviderIfNeeded (handler http.Handler, r *http.Request) error
References: http.HandlerFunc, http.NewServeMux, http.Request, http.ResponseWriter, http.Serve, memconn.Provider, time.Now.
func createMockReadCloserWithError
createMockReadCloserWithError (err error) *MockReadCloser
func ctxCheckLimits
Should we check Rate limits and Quotas?
ctxCheckLimits (r *http.Request) bool
References: ctx.CheckLoopLimits, httpctx.IsSelfLooping.
func ctxGetAuthToken
ctxGetAuthToken (r *http.Request) string
References: ctx.GetAuthToken.
func ctxGetCacheOptions
ctxGetCacheOptions returns a cache key if we need to cache request
ctxGetCacheOptions (r *http.Request) *cacheOptions
References: ctx.CacheOptions.
func ctxGetDefaultVersion
ctxGetDefaultVersion (r *http.Request) bool
References: ctx.VersionDefault.
func ctxGetDoNotTrack
ctxGetDoNotTrack (r *http.Request) bool
References: ctx.DoNotTrackThisEndpoint.
func ctxGetGraphQLIsWebSocketUpgrade
ctxGetGraphQLIsWebSocketUpgrade (r *http.Request) bool
References: ctx.GraphQLIsWebSocketUpgrade.
func ctxGetGraphQLRequest
ctxGetGraphQLRequest (r *http.Request) *gql.Request
References: ctx.GraphQLRequest, gql.Request.
func ctxGetGraphQLRequestV2
ctxGetGraphQLRequestV2 (r *http.Request) *gqlv2.Request
References: ctx.GraphQLRequest, gqlv2.Request.
func ctxGetOperation
ctxGetOperation (r *http.Request) *Operation
References: ctx.OASOperation.
func ctxGetOrigRequestURL
ctxGetOrigRequestURL (r *http.Request) *url.URL
References: ctx.OrigRequestURL, url.URL.
func ctxGetRequestMethod
ctxGetRequestMethod (r *http.Request) string
References: ctx.RequestMethod.
func ctxGetRequestStatus
ctxGetRequestStatus (r *http.Request) RequestStatus
References: ctx.RequestStatus.
func ctxGetSession
ctxGetSession (r *http.Request) *user.SessionState
References: ctx.GetSession.
func ctxGetSpanAttributes
ctxGetSpanAttributes (r *http.Request, mwName string) []otel.SpanAttribute
References: otel.SpanAttribute.
func ctxGetTrackedPath
ctxGetTrackedPath (r *http.Request) string
References: ctx.TrackThisEndpoint.
func ctxGetTransformRequestMethod
ctxGetTransformRequestMethod (r *http.Request) string
References: ctx.TransformedRequestMethod.
func ctxGetURLRewriteTarget
ctxGetURLRewriteTarget (r *http.Request) *url.URL
References: ctx.UrlRewriteTarget, url.URL.
func ctxGetUrlRewritePath
ctxGetUrlRewritePath (r *http.Request) string
References: ctx.UrlRewritePath.
func ctxGetVersionInfo
ctxGetVersionInfo (r *http.Request) *apidef.VersionInfo
References: apidef.VersionInfo, ctx.VersionData.
func ctxGetVersionName
ctxGetVersionName (r *http.Request) *string
References: ctx.VersionName.
func ctxIncLoopLevel
ctxIncLoopLevel (r *http.Request, loopLimit int)
func ctxIncThrottleLevel
ctxIncThrottleLevel (r *http.Request, throttleLimit int)
func ctxLoopLevel
ctxLoopLevel (r *http.Request) int
References: ctx.LoopLevel.
func ctxLoopLevelLimit
ctxLoopLevelLimit (r *http.Request) int
References: ctx.LoopLevelLimit.
func ctxLoopingEnabled
ctxLoopingEnabled (r *http.Request) bool
func ctxSetCacheOptions
ctxSetCacheOptions sets a cache key to use for the http request
ctxSetCacheOptions (r *http.Request, options *cacheOptions)
References: ctx.CacheOptions.
func ctxSetCheckLoopLimits
ctxSetCheckLoopLimits (r *http.Request, b bool)
References: ctx.CheckLoopLimits.
func ctxSetDefaultVersion
ctxSetDefaultVersion (r *http.Request)
References: ctx.VersionDefault.
func ctxSetDoNotTrack
ctxSetDoNotTrack (r *http.Request, b bool)
References: ctx.DoNotTrackThisEndpoint.
func ctxSetGraphQLIsWebSocketUpgrade
ctxSetGraphQLIsWebSocketUpgrade (r *http.Request, isWebSocketUpgrade bool)
References: ctx.GraphQLIsWebSocketUpgrade.
func ctxSetGraphQLRequest
ctxSetGraphQLRequest (r *http.Request, gqlRequest *gql.Request)
References: ctx.GraphQLRequest.
func ctxSetGraphQLRequestV2
ctxSetGraphQLRequestV2 (r *http.Request, gqlRequest *gqlv2.Request)
References: ctx.GraphQLRequest.
func ctxSetJWTContextVars
ctxSetJWTContextVars (s *APISpec, r *http.Request, token *jwt.Token)
func ctxSetLoopLevel
ctxSetLoopLevel (r *http.Request, value int)
References: ctx.LoopLevel.
func ctxSetLoopLimit
ctxSetLoopLimit (r *http.Request, limit int)
References: ctx.LoopLevelLimit.
func ctxSetOperation
ctxSetOperation (r *http.Request, op *Operation)
References: ctx.OASOperation.
func ctxSetOrigRequestURL
ctxSetOrigRequestURL (r *http.Request, url *url.URL)
References: ctx.OrigRequestURL.
func ctxSetRequestMethod
ctxSetRequestMethod (r *http.Request, path string)
References: ctx.RequestMethod.
func ctxSetRequestStatus
ctxSetRequestStatus (r *http.Request, stat RequestStatus)
References: ctx.RequestStatus.
func ctxSetSession
ctxSetSession (r *http.Request, s *user.SessionState, scheduleUpdate bool, hashKey bool)
References: ctx.SetSession.
func ctxSetSpanAttributes
ctxSetSpanAttributes (r *http.Request, mwName string, attrs ...otel.SpanAttribute)
func ctxSetThrottleLevel
ctxSetThrottleLevel (r *http.Request, value int)
References: ctx.ThrottleLevel.
func ctxSetThrottleLimit
ctxSetThrottleLimit (r *http.Request, limit int)
References: ctx.ThrottleLevelLimit.
func ctxSetTrace
ctxSetTrace (r *http.Request)
References: ctx.Trace.
func ctxSetTrackedPath
ctxSetTrackedPath (r *http.Request, p string)
References: ctx.TrackThisEndpoint.
func ctxSetTransformRequestMethod
ctxSetTransformRequestMethod (r *http.Request, path string)
References: ctx.TransformedRequestMethod.
func ctxSetURLRewriteTarget
ctxSetURLRewriteTarget (r *http.Request, url *url.URL)
References: ctx.UrlRewriteTarget.
func ctxSetUrlRewritePath
ctxSetUrlRewritePath (r *http.Request, path string)
References: ctx.UrlRewritePath.
func ctxSetVersionInfo
ctxSetVersionInfo (r *http.Request, v *apidef.VersionInfo)
References: ctx.VersionData.
func ctxSetVersionName
ctxSetVersionName (r *http.Request, vName *string)
References: ctx.VersionName.
func ctxThrottleLevel
ctxThrottleLevel (r *http.Request) int
References: ctx.ThrottleLevel.
func ctxThrottleLevelLimit
ctxThrottleLevelLimit (r *http.Request) int
References: ctx.ThrottleLevelLimit.
func ctxTraceEnabled
ctxTraceEnabled (r *http.Request) bool
References: ctx.Trace.
func dashboardServiceInit
dashboardServiceInit (gw *Gateway)
func deepCopyBody
Creates a deep copy of source request.Body and replaces target request.Body with it.
deepCopyBody (source *http.Request, target *http.Request) error
References: bytes.NewBuffer, httputil.IsStreamingRequest, io.NopCloser, io.ReadAll.
func defaultTykErrors
defaultTykErrors ()
References: config.TykError.
func doJSONExport
doJSONExport (w http.ResponseWriter, code int, obj interface{}, fileName string)
References: fmt.Sprintf, http.Error, http.StatusInternalServerError, http.StatusOK, json.MarshalIndent.
func doJSONWrite
doJSONWrite (w http.ResponseWriter, code int, obj interface{})
References: header.ApplicationJSON, header.ContentType, http.Error, http.StatusInternalServerError, http.StatusOK, json.NewEncoder, strconv.Itoa.
func dummyGetCertificate
dummyGetCertificate needed because TLSConfig require setting Certificates array or GetCertificate function from start, even if it get overridden by getTLSConfigForClient
dummyGetCertificate ( *tls.ClientHelloInfo) (*tls.Certificate, error)
func eraseSyncMap
eraseSyncMap uses native sync.Map functions to clear the map without needing to unsafely modify the value to nil.
eraseSyncMap (m *sync.Map)
func errorAndStatusCode
errorAndStatusCode (errType string) (error, int)
References: errors.New.
func estimateTagsCapacity
estimateTagsCapacity (session *user.SessionState, apiSpec *APISpec) int
func explicitRouteSubpaths
explicitRouteSubpaths (prefix string, handler http.Handler, enabled bool) http.Handler
References: strings.Contains, strings.HasSuffix.
func extractOASObjFromReq
extractOASObjFromReq (reqBody io.Reader) ([]byte, *oas.OAS, error)
References: ioutil.ReadAll, oas.OAS, openapi3.NewLoader.
func findRouteAndOperation
findRouteAndOperation (spec *APISpec, r *http.Request)
func fireEvent
fireEvent (name apidef.TykEvent, meta interface{}, handlers map[apidef.TykEvent][]config.TykEventHandler)
References: config.EventMessage, time.Now.
func firstVals
firstVals (vals map[string][]string) map[string]string
func fixFuncPath
fixFuncPath (pathPrefix string, funcs []apidef.MiddlewareDefinition)
References: filepath.Join.
func generateDomainPath
generateDomainPath (hostname,listenPath string) string
func generateGraphQLDataSource
generateGraphQLDataSource (gen ...func(graphQLDataSource *datasource.GraphQLDataSourceConfig)) json.RawMessage
References: datasource.GraphQLDataSourceConfig, datasource.TypeFieldConfiguration, json.Marshal, json.Unmarshal.
func generateGraphQLDataSourceV2
generateGraphQLDataSourceV2 (gen func(dataSource *apidef.GraphQLEngineDataSource, graphqlConf *apidef.GraphQLEngineDataSourceConfigGraphQL)) apidef.GraphQLEngineDataSource
References: apidef.GraphQLEngineDataSource, apidef.GraphQLEngineDataSourceConfigGraphQL, json.Marshal, json.Unmarshal.
func generateHMACEncodedSignature
generateHMACEncodedSignature (signatureString,secret string, algorithm string) (string, error)
References: base64.StdEncoding, errors.New, hash.Hash, hmac.New, sha1.New, sha256.New, sha512.New, sha512.New384, url.QueryEscape.
func generateHMACSignatureStringFromRequest
"Signature keyId="9876",algorithm="hmac-sha1",headers="x-test x-test-2",signature="queryEscape(base64(sig))"")
generateHMACSignatureStringFromRequest (r *http.Request, headers []string, path string) (string, error)
References: strings.ToLower, strings.TrimSpace.
func generateHeaderList
generateHeaderList (r *http.Request, headerList []string) []string
References: strings.ToLower, strings.TrimSpace, time.Now.
func generateOAuthPrefix
generateOAuthPrefix (apiID string) string
func generateRESTDataSource
generateRESTDataSource (gen ...func(restDataSource *datasource.HttpJsonDataSourceConfig)) json.RawMessage
References: datasource.HttpJsonDataSourceConfig, datasource.TypeFieldConfiguration, json.Marshal, json.Unmarshal.
func generateRESTDataSourceV2
generateRESTDataSourceV2 (gen func(dataSource *apidef.GraphQLEngineDataSource, restConf *apidef.GraphQLEngineDataSourceConfigREST)) apidef.GraphQLEngineDataSource
References: apidef.GraphQLEngineDataSource, apidef.GraphQLEngineDataSourceConfigREST, json.Marshal, json.Unmarshal.
func generateRSAEncodedSignature
generateRSAEncodedSignature (signatureString string, privateKey *rsa.PrivateKey, algorithm string) (string, error)
References: base64.StdEncoding, crypto.Hash, crypto.SHA256, cryptorand.Reader, hash.Hash, rsa.SignPKCS1v15, sha256.New.
func getAPIURL
getAPIURL (apiDef apidef.APIDefinition, gwConfig config.Config) string
References: net.JoinHostPort, strconv.Itoa, url.URL.
func getCipherAliases
getCipherAliases (ciphers []string) []uint16
References: crypto.ResolveCipher.
func getClientValidator
getClientValidator (helloInfo *tls.ClientHelloInfo, certPool *x509.CertPool) func([][]byte, [][]*x509.Certificate) error
References: errors.New, time.Now, x509.Certificate, x509.ExtKeyUsage, x509.ExtKeyUsageClientAuth, x509.NewCertPool, x509.ParseCertificate, x509.VerifyOptions.
func getDateHeader
getDateHeader (r *http.Request) (string, string)
References: logrus.Fields, strings.ToLower.
func getFieldValues
getFieldValues (authHeader string) (*HMACFieldValues, error)
References: errors.New, logrus.Fields, strconv.Unquote, strings.Split, strings.ToLower.
func getIDExtractor
getIDExtractor (spec *APISpec) IdExtractor
func getJWK
getJWK gets the JWK from URL.
getJWK (url string, jwtSSLInsecureSkipVerify bool) (*jose.JSONWebKeySet, error)
References: http.Client, http.Transport, io.ReadAll, tls.Config.
func getMapContext
getMapContext (m interface{}, k string) interface{}
func getMemConnProvider
getMemConnProvider return the cached memconn.Provider, if it's available in the cache.
getMemConnProvider (addr string) (*memconn.Provider, error)
References: fmt.Errorf, net.SplitHostPort.
func getPID
getPID () string
References: os.Getpid, strconv.Itoa.
func getSampleOASAPI
getSampleOASAPI () oas.OAS
References: oas.Info, oas.ListenPath, oas.OAS, oas.Server, oas.State, oas.Upstream, oas.XTykAPIGateway, openapi3.Info, openapi3.Paths, openapi3.T.
func getScopeFromClaim
getScopeFromClaim (claims jwt.MapClaims, scopeClaimName string) []string
References: strings.Split.
func getSessionTags
getSessionTags (session *user.SessionState) []string
func getStorageForPython
getStorageForPython (ctx context.Context) *storage.RedisCluster
References: config.Config, storage.NewConnectionHandler, storage.RedisCluster.
func getStreamingMiddleware
getStreamingMiddleware (base *BaseMiddleware) TykMiddleware
func getTagListAsString
getTagListAsString (tags []string) string
References: strings.Join.
func getUpstreamBasicAuthMw
getUpstreamBasicAuthMw (base *BaseMiddleware) TykMiddleware
func getUpstreamOAuthMw
getUpstreamOAuthMw (base *BaseMiddleware) TykMiddleware
func getUserIDFromClaim
getUserIDFromClaim parses jwt claims and get the userID from provided identityBaseField.
getUserIDFromClaim (claims jwt.MapClaims, identityBaseField string) (string, error)
References: errors.New.
func graphqlDataSourceHandler
graphqlDataSourceHandler (w http.ResponseWriter, r *http.Request)
func graphqlProxyUpstreamHandler
graphqlProxyUpstreamHandler (w http.ResponseWriter, r *http.Request)
References: graphql.Request, graphql.UnmarshalHttpRequest, http.CanonicalHeaderKey, http.StatusBadRequest, http.StatusInternalServerError, http.StatusOK, json.Unmarshal, strconv.Atoi.
func graphqlProxyUpstreamHandlerError
graphqlProxyUpstreamHandlerError (w http.ResponseWriter, r *http.Request)
References: http.StatusInternalServerError.
func greaterThanFloat64
greaterThanFloat64 checks whether first float64 value is bigger than second float64 value. -1 means infinite and the biggest value.
greaterThanFloat64 (first,second float64) bool
func greaterThanInt
greaterThanInt checks whether first int value is bigger than second int value. -1 means infinite and the biggest value.
greaterThanInt (first,second int) bool
func greaterThanInt64
greaterThanInt64 checks whether first int64 value is bigger than second int64 value. -1 means infinite and the biggest value.
greaterThanInt64 (first,second int64) bool
func handleDashboardRegistration
handleDashboardRegistration (gw *Gateway)
func handleInMemoryLoop
handleInMemoryLoop (handler http.Handler, r *http.Request) (*http.Response, error)
func handleOtelTracedResponse
handleOtelTracedResponse handles the tracing for the response middlewares when otel is enabled in the gateway
handleOtelTracedResponse (rh TykResponseHandler, rw http.ResponseWriter, res *http.Response, req *http.Request, ses *user.SessionState) error
References: errors.New, otel.SPAN_STATUS_ERROR, otel.Span, otel.SpanFromContext.
func handleResponse
handleResponse (rh TykResponseHandler, rw http.ResponseWriter, res *http.Response, req *http.Request, ses *user.SessionState, shouldTrace bool) error
References: trace.Span.
func handleResponseChain
handleResponseChain (chain []TykResponseHandler, rw http.ResponseWriter, res *http.Response, req *http.Request, ses *user.SessionState) (bool, error)
References: trace.IsEnabled.
func init
init ()
References: openapi3.DefineStringFormatCallback, openapi3.SchemaErrorDetailsDisabled, time.DateOnly, time.Parse, time.RFC3339.
func initAuthKeyErrors
initAuthKeyErrors ()
References: config.TykError, http.StatusForbidden, http.StatusUnauthorized.
func initOauth2KeyExistsErrors
initOauth2KeyExistsErrors ()
References: config.TykError, http.StatusBadRequest, http.StatusForbidden.
func initProxy
initProxy (proto string, tlsConfig *tls.Config) *httpProxyHandler
References: http.Handler, http.HandlerFunc, http.MethodConnect, http.Request, http.ResponseWriter, http.Server, net.Listen, tls.Conn, tls.Listen.
func intersection
intersection gets intersection of the given two slices.
intersection (a []string, b []string) []string
func introspect
introspect (opts apidef.Introspection, accessToken string) (jwt.MapClaims, error)
References: fmt.Errorf, http.Post, http.StatusOK, io.ReadAll, json.Unmarshal, strings.NewReader, url.Values.
func invalidateTokens
invalidate tokens if we had a new policy
invalidateTokens (prevClient ExtendedOsinClientInterface, updatedClient OAuthClient, oauthManager OAuthManagerInterface)
func isBodyHashRequired
isBodyHashRequired (request *http.Request) bool
References: http.MethodPatch, http.MethodPost, http.MethodPut.
func isCORSPreflight
isCORSPreflight (r *http.Request) bool
References: http.MethodOptions.
func isExpired
isExpired (claims jwt.MapClaims) bool
References: time.Now, time.Unix.
func isGraphQLProxyOnly
isGraphQLProxyOnly (apiSpec *APISpec) bool
References: apidef.GraphQLExecutionModeProxyOnly, apidef.GraphQLExecutionModeSubgraph.
func isLoop
isLoop (r *http.Request) (bool, error)
References: fmt.Errorf.
func isPayloadSignatureValid
isPayloadSignatureValid (notification Notification) bool
References: base64.StdEncoding, crypto.SHA256, hex.EncodeToString, sha256.Sum256.
func isSafeMethod
isSafeMethod (method string) bool
References: http.MethodGet, http.MethodHead, http.MethodOptions.
func jsonMarshalString
jsonMarshalString (i interface{}) string
References: json.Marshal.
func jwkURLsChanged
jwkURLsChanged (a,b []apidef.JWK) bool
func listenPathLength
listenPathLength (listenPath string) int
References: strings.Contains, strings.Count, strings.Split.
func loadBundleManifest
loadBundleManifest will parse the manifest file and return the bundle parameters.
loadBundleManifest (bundle *Bundle, spec *APISpec, skipVerification bool) error
References: filepath.Join, json.NewDecoder, logrus.Fields, os.Open.
func loadKeyValues
parses v which is a string of key1=value1,,key2=value2 ... format and returns a map of key:value pairs.
loadKeyValues (v string) map[string]string
References: scanner.EOF, scanner.Scanner, strings.NewReader.
func mapScopeToPolicies
mapScopeToPolicies (mapping map[string]string, scope []string) []string
func mapStrsToIfaces
mapStrsToIfaces (m map[string]string) map[string]interface{}
func mockFromConfig
mockFromConfig (tykMockRespOp *oas.MockResponse) (int, []byte, []oas.Header)
References: http.StatusOK.
func mockFromOAS
mockFromOAS (r *http.Request, operation *openapi3.Operation, fromOASExamples *oas.FromOASExamples) (int, string, []byte, []oas.Header, error)
References: errors.New, fmt.Errorf, fmt.Sprintf, http.StatusBadRequest, http.StatusForbidden, http.StatusNotFound, json.Marshal, oas.ExampleExtractor, oas.Header, sort.Slice, strconv.Atoi, strconv.Itoa.
func needsGraphQLExecutionEngine
needsGraphQLExecutionEngine (apiSpec *APISpec) bool
References: apidef.GraphQLConfigVersion2, apidef.GraphQLConfigVersion3Preview, apidef.GraphQLExecutionModeExecutionEngine, apidef.GraphQLExecutionModeProxyOnly, apidef.GraphQLExecutionModeSubgraph, apidef.GraphQLExecutionModeSupergraph.
func nestedMapLookup
nestedMapLookup (m map[string]interface{}, ks ...string) interface{}
func newExtractor
newExtractor is called from the CP middleware for every API that specifies extractor settings.
newExtractor (referenceSpec *APISpec, mw *BaseMiddleware)
References: apidef.IDExtractorConfig, apidef.RegexExtractor, apidef.ValueExtractor, apidef.XPathExtractor, mapstructure.Decode.
func newIntrospectionCache
newIntrospectionCache (gw *Gateway) *introspectionCache
References: storage.RedisCluster.
func newNopCloserBuffer
newNopCloserBuffer creates a new instance of a *nopCloserBuffer.
newNopCloserBuffer (buf io.ReadCloser) (*nopCloserBuffer, error)
func nopCloseRequestBody
nopCloseRequestBody (r *http.Request)
func nopCloseRequestBodyErr
nopCloseRequestBodyErr (r *http.Request) error
func nopCloseResponseBody
nopCloseResponseBody (r *http.Response)
func oauthClientStorageID
oauthClientStorageID (clientID string) string
func overrideTykErrors
overrideTykErrors (gw *Gateway)
func parseForm
parseForm (r *http.Request)
References: bytes.Buffer, io.TeeReader, ioutil.NopCloser.
func parseJWK
parseJWK (buf []byte) (*jose.JSONWebKeySet, error)
References: json.Unmarshal.
func parseJWTKey
parseJWTKey parses JWT key based on signing Method
parseJWTKey (signingMethod string, secret interface{}) (interface{}, error)
References: errors.New.
func parsePoliciesFromRPC
parsePoliciesFromRPC (list string, allowExplicit bool) (map[string]user.Policy, error)
References: json.Unmarshal, user.Policy.
func proxyFromAPI
proxyFromAPI (api *APISpec) func(*http.Request) (*url.URL, error)
References: http.ProxyFromEnvironment, http.Request, url.Parse, url.URL.
func proxyTimeout
proxyTimeout (spec *APISpec) float64
func pullBundle
pullBundle (getter BundleGetter, backoffMultiplier float64) ([]byte, error)
References: backoff.NewExponentialBackOff, backoff.Retry, backoff.WithMaxRetries, time.Second.
func randStringBytes
randStringBytes (n int) string
References: mathrand.Intn.
func rawKeysWithAllowanceScope
rawKeysWithAllowanceScope (keys []string, keyName string, session *user.SessionState) []string
func readBody
readBody (req *http.Request) ([]byte, error)
References: ioutil.ReadAll.
func recordDetail
recordDetail (r *http.Request, spec *APISpec) bool
References: httputil.IsStreamingRequest.
func recordDetailUnsafe
recordDetailUnsafe (r *http.Request, spec *APISpec) bool
References: ctx.OrgSessionContext, user.SessionState.
func recordGraphDetails
recordGraphDetails (rec *analytics.AnalyticsRecord, r *http.Request, resp *http.Response, spec *APISpec)
References: apidef.GraphQLExecutionModeSubgraph, bytes.NewBuffer, graphqlinternal.NewGraphStatsExtractor, httputil.RemoveResponseTransferEncoding, io.NopCloser, io.ReadAll.
func recoverFromLoadApiPanic
recoverFromLoadApiPanic (spec *APISpec, err any) error
References: debug.Stack, fmt.Errorf.
func removeDuplicateCORSHeader
removeDuplicateCORSHeader (dst,src http.Header)
References: http.CanonicalHeaderKey.
func replaceNonAlphaNumeric
replaceNonAlphaNumeric (in string) string
func reportHealthValue
reportHealthValue is a shortcut we can use throughout the app to push a health check value
reportHealthValue (spec *APISpec, counter HealthPrefix, value string)
func requestIPHops
requestIPHops (r *http.Request) string
References: net.SplitHostPort, strings.Join.
func resetAPILimits
resetAPILimits (accessRights map[string]user.AccessDefinition)
References: user.APILimit.
func respBodyReader
respBodyReader (req *http.Request, resp *http.Response) io.ReadCloser
References: bytes.NewReader, flate.NewReader, gzip.NewReader, header.AcceptEncoding, header.ContentEncoding, ioutil.NopCloser.
func restDataSourceHandler
restDataSourceHandler (w http.ResponseWriter, r *http.Request)
func restDataSourceHandlerV2
restDataSourceHandlerV2 (w http.ResponseWriter, r *http.Request)
func restHeadersDataSourceHandler
restHeadersDataSourceHandler (w http.ResponseWriter, r *http.Request)
References: json.NewEncoder.
func roundValue
roundValue (untruncated float64) float64
func sanitizeConfig
sanitizeConfig (mc map[string]interface{}) map[string]interface{}
func sanitizeKey
sanitizeKey (b *bytes.Buffer, s string)
func saveBundle
saveBundle will save a bundle to the disk, see ZipBundleSaver methods for reference.
saveBundle (bundle *Bundle, destPath string, spec *APISpec) error
func setCustomHeader
setCustomHeader (h http.Header, key string, value string, ignoreCanonical bool)
func setCustomHeaderMultipleValues
setCustomHeaderMultipleValues accepts multiple values for a key header and append it
setCustomHeaderMultipleValues (h http.Header, key string, values []string, ignoreCanonical bool)
func shouldAddConfigData
shouldAddConfigData (spec *APISpec) bool
func shouldPerformTracing
shouldPerformTracing (rh TykResponseHandler, baseMw *BaseTykResponseHandler) bool
func shouldReloadSpec
shouldReloadSpec (existingSpec,newSpec *APISpec) bool
References: apidef.GrpcDriver, middleware.Enabled.
func shouldRewriteHost
shouldRewriteHost (oldURL,newURL *url.URL) bool
func singleJoiningSlash
singleJoiningSlash (targetPath,subPath string, disableStripSlash bool) string
References: strings.TrimLeft, strings.TrimRight.
func sortSpecsByListenPath
sortSpecsByListenPath (specs []*APISpec)
References: sort.Slice.
func specToJson
specToJson (spec *APISpec) string
References: json.Marshal.
func stripBearer
stripBearer (token string) string
References: strings.ToUpper.
func stripSignature
stripSignature (token string) string
References: strings.TrimPrefix, strings.TrimSpace.
func stripSlashes
stripSlashes removes any trailing slashes from the request's URL path.
stripSlashes (next http.Handler) http.Handler
References: http.HandlerFunc, http.Request, http.ResponseWriter, strings.TrimRight.
func subGraphAccountsHandlerAllAccounts
subGraphAccountsHandlerAllAccounts (w http.ResponseWriter, req *http.Request)
func subgraphAccountsHandler
subgraphAccountsHandler (w http.ResponseWriter, r *http.Request)
func subgraphReviewsHandler
subgraphReviewsHandler (w http.ResponseWriter, r *http.Request)
func syncHeadersAndMultiValueHeaders
syncHeadersAndMultiValueHeaders synchronizes the content of 'headers' and 'multiValueHeaders'. If a key is updated or added in 'headers', the corresponding key in 'multiValueHeaders' is also updated or added. If a key is removed from 'headers', the corresponding key in 'multiValueHeaders' is also removed. If multiValuesHeaders contains a key with multiple values and the same key is present in headers, the first value in multiValuesHeaders is updated with the value from headers, while the remaining values remain unchanged.
syncHeadersAndMultiValueHeaders (headers map[string]string, multiValueHeaders []*coprocess.Header) []*coprocess.Header
References: coprocess.Header.
func syncResourcesWithReload
syncResourcesWithReload (resource string, conf config.Config, syncFunc func() (int, error)) (int, error)
References: fmt.Errorf, time.Duration, time.Second, time.Sleep.
func tagHeaders
tagHeaders (r *http.Request, th []string, tags []string) []string
References: strings.ToLower.
func target
target (listenAddress string, listenPort int) string
References: fmt.Sprintf.
func timeValidateJWTClaims
timeValidateJWTClaims validates JWT with provided clock skew, to overcome skew occurred with distributed systems.
timeValidateJWTClaims (c jwt.MapClaims, expiresAt,issuedAt,notBefore uint64) *jwt.ValidationError
References: errors.New, time.Now.
func tlsClientConfig
tlsClientConfig (s *APISpec, gw *Gateway) *tls.Config
References: tls.Certificate, tls.Config, tls.RenegotiateFreelyAsClient, url.Parse.
func toScopeStringsSlice
toScopeStringsSlice (v interface{}, scopeSlice *[]string, nested bool) []string
References: strings.Split.
func transformBody
transformBody (r *http.Request, tmeta *TransformSpec, t *TransformMiddleware) error
References: apidef.RequestJSON, apidef.RequestXML, bytes.Buffer, bytes.NewBufferString, fmt.Errorf, io.NopCloser, ioutil.ReadAll, json.Unmarshal, mxj.NewMapXml, mxj.XmlCharsetReader.
func trimCategories
trimCategories (name string) string
References: strings.Index.
func updateOASServers
updateOASServers (spec *APISpec, conf config.Config, apiDef *apidef.APIDefinition, oasObj *oas.OAS)
func upgradeType
upgradeType (h http.Header) string
References: httpguts.HeaderValuesContainsToken, strings.ToLower.
func urlFromService
urlFromService (spec *APISpec, gw *Gateway) (*apidef.HostList, error)
References: apidef.HostList, apidef.NewHostList.
func userRatesCheck
userRatesCheck (w http.ResponseWriter, r *http.Request)
References: http.StatusBadRequest, http.StatusOK.
func valToStr
valToStr (v interface{}) string
References: reflect.TypeOf, strconv.FormatFloat, strconv.FormatInt, strings.Join, strings.TrimPrefix, url.QueryEscape, url.Values.
func validateAPIDef
validateAPIDef (apiDef *apidef.APIDefinition) *apiStatusMessage
References: apidef.DefaultValidationRuleSet, apidef.Validate, fmt.Sprintf.
func validateCommonName
validateCommonName (host string, cert *x509.Certificate) error
References: errors.New.
func validateRSAEncodedSignature
validateRSAEncodedSignature (signatureString string, publicKey *rsa.PublicKey, algorithm string, signature string) (bool, error)
References: base64.StdEncoding, crypto.Hash, crypto.SHA256, hash.Hash, rsa.VerifyPKCS1v15, sha256.New.
func writeNewConfiguration
writeNewConfiguration (payload ConfigPayload) error
References: ioutil.WriteFile, json.MarshalIndent.
func writePIDFile
writePIDFile (file string) error
References: filepath.Dir, ioutil.WriteFile, os.MkdirAll.
func writeProfiles
writeProfiles ()
References: cli.BlockProfile, cli.MutexProfile, os.Create, pprof.Lookup.
func compileTransformJQPathSpec
compileTransformJQPathSpec (paths []apidef.TransformJQMeta, stat URLStatus) []URLSpec
func getMatchPathAndMethod
getMatchPathAndMethod retrieves the match path and method from the request based on the mode.
getMatchPathAndMethod (r *http.Request, mode URLStatus) (string, string)
References: strings.HasPrefix.
func getURLStatus
getURLStatus (stat URLStatus) RequestStatus
func getVersionFromRequest
getVersionFromRequest (r *http.Request) string
References: apidef.HeaderLocation, apidef.URLLocation, apidef.URLParamLocation, regexp.Compile, strings.Replace, strings.Split, strings.TrimPrefix.
func hasVirtualEndpoint
hasVirtualEndpoint () bool
func injectIntoReqContext
injectIntoReqContext (req *http.Request)
References: ctx.SetDefinition, ctx.SetOASDefinition.
func isListeningOnPort
isListeningOnPort checks whether the API listens on the given port.
isListeningOnPort (port int, gwConfig *config.Config) bool
func isStreamingAPI
isStreamingAPI () bool
References: streams.ExtensionTykStreaming.
func setHasMock
setHasMock ()
func validateHTTP
validateHTTP () error
func validateTCP
validateTCP () error
References: errors.New.
func getAuthType
getAuthType overrides BaseMiddleware.getAuthType.
getAuthType () string
References: apidef.AuthTokenType.
func reportInvalidKey
reportInvalidKey (key string, r *http.Request, msg string, errMsg string) (error, int)
func setContextVars
setContextVars (r *http.Request, token string)
func validateSignature
validateSignature (r *http.Request, key string) (error, int)
References: errors.New, http.StatusInternalServerError, http.StatusOK, signaturevalidator.SignatureValidator.
func emitRateLimitEvent
emitRateLimitEvent emits a specific rate limit event with an optional custom message.
emitRateLimitEvent (r *http.Request, e event.Event, message string, rateLimitKey string)
References: event.String, request.RealIP.
func emitRateLimitEvents
emitRateLimitEvents emits rate limit related events based on the request context.
emitRateLimitEvents (r *http.Request, rateLimitKey string)
References: event.Get, event.RateLimitSmoothingDown, event.RateLimitSmoothingUp.
func generateSessionID
generateSessionID (id string) string
References: fmt.Sprintf, md5.Sum.
func getAuthToken
getAuthToken (authType string, r *http.Request) (string, apidef.AuthConfig)
References: apidef.AuthTokenType, apidef.JWTType, header.Authorization.
func getAuthType
getAuthType () string
func handleRateLimitFailure
handleRateLimitFailure handles the actions to be taken when a rate limit failure occurs.
handleRateLimitFailure (r *http.Request, e event.Event, message string, rateLimitKey string) (error, int)
References: errors.New, http.StatusTooManyRequests.
func basicAuthBodyCredentials
basicAuthBodyCredentials (w http.ResponseWriter, r *http.Request) (string, error, int)
References: bytes.NewReader, errors.New, http.StatusBadRequest, ioutil.NopCloser, ioutil.ReadAll.
func basicAuthHeaderCredentials
basicAuthHeaderCredentials (w http.ResponseWriter, r *http.Request) (string, error, int)
References: base64.StdEncoding, errors.New, http.StatusBadRequest, strings.Split.
func checkPassword
checkPassword (session *user.SessionState, plainPassword string, logger *logrus.Entry) error
References: storage.HashStr, user.HashBCrypt, user.HashMurmur128, user.HashMurmur32, user.HashMurmur64, user.HashPlainText, user.HashSha256.
func compareHashAndPassword
compareHashAndPassword (hash string, password string, logEntry *logrus.Entry) error
References: bcrypt.CompareHashAndPassword, murmur3.New64.
func doBcryptWithCache
doBcryptWithCache (cacheDuration int64, hashedPassword []byte, password []byte) error
References: bcrypt.CompareHashAndPassword, murmur3.New64.
func getAuthType
getAuthType overrides BaseMiddleware.getAuthType.
getAuthType () string
References: apidef.BasicType.
func handleAuthFail
handleAuthFail (w http.ResponseWriter, r *http.Request, token string) (error, int)
func requestForBasicAuth
requestForBasicAuth sends error code and message along with WWW-Authenticate header to client.
requestForBasicAuth (w http.ResponseWriter, msg string) (error, int)
References: errors.New, header.WWWAuthenticate, http.StatusUnauthorized.
func doRequest
doRequest will make the same request but return a BatchReplyUnit
doRequest (req *http.Request, relURL string) BatchReplyUnit
References: http.Client, http.Transport, ioutil.ReadAll, tls.Certificate, tls.Config.
func getAuthType
getAuthType overrides BaseMiddleware.getAuthType.
getAuthType () string
References: apidef.CoprocessType.
func getAvgCount
getAvgCount (prefix HealthPrefix) float64
func clearCacheForKey
clearCacheForKey (keyName string, hashed bool)
References: storage.HashKey.
func deleteRawKeysWithAllowanceScope
deleteRawKeysWithAllowanceScope (store storage.Handler, session *user.SessionState, keyName string)
func generateVirtualSessionFor
generateVirtualSessionFor generates a virtual session for the given access token by using its identifier.
generateVirtualSessionFor (r *http.Request, sessionID string) user.SessionState
References: user.APILimit, user.AccessDefinition.
func getAuthType
getAuthType overrides BaseMiddleware.getAuthType.
getAuthType () string
References: apidef.ExternalOAuthType.
func getSecretFromJWKOrConfig
getSecretFromJWKOrConfig gets the secret to verify jwt signature from API definition or from JWK set if config is set to a URL
getSecretFromJWKOrConfig (kid interface{}, jwtValidation apidef.JWTValidation) (interface{}, error)
References: base64.StdEncoding.
func getSecretFromJWKURL
getSecretFromJWKURL gets the secret to verify jwt signature from a JWK URL.
getSecretFromJWKURL (url string, kid interface{}) (interface{}, error)
References: cache.DefaultExpiration.
func introspection
introspection makes an introspection request to third-party provider to check whether the access token is valid or not. The access token can be both JWT and opaque type.
introspection (accessToken string) (bool, string, error)
References: errors.New, fmt.Errorf.
func jwt
jwt makes access token validation without making a network call and validates access token locally. The access token should be JWT type.
jwt (accessToken string) (bool, string, error)
References: errors.New, fmt.Errorf.
func addBatchEndpoint
addBatchEndpoint (spec *APISpec, subrouter *mux.Router)
func addOAuthHandlers
Create API-specific OAuth handlers and respective auth servers
addOAuthHandlers (spec *APISpec, muxer *mux.Router) *OAuthManager
References: http.StatusForbidden, osin.NewServerConfig, storage.RedisCluster.
func addPubSubDelay
addPubSubDelay sleeps for duration
addPubSubDelay (dur time.Duration)
References: time.Sleep.
func afterConfSetup
afterConfSetup takes care of non-sensical config values (such as zero timeouts) and sets up a few globals that depend on the config.
afterConfSetup ()
References: config.DefaultOTelResourceName, regexp.ResetCache, rpc.GlobalRPCCallTimeout, rpc.GlobalRPCPingTimeout, time.Duration, time.Second.
func allApisAreMTLS
allApisAreMTLS () bool
func apiHandler
apiHandler (w http.ResponseWriter, r *http.Request)
References: afero.NewOsFs, apidef.APIDefinition, apidef.HeaderBaseAPIID, http.MethodDelete, http.MethodGet, http.MethodPost, http.MethodPut, http.StatusBadRequest, mux.Vars.
func apiOASExportHandler
apiOASExportHandler (w http.ResponseWriter, r *http.Request)
References: fmt.Sprintf, mux.Vars.
func apiOASGetHandler
apiOASGetHandler (w http.ResponseWriter, r *http.Request)
References: apidef.HeaderBaseAPIID, mux.Vars, oas.OAS.
func apiOASPatchHandler
apiOASPatchHandler (w http.ResponseWriter, r *http.Request)
References: afero.NewOsFs, apidef.ErrAPINotFound, apidef.ErrAPINotMigrated, bytes.NewReader, http.StatusBadRequest, http.StatusInternalServerError, http.StatusNotFound, ioutil.NopCloser, mux.Vars, oas.GetTykExtensionConfigParams, oas.OAS, oas.RetainOldServerURL, oas.XTykAPIGateway, strings.TrimSpace.
func apiOASPostHandler
apiOASPostHandler (w http.ResponseWriter, r *http.Request)
References: afero.NewOsFs.
func apiOASPutHandler
apiOASPutHandler (w http.ResponseWriter, r *http.Request)
References: afero.NewOsFs, http.StatusBadRequest, mux.Vars.
func apisByIDLen
apisByIDLen () int
func applyPoliciesAndSave
applyPoliciesAndSave (keyName string, session *user.SessionState, spec *APISpec, isHashed bool) error
func backupConfiguration
backupConfiguration () error
References: ioutil.WriteFile, json.MarshalIndent, time.Now.
func basicAuthHashAlgo
basicAuthHashAlgo () string
References: user.HashBCrypt, user.IsHashType.
func blockInDashboardMode
blockInDashboardMode (next http.HandlerFunc) http.HandlerFunc
References: http.Request, http.ResponseWriter, http.StatusInternalServerError.
func buildDashboardConnStr
buildDashboardConnStr (resource string) string
References: time.Second, time.Sleep.
func cacheClose
cacheClose will close the caches in *Gateway, cleaning up the goroutines.
cacheClose ()
func cacheCreate
cacheCreate will create the caches in *Gateway.
cacheCreate ()
References: cache.New.
func certHandler
certHandler (w http.ResponseWriter, r *http.Request)
References: certs.CertificateAny, certs.CertificateBasics, certs.CertificateMeta, certs.ExtractCertificateBasics, certs.ExtractCertificateMeta, http.StatusForbidden, http.StatusNotFound, http.StatusOK, ioutil.ReadAll, mux.Vars, sha256.Size, strings.Split.
func checkAndApplyTrialPeriod
checkAndApplyTrialPeriod (keyName string, newSession *user.SessionState, isHashed bool)
References: time.Now.
func checkIsAPIOwner
checkIsAPIOwner will ensure that the accessor of the tyk API has the correct security credentials - this is a shared secret between the client and the owner and is set in the tyk.conf file. This should never be made public!
checkIsAPIOwner (next http.Handler) http.Handler
References: header.XTykAuthorization, http.HandlerFunc, http.Request, http.ResponseWriter, http.StatusForbidden.
func configureAuthAndOrgStores
configureAuthAndOrgStores (gs *generalStores, spec *APISpec) (storage.Handler, storage.Handler, storage.Handler)
func controlAPICheckClientCertificate
controlAPICheckClientCertificate (certLevel string, next http.Handler) http.Handler
References: certs.CertificatePublic, crypto.ValidateRequestCerts, http.HandlerFunc, http.Request, http.ResponseWriter, http.StatusForbidden.
func createDynamicMiddleware
createDynamicMiddleware (name string, isPre,useSession bool, baseMid *BaseMiddleware) func(http.Handler) http.Handler
func createKeyHandler
createKeyHandler (w http.ResponseWriter, r *http.Request)
References: http.StatusBadRequest, http.StatusInternalServerError, http.StatusOK, json.NewDecoder, logrus.Fields, storage.HashKey, strconv.Itoa, time.Now, user.SessionState.
func createMiddleware
Generic middleware caller to make extension easier
createMiddleware (actualMW TykMiddleware) func(http.Handler) http.Handler
References: health.Kvs, http.Handler, http.HandlerFunc, http.Request, http.ResponseWriter, newrelic.FromContext, request.RealIP, strconv.Itoa, time.Now, time.Since.
func createOauthClient
createOauthClient (w http.ResponseWriter, r *http.Request)
References: http.StatusBadRequest, http.StatusInternalServerError, http.StatusOK, json.NewDecoder, logrus.Fields, osin.ServerConfig, storage.RedisCluster, uuid.NewHex.
func createResponseMiddlewareChain
Create the response processor chain
createResponseMiddlewareChain (spec *APISpec, responseFuncs []apidef.MiddlewareDefinition)
References: storage.RedisCluster, strings.HasSuffix.
func customDialTLSCheck
customDialTLSCheck (spec *APISpec, tc *tls.Config) func(network, addr string) (net.Conn, error)
References: errors.New, net.Conn, net.SplitHostPort, tls.Dial.
func determineHealthStatus
determineHealthStatus (failCount int, criticalFailure bool, totalChecks int) (HealthCheckStatus, int)
References: http.StatusOK, http.StatusServiceUnavailable.
func dialWithServiceDiscovery
dialWithServiceDiscovery (spec *APISpec, dial dialFn) dialFn
References: net.Conn, url.Parse.
func doAddOrUpdate
doAddOrUpdate (keyName string, newSession *user.SessionState, dontReset bool, isHashed bool) error
References: errors.New, logrus.Fields, strconv.Itoa, time.Now.
func evaluateHealthChecks
evaluateHealthChecks (checks map[string]HealthCheckItem) (int, bool)
func findInternalHttpHandlerByNameOrID
findInternalHttpHandlerByNameOrID (apiNameOrID string) (http.Handler, *APISpec, bool)
func flushNetworkAnalytics
flushNetworkAnalytics (ctx context.Context)
References: analytics.AnalyticsRecord, time.NewTicker, time.Second.
func forceResponse
forceResponse (w http.ResponseWriter, r *http.Request, newResponseData *VMResponseObject, spec *APISpec, session *user.SessionState, isPre bool, logger *logrus.Entry) *http.Response
References: bytes.NewReader, http.Response, http.TimeFormat, strings.HasPrefix, time.Now, url.Parse.
func fuzzyFindAPI
fuzzyFindAPI (search string) *APISpec
References: strings.EqualFold.
func gatherHealthChecks
gatherHealthChecks ()
References: errors.New, rpc.Login, storage.RedisCluster, sync.WaitGroup, time.Now, time.RFC3339.
func generateSessionFromPolicy
generateSessionFromPolicy (policyID,orgID string, enforceOrg bool) (user.SessionState, error)
References: errors.New, time.Now, user.AccessDefinition, user.SessionState.
func generateSubRoutes
generateSubRoutes (spec *APISpec, router *mux.Router, logger *logrus.Entry)
References: cors.New, cors.Options.
func generateToken
generateToken (orgID,keyID string, customHashKeyFunction ...string) string
References: logrus.Fields, storage.GenerateToken, strings.TrimPrefix.
func getAPIDefinition
getAPIDefinition (apiID string) (*apidef.APIDefinition, error)
References: errors.New.
func getApiClients
getApiClients (apiID string) ([]ExtendedOsinClientInterface, apiStatusMessage, int)
References: http.StatusNotFound, http.StatusOK, logrus.Fields.
func getApiSpec
getApiSpec (apiID string) *APISpec
func getApisForOauthApp
getApisForOauthApp (w http.ResponseWriter, r *http.Request)
References: http.StatusOK, mux.Vars.
func getApisForOauthClientId
getApisForOauthClientId (oauthClientId string, orgId string) []string
References: http.StatusOK.
func getApisIdsForOrg
getApisIdsForOrg (orgID string) []string
func getBundleDestPath
getBundleDestPath (spec *APISpec) string
References: filepath.Join.
func getExistingConfig
getExistingConfig () (map[string]interface{}, error)
References: json.NewDecoder, os.Open.
func getExplicitLogEntryForRequest
getExplicitLogEntryForRequest (logger *logrus.Entry, path string, IP string, key string, data map[string]interface{}) *logrus.Entry
References: logrus.Fields.
func getGlobalMDCBStorageHandler
getGlobalMDCBStorageHandler (keyPrefix string, hashKeys bool) storage.Handler
References: logrus.Fields, logrus.New, storage.NewMdcbStorage, storage.RedisCluster.
func getGlobalStorageHandler
getGlobalStorageHandler (keyPrefix string, hashKeys bool) storage.Handler
References: storage.RedisCluster.
func getHashedBundleName
getHashedBundleName (bundleName string) (string, error)
References: fmt.Sprintf, io.WriteString, md5.New.
func getHealthCheckInfo
getHealthCheckInfo () map[string]HealthCheckItem
func getHostDetails
getHostDetails ()
References: os.Getpid, os.Hostname.
func getLogEntryForRequest
getLogEntryForRequest (logger *logrus.Entry, r *http.Request, key string, data map[string]interface{}) *logrus.Entry
References: logrus.Fields, logrus.NewEntry, request.RealIP.
func getOauthClientDetails
Get client details
getOauthClientDetails (keyName,apiID string) (interface{}, int)
References: http.StatusNotFound, http.StatusOK, logrus.Fields, osin.ServerConfig, storage.RedisCluster.
func getOauthClients
List Clients
getOauthClients (apiID string) (interface{}, int)
References: http.StatusOK, logrus.Fields.
func getPinnedPublicKeys
getPinnedPublicKeys (host string, spec *APISpec, conf config.Config) []string
References: strings.Split, strings.SplitN.
func getSessionAndCreate
getSessionAndCreate (keyName string, r *RPCStorageHandler, isHashed bool, orgId string)
References: storage.HashKey.
func getSpecForOrg
getSpecForOrg (orgID string) *APISpec
func getTLSConfigForClient
getTLSConfigForClient (baseConfig *tls.Config, listenPort int) func(hello *tls.ClientHelloInfo) (*tls.Config, error)
References: apidef.AuthTokenType, cache.DefaultExpiration, certs.CertificatePrivate, certs.CertificatePublic, crypto.IsPublicKey, http.Request, mux.NewRouter, mux.RouteMatch, strconv.Itoa, sync.Once, time.Millisecond, time.Sleep, tls.Certificate, tls.ClientAuthType, tls.ClientHelloInfo, tls.Config, tls.LoadX509KeyPair, tls.NoClientCert, tls.RequestClientCert, tls.RequireAndVerifyClientCert, url.URL, x509.NewCertPool.
func getTagHash
getTagHash () string
func getUpstreamCertificate
getUpstreamCertificate (host string, spec *APISpec) *tls.Certificate
References: certs.CertificatePrivate, strings.SplitN.
func gracefulShutdown
gracefulShutdown performs a graceful shutdown of all services
gracefulShutdown (ctx context.Context) error
References: fmt.Errorf, http.Server, sync.WaitGroup.
func groupResetHandler
groupResetHandler (w http.ResponseWriter, r *http.Request)
References: http.StatusOK, logrus.Fields.
func grpcCallOpts
grpcCallOpts () grpc.DialOption
References: grpc.CallOption, grpc.MaxCallRecvMsgSize, grpc.MaxCallSendMsgSize, grpc.WithDefaultCallOptions.
func handleAddApi
handleAddApi (r *http.Request, fs afero.Fs, oasEndpoint bool) (interface{}, int)
References: apidef.APIDefinition, apidef.DefaultAPIVersionKey, apidef.HeaderLocation, apidef.Self, http.StatusBadRequest, http.StatusOK, json.Marshal, json.NewDecoder, json.Unmarshal, oas.OAS.
func handleAddKey
handleAddKey (keyName,sessionString,orgId string)
References: json.Unmarshal, logrus.Fields, strconv.Itoa, time.Now, user.SessionState.
func handleAddOrUpdate
handleAddOrUpdate (keyName string, r *http.Request, isHashed bool) (interface{}, int)
References: bytes.NewReader, http.MethodPost, http.MethodPut, http.StatusBadRequest, http.StatusInternalServerError, http.StatusNotFound, http.StatusOK, ioutil.NopCloser, ioutil.ReadAll, json.Unmarshal, storage.HashKey, storage.TokenOrg, time.Now, time.Unix, user.SessionState.
func handleAddOrUpdatePolicy
handleAddOrUpdatePolicy (polID string, r *http.Request) (interface{}, int)
References: filepath.Join, http.MethodPost, http.MethodPut, http.StatusBadRequest, http.StatusInternalServerError, http.StatusOK, ioutil.WriteFile, json.MarshalIndent, json.NewDecoder, user.Policy.
func handleDashboardZeroConfMessage
handleDashboardZeroConfMessage (payload string)
References: json.Unmarshal.
func handleDeleteAPI
handleDeleteAPI (apiID string) (interface{}, int)
References: afero.NewOsFs, apidef.ErrAPINotFound, filepath.Clean, filepath.Join, http.StatusInternalServerError, http.StatusNotFound, http.StatusOK, json.Marshal, json.Unmarshal, os.Remove, os.Stat.
func handleDeleteHashedKey
handleDeleteHashedKey (keyName,orgID,apiID string, resetQuota bool) (interface{}, int)
References: http.StatusBadRequest, http.StatusNotFound, http.StatusOK.
func handleDeleteHashedKeyWithLogs
handleDeleteHashedKeyWithLogs is a wrapper for handleDeleteHashedKey with logs
handleDeleteHashedKeyWithLogs (keyName,orgID,apiID string, resetQuota bool) (interface{}, int)
References: http.StatusOK, logrus.Fields.
func handleDeleteKey
handleDeleteKey (keyName,orgID,apiID string, resetQuota bool) (interface{}, int)
References: http.StatusBadRequest, http.StatusNotFound, http.StatusOK, logrus.Fields.
func handleDeleteOAuthClient
Delete Client
handleDeleteOAuthClient (keyName,apiID string) (interface{}, int)
References: http.StatusInternalServerError, http.StatusNotFound, http.StatusOK, logrus.Fields.
func handleDeleteOrgKey
handleDeleteOrgKey (orgID string) (interface{}, int)
References: http.StatusBadRequest, http.StatusNotFound, http.StatusOK, logrus.Fields.
func handleDeletePolicy
handleDeletePolicy (polID string) (interface{}, int)
References: filepath.Join, http.StatusInternalServerError, http.StatusOK, os.Remove, os.Stat.
func handleForcedResponse
handleForcedResponse (rw http.ResponseWriter, res *http.Response, ses *user.SessionState, spec *APISpec)
References: header.XRateLimitLimit, header.XRateLimitRemaining, header.XRateLimitReset, io.Copy, strconv.Itoa.
func handleGetAPI
handleGetAPI (apiID string, oasEndpoint bool) (interface{}, int)
References: apidef.ErrAPINotFound, apidef.ErrOASGetForOldAPI, fmt.Sprintf, http.StatusBadRequest, http.StatusNotFound, http.StatusOK, logrus.Fields.
func handleGetAPIList
handleGetAPIList () (interface{}, int)
References: apidef.APIDefinition, http.StatusOK.
func handleGetAPIListOAS
handleGetAPIListOAS (modePublic bool) (interface{}, int)
References: http.StatusOK, oas.OAS.
func handleGetAPIOAS
handleGetAPIOAS (apiID string, modePublic bool) (interface{}, int)
References: oas.OAS.
func handleGetAllKeys
handleGetAllKeys (filter string) (interface{}, int)
References: base64.NoPadding, base64.StdEncoding, fmt.Sprintf, http.StatusOK, logrus.Fields, strings.HasPrefix.
func handleGetAllOrgKeys
handleGetAllOrgKeys (filter string) (interface{}, int)
References: http.StatusNotFound, http.StatusOK, strings.HasPrefix.
func handleGetDetail
handleGetDetail (sessionKey,apiID,orgID string, byHash bool) (interface{}, int)
References: errors.Is, http.StatusBadRequest, http.StatusNotFound, http.StatusOK, logrus.Fields, redis.Nil, storage.HashKey, storage.TokenOrg, strconv.Atoi.
func handleGetOrgDetail
handleGetOrgDetail (orgID string) (interface{}, int)
References: http.StatusNotFound, http.StatusOK, logrus.Fields.
func handleGetPolicy
handleGetPolicy (polID string) (interface{}, int)
References: http.StatusNotFound, http.StatusOK, logrus.Fields.
func handleGetPolicyList
handleGetPolicyList () (interface{}, int)
References: http.StatusOK, user.Policy.
func handleGetSortedSetRange
handleGetSortedSetRange (keyName,scoreFrom,scoreTo string) ([]string, []float64, error)
func handleGlobalAddToSortedSet
handleGlobalAddToSortedSet (keyName,value string, score float64)
func handleKeySpaceEventCacheFlush
handleKeySpaceEventCacheFlush (payload string)
References: strings.Split.
func handleNewConfiguration
handleNewConfiguration (payload string)
References: config.Load, json.Unmarshal, logrus.Fields, syscall.Kill, syscall.SIGUSR2.
func handleOrgAddOrUpdate
handleOrgAddOrUpdate (orgID string, r *http.Request) (interface{}, int)
References: http.MethodPost, http.StatusBadRequest, http.StatusInternalServerError, http.StatusNotFound, http.StatusOK, json.NewDecoder, logrus.Fields, storage.HashKey, strconv.Itoa, time.Now, user.SessionState.
func handleRedisEvent
handleRedisEvent (v interface{}, handled func(NotificationCommand), reloaded func())
References: json.Unmarshal, temporalmodel.Message, temporalmodel.MessageTypeMessage.
func handleRemoveSortedSetRange
handleRemoveSortedSetRange (keyName,scoreFrom,scoreTo string) error
func handleSendMiniConfig
handleSendMiniConfig (payload string)
References: json.Marshal, json.Unmarshal, logrus.Fields, time.Now.
func handleUpdateApi
handleUpdateApi (apiID string, r *http.Request, fs afero.Fs, oasEndpoint bool) (interface{}, int)
References: apidef.APIDefinition, apidef.ErrAPINotFound, apidef.ErrAPINotMigrated, apidef.ErrClassicAPIExpected, http.StatusBadRequest, http.StatusNotFound, http.StatusOK, json.NewDecoder, oas.OAS.
func handleUpdateHashedKey
handleUpdateHashedKey (keyName string, applyPolicies []string) (interface{}, int)
References: http.StatusInternalServerError, http.StatusNotFound, http.StatusOK, logrus.Fields, strconv.Itoa, time.Now.
func handleUserKeyReset
handleUserKeyReset processes a user key reset notification
handleUserKeyReset (payload string)
References: strings.Split.
func healthCheckhandler
healthCheckhandler (w http.ResponseWriter, r *http.Request)
References: http.StatusBadRequest, http.StatusNotFound, http.StatusOK.
func initGenericEventHandlers
initGenericEventHandlers ()
References: apidef.TykEvent, config.TykEventHandler.
func initHealthCheck
initHealthCheck (ctx context.Context)
References: context.Context, logrus.Fields, time.NewTicker, time.Second.
func initNormalisationPatterns
initNormalisationPatterns () config.NormaliseURLPatterns
References: regexp.Compile, regexp.MustCompile.
func initSystem
initSystem () error
References: cli.Conf, cli.DebugMode, cli.Port, config.Config, config.DefaultDashPolicyRecordName, config.DefaultDashPolicySource, config.Global, config.Load, context.Background, context.WithTimeout, gorpc.SetErrorLogger, ioutil.Discard, logger.NewFormatter, logrus.DebugLevel, logrus.ErrorLevel, logrus.WarnLevel, os.Getenv, rpc.Instrument, rpc.Log, stdlog.SetOutput, strconv.Atoi, strings.ToLower, time.Second, tls.VersionTLS12.
func initialiseClient
initialiseClient () *http.Client
References: http.Client, http.Transport, time.Duration, time.Second, tls.Config.
func invalidateAPICache
invalidateAPICache (apiID string) bool
References: fmt.Sprintf, storage.RedisCluster.
func invalidateCacheHandler
invalidateCacheHandler (w http.ResponseWriter, r *http.Request)
References: errors.New, http.StatusInternalServerError, http.StatusOK, logrus.Fields, mux.Vars.
func invalidateOauthRefresh
invalidateOauthRefresh (w http.ResponseWriter, r *http.Request)
References: http.StatusBadRequest, http.StatusInternalServerError, http.StatusNotFound, http.StatusOK, logrus.Fields, mux.Vars.
func isCriticalFailure
isCriticalFailure (component string) bool
func isRPCMode
isRPCMode () bool
func isRunningTests
isRunningTests () bool
func keyHandler
keyHandler (w http.ResponseWriter, r *http.Request)
References: http.MethodDelete, http.MethodGet, http.MethodPost, http.MethodPut, http.StatusNotFound, http.StatusOK, mux.Vars.
func kvStore
kvStore (value string) (string, error)
References: fmt.Errorf, fmt.Sprintf, os.Getenv, strings.HasPrefix, strings.ToUpper, strings.TrimPrefix.
func liveCheckHandler
liveCheckHandler (w http.ResponseWriter, r *http.Request)
References: fmt.Sprintf, header.ApplicationJSON, http.MethodGet, http.StatusMethodNotAllowed, http.StatusText, json.NewEncoder.
func loadApps
Create the individual API (app) specs based on live configurations and assign middleware
loadApps (specs []*APISpec)
References: http.HandlerFunc, mux.NewRouter, sync.Map, trace.AddTracer, trace.IsEnabled.
func loadBundle
loadBundle wraps the load and save steps, it will return if an error occurs at any point.
loadBundle (spec *APISpec) error
References: logrus.Fields, os.MkdirAll, os.RemoveAll, os.Stat.
func loadControlAPIEndpoints
loadControlAPIEndpoints loads the endpoints used for controlling the Gateway.
loadControlAPIEndpoints (muxer *mux.Router)
References: cli.HTTPProfile, http.MethodDelete, http.MethodGet, http.MethodPatch, http.MethodPost, http.MethodPut, http.StripPrefix, mux.NewRouter, pprofhttp.Index, pprofhttp.Profile.
func loadCustomMiddleware
loadCustomMiddleware (spec *APISpec) ([]string, apidef.MiddlewareDefinition, []apidef.MiddlewareDefinition, []apidef.MiddlewareDefinition, []apidef.MiddlewareDefinition, []apidef.MiddlewareDefinition, apidef.MiddlewareDriver)
References: apidef.MiddlewareDefinition, apidef.MiddlewareDriver, filepath.Base, filepath.Glob, filepath.Join, strings.HasSuffix, strings.Split.
func loadGlobalApps
loadGlobalApps ()
func loadGraphQLPlayground
loadGraphQLPlayground (spec *APISpec, subrouter *mux.Router)
References: fmt.Sprintf, http.MethodGet, http.Request, http.ResponseWriter, http.StatusInternalServerError, path.Join.
func loadHTTPService
loadHTTPService has two responsibilities:
- register gorilla/mux routing handless with proxyMux directly (wrapped),
- return a raw http.Handler for tyk://ID urls.
loadHTTPService (spec *APISpec, apisByListen map[string]int, gs *generalStores, muxer *proxyMux) (*ChainObject, error)
References: fmt.Errorf, httputil.ValidatePath, logrus.NewEntry, mux.NewRouter, newrelic.Mount.
func loadTCPService
loadTCPService (spec *APISpec, gs *generalStores, muxer *proxyMux)
func logPubSubError
isPubSubError returns true if err != nil, logs error
logPubSubError (err error, message string) bool
func makeImportedOASTykAPI
makeImportedOASTykAPI (next http.HandlerFunc) http.HandlerFunc
References: bytes.NewReader, http.Request, http.ResponseWriter, http.StatusBadRequest, ioutil.NopCloser, oas.GetTykExtensionConfigParams, oas.TykExtensionConfigParams.
func mwAppendEnabled
mwAppendEnabled (chain *[]alice.Constructor, mw TykMiddleware) bool
func mwList
mwList (mws ...TykMiddleware) []alice.Constructor
References: alice.Constructor.
func newRedisHook
newRedisHook () *redisChannelHook
References: logrus.JSONFormatter, storage.RedisCluster.
func nextTarget
nextTarget (targetData *apidef.HostList, spec *APISpec) (string, error)
References: fmt.Errorf.
func oAuthClientHandler
oAuthClientHandler (w http.ResponseWriter, r *http.Request)
References: http.MethodDelete, http.MethodGet, http.MethodPut, mux.Vars.
func oAuthClientTokensHandler
oAuthClientTokensHandler (w http.ResponseWriter, r *http.Request)
References: http.StatusInternalServerError, http.StatusNotFound, http.StatusOK, logrus.Fields, mux.Vars, strconv.Atoi.
func oAuthTokensHandler
oAuthTokensHandler (w http.ResponseWriter, r *http.Request)
References: http.StatusBadRequest, http.StatusInternalServerError, http.StatusOK, http.StatusUnprocessableEntity.
func obfuscateKey
obfuscateKey (keyName string) string
func onServerStatusReceivedHandler
onServerStatusReceivedHandler (payload string)
References: drl.Server, json.Unmarshal, logrus.Fields.
func orgHandler
orgHandler (w http.ResponseWriter, r *http.Request)
References: mux.Vars.
func polHandler
polHandler (w http.ResponseWriter, r *http.Request)
References: http.MethodDelete, http.MethodGet, http.MethodPost, http.MethodPut, http.StatusBadRequest, mux.Vars.
func policyUpdateHandler
policyUpdateHandler (w http.ResponseWriter, r *http.Request)
References: http.StatusBadRequest, json.NewDecoder, mux.Vars.
func populateHostListByApiSpec
populateHostListByApiSpec (hostList *[]HostData, spec *APISpec)
References: logrus.Fields.
func preLoadVirtualMetaCode
preLoadVirtualMetaCode (meta *apidef.VirtualMeta, j *JSVM)
References: apidef.UseBlob, apidef.UseFile, base64.StdEncoding, os.Open.
func prepareStorage
prepareStorage () generalStores
References: storage.RedisCluster.
func previewKeyHandler
previewKeyHandler (w http.ResponseWriter, r *http.Request)
References: http.StatusInternalServerError, http.StatusOK, json.NewDecoder, logrus.Fields, strconv.Itoa, time.Now, user.SessionState.
func processSpec
processSpec (spec *APISpec, apisByListen map[string]int, gs *generalStores, logger *logrus.Entry) *ChainObject
References: alice.Constructor, alice.New, apidef.GoPluginDriver, apidef.MiddlewareDefinition, apidef.MiddlewareDriver, apidef.NewHostListFromList, apidef.OttoDriver, coprocess.HookType_CustomKeyCheck, coprocess.HookType_Post, coprocess.HookType_PostKeyAuth, coprocess.HookType_Pre, filepath.Join, http.Handler, http.HandlerFunc, logrus.Fields, otel.ApidefSpanAttributes, otel.HTTPHandler, otel.SpanAttribute, path.Join, rpc.IsEmergencyMode, storage.RedisCluster, strings.ToLower, tls.VersionTLS12, trace.Handle, trace.IsEnabled, url.Parse.
func purgeLapsedOAuthTokens
purgeLapsedOAuthTokens () error
References: internalerrors.Formatter, multierror.Append, multierror.Error, storage.RedisCluster, strconv.FormatInt, sync.WaitGroup, time.Minute, time.Now.
func reLogin
reLogin ()
References: time.Second, time.Sleep.
func readGraphqlPlaygroundTemplate
readGraphqlPlaygroundTemplate ()
References: filepath.Join, ioutil.ReadDir, logrus.Fields, texttemplate.ParseFiles.
func readinessHandler
readinessHandler is a dedicated endpoint for readiness probes It checks if the gateway is ready to serve requests by verifying:
- Redis connection status
- API definitions loaded successfully Unlike liveCheckHandler which always returns 200 OK, readinessHandler returns 503 Service Unavailable if the gateway is not ready to serve requests
readinessHandler (w http.ResponseWriter, r *http.Request)
References: header.ApplicationJSON, http.MethodGet, http.StatusMethodNotAllowed, http.StatusOK, http.StatusServiceUnavailable, http.StatusText, json.NewEncoder.
func recordTCPHit
nolint
recordTCPHit (specID string, doNotTrack bool) func(tcp.Stat)
References: atomic.AddInt64, tcp.Closed, tcp.Open, tcp.Stat.
func reloadLoop
reloadLoop (tick <-chan time.Time, complete ...func())
References: time.Now, time.Since.
func reloadQueueLoop
reloadQueueLoop (cb ...func())
func reloadURLStructure
reloadURLStructure will queue an API reload. The reload will eventually create a new muxer, reload all the app configs for an instance and then replace the DefaultServeMux with the new one. This enables a reconfiguration to take place without stopping any requests from being handled.
done will be called when the reload is finished. Note that if a reload is already queued, another won't be queued, but done will still be called when said queued reload is finished.
reloadURLStructure (done func())
func replaceVariables
replaceVariables (in string, vars []string, vals map[string]interface{}, label string, escape bool) string
References: fmt.Sprintf, logrus.Fields, os.Getenv, strings.HasPrefix, strings.Replace, strings.ToUpper, url.QueryEscape.
func resetHandler
resetHandler will try to queue a reload. If fn is nil and block=true was in the URL parameters, it will block until the reload is done. Otherwise, it won't block and fn will be called once the reload is finished.
resetHandler (fn func()) http.HandlerFunc
References: http.Request, http.ResponseWriter, http.StatusOK, logrus.Fields, sync.WaitGroup.
func responseMWAppendEnabled
responseMWAppendEnabled (chain *[]TykResponseHandler, responseMW TykResponseHandler) bool
func responseProcessorByName
responseProcessorByName (name string, baseHandler BaseTykResponseHandler) TykResponseHandler
func rotateOauthClient
rotateOauthClient (keyName,apiID string) (interface{}, int)
References: http.StatusInternalServerError, http.StatusNotFound, http.StatusOK, logrus.Fields.
func rotateOauthClientHandler
rotateOauthClientHandler (w http.ResponseWriter, r *http.Request)
References: mux.Vars.
func rpcReloadLoop
rpcReloadLoop (rpcKey string)
func saveRPCDefinitionsBackup
saveRPCDefinitionsBackup (list string) error
References: crypto.Encrypt, crypto.GetPaddedString, errors.New, json.Valid, storage.RedisCluster.
func saveRPCPoliciesBackup
saveRPCPoliciesBackup (list string) error
References: crypto.Encrypt, crypto.GetPaddedString, errors.New, json.Valid, storage.RedisCluster.
func schemaHandler
schemaHandler (w http.ResponseWriter, r *http.Request)
References: http.MethodGet, http.StatusNotFound, http.StatusOK, oas.GetOASSchema.
func setBasicAuthSessionPassword
setBasicAuthSessionPassword (session *user.SessionState)
References: bcrypt.GenerateFromPassword, storage.HashStr, user.HashBCrypt, user.HashPlainText, user.HashType.
func setCurrentHealthCheckInfo
setCurrentHealthCheckInfo (h map[string]model.HealthCheckItem)
func setTestMode
setTestMode (v bool)
func setUpConsul
setUpConsul () error
References: kv.NewConsul.
func setUpVault
setUpVault () error
References: kv.NewVault.
func setupGlobals
Create all globals and init connection handlers
setupGlobals ()
References: certs.NewCertificateManager, certs.NewSlaveCertManager, checkup.Run, dnscache.NewDnsCacheManager, filepath.Join, htmltemplate.Must, htmltemplate.ParseGlob, rpc.Purger, serializer.PROTOBUF_SERIALIZER, storage.RedisCluster, texttemplate.Must, texttemplate.ParseGlob, time.Duration, time.Second.
func setupInstrumentation
setupInstrumentation handles all the initialisation of the instrumentation handler
setupInstrumentation ()
References: cli.LogInstrumentation, os.Getenv.
func setupLogger
setupLogger ()
References: gas.Dial, grayloghook.NewGraylogHook, logrus.ErrorLevel, logrus.FatalLevel, logrus.Fields, logrus.Hook, logrus.Level, logrus.PanicLevel, logrussentry.NewSentryHook, logrussyslog.NewSyslogHook, logstashhook.DefaultFormatter, logstashhook.New, net.Conn, net.Dial, syslog.LOG_INFO.
func setupPortsWhitelist
setupPortsWhitelist ()
References: config.PortWhiteList.
func shouldReload
shouldReload returns true if we should perform any reload. Reloads happens if we have reload callback queued.
shouldReload () ([]func(), bool)
func skipSpecBecauseInvalid
skipSpecBecauseInvalid (spec *APISpec, logger *logrus.Entry) bool
References: strings.Contains, url.Parse.
func start
start ()
References: scheduler.NewJob, scheduler.NewScheduler, time.Duration, time.Second, time.Tick.
func startDRL
startDRL ()
References: drl.DRL.
func startPubSubLoop
startPubSubLoop ()
References: storage.RedisCluster, time.Second.
func startRateLimitNotifications
startRateLimitNotifications ()
References: time.Duration, time.Second, time.Sleep.
func startServer
startServer ()
References: mux.NewRouter.
func syncAPISpecs
syncAPISpecs () (int, error)
func syncPolicies
syncPolicies () (int, error)
References: user.Policy.
func traceHandler
Tracing request Used to test API definition by sending sample request, and analysisng output of both response and logs
requestBody:
content:
application/json:
schema:
"$ref": "#/definitions/traceRequest"
examples:
request:
method: GET
path: /get
headers:
Authorization: key
spec:
api_name: "Test"
responses:
200:
description: Success tracing request
schema:
"$ref": "#/definitions/traceResponse"
examples:
message: "ok"
response:
code: 200
headers:
Header: value
body: body-value
logs: {...}\n{...}
traceHandler (w http.ResponseWriter, r *http.Request)
References: apidef.APIDefinition, bytes.Buffer, http.StatusBadRequest, http.StatusInternalServerError, http.StatusOK, httptest.NewRecorder, httputil.DumpRequest, httputil.DumpResponse, json.NewDecoder, logrus.DebugLevel, logrus.JSONFormatter, logrus.New, logrus.NewEntry, model.MergedAPI, mux.NewRouter.
func updateKeyInStore
updateKeyInStore updates the API key in the specified KV store
updateKeyInStore (keyPath,newKey string)
References: kv.Store, strings.HasPrefix, strings.TrimPrefix.
func updateOauthClient
Update Client
updateOauthClient (keyName,apiID string, r *http.Request) (interface{}, int)
References: http.StatusBadRequest, http.StatusInternalServerError, http.StatusNotFound, http.StatusOK, json.NewDecoder, logrus.Fields.
func urlRewrite
urlRewrite (meta *apidef.URLRewriteMeta, r *http.Request) (string, error)
References: apidef.Any, fmt.Errorf, regexp.Compile, strconv.Itoa, strings.Replace, url.PathUnescape.
func validateOAS
validateOAS (next http.HandlerFunc) http.HandlerFunc
References: apidef.ErrImportWithTykExtension, apidef.ErrPayloadWithoutTykExtension, bytes.NewReader, http.MethodPost, http.MethodPut, http.Request, http.ResponseWriter, http.StatusBadRequest, ioutil.NopCloser, oas.GetValidationOptionsFromConfig, oas.ValidateOASObject, strings.HasSuffix.
func validatePublicKeys
validatePublicKeys (host string, conn *tls.Conn, spec *APISpec) bool
References: crypto.HexSHA256, x509.MarshalPKIXPublicKey.
func verifyPeerCertificatePinnedCheck
verifyPeerCertificatePinnedCheck (spec *APISpec, tlsConfig *tls.Config) func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error
References: crypto.HexSHA256, errors.New, x509.Certificate, x509.MarshalPKIXPublicKey, x509.ParseCertificate.
func writeOASAndAPIDefToFile
writeOASAndAPIDefToFile (fs afero.Fs, apiDef *apidef.APIDefinition, oasObj *oas.OAS) (error, int)
func writeToFile
writeToFile (fs afero.Fs, newDef interface{}, filename string) (error, int)
References: errors.New, filepath.Join, http.StatusInternalServerError, ioutil.WriteFile, json.MarshalIndent.
func loadAnalyticsPlugin
loadAnalyticsPlugin () bool
References: goplugin.GetAnalyticsHandler, logrus.Fields.
func processRecord
processRecord (record *analytics.AnalyticsRecord) error
References: errors.New, fmt.Sprint, time.Now, time.Since.
func goPluginFromRequest
goPluginFromRequest (r *http.Request) (*GoPluginMiddleware, bool)
func loadPlugin
loadPlugin loads the plugin file from m.Path, it will try converting it to the tyk version aware format: {plugin_name}{tyk_version}{os}_{arch}.so if the file doesn't exist then it will again but with a gw version that is not prefixed by 'v' later, it will try with m.path which can be {plugin_name}.so
loadPlugin () bool
References: fmt.Errorf, goplugin.FileSystemStorage, goplugin.GetHandler, goplugin.GetPluginFileNameToLoad, logrus.Fields.
func block
block (logger *logrus.Entry) (error, int)
References: errors.New, http.StatusForbidden.
func pass
pass () (error, int)
References: http.StatusOK.
func handleComplexityFailReason
handleComplexityFailReason (failReason ComplexityFailReason) (error, int)
References: http.StatusForbidden, http.StatusInternalServerError, http.StatusOK.
func checkForUnsupportedUsage
checkForUnsupportedUsage () error
References: errors.New.
func isGraphQLConfigVersion1
isGraphQLConfigVersion1 () bool
References: apidef.GraphQLConfigVersion1, apidef.GraphQLConfigVersionNone.
func isSupergraphAPIDefinition
isSupergraphAPIDefinition () bool
References: apidef.GraphQLExecutionModeSupergraph.
func websocketUpgradeAllowed
websocketUpgradeAllowed () bool
References: apidef.GraphQLConfigVersion1, apidef.GraphQLConfigVersionNone.
func websocketUpgradeUsesGraphQLProtocol
websocketUpgradeUsesGraphQLProtocol (r *http.Request) bool
References: gqlwebsocket.ProtocolGraphQLTransportWS, gqlwebsocket.ProtocolGraphQLWS, header.SecWebSocketProtocol.
func validateFieldRestrictions
validateFieldRestrictions (gqlRequest *graphql.Request, fieldRestrictionList graphql.FieldRestrictionList, schema *graphql.Schema) GraphqlGranularAccessResult
References: graphql.DefaultFieldsValidator.
func isHeartBeatStopped
isHeartBeatStopped () bool
References: atomic.LoadInt32.
func newRequest
newRequest (method,endpoint string) *http.Request
References: header.XTykHostname, header.XTykSessionID, http.NewRequest.
func sendHeartBeat
sendHeartBeat (req *http.Request, client *http.Client) error
References: errors.New, header.XTykNodeID, header.XTykNonce, http.StatusForbidden, http.StatusOK, json.NewDecoder.
func authorizationError
authorizationError (r *http.Request) (error, int)
References: errors.New, http.StatusBadRequest.
func getAuthType
getAuthType overrides BaseMiddleware.getAuthType.
getAuthType () string
References: apidef.HMACType.
func getRSACertificateIdAndSessionForKeyID
getRSACertificateIdAndSessionForKeyID (r *http.Request, keyId string) (string, user.SessionState, error)
References: errors.New.
func getSecretAndSessionForKeyID
getSecretAndSessionForKeyID (r *http.Request, keyId string) (string, user.SessionState, error)
References: errors.New.
func hasLowerCaseEscaped
hasLowerCaseEscaped (signature string) (bool, []string)
func replaceWithUpperCase
replaceWithUpperCase (originalSignature string, lowercaseList []string) string
References: strings.Replace, strings.ToUpper.
func setContextVars
setContextVars (r *http.Request, token string)
func checkPollerLoop
checkPollerLoop (ctx context.Context)
References: logrus.Fields.
func getHostKey
getHostKey (report HostHealthReport) string
func execCheck
execCheck ()
References: errors.Is, tunny.ErrPoolNotRunning.
func getStaggeredTime
getStaggeredTime () time.Duration
References: mathrand.Intn, mathrand.Seed, time.Duration, time.Now, time.Second.
func isOpen
isOpen () bool
References: atomic.LoadInt32.
func handleError
handleError (r *http.Request, blacklistedIP string) (error, int)
References: errors.New, http.StatusForbidden.
func getAuthType
getAuthType overrides BaseMiddleware.getAuthType.
getAuthType () string
References: apidef.JWTType.
func getBasePolicyID
getBasePolicyID (r *http.Request, claims jwt.MapClaims) (string, bool)
func getIdentityFromToken
getIdentityFromToken (token *jwt.Token) (string, error)
func getOAuthClientIDFromClaim
getOAuthClientIDFromClaim (claims jwt.MapClaims) string
func getPolicyIDFromToken
getPolicyIDFromToken (claims jwt.MapClaims) (string, bool)
func getSecretFromMultipleJWKURIs
getSecretFromMultipleJWKURIs (jwkURIs []apidef.JWK, kidVal interface{}, keyType string) (interface{}, error)
References: apidef.APIDefinition, apidef.JWK, cache.DefaultExpiration, errors.New.
func getSecretFromURL
getSecretFromURL (url string, kidVal interface{}, keyType string) (interface{}, error)
References: apidef.APIDefinition, base64.StdEncoding, cache.DefaultExpiration, errors.New.
func getSecretToVerifySignature
getSecretToVerifySignature (r *http.Request, token *jwt.Token) (interface{}, error)
References: base64.StdEncoding, errors.New.
func getUserIdFromClaim
getUserIdFromClaim (claims jwt.MapClaims) (string, error)
func legacyGetSecretFromURL
legacyGetSecretFromURL (url,kid,keyType string) (interface{}, error)
References: base64.StdEncoding, bytes.Contains, cache.DefaultExpiration, errors.New, http.Client, http.Transport, json.NewDecoder, strings.ToLower, tls.Config.
func processCentralisedJWT
processCentralisedJWT Will check a JWT token centrally against the secret stored in the API Definition.
processCentralisedJWT (r *http.Request, token *jwt.Token) (error, int)
References: apidef.JWTClaim, apidef.UnsetAuth, cache.DefaultExpiration, errors.New, fmt.Sprintf, http.StatusForbidden, http.StatusInternalServerError, http.StatusOK, md5.Sum, osin.ServerConfig, storage.RedisCluster.
func processOneToOneTokenMap
processOneToOneTokenMap (r *http.Request, token *jwt.Token) (error, int)
References: errors.New, http.StatusForbidden, http.StatusNotFound, http.StatusOK.
func reportLoginFailure
reportLoginFailure (tykId string, r *http.Request)
func specCacheKey
specCacheKey (spec *APISpec, prefix string) string
func timeValidateJWTClaims
timeValidateJWTClaims (c jwt.MapClaims) *jwt.ValidationError
func notifyReadOnly
notifyReadOnly () bool
func proxyForRequest
proxyForRequest (r *http.Request) *ReverseProxy
References: logrus.Fields.
func generateOAuthOutputFromOsinResponse
generateOAuthOutputFromOsinResponse (osinResponse *osin.Response) []byte
References: bytes.Buffer, json.NewEncoder.
func notifyClientOfNewOauth
notifyClientOfNewOauth (notification NewOAuthNotification)
func getAuthType
getAuthType overrides BaseMiddleware.getAuthType.
getAuthType () string
References: apidef.OAuthType.
func dummyErrorHandler
We don't want any of the error handling, we use our own
dummyErrorHandler (e error, w http.ResponseWriter, r *http.Request) bool
func getAuthType
getAuthType () string
References: apidef.OIDCType.
func getProviders
getProviders () ([]openid.Provider, error)
References: base64.StdEncoding, logrus.Fields, openid.NewProvider, openid.Provider.
func reportLoginFailure
reportLoginFailure (tykId string, r *http.Request)
References: logrus.Fields.
func getOrgHasNoSession
getOrgHasNoSession () bool
func setOrgHasNoSession
setOrgHasNoSession (val bool)
func buildNodeInfo
buildNodeInfo () []byte
References: json.Marshal, model.GWStats, model.HostDetails, model.NodeData, time.Second.
func cleanKey
cleanKey (keyName string) string
References: strings.Replace.
func deleteUsingTokenID
Function to handle fallback deletion using token ID
deleteUsingTokenID (key,orgId string, resetQuota bool, status int) (int, error)
References: http.StatusNotFound, storage.TokenID.
func fixKey
fixKey (keyName string) string
func getGroupLoginCallback
getGroupLoginCallback (synchroniserEnabled bool) func(userKey string, groupID string) interface{}
References: model.GroupLoginRequest, rpc.NewSyncForcer.
func hashKey
hashKey (in string) string
References: storage.HashStr.
func handleQuotaFailure
handleQuotaFailure (r *http.Request, token string) (error, int)
References: errors.New, http.StatusForbidden, request.RealIP.
func getSession
getSession (r *http.Request) *user.SessionState
References: fmt.Sprintf, storage.HashKey, storage.HashStr, user.SessionState.
func shouldEnable
shouldEnable () bool
func recordWorker
recordWorker ()
References: fmt.Sprintf, mathrand.Intn, mathrand.Seed, storage.HashKey, strings.HasPrefix, time.NewTimer, time.Now, time.Since.
func decodePayload
decodePayload (payload string) (string, string, error)
References: base64.StdEncoding, errors.New, strings.Split.
func getCacheKeyFromHeaders
getCacheKeyFromHeaders (r *http.Request) string
func isTimeStampExpired
isTimeStampExpired (timestamp string) bool
References: strconv.ParseInt, time.Now, time.Unix.
func getRequestPath
getRequestPath (r *http.Request) string
func isRequestSigningConfigValid
isRequestSigningConfigValid () bool
References: strings.HasPrefix.
func checkRequestLimit
checkRequestLimit (r *http.Request, sizeLimit int64) (error, int)
References: errors.New, header.ContentLength, http.StatusBadRequest, http.StatusOK, logrus.Fields, strconv.ParseInt.
func encodePayload
encodePayload (payload string, timestamp int64) string
References: base64.StdEncoding, fmt.Sprint.
func getTimeTTL
getTimeTTL (cacheTTL int64) int64
References: time.Now.
func addAuthInfo
addAuthInfo (outReq,req *http.Request)
References: core.GetUpstreamAuth.
func copyBuffer
copyBuffer (dst io.Writer, src io.Reader) (int64, error)
References: context.Canceled, errors.Is, io.EOF, io.ErrShortWrite, logrus.Fields.
func defaultTransport
defaultTransport (dialerTimeout float64) *http.Transport
References: http.Transport, net.Dialer, time.Duration, time.Second.
func flushInterval
flushInterval returns the p.FlushInterval value, conditionally overriding its value for a specific request/response.
flushInterval (res *http.Response) time.Duration
func handleGraphQL
handleGraphQL (roundTripper *TykRoundTripper, outreq *http.Request, w http.ResponseWriter) (*http.Response, bool, error)
References: apidef.RequestHeadersRewriteConfig, graphengine.ProxyOnlyHeadersConfig, graphengine.ReverseProxyHeadersConfig, graphengine.ReverseProxyParams, textproto.CanonicalMIMEHeaderKey.
func handleOutboundRequest
handleOutboundRequest (roundTripper *TykRoundTripper, outreq *http.Request, w http.ResponseWriter) (*http.Response, bool, time.Duration, error)
References: time.Now, time.Since.
func handleUpgradeResponse
handleUpgradeResponse (rw http.ResponseWriter, req *http.Request, res *http.Response) error
References: fmt.Errorf, http.Hijacker, io.ReadWriteCloser, ioutil.NopCloser, strings.NewReader.
func httpTransport
httpTransport (timeOut float64, rw http.ResponseWriter, req *http.Request, outReq *http.Request) *TykRoundTripper
References: http2.ConfigureTransport, http2.Transport, net.Conn, net.Dial, tls.Config, tls.RenegotiateFreelyAsClient.
func mockResponse
mockResponse (r *http.Request) (*http.Response, error)
References: bytes.NewBuffer, fmt.Errorf, header.ContentType, http.Header, http.Response, io.NopCloser, oas.Header.
func sendRequestToUpstream
sendRequestToUpstream (roundTripper *TykRoundTripper, outreq *http.Request) (*http.Response, error)
func setCommonNameVerifyPeerCertificate
setCommonNameVerifyPeerCertificate (tlsConfig *tls.Config, hostName string)
References: errors.New, time.Now, x509.Certificate, x509.NewCertPool, x509.ParseCertificate, x509.VerifyOptions.
func addPortFromObject
addPortFromObject (host string, obj *gabs.Container) string
References: strconv.Itoa.
func decodeToNameSpace
decodeToNameSpace (namespace string, jsonParsed *gabs.Container) interface{}
func decodeToNameSpaceAsArray
decodeToNameSpaceAsArray (namespace string, jsonParsed *gabs.Container) []*gabs.Container
func getServiceData
getServiceData (name string) (string, error)
References: http.Get, ioutil.ReadAll.
func isList
isList (val string) bool
References: strings.HasPrefix.
func rawListToObj
rawListToObj (rawData string) string
func doRollingWindowWrite
doRollingWindowWrite (r *http.Request, session *user.SessionState, rateLimiterKey string, apiLimit *user.APILimit, dryRun bool) bool
References: context.Context, rate.NewSlidingLogRedis, time.Duration, time.Now, time.Second.
func limitDRL
limitDRL (bucketKey string, apiLimit *user.APILimit, dryRun bool) bool
References: time.Duration, time.Now, time.Second.
func limitRedis
limitRedis (r *http.Request, session *user.SessionState, rateLimiterKey string, apiLimit *user.APILimit, dryRun bool) bool
func limitSentinel
limitSentinel (r *http.Request, session *user.SessionState, rateLimiterKey string, apiLimit *user.APILimit, dryRun bool) bool
func updateSessionQuota
updateSessionQuota updates session attached access rights.
When limits are defined, QuotaRemaining and QuotaRenews is updated for a matching access rights definition for an api, and the session root.
updateSessionQuota (session *user.SessionState, scope string, remaining int64, renews int64)
func flush
flush ()
func getPrefixBuffer
getPrefixBuffer (job,event,suffix string) prefixBuffer
References: bytes.Buffer.
func loop
loop ()
References: time.NewTicker.
func processCmd
processCmd (cmd *statsdEmitCmd)
func processComplete
processComplete (job string, status health.CompletionStatus, nanos int64)
func processEvent
processEvent (job,event,extra string)
func processGauge
processGauge (job,event string, value float64)
References: strconv.AppendFloat.
func processTiming
processTiming (job,event string, nanos int64)
func writeNanosToTimingBuf
writeNanosToTimingBuf (nanos int64)
References: strconv.AppendFloat, strconv.AppendInt, time.Millisecond.
func writeSanitizedKeys
writeSanitizedKeys (b *bytes.Buffer, keys ...string)
func writeStatsDMetric
assumes b is a well-formed statsd metric like "job.event:1|c\n" (including newline)
writeStatsDMetric (b []byte)
func stripFromHeaders
strips auth key from headers
stripFromHeaders (r *http.Request, config *apidef.AuthConfig)
References: strings.HasPrefix, strings.Join, strings.Split, strings.TrimSpace.
func stripFromParams
strips auth from query string params
stripFromParams (r *http.Request, config *apidef.AuthConfig)
References: url.Parse.
func controlProxy
controlProxy () *proxy
func getMainRouter
getMainRouter (m *proxyMux) *mux.Router
func mainProxy
mainProxy () *proxy
func mainRouter
mainRouter () *mux.Router
func newGateway
newGateway (genConf func(globalConf *config.Config)) *Gateway
References: cli.Init, config.Config, config.FillEnv, config.WriteDefault, context.Background, context.WithTimeout, filepath.Dir, filepath.Join, http.ErrServerClosed, http.Server, httputil.NewConnectionWatcher, ioutil.TempDir, mathrand.Intn, net.Listen, net.SplitHostPort, otel.InitOpenTelemetry, runtime.Caller, storage.HashMurmur64, strconv.Atoi, time.NewTicker, time.Second.
func reloadSimulation
simulate reloads in the background, i.e. writes to global variables that should not be accessed in a racy way like the policies and api specs maps.
reloadSimulation (ctx context.Context, gw *Gateway)
References: time.Millisecond, time.Sleep, user.Policy.
func start
start (genConf func(globalConf *config.Config)) *Gateway
References: context.Background, context.WithCancel, http.Request, test.HTTPTestRunner, test.HttpServerRunner, test.NewRequest, test.TestCase.
func testHttpHandler
testHttpHandler (gw *Gateway) *mux.Router
References: fmt.Sprintf, http.Error, http.HandlerFunc, http.Request, http.ResponseWriter, http.StatusInternalServerError, http.StatusMethodNotAllowed, http.StatusText, io.WriteString, ioutil.ReadAll, json.NewEncoder, mux.NewRouter, mux.Vars, strconv.Atoi, websocket.Upgrader.
func withAuth
withAuth (r *http.Request) *http.Request
func matchesMethod
matchesMethod checks if the given method matches the method required by the URLSpec for the current status.
matchesMethod (method string) bool
func matchesPath
matchesPath takes the input string and matches it against an internal regex. it will match the regex against the clean URL with stripped listen path first, then it will match against the full URL including the listen path as provided. APISpec to provide URL sanitization of the input is passed along.
matchesPath (reqPath string, api *APISpec) bool
func modeSpecificSpec
modeSpecificSpec returns the respective field of URLSpec if it matches the given mode. Deprecated: Usage should not increase.
modeSpecificSpec (mode URLStatus) (interface{}, bool)
func formatError
formatError (schemaErrors []gojsonschema.ResultError) error
References: errors.New.
func getMetaFromRequest
getMetaFromRequest (r *http.Request) *apidef.VirtualMeta
References: apidef.VirtualMeta.
func checkURL
checkURL (r string) bool
References: logrus.Fields, url.ParseRequestURI.
func getRequestMethod
getRequestMethod (m string) WebHookRequestMethod
References: logrus.Fields, strings.ToUpper.
func setHookFired
setHookFired will create an expiring key for the checksum of the event
setHookFired (checksum string)
References: logrus.Fields.
func getHttpResponse
getHttpResponse (r *http.Request) *http.Response
References: bytes.NewReader, http.Response, http.StatusText, ioutil.NopCloser.
func handleRequestLimits
handleRequestLimits (w http.ResponseWriter, r *http.Request) bool
References: http.MaxBytesReader, httputil.EntityTooLarge.
func copyHeader
copyHeader (dst,src http.Header)
func handleHTTP
handleHTTP (w http.ResponseWriter, req *http.Request)
References: http.DefaultTransport, http.Error, http.StatusServiceUnavailable, io.Copy.
func handleTunneling
handleTunneling (w http.ResponseWriter, r *http.Request)
References: http.Error, http.Hijacker, http.StatusInternalServerError, http.StatusOK, http.StatusServiceUnavailable, net.DialTimeout, time.Second.
func transfer
transfer (destination io.WriteCloser, source io.ReadCloser)
References: io.Copy.
func delayedFlush
delayedFlush ()
func stop
stop ()
func copy
copy creates a copy of the io.Reader when we read from it (lazy).
copy () error
References: io.Copy.
func addTCPService
addTCPService (spec *APISpec, modifier *tcp.Modifier, gw *Gateway)
References: logrus.Fields, net.Dial, tcp.Proxy.
func generateListener
generateListener (listenPort int, protocol string, gw *Gateway) (net.Listener, error)
References: http2.NextProtoTLS, net.Listen, strconv.Itoa, tls.Config, tls.Listen, tls.NoClientCert.
func getProxy
getProxy (listenPort int, conf config.Config) *proxy
func handle404
handle404 (w http.ResponseWriter, r *http.Request)
References: fmt.Fprint, fmt.Sprintf, http.StatusNotFound, http.StatusText.
func router
router (port int, protocol string, conf config.Config) *mux.Router
func serve
serve (gw *Gateway)
References: h2c.NewHandler, http.Server, http2.Server, net.SplitHostPort, strconv.Atoi, strconv.Itoa, time.Duration, time.Second.
func setRouter
setRouter (port int, protocol string, router *mux.Router, conf config.Config)
References: logrus.Fields.
func swap
swap (new *proxyMux, gw *Gateway)
References: context.Background, context.WithTimeout, time.Second.
func toRequest
toRequest (ignoreCanonicalMIMEHeaderKey bool) (*http.Request, error)
References: http.NewRequest, strings.NewReader.
func compileCachedPathSpec
compileCachedPathSpec (oldpaths []string, newpaths []apidef.CacheMeta, conf config.Config) []URLSpec
func compileCircuitBreakerPathSpec
compileCircuitBreakerPathSpec (paths []apidef.CircuitBreakerMeta, stat URLStatus, apiSpec *APISpec, conf config.Config) []URLSpec
References: backoff.StopBackOff, circuit.Breaker, circuit.BreakerReset, circuit.BreakerStop, circuit.BreakerTripped, circuit.NewRateBreaker, time.Duration, time.Second, time.Sleep.
func compileExtendedPathSpec
compileExtendedPathSpec (ignoreEndpointCase bool, paths []apidef.EndPointMeta, specType URLStatus, conf config.Config) []URLSpec
func compileGopluginPathsSpec
compileGopluginPathsSpec (paths []apidef.GoPluginMeta, stat URLStatus, _ *APISpec, conf config.Config) []URLSpec
func compileInjectedHeaderSpec
compileInjectedHeaderSpec (paths []apidef.HeaderInjectionMeta, stat URLStatus, conf config.Config) []URLSpec
func compileInternalPathsSpec
compileInternalPathsSpec (paths []apidef.InternalMeta, stat URLStatus, conf config.Config) []URLSpec
func compileMethodTransformSpec
compileMethodTransformSpec (paths []apidef.MethodTransformMeta, stat URLStatus, conf config.Config) []URLSpec
func compileMockResponsePathSpec
compileMockResponsePathSpec (ignoreEndpointCase bool, paths []apidef.MockResponseMeta, specType URLStatus, conf config.Config) []URLSpec
func compilePathSpec
compilePathSpec (paths []string, specType URLStatus, conf config.Config) []URLSpec
func compilePersistGraphQLPathSpec
compilePersistGraphQLPathSpec (paths []apidef.PersistGraphQLMeta, stat URLStatus, apiSpec *APISpec, conf config.Config) []URLSpec
func compileRateLimitPathsSpec
compileRateLimitPathsSpec (paths []apidef.RateLimitMeta, stat URLStatus, conf config.Config) []URLSpec
func compileRequestSizePathSpec
compileRequestSizePathSpec (paths []apidef.RequestSizeMeta, stat URLStatus, conf config.Config) []URLSpec
func compileTimeoutPathSpec
compileTimeoutPathSpec (paths []apidef.HardTimeoutMeta, stat URLStatus, conf config.Config) []URLSpec
func compileTrackedEndpointPathsSpec
compileTrackedEndpointPathsSpec (paths []apidef.TrackEndpointMeta, stat URLStatus, conf config.Config) []URLSpec
func compileTransformPathSpec
compileTransformPathSpec (paths []apidef.TemplateMeta, stat URLStatus, conf config.Config) []URLSpec
References: apidef.UseBlob, apidef.UseFile, errors.New.
func compileURLRewritesPathSpec
compileURLRewritesPathSpec (paths []apidef.URLRewriteMeta, stat URLStatus, conf config.Config) []URLSpec
func compileUnTrackedEndpointPathsSpec
compileUnTrackedEndpointPathsSpec (paths []apidef.TrackEndpointMeta, stat URLStatus, conf config.Config) []URLSpec
func compileValidateJSONPathsSpec
compileValidateJSONPathsSpec (paths []apidef.ValidatePathMeta, stat URLStatus, conf config.Config) []URLSpec
References: gojsonschema.NewGoLoader.
func compileVirtualPathsSpec
compileVirtualPathsSpec (paths []apidef.VirtualMeta, stat URLStatus, apiSpec *APISpec, conf config.Config) []URLSpec
func filterSprigFuncs
filterSprigFuncs () texttemplate.FuncMap
References: texttemplate.FuncMap.
func generateRegex
generateRegex (stringSpec string, newSpec *URLSpec, specType URLStatus, conf config.Config)
References: httputil.PreparePathRegexp, regexp.Compile.
func getExtendedPathSpecs
getExtendedPathSpecs (apiVersionDef apidef.VersionInfo, apiSpec *APISpec, conf config.Config) ([]URLSpec, bool)
func getPathSpecs
getPathSpecs (apiVersionDef apidef.VersionInfo, conf config.Config) ([]URLSpec, bool)
func loadBlobTemplate
loadBlobTemplate (blob string) (*texttemplate.Template, error)
References: apidef.Template, base64.StdEncoding.
func loadDefFromFilePath
loadDefFromFilePath (filePath string) (*APISpec, error)
References: apidef.APIDefinition, json.Unmarshal, model.MergedAPI, oas.OAS, openapi3.NewLoader, openapi3.ReadFromFile, os.ReadFile.
func loadFileTemplate
loadFileTemplate (path string) (*texttemplate.Template, error)
References: apidef.Template, filepath.Base.
func prepareSpecs
prepareSpecs (apiDefs []model.MergedAPI, gwConfig config.Config, fromRPC bool) []*APISpec
func processRPCDefinitions
processRPCDefinitions (apiCollection string, gw *Gateway) ([]*APISpec, error)
References: json.Unmarshal, model.MergedAPI, model.NewMergedAPIList.
func replaceConsulSecrets
replaceConsulSecrets (input *string) error
References: kv.Consul, strings.Replace, strings.TrimPrefix.
func replaceSecrets
replaceSecrets (in []byte) []byte
References: os.Getenv, strings.Contains, strings.Replace.
func replaceVaultSecrets
replaceVaultSecrets (input *string) error
References: errors.New, fmt.Sprintf, kv.Vault, strings.Replace.
func checkClockSkew
checkClockSkew (dateHeaderValue string) bool
References: math.Abs, time.Now, time.Parse.
func checkLimit
checkLimit (sessionData *user.SessionState, key string, quotaMax,quotaRemaining,quotaRenews int64) bool
References: time.Now, time.Unix.
func getListener
getListener returns a net.Listener for this proxy. If useProxyProtocol is true it wraps the underlying listener to support proxyprotocol.
getListener () net.Listener
References: proxyproto.Listener.
func copyFromBackend
copyFromBackend (errc chan<- error)
References: io.Copy.
func copyToBackend
copyToBackend (errc chan<- error)
References: io.Copy.
Tests
Files: 82. Third party imports: 25. Imports from organisation: 8. Tests: 561. Benchmarks: 50.
Constants
const (
internalTLSErr = "tls: unrecognized name"
badcertErr = "tls: bad certificate"
certNotMatchErr = "Client TLS certificate is required"
unknownCertAuthorityErr = "unknown certificate authority"
)
const (
extractorValueInput = "testkey"
extractorRegexExpr = "prefix-(.*)"
extractorRegexInput = "prefix-testkey123"
extractorRegexMatchIndex = 0
extractorXPathExpr = "//object/key"
extractorXPathInput = "<object><key>thevalue</key></object>"
extractorHeaderName = "testheader"
extractorParamName = "testparam"
)
const (
gqlContinentQuery = `
query {
continent(code: "NG"){
code
name
}
}
`
gqlContinentQueryVariable = `
query ($code: ID!){
continent(code: $code){
name
}
}`
gqlStateQueryVariable = `
query ($filter: StateFilterInput) {
state(filter: $filter) {
name
}
}`
)
const (
authRedirectUri = "http://client.oauth.com"
authRedirectUri2 = "http://client2.oauth.com"
authClientID = "1234"
authClientSecret = "aabbccdd"
)
const (
RevokeOauthHashedToken = "RevokeOauthHashedToken"
RevokeOauthToken = "RevokeOauthToken"
RevokeOauthRefreshToken = "RevokeOauthRefreshToken"
RevokeOauthRefreshHashedToken = "RevokeOauthRefreshHashedToken" // we do not support hashed refresh tokens yet
DefaultOrg = "default-org-id"
)
const apiDefListTest = `[{
"api_id": "1",
"definition": {
"location": "header",
"key": "version"
},
"auth": {"auth_header_name": "authorization"},
"version_data": {
"versions": {
"v1": {"name": "v1"}
}
},
"proxy": {
"listen_path": "/v1",
"target_url": "` + TestHttpAny + `"
}
}]`
const apiDefListTest2 = `[{
"api_id": "1",
"definition": {
"location": "header",
"key": "version"
},
"auth": {"auth_header_name": "authorization"},
"version_data": {
"versions": {
"v1": {"name": "v1"}
}
},
"proxy": {
"listen_path": "/v1",
"target_url": "` + TestHttpAny + `"
}
},
{
"api_id": "2",
"definition": {
"location": "header",
"key": "version"
},
"auth": {"auth_header_name": "authorization"},
"version_data": {
"versions": {
"v2": {"name": "v2"}
}
},
"proxy": {
"listen_path": "/v2",
"target_url": "` + TestHttpAny + `"
}
}]`
const apiTestDef = `{
"api_id": "1",
"definition": {
"location": "header",
"key": "version"
},
"auth": {"auth_header_name": "authorization"},
"version_data": {
"versions": {
"v1": {"name": "v1"}
}
},
"proxy": {
"listen_path": "/v1",
"target_url": "` + TestHttpAny + `"
}
}`
const authKeyDef = `{
"api_id": "31",
"org_id": "default",
"auth": {"auth_header_name": "authorization"},
"version_data": {
"not_versioned": true,
"versions": {
"v1": {"name": "v1"}
}
},
"proxy": {
"listen_path": "/auth_key_test/",
"target_url": "` + TestHttpAny + `"
}
}`
const closedRLDefSmall = `{
"api_id": "31445455",
"org_id": "default",
"auth": {"auth_header_name": "authorization"},
"version_data": {
"not_versioned": true,
"versions": {
"v1": {"name": "v1"}
}
},
"proxy": {
"listen_path": "/rl_closed_test/",
"target_url": "` + TestHttpAny + `"
},
"global_rate_limit": {
"rate": 3,
"per": 1
}
}`
const consul = `
[{
"Node": "foobar",
"Address": "10.1.10.12",
"ServiceID": "redis",
"ServiceName": "redis",
"ServicePort": 8000
},
{
"Node": "foobar2",
"Address": "10.1.10.13",
"ServiceID": "redis",
"ServiceName": "redis",
"ServicePort": 8000
}]
`
const defaultTestPol = `{
"ID": "default-test",
"rate": 1000,
"per": 1,
"quota_max": 100,
"quota_renewal_rate": 60,
"access_rights": {
"41433797848f41a558c1573d3e55a410": {
"api_name": "My API",
"api_id": "41433797848f41a558c1573d3e55a410",
"versions": [
"Default"
]
}
},
"org_id": "54de205930c55e15bd000001",
"hmac_enabled": false
}`
const etcd = `{
"action": "get",
"node": {
"key": "/services/single",
"value": "httpbin.org:6000",
"modifiedIndex": 6,
"createdIndex": 6
}
}`
const eureka_real = `{
"application": {
"name": "ROUTE",
"instance": [{
"hostName": "ip-172-31-57-136",
"app": "ROUTE",
"ipAddr": "172.31.57.136",
"status": "UP",
"overriddenstatus": "UNKNOWN",
"port": {
"@enabled": "true",
"$": "60565"
},
"securePort": {
"@enabled": "false",
"$": "443"
},
"countryId": 1,
"dataCenterInfo": {
"@class": "com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo",
"name": "MyOwn"
},
"leaseInfo": {
"renewalIntervalInSecs": 10,
"durationInSecs": 10,
"registrationTimestamp": 1460471383902,
"lastRenewalTimestamp": 1460471403565,
"serviceUpTimestamp": 1460471383340
},
"metadata": {
"instanceId": "route:f673c15eebfc456a3c679a55d234a8ca",
"payment": "perCall",
"providerName": "MisterA"
},
"homePageUrl": "http:\/\/ip-172-31-57-136:60565\/",
"statusPageUrl": "http:\/\/ip-172-31-57-136:60565\/info",
"healthCheckUrl": "http:\/\/ip-172-31-57-136:60565\/health",
"vipAddress": "route",
"lastUpdatedTimestamp": 1460471383902,
"lastDirtyTimestamp": 1460471429751,
"actionType": "ADDED"
}, {
"hostName": "ip-172-31-13-37",
"app": "ROUTE",
"ipAddr": "172.31.13.37",
"status": "UP",
"overriddenstatus": "UNKNOWN",
"port": {
"@enabled": "true",
"$": "50045"
},
"securePort": {
"@enabled": "false",
"$": "443"
},
"countryId": 1,
"dataCenterInfo": {
"@class": "com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo",
"name": "MyOwn"
},
"leaseInfo": {
"renewalIntervalInSecs": 10,
"durationInSecs": 10,
"registrationTimestamp": 1460471387114,
"lastRenewalTimestamp": 1460471407062,
"serviceUpTimestamp": 1460471386750
},
"metadata": {
"instanceId": "route:838ba7845f1fd63d94c10ca9efdf77a5",
"payment": "flat",
"providerName": "MissB"
},
"homePageUrl": "http:\/\/ip-172-31-13-37:50045\/",
"statusPageUrl": "http:\/\/ip-172-31-13-37:50045\/info",
"healthCheckUrl": "http:\/\/ip-172-31-13-37:50045\/health",
"vipAddress": "route",
"lastUpdatedTimestamp": 1460471387114,
"lastDirtyTimestamp": 1460471360189,
"actionType": "ADDED"
}]
}
}`
const expectedFeatures = `[{"name":"Patriots Path, Mendham, NJ 07945, USA","location":{"latitude":407838351,"longitude":-746143763}},{"name":"101 New Jersey 10, Whippany, NJ 07981, USA","location":{"latitude":408122808,"longitude":-743999179}},{"name":"U.S. 6, Shohola, PA 18458, USA","location":{"latitude":413628156,"longitude":-749015468}},{"name":"5 Conners Road, Kingston, NY 12401, USA","location":{"latitude":419999544,"longitude":-740371136}},{"name":"Mid Hudson Psychiatric Center, New Hampton, NY 10958, USA","location":{"latitude":414008389,"longitude":-743951297}},{"name":"287 Flugertown Road, Livingston Manor, NY 12758, USA","location":{"latitude":419611318,"longitude":-746524769}},{"name":"4001 Tremley Point Road, Linden, NJ 07036, USA","location":{"latitude":406109563,"longitude":-742186778}},{"name":"352 South Mountain Road, Wallkill, NY 12589, USA","location":{"latitude":416802456,"longitude":-742370183}},{"name":"Bailey Turn Road, Harriman, NY 10926, USA","location":{"latitude":412950425,"longitude":-741077389}},{"name":"193-199 Wawayanda Road, Hewitt, NJ 07421, USA","location":{"latitude":412144655,"longitude":-743949739}},{"name":"406-496 Ward Avenue, Pine Bush, NY 12566, USA","location":{"latitude":415736605,"longitude":-742847522}},{"name":"162 Merrill Road, Highland Mills, NY 10930, USA","location":{"latitude":413843930,"longitude":-740501726}},{"name":"Clinton Road, West Milford, NJ 07480, USA","location":{"latitude":410873075,"longitude":-744459023}},{"name":"16 Old Brook Lane, Warwick, NY 10990, USA","location":{"latitude":412346009,"longitude":-744026814}},{"name":"3 Drake Lane, Pennington, NJ 08534, USA","location":{"latitude":402948455,"longitude":-747903913}},{"name":"6324 8th Avenue, Brooklyn, NY 11220, USA","location":{"latitude":406337092,"longitude":-740122226}},{"name":"1 Merck Access Road, Whitehouse Station, NJ 08889, USA","location":{"latitude":406421967,"longitude":-747727624}},{"name":"78-98 Schalck Road, Narrowsburg, NY 12764, USA","location":{"latitude":416318082,"longitude":-749677716}},{"name":"282 Lakeview Drive Road, Highland Lake, NY 12743, USA","location":{"latitude":415301720,"longitude":-748416257}},{"name":"330 Evelyn Avenue, Hamilton Township, NJ 08619, USA","location":{"latitude":402647019,"longitude":-747071791}},{"name":"New York State Reference Route 987E, Southfields, NY 10975, USA","location":{"latitude":412567807,"longitude":-741058078}},{"name":"103-271 Tempaloni Road, Ellenville, NY 12428, USA","location":{"latitude":416855156,"longitude":-744420597}},{"name":"1300 Airport Road, North Brunswick Township, NJ 08902, USA","location":{"latitude":404663628,"longitude":-744820157}},{"location":{"latitude":407113723,"longitude":-749746483}},{"location":{"latitude":402133926,"longitude":-743613249}},{"location":{"latitude":400273442,"longitude":-741220915}},{"location":{"latitude":411236786,"longitude":-744070769}},{"name":"211-225 Plains Road, Augusta, NJ 07822, USA","location":{"latitude":411633782,"longitude":-746784970}},{"location":{"latitude":415830701,"longitude":-742952812}},{"name":"165 Pedersen Ridge Road, Milford, PA 18337, USA","location":{"latitude":413447164,"longitude":-748712898}},{"name":"100-122 Locktown Road, Frenchtown, NJ 08825, USA","location":{"latitude":405047245,"longitude":-749800722}},{"location":{"latitude":418858923,"longitude":-746156790}},{"name":"650-652 Willi Hill Road, Swan Lake, NY 12783, USA","location":{"latitude":417951888,"longitude":-748484944}},{"name":"26 East 3rd Street, New Providence, NJ 07974, USA","location":{"latitude":407033786,"longitude":-743977337}},{"location":{"latitude":417548014,"longitude":-740075041}},{"location":{"latitude":410395868,"longitude":-744972325}},{"location":{"latitude":404615353,"longitude":-745129803}},{"name":"611 Lawrence Avenue, Westfield, NJ 07090, USA","location":{"latitude":406589790,"longitude":-743560121}},{"name":"18 Lannis Avenue, New Windsor, NY 12553, USA","location":{"latitude":414653148,"longitude":-740477477}},{"name":"82-104 Amherst Avenue, Colonia, NJ 07067, USA","location":{"latitude":405957808,"longitude":-743255336}},{"name":"170 Seven Lakes Drive, Sloatsburg, NY 10974, USA","location":{"latitude":411733589,"longitude":-741648093}},{"name":"1270 Lakes Road, Monroe, NY 10950, USA","location":{"latitude":412676291,"longitude":-742606606}},{"name":"509-535 Alphano Road, Great Meadows, NJ 07838, USA","location":{"latitude":409224445,"longitude":-748286738}},{"name":"652 Garden Street, Elizabeth, NJ 07202, USA","location":{"latitude":406523420,"longitude":-742135517}},{"name":"349 Sea Spray Court, Neptune City, NJ 07753, USA","location":{"latitude":401827388,"longitude":-740294537}},{"name":"13-17 Stanley Street, West Milford, NJ 07480, USA","location":{"latitude":410564152,"longitude":-743685054}},{"name":"47 Industrial Avenue, Teterboro, NJ 07608, USA","location":{"latitude":408472324,"longitude":-740726046}},{"name":"5 White Oak Lane, Stony Point, NY 10980, USA","location":{"latitude":412452168,"longitude":-740214052}},{"name":"Berkshire Valley Management Area Trail, Jefferson, NJ, USA","location":{"latitude":409146138,"longitude":-746188906}},{"name":"1007 Jersey Avenue, New Brunswick, NJ 08901, USA","location":{"latitude":404701380,"longitude":-744781745}},{"name":"6 East Emerald Isle Drive, Lake Hopatcong, NJ 07849, USA","location":{"latitude":409642566,"longitude":-746017679}},{"name":"1358-1474 New Jersey 57, Port Murray, NJ 07865, USA","location":{"latitude":408031728,"longitude":-748645385}},{"name":"367 Prospect Road, Chester, NY 10918, USA","location":{"latitude":413700272,"longitude":-742135189}},{"name":"10 Simon Lake Drive, Atlantic Highlands, NJ 07716, USA","location":{"latitude":404310607,"longitude":-740282632}},{"name":"11 Ward Street, Mount Arlington, NJ 07856, USA","location":{"latitude":409319800,"longitude":-746201391}},{"name":"300-398 Jefferson Avenue, Elizabeth, NJ 07201, USA","location":{"latitude":406685311,"longitude":-742108603}},{"name":"43 Dreher Road, Roscoe, NY 12776, USA","location":{"latitude":419018117,"longitude":-749142781}},{"name":"Swan Street, Pine Island, NY 10969, USA","location":{"latitude":412856162,"longitude":-745148837}},{"name":"66 Pleasantview Avenue, Monticello, NY 12701, USA","location":{"latitude":416560744,"longitude":-746721964}},{"location":{"latitude":405314270,"longitude":-749836354}},{"location":{"latitude":414219548,"longitude":-743327440}},{"name":"565 Winding Hills Road, Montgomery, NY 12549, USA","location":{"latitude":415534177,"longitude":-742900616}},{"name":"231 Rocky Run Road, Glen Gardner, NJ 08826, USA","location":{"latitude":406898530,"longitude":-749127080}},{"name":"100 Mount Pleasant Avenue, Newark, NJ 07104, USA","location":{"latitude":407586880,"longitude":-741670168}},{"name":"517-521 Huntington Drive, Manchester Township, NJ 08759, USA","location":{"latitude":400106455,"longitude":-742870190}},{"location":{"latitude":400066188,"longitude":-746793294}},{"name":"40 Mountain Road, Napanoch, NY 12458, USA","location":{"latitude":418803880,"longitude":-744102673}},{"location":{"latitude":414204288,"longitude":-747895140}},{"location":{"latitude":414777405,"longitude":-740615601}},{"name":"48 North Road, Forestburgh, NY 12777, USA","location":{"latitude":415464475,"longitude":-747175374}},{"location":{"latitude":404062378,"longitude":-746376177}},{"location":{"latitude":405688272,"longitude":-749285130}},{"location":{"latitude":400342070,"longitude":-748788996}},{"location":{"latitude":401809022,"longitude":-744157964}},{"name":"9 Thompson Avenue, Leonardo, NJ 07737, USA","location":{"latitude":404226644,"longitude":-740517141}},{"location":{"latitude":410322033,"longitude":-747871659}},{"location":{"latitude":407100674,"longitude":-747742727}},{"name":"213 Bush Road, Stone Ridge, NY 12484, USA","location":{"latitude":418811433,"longitude":-741718005}},{"location":{"latitude":415034302,"longitude":-743850945}},{"location":{"latitude":411349992,"longitude":-743694161}},{"name":"1-17 Bergen Court, New Brunswick, NJ 08901, USA","location":{"latitude":404839914,"longitude":-744759616}},{"name":"35 Oakland Valley Road, Cuddebackville, NY 12729, USA","location":{"latitude":414638017,"longitude":-745957854}},{"location":{"latitude":412127800,"longitude":-740173578}},{"location":{"latitude":401263460,"longitude":-747964303}},{"location":{"latitude":412843391,"longitude":-749086026}},{"location":{"latitude":418512773,"longitude":-743067823}},{"name":"42-102 Main Street, Belford, NJ 07718, USA","location":{"latitude":404318328,"longitude":-740835638}},{"location":{"latitude":419020746,"longitude":-741172328}},{"location":{"latitude":404080723,"longitude":-746119569}},{"location":{"latitude":401012643,"longitude":-744035134}},{"location":{"latitude":404306372,"longitude":-741079661}},{"location":{"latitude":403966326,"longitude":-748519297}},{"location":{"latitude":405002031,"longitude":-748407866}},{"location":{"latitude":409532885,"longitude":-742200683}},{"location":{"latitude":416851321,"longitude":-742674555}},{"name":"3387 Richmond Terrace, Staten Island, NY 10303, USA","location":{"latitude":406411633,"longitude":-741722051}},{"name":"261 Van Sickle Road, Goshen, NY 10924, USA","location":{"latitude":413069058,"longitude":-744597778}},{"location":{"latitude":418465462,"longitude":-746859398}},{"location":{"latitude":411733222,"longitude":-744228360}},{"name":"3 Hasta Way, Newton, NJ 07860, USA","location":{"latitude":410248224,"longitude":-747127767}}]`
const gqlCountriesSchema = `directive @cacheControl(
maxAge: Int
scope: CacheControlScope
) on FIELD_DEFINITION | OBJECT | INTERFACE
enum CacheControlScope {
PUBLIC
PRIVATE
}
type Continent {
code: ID!
name: String!
countries: [Country!]!
}
input ContinentFilterInput {
code: StringQueryOperatorInput
}
type Country {
code: ID!
name: String!
native: String!
phone: String!
continent: Continent!
capital: String
currency: String
languages: [Language!]!
emoji: String!
emojiU: String!
states: [State!]!
}
input CountryFilterInput {
code: StringQueryOperatorInput
currency: StringQueryOperatorInput
continent: StringQueryOperatorInput
}
type Language {
code: ID!
name: String
native: String
rtl: Boolean!
}
input LanguageFilterInput {
code: StringQueryOperatorInput
}
input StateFilterInput{
code: StringQueryOperatorInput
compulsory: String!
}
type Query {
continents(filter: ContinentFilterInput): [Continent!]!
continent(code: ID!): Continent
countries(filter: CountryFilterInput): [Country!]!
country(code: ID!): Country
languages(filter: LanguageFilterInput): [Language!]!
language(code: ID!): Language
state(filter: StateFilterInput): [State!]!
}
type State {
code: String
name: String!
country: Country!
}
input StringQueryOperatorInput {
eq: String
ne: String
in: [String]
nin: [String]
regex: String
glob: String
}
scalar Upload`
const gqlCountriesSilentTypeIntrospectionQuery = `{
__type(name:"Country") {
name
fields {
name
type {
kind
}
}
}
}`
const gqlCountriesTypeIntrospectionQuery = `query IntrospectionQuery {
__type(name:"Country") {
name
fields {
name
type {
kind
}
}
}
}`
const gqlCountriesTypeIntrospectionQueryWithMultipleFields = `query IntrospectionQuery {
countries {
name
continent {
code
name
countries {
code
}
}
}
__type(name:"Country") {
name
}
}`
const gqlIntrospectionQuery = `query IntrospectionQuery {
__schema {
queryType {
name
}
mutationType {
name
}
subscriptionType {
name
}
types {
...FullType
}
directives {
name
description
locations
args {
...InputValue
}
}
}
}
fragment FullType on __Type {
kind
name
description
fields(includeDeprecated: true) {
name
description
args {
...InputValue
}
type {
...TypeRef
}
isDeprecated
deprecationReason
}
inputFields {
...InputValue
}
interfaces {
...TypeRef
}
enumValues(includeDeprecated: true) {
name
description
isDeprecated
deprecationReason
}
possibleTypes {
...TypeRef
}
}
fragment InputValue on __InputValue {
name
description
type {
...TypeRef
}
defaultValue
}
fragment TypeRef on __Type {
kind
name
ofType {
kind
name
ofType {
kind
name
ofType {
kind
name
ofType {
kind
name
ofType {
kind
name
ofType {
kind
name
ofType {
kind
name
}
}
}
}
}
}
}
}`
const gqlInvalidCountriesQuery = `{
countries {
name
continent {
code
name
countries {
code
}
}
}`
const gqlMergedSupergraphSDL = `type Query {
me: User
allUsers: [User]
topProducts(first: Int = 5): [Product]
}
type Subscription {
review: Review!
}
type User {
id: ID!
username: String!
reviews: [Review]
account: [BankAccount!]
}
type BankAccount {
number: String
balance: Float
}
type Product {
upc: String!
name: String!
price: Int!
reviews: [Review]
}
type Review {
body: String!
author: User!
product: Product!
}`
const gqlProxyUpstreamSchema = `type Query {
hello(name: String!): String!
httpMethod: String!
}`
const gqlSchemaIntrospectionQueryWithMultipleFields = `query IntrospectionQuery {
countries {
name
continent {
code
name
countries {
code
}
}
}
__schema {
queryType {
name
}
}
}`
const gqlSubgraphQueryReviews = `query Subgraph($_representations: [_Any!]!) {
_entities(representations: $_representations) {
... on User {
reviews {
body
}
}
}
}`
const gqlSubgraphSDLAccounts = `extend type Query {
me: User
allUsers: [User]
}
type User @key(fields: "id") {
id: ID!
username: String!
}`
const gqlSubgraphSDLBankAccounts = `
extend type User @key(fields: "id") {
id: ID! @extends
account: [BankAccount!]
}
type BankAccount {
number: String
balance: Float
}
`
const gqlSubgraphSDLReviews = `type Review {
body: String!
author: User! @provides(fields: "username")
product: Product!
}
extend type User @key(fields: "id") {
id: ID! @external
reviews: [Review]
}
extend type Product @key(fields: "upc") {
upc: String! @external
reviews: [Review]
}`
const gqlSubgraphSchemaAccounts = `scalar _Any
scalar _FieldSet
union _Entity = User
type _Service {
sdl: String
}
type Query {
me: User
allUsers: [User]
_entities(representations: [_Any!]!): [_Entity]!
_service: _Service!
}
type User @key(fields: "id"){
id: ID!
username: String!
}
directive @external on FIELD_DEFINITION
directive @requires(fields: _FieldSet!) on FIELD_DEFINITION
directive @provides(fields: _FieldSet!) on FIELD_DEFINITION
directive @key(fields: _FieldSet!) on OBJECT | INTERFACE
directive @extends on OBJECT | INTERFACE`
const gqlSubgraphSchemaBankAccounts = `
extend type User @key(fields: "id"){
id: ID! @extends
account: [BankAccount!]
}
type BankAccount {
number: String
balance: Float
}
scalar _Any
scalar _FieldSet
union _Entity = User
type _Service {
sdl: String
}
type Query {
_entities(representations: [_Any!]!): [_Entity]!
_service: _Service!
}
type BankAccount {
number: String
balance: Float
}
type User @key(fields: "id"){
id: ID! @extends
account: [BankAccount!]
}
directive @external on FIELD_DEFINITION
directive @requires(fields: _FieldSet!) on FIELD_DEFINITION
directive @provides(fields: _FieldSet!) on FIELD_DEFINITION
directive @key(fields: _FieldSet!) on OBJECT | INTERFACE
directive @extends on OBJECT | INTERFACE`
const gqlSubgraphSchemaReviews = `scalar _Any
scalar _FieldSet
union _Entity = User | Product
type _Service {
sdl: String
}
type Query {
_entities(representations: [_Any!]!): [_Entity]!
_service: _Service!
}
type Review {
body: String!
author: User! @provides(fields: "username")
product: Product!
}
type User @key(fields: "id") {
id: ID! @external
reviews: [Review]
}
type Product @key(fields: "upc") {
upc: String! @external
name: String! @external
reviews: [Review] @requires(fields: "name")
}
directive @external on FIELD_DEFINITION
directive @requires(fields: _FieldSet!) on FIELD_DEFINITION
directive @provides(fields: _FieldSet!) on FIELD_DEFINITION
directive @key(fields: _FieldSet!) on OBJECT | INTERFACE
directive @extends on OBJECT | INTERFACE`
const gqlSubgraphVariables = `{
"_representations": [
{
"__typename": "User",
"id": "1"
}
]
}`
const hmacAuthDef = `{
"api_id": "1",
"org_id": "default",
"enable_signature_checking": true,
"hmac_allowed_clock_skew": 5000,
"auth": {"auth_header_name": "authorization"},
"version_data": {
"not_versioned": true,
"versions": {
"v1": {"name": "v1"}
}
},
"proxy": {
"listen_path": "/v1",
"target_url": "` + TestHttpAny + `"
}
}`
// openssl rsa -in app.rsa -pubout > app.rsa.pub
const jwtRSAPubKey = `
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyqZ4rwKF8qCExS7kpY4c
nJa/37FMkJNkalZ3OuslLB0oRL8T4c94kdF4aeNzSFkSe2n99IBI6Ssl79vbfMZb
+t06L0Q94k+/P37x7+/RJZiff4y1VGjrnrnMI2iu9l4iBBRYzNmG6eblroEMMWlg
k5tysHgxB59CSNIcD9gqk1hx4n/FgOmvKsfQgWHNlPSDTRcWGWGhB2/XgNVYG2pO
lQxAPqLhBHeqGTXBbPfGF9cHzixpsPr6GtbzPwhsQ/8bPxoJ7hdfn+rzztks3d6+
HWURcyNTLRe0mjXjjee9Z6+gZ+H+fS4pnP9tqT7IgU6ePUWTpjoiPtLexgsAa/ct
jQIDAQAB
-----END PUBLIC KEY-----
`
const jwtRSAPubKeyinvalid = `
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyqZ4rwKF8qCExS7kpY4c
nJa/37FMkJNkalZ3OuslLB0oRL8T4c94kdF4aeNzSFkSe2n99IBI6Ssl79vbfMZb
+t06L0Q94k+/P37x7+/RJZiff4y1VGjrnrnMI2iu9l4iBBRYzNmG6eblroEMMWlg
k5tysHgxB59CSNIcD9gqk1hx4n/FgOmvKsfQgWHNlPSDTRcWGWGhB2/XgNVYG2pO
lQxAPqLhBHeqGTXBbPfGF9cHzixpsPr6GtbzPwhsQ/8bPxoJ7hdfn+rzztks3d6+
HWURcyNTLRe0mjXjjee9Z6+gZ+H+fS4pnP9tqT7IgU6ePUWTpjoiPtLexgsAa/ct
jQIDAQAB!!!!
-----END PUBLIC KEY-----
`
const keyRules = `{
"last_check": 1402492859,
"org_id": "53ac07777cbb8c2d53000002",
"rate": 3,
"per": 1,
"quota_max": -1,
"quota_renews": 1399567002,
"quota_remaining": 10,
"quota_renewal_rate": 300
}`
const keyRulesWithMetadata = `{
"last_check": 1402492859,
"org_id": "53ac07777cbb8c2d53000002",
"rate": 3,
"per": 1,
"quota_max": -1,
"quota_renews": 1399567002,
"quota_remaining": 10,
"quota_renewal_rate": 300,
"meta_data": {"key": "meta", "foo": "keybar"}
}`
const mesosphere = `{
"tasks": [{
"id": "myservice.7fc21d4c-eabb-11e5-b381-066c48d09c8f",
"host": "httpbin.org",
"ports": [80],
"startedAt": "2016-03-15T14:37:55.941Z",
"stagedAt": "2016-03-15T14:37:52.792Z",
"version": "2016-03-15T14:37:52.726Z",
"slaveId": "d70867df-fdb2-4889-abeb-0829c742fded-S2",
"appId": "/httpbin"
}]
}`
const multiAuthBackwardsCompatible = `{
"api_id": "31",
"org_id": "default",
"auth": {
"auth_header_name": "token",
"use_param": true
},
"version_data": {
"not_versioned": true,
"versions": {
"v1": {"name": "v1"}
}
},
"proxy": {
"listen_path": "/auth_key_test/",
"target_url": "` + TestHttpAny + `"
}
}`
const multiAuthDef = `{
"api_id": "31",
"org_id": "default",
"auth": {
"auth_header_name": "authorization",
"param_name": "token",
"use_param": true,
"use_cookie": true,
"cookie_name": "oreo"
},
"version_data": {
"not_versioned": true,
"versions": {
"v1": {"name": "v1"}
}
},
"proxy": {
"listen_path": "/auth_key_test/",
"target_url": "` + TestHttpAny + `"
}
}`
const multiAuthDev = `{
"api_id": "55",
"org_id": "default",
"use_basic_auth": true,
"use_standard_auth": true,
"base_identity_provided_by": "auth_token",
"auth_configs": {
"basic": {"auth_header_name": "Authorization"},
"authToken": {"auth_header_name": "x-standard-auth"}
},
"version_data": {
"not_versioned": true,
"versions": {
"v1": {"name": "v1"}
}
},
"proxy": {
"listen_path": "/v1",
"target_url": "` + TestHttpAny + `"
}
}`
const nested = `{
"action": "get",
"node": {
"key": "/services/single",
"value": "{\"hostname\": \"httpbin.org\", \"port\": \"80\"}",
"modifiedIndex": 6,
"createdIndex": 6
}
}`
const nested_consul = `
[{
"Name": "beep",
"Data": "{\"hostname\": \"httpbin1.org\", \"port\": \"80\"}"
},
{
"Name": "boop",
"Data": "{\"hostname\": \"httpbin2.org\", \"port\": \"80\"}"
}]`
const nested_list = `{
"action": "get",
"node": {
"key": "/services/single",
"value": "[{\"hostname\": \"httpbin.org\", \"port\": \"80\"}, {\"hostname\": \"httpbin2.org\", \"port\": \"80\"}]",
"modifiedIndex": 6,
"createdIndex": 6
}
}`
const openRLDefSmall = `{
"api_id": "313232",
"org_id": "default",
"auth": {"auth_header_name": "authorization"},
"use_keyless": true,
"version_data": {
"not_versioned": true,
"versions": {
"v1": {"name": "v1"}
}
},
"proxy": {
"listen_path": "/rl_test/",
"target_url": "` + TestHttpAny + `"
},
"global_rate_limit": {
"rate": 3,
"per": 1
}
}`
const sampleUptimeTestAPI = `{
"api_id": "test",
"use_keyless": true,
"version_data": {
"not_versioned": true,
"versions": {
"v1": {"name": "v1"}
}
},
"uptime_tests": {
"check_list": [
{
"url": "{{.Host1}}/get",
"method": "GET"
},
{
"url": "{{.Host2}}/get",
"method": "GET"
}
]
},
"proxy": {
"listen_path": "/",
"enable_load_balancing": true,
"check_host_against_uptime_tests": true,
"target_list": [
"{{.Host1}}",
"{{.Host2}}"
]
}
}`
const testBatchRequest = `{
"requests": [
{
"method": "GET",
"headers": {
"test-header-1": "test-1",
"test-header-2": "test-2"
},
"relative_url": "get/?param1=this"
},
{
"method": "POST",
"body": "TEST BODY",
"relative_url": "post/"
},
{
"method": "PUT",
"relative_url": "put/"
}
],
"suppress_parallel_execution": true
}`
const testGQLQueryCountries = `
{
countries{
code
}
}
`
const testGQLQueryCountry = `
query country($code: ID!){
country(code: $code){
code
}
}`
const testGQLQueryCountryCode = `
query country($countryCode: ID!){
country(code: $countryCode){
code
}
}`
const testOASForValidateRequest = `{
"openapi": "3.0.0",
"components": {
"schemas": {
"Country": {
"properties": {
"name": {
"type": "string"
}
}
},
"Owner": {
"properties": {
"name": {
"type": "string"
},
"country": {
"$ref": "#/components/schemas/Country"
}
}
},
"Product": {
"properties": {
"name": {
"type": "string"
},
"owner": {
"$ref": "#/components/schemas/Owner"
},
"createdAt": {
"type": "string",
"format": "date-time"
},
"expiryOn": {
"type": "string",
"format": "date"
}
}
}
}
},
"info": {
"title": "validate-request",
"version": "1.0.0"
},
"paths": {
"/post": {
"post": {
"operationId": "postpost",
"parameters": [{
"name": "id",
"in": "query",
"required": false,
"schema": {
"type": "integer"
},
"description": "description"
}],
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Product"
}
}
}
},
"responses": {
"200": {
"description": ""
}
}
}
}
},
"servers": [
{
"url": "/"
}
]
}`
const testQueryContinentCode = `
query continent($code: ID!) {
continent(code: $code){
code
}
}`
const virtBatchTest = `function batchTest(request, session, config) {
// Set up a response object
var response = {
Body: "",
Headers: {
"content-type": "application/json"
},
Code: 202
}
// Batch request
var batch = {
"requests": [
{
"method": "GET",
"headers": {
"X-CertificateOuid": "X-CertificateOuid"
},
"body": "",
"relative_url": "{upstream_URL}"
},
{
"method": "GET",
"headers": {
"X-CertificateOuid": "X-CertificateOuid"
},
"body": "",
"relative_url": "{upstream_URL}"
}
],
"suppress_parallel_execution": false
}
var newBody = TykBatchRequest(JSON.stringify(batch))
var asJS = JSON.parse(newBody)
for (var i in asJS) {
if (asJS[i].code == 0) {
response.Code = 500
}
}
return TykJsResponse(response, session.meta_data)
}`
const virtTestJS = `
function testVirtData(request, session, config) {
var resp = {
Body: "foobar",
Headers: {
"data-foo": config.config_data.foo,
"data-bar-y": config.config_data.bar.y.toString(),
"x-tyk-cache-action-set": "1",
"x-tyk-cache-action-set-ttl": "10",
},
Code: 202
}
return TykJsResponse(resp, session.meta_data)
}
`
Vars
var (
proxyOnErrorEnabled = true
keylessAuthEnabled = true
cacheEnabled = true
proxyOnErrorDisabled = false
keylessAuthDisabled = false
cacheDisabled = false
)
var algoList = [4]string{"hmac-sha1", "hmac-sha256", "hmac-sha384", "hmac-sha512"}
var bundleWithBadSignature = map[string]string{
"manifest.json": `
{
"file_list": [],
"custom_middleware": {
"driver": "grpc",
"auth_check": {
"name": "MyAuthHook"
}
},
"checksum": "d41d8cd98f00b204e9800998ecf8427e",
"signature": "dGVzdC1wdWJsaWMta2V5"
}
`,
}
exampleData is a copy of testdata/route_guide_db.json. It's to avoid
specifying file path with go run.
var exampleData = []byte(`[{
"location": {
"latitude": 407838351,
"longitude": -746143763
},
"name": "Patriots Path, Mendham, NJ 07945, USA"
}, {
"location": {
"latitude": 408122808,
"longitude": -743999179
},
"name": "101 New Jersey 10, Whippany, NJ 07981, USA"
}, {
"location": {
"latitude": 413628156,
"longitude": -749015468
},
"name": "U.S. 6, Shohola, PA 18458, USA"
}, {
"location": {
"latitude": 419999544,
"longitude": -740371136
},
"name": "5 Conners Road, Kingston, NY 12401, USA"
}, {
"location": {
"latitude": 414008389,
"longitude": -743951297
},
"name": "Mid Hudson Psychiatric Center, New Hampton, NY 10958, USA"
}, {
"location": {
"latitude": 419611318,
"longitude": -746524769
},
"name": "287 Flugertown Road, Livingston Manor, NY 12758, USA"
}, {
"location": {
"latitude": 406109563,
"longitude": -742186778
},
"name": "4001 Tremley Point Road, Linden, NJ 07036, USA"
}, {
"location": {
"latitude": 416802456,
"longitude": -742370183
},
"name": "352 South Mountain Road, Wallkill, NY 12589, USA"
}, {
"location": {
"latitude": 412950425,
"longitude": -741077389
},
"name": "Bailey Turn Road, Harriman, NY 10926, USA"
}, {
"location": {
"latitude": 412144655,
"longitude": -743949739
},
"name": "193-199 Wawayanda Road, Hewitt, NJ 07421, USA"
}, {
"location": {
"latitude": 415736605,
"longitude": -742847522
},
"name": "406-496 Ward Avenue, Pine Bush, NY 12566, USA"
}, {
"location": {
"latitude": 413843930,
"longitude": -740501726
},
"name": "162 Merrill Road, Highland Mills, NY 10930, USA"
}, {
"location": {
"latitude": 410873075,
"longitude": -744459023
},
"name": "Clinton Road, West Milford, NJ 07480, USA"
}, {
"location": {
"latitude": 412346009,
"longitude": -744026814
},
"name": "16 Old Brook Lane, Warwick, NY 10990, USA"
}, {
"location": {
"latitude": 402948455,
"longitude": -747903913
},
"name": "3 Drake Lane, Pennington, NJ 08534, USA"
}, {
"location": {
"latitude": 406337092,
"longitude": -740122226
},
"name": "6324 8th Avenue, Brooklyn, NY 11220, USA"
}, {
"location": {
"latitude": 406421967,
"longitude": -747727624
},
"name": "1 Merck Access Road, Whitehouse Station, NJ 08889, USA"
}, {
"location": {
"latitude": 416318082,
"longitude": -749677716
},
"name": "78-98 Schalck Road, Narrowsburg, NY 12764, USA"
}, {
"location": {
"latitude": 415301720,
"longitude": -748416257
},
"name": "282 Lakeview Drive Road, Highland Lake, NY 12743, USA"
}, {
"location": {
"latitude": 402647019,
"longitude": -747071791
},
"name": "330 Evelyn Avenue, Hamilton Township, NJ 08619, USA"
}, {
"location": {
"latitude": 412567807,
"longitude": -741058078
},
"name": "New York State Reference Route 987E, Southfields, NY 10975, USA"
}, {
"location": {
"latitude": 416855156,
"longitude": -744420597
},
"name": "103-271 Tempaloni Road, Ellenville, NY 12428, USA"
}, {
"location": {
"latitude": 404663628,
"longitude": -744820157
},
"name": "1300 Airport Road, North Brunswick Township, NJ 08902, USA"
}, {
"location": {
"latitude": 407113723,
"longitude": -749746483
},
"name": ""
}, {
"location": {
"latitude": 402133926,
"longitude": -743613249
},
"name": ""
}, {
"location": {
"latitude": 400273442,
"longitude": -741220915
},
"name": ""
}, {
"location": {
"latitude": 411236786,
"longitude": -744070769
},
"name": ""
}, {
"location": {
"latitude": 411633782,
"longitude": -746784970
},
"name": "211-225 Plains Road, Augusta, NJ 07822, USA"
}, {
"location": {
"latitude": 415830701,
"longitude": -742952812
},
"name": ""
}, {
"location": {
"latitude": 413447164,
"longitude": -748712898
},
"name": "165 Pedersen Ridge Road, Milford, PA 18337, USA"
}, {
"location": {
"latitude": 405047245,
"longitude": -749800722
},
"name": "100-122 Locktown Road, Frenchtown, NJ 08825, USA"
}, {
"location": {
"latitude": 418858923,
"longitude": -746156790
},
"name": ""
}, {
"location": {
"latitude": 417951888,
"longitude": -748484944
},
"name": "650-652 Willi Hill Road, Swan Lake, NY 12783, USA"
}, {
"location": {
"latitude": 407033786,
"longitude": -743977337
},
"name": "26 East 3rd Street, New Providence, NJ 07974, USA"
}, {
"location": {
"latitude": 417548014,
"longitude": -740075041
},
"name": ""
}, {
"location": {
"latitude": 410395868,
"longitude": -744972325
},
"name": ""
}, {
"location": {
"latitude": 404615353,
"longitude": -745129803
},
"name": ""
}, {
"location": {
"latitude": 406589790,
"longitude": -743560121
},
"name": "611 Lawrence Avenue, Westfield, NJ 07090, USA"
}, {
"location": {
"latitude": 414653148,
"longitude": -740477477
},
"name": "18 Lannis Avenue, New Windsor, NY 12553, USA"
}, {
"location": {
"latitude": 405957808,
"longitude": -743255336
},
"name": "82-104 Amherst Avenue, Colonia, NJ 07067, USA"
}, {
"location": {
"latitude": 411733589,
"longitude": -741648093
},
"name": "170 Seven Lakes Drive, Sloatsburg, NY 10974, USA"
}, {
"location": {
"latitude": 412676291,
"longitude": -742606606
},
"name": "1270 Lakes Road, Monroe, NY 10950, USA"
}, {
"location": {
"latitude": 409224445,
"longitude": -748286738
},
"name": "509-535 Alphano Road, Great Meadows, NJ 07838, USA"
}, {
"location": {
"latitude": 406523420,
"longitude": -742135517
},
"name": "652 Garden Street, Elizabeth, NJ 07202, USA"
}, {
"location": {
"latitude": 401827388,
"longitude": -740294537
},
"name": "349 Sea Spray Court, Neptune City, NJ 07753, USA"
}, {
"location": {
"latitude": 410564152,
"longitude": -743685054
},
"name": "13-17 Stanley Street, West Milford, NJ 07480, USA"
}, {
"location": {
"latitude": 408472324,
"longitude": -740726046
},
"name": "47 Industrial Avenue, Teterboro, NJ 07608, USA"
}, {
"location": {
"latitude": 412452168,
"longitude": -740214052
},
"name": "5 White Oak Lane, Stony Point, NY 10980, USA"
}, {
"location": {
"latitude": 409146138,
"longitude": -746188906
},
"name": "Berkshire Valley Management Area Trail, Jefferson, NJ, USA"
}, {
"location": {
"latitude": 404701380,
"longitude": -744781745
},
"name": "1007 Jersey Avenue, New Brunswick, NJ 08901, USA"
}, {
"location": {
"latitude": 409642566,
"longitude": -746017679
},
"name": "6 East Emerald Isle Drive, Lake Hopatcong, NJ 07849, USA"
}, {
"location": {
"latitude": 408031728,
"longitude": -748645385
},
"name": "1358-1474 New Jersey 57, Port Murray, NJ 07865, USA"
}, {
"location": {
"latitude": 413700272,
"longitude": -742135189
},
"name": "367 Prospect Road, Chester, NY 10918, USA"
}, {
"location": {
"latitude": 404310607,
"longitude": -740282632
},
"name": "10 Simon Lake Drive, Atlantic Highlands, NJ 07716, USA"
}, {
"location": {
"latitude": 409319800,
"longitude": -746201391
},
"name": "11 Ward Street, Mount Arlington, NJ 07856, USA"
}, {
"location": {
"latitude": 406685311,
"longitude": -742108603
},
"name": "300-398 Jefferson Avenue, Elizabeth, NJ 07201, USA"
}, {
"location": {
"latitude": 419018117,
"longitude": -749142781
},
"name": "43 Dreher Road, Roscoe, NY 12776, USA"
}, {
"location": {
"latitude": 412856162,
"longitude": -745148837
},
"name": "Swan Street, Pine Island, NY 10969, USA"
}, {
"location": {
"latitude": 416560744,
"longitude": -746721964
},
"name": "66 Pleasantview Avenue, Monticello, NY 12701, USA"
}, {
"location": {
"latitude": 405314270,
"longitude": -749836354
},
"name": ""
}, {
"location": {
"latitude": 414219548,
"longitude": -743327440
},
"name": ""
}, {
"location": {
"latitude": 415534177,
"longitude": -742900616
},
"name": "565 Winding Hills Road, Montgomery, NY 12549, USA"
}, {
"location": {
"latitude": 406898530,
"longitude": -749127080
},
"name": "231 Rocky Run Road, Glen Gardner, NJ 08826, USA"
}, {
"location": {
"latitude": 407586880,
"longitude": -741670168
},
"name": "100 Mount Pleasant Avenue, Newark, NJ 07104, USA"
}, {
"location": {
"latitude": 400106455,
"longitude": -742870190
},
"name": "517-521 Huntington Drive, Manchester Township, NJ 08759, USA"
}, {
"location": {
"latitude": 400066188,
"longitude": -746793294
},
"name": ""
}, {
"location": {
"latitude": 418803880,
"longitude": -744102673
},
"name": "40 Mountain Road, Napanoch, NY 12458, USA"
}, {
"location": {
"latitude": 414204288,
"longitude": -747895140
},
"name": ""
}, {
"location": {
"latitude": 414777405,
"longitude": -740615601
},
"name": ""
}, {
"location": {
"latitude": 415464475,
"longitude": -747175374
},
"name": "48 North Road, Forestburgh, NY 12777, USA"
}, {
"location": {
"latitude": 404062378,
"longitude": -746376177
},
"name": ""
}, {
"location": {
"latitude": 405688272,
"longitude": -749285130
},
"name": ""
}, {
"location": {
"latitude": 400342070,
"longitude": -748788996
},
"name": ""
}, {
"location": {
"latitude": 401809022,
"longitude": -744157964
},
"name": ""
}, {
"location": {
"latitude": 404226644,
"longitude": -740517141
},
"name": "9 Thompson Avenue, Leonardo, NJ 07737, USA"
}, {
"location": {
"latitude": 410322033,
"longitude": -747871659
},
"name": ""
}, {
"location": {
"latitude": 407100674,
"longitude": -747742727
},
"name": ""
}, {
"location": {
"latitude": 418811433,
"longitude": -741718005
},
"name": "213 Bush Road, Stone Ridge, NY 12484, USA"
}, {
"location": {
"latitude": 415034302,
"longitude": -743850945
},
"name": ""
}, {
"location": {
"latitude": 411349992,
"longitude": -743694161
},
"name": ""
}, {
"location": {
"latitude": 404839914,
"longitude": -744759616
},
"name": "1-17 Bergen Court, New Brunswick, NJ 08901, USA"
}, {
"location": {
"latitude": 414638017,
"longitude": -745957854
},
"name": "35 Oakland Valley Road, Cuddebackville, NY 12729, USA"
}, {
"location": {
"latitude": 412127800,
"longitude": -740173578
},
"name": ""
}, {
"location": {
"latitude": 401263460,
"longitude": -747964303
},
"name": ""
}, {
"location": {
"latitude": 412843391,
"longitude": -749086026
},
"name": ""
}, {
"location": {
"latitude": 418512773,
"longitude": -743067823
},
"name": ""
}, {
"location": {
"latitude": 404318328,
"longitude": -740835638
},
"name": "42-102 Main Street, Belford, NJ 07718, USA"
}, {
"location": {
"latitude": 419020746,
"longitude": -741172328
},
"name": ""
}, {
"location": {
"latitude": 404080723,
"longitude": -746119569
},
"name": ""
}, {
"location": {
"latitude": 401012643,
"longitude": -744035134
},
"name": ""
}, {
"location": {
"latitude": 404306372,
"longitude": -741079661
},
"name": ""
}, {
"location": {
"latitude": 403966326,
"longitude": -748519297
},
"name": ""
}, {
"location": {
"latitude": 405002031,
"longitude": -748407866
},
"name": ""
}, {
"location": {
"latitude": 409532885,
"longitude": -742200683
},
"name": ""
}, {
"location": {
"latitude": 416851321,
"longitude": -742674555
},
"name": ""
}, {
"location": {
"latitude": 406411633,
"longitude": -741722051
},
"name": "3387 Richmond Terrace, Staten Island, NY 10303, USA"
}, {
"location": {
"latitude": 413069058,
"longitude": -744597778
},
"name": "261 Van Sickle Road, Goshen, NY 10924, USA"
}, {
"location": {
"latitude": 418465462,
"longitude": -746859398
},
"name": ""
}, {
"location": {
"latitude": 411733222,
"longitude": -744228360
},
"name": ""
}, {
"location": {
"latitude": 410248224,
"longitude": -747127767
},
"name": "3 Hasta Way, Newton, NJ 07860, USA"
}]`)
var grpcBundleWithAuthCheck = map[string]string{
"manifest.json": `
{
"file_list": [],
"custom_middleware": {
"driver": "grpc",
"auth_check": {
"name": "MyAuthHook"
}
},
"checksum": "d41d8cd98f00b204e9800998ecf8427e"
}
`,
}
var (
handlerTypes = []apidef.TykEventHandlerName{
EH_LogHandler,
EH_WebHook,
}
)
var overrideResponseJSVM = map[string]string{
"manifest.json": `
{
"file_list": [],
"custom_middleware": {
"driver": "otto",
"pre": [{
"name": "pre",
"path": "pre.js"
}]
},
"checksum": "d41d8cd98f00b204e9800998ecf8427e"
}
`,
"pre.js": `
var pre = new TykJS.TykMiddleware.NewMiddleware({});
pre.NewProcessRequest(function(request, session) {
if (request.Params["response_body"]) {
request.ReturnOverrides.ResponseBody = 'foobar'
} else {
request.ReturnOverrides.ResponseError = '{"foo": "bar"}'
}
request.ReturnOverrides.ResponseCode = parseInt(request.Params["status"])
request.ReturnOverrides.ResponseHeaders = {"X-Foo": "Bar"}
if (request.Params["override"]) {
request.ReturnOverrides.OverrideError = true
}
return pre.ReturnData(request, {});
});
`,
}
var overrideResponsePython = map[string]string{
"manifest.json": `
{
"file_list": [
"middleware.py"
],
"custom_middleware": {
"driver": "python",
"pre": [{
"name": "MyRequestHook"
}]
},
"checksum": "81f585cdf7bf352e3c33ed62396b1e8e"
}
`,
"middleware.py": `
from tyk.decorators import *
from gateway import TykGateway as tyk
@Hook
def MyRequestHook(request, response, session, metadata, spec):
request.object.return_overrides.headers['X-Foo'] = 'Bar'
request.object.return_overrides.response_code = int(request.object.params["status"])
if request.object.params["response_body"] == "true":
request.object.return_overrides.response_body = "foobar"
else:
request.object.return_overrides.response_error = "{\"foo\": \"bar\"}"
if request.object.params["override"]:
request.object.return_overrides.override_error = True
return request, session
`,
}
var sess = user.SessionState{
OrgID: "TestBaseMiddleware_OrgSessionExpiry",
DataExpires: 110,
}
var testBlackListIPData = []struct {
remote, forwarded, xRealIP string
wantCode int
}{
{"127.0.0.1:80", "", "", http.StatusForbidden}, // remote exact match
{"127.0.0.2:80", "", "", http.StatusForbidden}, // remote CIDR match
{"10.0.0.1:80", "", "", http.StatusOK}, // no match
{"10.0.0.1:80", "127.0.0.1", "", http.StatusForbidden}, // forwarded exact match
{"10.0.0.1:80", "127.0.0.2", "", http.StatusForbidden}, // forwarded CIDR match
{"10.0.0.1:80", "", "bob", http.StatusOK}, // no match
}
var (
testBundlesPath = filepath.Join(testMiddlewarePath, "bundles")
)
var testJsonSchema = `{
"title": "Person",
"type": "object",
"properties": {
"firstName": {
"type": "string"
},
"lastName": {
"type": "string"
},
"age": {
"description": "Age in years",
"type": "integer",
"minimum": 0
},
"objs":{
"enum":["a","b","c"],
"type":"string"
}
},
"required": ["firstName", "lastName"]
}`
var testRewriterData = []struct {
name string
pattern, to string
in, want string
}{
{
"Encoded",
"/test/payment-intents", "/change/to/me",
"/test/payment%2Dintents", "/change/to/me",
},
{
"MatchEncodedChars",
"^(.+)%2[Dd](.+)$", "/change/to/me",
"/test/payment%2Dintents", "/change/to/me",
},
{
"Straight",
"/test/straight/rewrite", "/change/to/me",
"/test/straight/rewrite", "/change/to/me",
},
{
"OneVal",
"test/val/(.*)", "change/to/$1",
"/test/val/VALUE", "change/to/VALUE",
},
{
"OneVal Special Case",
"test/val/(.*)", "/test/val/$1",
"/test/val/VALUE%2C", "/test/val/VALUE%2C",
},
{
"OneVal Special Case With Query Param Encoded",
"test/val/(.*)", "/test/val/$1",
"/test/val/VALUE%2C?a=te%2Cst", "/test/val/VALUE%2C?a=te%2Cst",
},
{
"ThreeVals",
"/test/val/(.*)/space/(.*)/and/then/(.*)", "/change/to/$1/$2/$3",
"/test/val/ONE/space/TWO/and/then/THREE", "/change/to/ONE/TWO/THREE",
},
{
"Reverse",
"/test/val/(.*)/space/(.*)/and/then/(.*)", "/change/to/$3/$2/$1",
"/test/val/ONE/space/TWO/and/then/THREE", "/change/to/THREE/TWO/ONE",
},
{
"Missing",
"/test/val/(.*)/space/(.*)/and/then/(.*)", "/change/to/$1/$2",
"/test/val/ONE/space/TWO/and/then/THREE", "/change/to/ONE/TWO",
},
{
"MissingAgain",
"/test/val/(.*)/space/(.*)/and/then/(.*)", "/change/to/$3/$1",
"/test/val/ONE/space/TWO/and/then/THREE", "/change/to/THREE/ONE",
},
{
"QS",
"(.*)", "$1&newParam=that",
"/foo/bar?param1=this", "/foo/bar?param1=this&newParam=that",
},
{
"QS2",
"/test/val/(.*)/space/(.*)/and/then(.*)", "/change/to/$2/$1$3",
"/test/val/ONE/space/TWO/and/then?param1=this", "/change/to/TWO/ONE?param1=this",
},
}
var testWhiteListIPData = []struct {
remote, forwarded, xRealIP string
wantCode int
}{
{"127.0.0.1:80", "", "", http.StatusOK}, // remote exact match
{"127.0.0.2:80", "", "", http.StatusOK}, // remote CIDR match
{"10.0.0.1:80", "", "", http.StatusForbidden}, // no match
{"10.0.0.1:80", "127.0.0.1", "", http.StatusOK}, // forwarded exact match
{"10.0.0.1:80", "127.0.0.2", "", http.StatusOK}, // forwarded CIDR match
{"10.0.0.1:80", "", "bob", http.StatusForbidden}, // no match
}
Types
JwtCreator
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| type |
|
No comment on field. |
type JwtCreator func() *user.SessionState
TestAuth
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
|
No comment on field. | |
| HeaderKey |
|
No comment on field. |
| QueryParam |
|
No comment on field. |
type TestAuth struct {
apidef.AuthConfig
HeaderKey string
QueryParam string
}
answers
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| mu |
|
No comment on field. |
| ping |
|
No comment on field. |
| cancel |
|
No comment on field. |
type answers struct {
mu sync.RWMutex
ping, fail, up bool
cancel func()
}
bindAPIDefFunc
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| type |
|
No comment on field. |
type bindAPIDefFunc = func(*APISpec)
bindContextFunc
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| type |
|
No comment on field. |
type bindContextFunc = func(context.Context) context.Context
configTestReverseProxyDnsCache
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
|
No comment on field. | |
| etcHostsMap |
|
No comment on field. |
| dnsConfig |
|
No comment on field. |
type configTestReverseProxyDnsCache struct {
*testing.T
etcHostsMap map[string][]string
dnsConfig config.DnsCacheConfig
}
countingStorageHandler
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| deleteRawKeyMutex |
|
No comment on field. |
| deleteRawKeyCount |
|
No comment on field. |
type countingStorageHandler struct {
deleteRawKeyMutex *sync.Mutex
deleteRawKeyCount int
}
customListener
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| L |
|
No comment on field. |
type customListener struct {
L net.Listener
}
gatewayGetHostDetailsTestCheckFn
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| type |
|
No comment on field. |
type gatewayGetHostDetailsTestCheckFn func(*testing.T, *test.BufferedLogger, *Gateway)
loginCredsOrToken
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| Username |
|
No comment on field. |
| Password |
|
No comment on field. |
| TokenBasedAuth |
|
No comment on field. |
| Token |
|
No comment on field. |
type loginCredsOrToken struct {
Username string
Password string
TokenBasedAuth bool
Token string
}
mockStore
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
|
No comment on field. | |
| DetailNotFound |
|
DetailNotFound is used to make mocked SessionDetail return (x,false), as if it don't find the session in the mocked storage. |
type mockStore struct {
SessionHandler
//DetailNotFound is used to make mocked SessionDetail return (x,false), as if it don't find the session in the mocked storage.
DetailNotFound bool
}
modifiedMiddleware
modifiedMiddleware is a sample custom middleware component, must inherit TykMiddleware so you have access to spec and definition data
| Field name | Field type | Comment |
|---|---|---|
|
No comment on field. |
type modifiedMiddleware struct {
*BaseMiddleware
}
modifiedMiddlewareConfig
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| CustomData |
|
No comment on field. |
type modifiedMiddlewareConfig struct {
CustomData string `mapstructure:"custom_data" json:"custom_data"`
}
routeGuideServer
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| savedFeatures |
|
No comment on field. |
|
No comment on field. | |
| mu |
|
No comment on field. |
| routeNotes |
|
No comment on field. |
type routeGuideServer struct {
savedFeatures []*pb.Feature // read-only after initialized
pb.UnimplementedRouteGuideServer
mu sync.Mutex // protects routeNotes
routeNotes map[string][]*pb.RouteNote
}
server
server is used to implement helloworld.GreeterServer.
| Field name | Field type | Comment |
|---|---|---|
|
No comment on field. |
type server struct {
pbexample.UnimplementedGreeterServer
}
testAuthFailEventHandler
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| cb |
|
No comment on field. |
type testAuthFailEventHandler struct {
cb func(config.EventMessage)
}
testContextVarsData
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| Method |
|
No comment on field. |
| URL |
|
No comment on field. |
| Data |
|
No comment on field. |
| ExpectedCtxDataObject |
|
No comment on field. |
| Header |
|
No comment on field. |
type testContextVarsData struct {
Method string
URL string
Data string
ExpectedCtxDataObject map[string]interface{}
Header http.Header
}
testEventHandler
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| cb |
|
No comment on field. |
type testEventHandler struct {
cb func(config.EventMessage)
}
testRewriterCase
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| name |
|
No comment on field. |
| meta |
|
No comment on field. |
| reqMaker |
|
No comment on field. |
| want |
|
No comment on field. |
type testRewriterCase struct {
name string
meta *apidef.URLRewriteMeta
reqMaker func() *http.Request
want string
}
tokenData
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| AccessToken |
|
No comment on field. |
| RefreshToken |
|
No comment on field. |
type tokenData struct {
AccessToken string `json:"access_token"`
RefreshToken string `json:"refresh_token"`
}