github.com/anuvu/tyk@v2.9.0-beta9-dl-apic+incompatible/gateway/api_definition.go (about)

     1  package gateway
     2  
     3  import (
     4  	"encoding/base64"
     5  	"encoding/json"
     6  	"errors"
     7  	"fmt"
     8  	"io"
     9  	"io/ioutil"
    10  	"net/http"
    11  	"net/url"
    12  	"os"
    13  	"path/filepath"
    14  	"strings"
    15  	"sync"
    16  	"sync/atomic"
    17  	"text/template"
    18  	"time"
    19  
    20  	sprig "gopkg.in/Masterminds/sprig.v2"
    21  
    22  	"github.com/TykTechnologies/tyk/headers"
    23  	"github.com/TykTechnologies/tyk/rpc"
    24  
    25  	circuit "github.com/rubyist/circuitbreaker"
    26  	"github.com/sirupsen/logrus"
    27  
    28  	"github.com/TykTechnologies/gojsonschema"
    29  	"github.com/TykTechnologies/tyk/apidef"
    30  	"github.com/TykTechnologies/tyk/config"
    31  	"github.com/TykTechnologies/tyk/regexp"
    32  	"github.com/TykTechnologies/tyk/storage"
    33  	"github.com/garyburd/redigo/redis"
    34  )
    35  
    36  //const used by cache middleware
    37  const SAFE_METHODS = "SAFE_METHODS"
    38  
    39  const (
    40  	LDAPStorageEngine apidef.StorageEngineCode = "ldap"
    41  	RPCStorageEngine  apidef.StorageEngineCode = "rpc"
    42  )
    43  
    44  // Constants used by the version check middleware
    45  const (
    46  	headerLocation    = "header"
    47  	urlParamLocation  = "url-param"
    48  	urlLocation       = "url"
    49  	expiredTimeFormat = "2006-01-02 15:04"
    50  )
    51  
    52  // URLStatus is a custom enum type to avoid collisions
    53  type URLStatus int
    54  
    55  // Enums representing the various statuses for a VersionInfo Path match during a
    56  // proxy request
    57  const (
    58  	_ URLStatus = iota
    59  	Ignored
    60  	WhiteList
    61  	BlackList
    62  	Cached
    63  	Transformed
    64  	TransformedJQ
    65  	HeaderInjected
    66  	HeaderInjectedResponse
    67  	TransformedResponse
    68  	TransformedJQResponse
    69  	HardTimeout
    70  	CircuitBreaker
    71  	URLRewrite
    72  	VirtualPath
    73  	RequestSizeLimit
    74  	MethodTransformed
    75  	RequestTracked
    76  	RequestNotTracked
    77  	ValidateJSONRequest
    78  	Internal
    79  )
    80  
    81  // RequestStatus is a custom type to avoid collisions
    82  type RequestStatus string
    83  
    84  // Statuses of the request, all are false-y except StatusOk and StatusOkAndIgnore
    85  const (
    86  	VersionNotFound                RequestStatus = "Version information not found"
    87  	VersionDoesNotExist            RequestStatus = "This API version does not seem to exist"
    88  	VersionWhiteListStatusNotFound RequestStatus = "WhiteListStatus for path not found"
    89  	VersionExpired                 RequestStatus = "Api Version has expired, please check documentation or contact administrator"
    90  	EndPointNotAllowed             RequestStatus = "Requested endpoint is forbidden"
    91  	StatusOkAndIgnore              RequestStatus = "Everything OK, passing and not filtering"
    92  	StatusOk                       RequestStatus = "Everything OK, passing"
    93  	StatusCached                   RequestStatus = "Cached path"
    94  	StatusTransform                RequestStatus = "Transformed path"
    95  	StatusTransformResponse        RequestStatus = "Transformed response"
    96  	StatusTransformJQ              RequestStatus = "Transformed path with JQ"
    97  	StatusTransformJQResponse      RequestStatus = "Transformed response with JQ"
    98  	StatusHeaderInjected           RequestStatus = "Header injected"
    99  	StatusMethodTransformed        RequestStatus = "Method Transformed"
   100  	StatusHeaderInjectedResponse   RequestStatus = "Header injected on response"
   101  	StatusRedirectFlowByReply      RequestStatus = "Exceptional action requested, redirecting flow!"
   102  	StatusHardTimeout              RequestStatus = "Hard Timeout enforced on path"
   103  	StatusCircuitBreaker           RequestStatus = "Circuit breaker enforced"
   104  	StatusURLRewrite               RequestStatus = "URL Rewritten"
   105  	StatusVirtualPath              RequestStatus = "Virtual Endpoint"
   106  	StatusRequestSizeControlled    RequestStatus = "Request Size Limited"
   107  	StatusRequestTracked           RequestStatus = "Request Tracked"
   108  	StatusRequestNotTracked        RequestStatus = "Request Not Tracked"
   109  	StatusValidateJSON             RequestStatus = "Validate JSON"
   110  	StatusInternal                 RequestStatus = "Internal path"
   111  )
   112  
   113  // URLSpec represents a flattened specification for URLs, used to check if a proxy URL
   114  // path is on any of the white, black or ignored lists. This is generated as part of the
   115  // configuration init
   116  type URLSpec struct {
   117  	Spec                      *regexp.Regexp
   118  	Status                    URLStatus
   119  	MethodActions             map[string]apidef.EndpointMethodMeta
   120  	CacheConfig               EndPointCacheMeta
   121  	TransformAction           TransformSpec
   122  	TransformResponseAction   TransformSpec
   123  	TransformJQAction         TransformJQSpec
   124  	TransformJQResponseAction TransformJQSpec
   125  	InjectHeaders             apidef.HeaderInjectionMeta
   126  	InjectHeadersResponse     apidef.HeaderInjectionMeta
   127  	HardTimeout               apidef.HardTimeoutMeta
   128  	CircuitBreaker            ExtendedCircuitBreakerMeta
   129  	URLRewrite                *apidef.URLRewriteMeta
   130  	VirtualPathSpec           apidef.VirtualMeta
   131  	RequestSize               apidef.RequestSizeMeta
   132  	MethodTransform           apidef.MethodTransformMeta
   133  	TrackEndpoint             apidef.TrackEndpointMeta
   134  	DoNotTrackEndpoint        apidef.TrackEndpointMeta
   135  	ValidatePathMeta          apidef.ValidatePathMeta
   136  	Internal                  apidef.InternalMeta
   137  }
   138  
   139  type EndPointCacheMeta struct {
   140  	Method        string
   141  	CacheKeyRegex string
   142  }
   143  
   144  type TransformSpec struct {
   145  	apidef.TemplateMeta
   146  	Template *template.Template
   147  }
   148  
   149  type ExtendedCircuitBreakerMeta struct {
   150  	apidef.CircuitBreakerMeta
   151  	CB *circuit.Breaker `json:"-"`
   152  }
   153  
   154  // APISpec represents a path specification for an API, to avoid enumerating multiple nested lists, a single
   155  // flattened URL list is checked for matching paths and then it's status evaluated if found.
   156  type APISpec struct {
   157  	*apidef.APIDefinition
   158  	sync.RWMutex
   159  
   160  	RxPaths                  map[string][]URLSpec
   161  	WhiteListEnabled         map[string]bool
   162  	target                   *url.URL
   163  	AuthManager              AuthorisationHandler
   164  	SessionManager           SessionHandler
   165  	OAuthManager             *OAuthManager
   166  	OrgSessionManager        SessionHandler
   167  	EventPaths               map[apidef.TykEvent][]config.TykEventHandler
   168  	Health                   HealthChecker
   169  	JSVM                     JSVM
   170  	ResponseChain            []TykResponseHandler
   171  	RoundRobin               RoundRobin
   172  	URLRewriteEnabled        bool
   173  	CircuitBreakerEnabled    bool
   174  	EnforcedTimeoutEnabled   bool
   175  	LastGoodHostList         *apidef.HostList
   176  	HasRun                   bool
   177  	ServiceRefreshInProgress bool
   178  	HTTPTransport            http.RoundTripper
   179  	HTTPTransportCreated     time.Time
   180  	WSTransport              http.RoundTripper
   181  	WSTransportCreated       time.Time
   182  	GlobalConfig             config.Config
   183  	OrgHasNoSession          bool
   184  
   185  	middlewareChain http.Handler
   186  
   187  	shouldRelease bool
   188  }
   189  
   190  // Release re;leases all resources associated with API spec
   191  func (s *APISpec) Release() {
   192  	// mark spec as to be released
   193  	s.shouldRelease = true
   194  
   195  	// release circuit breaker resources
   196  	for _, path := range s.RxPaths {
   197  		for _, urlSpec := range path {
   198  			if urlSpec.CircuitBreaker.CB != nil {
   199  				// this will force CB-event reading routing to exit
   200  				urlSpec.CircuitBreaker.CB.Reset()
   201  
   202  				// TODO: close all circuit breaker's event receivers
   203  				// which should let event reading Go-routines to exit (need to change circuitbreaker package)
   204  			}
   205  		}
   206  	}
   207  
   208  	// release all other resources associated with spec
   209  }
   210  
   211  // APIDefinitionLoader will load an Api definition from a storage
   212  // system.
   213  type APIDefinitionLoader struct{}
   214  
   215  // Nonce to use when interacting with the dashboard service
   216  var ServiceNonce string
   217  
   218  // MakeSpec will generate a flattened URLSpec from and APIDefinitions' VersionInfo data. paths are
   219  // keyed to the Api version name, which is determined during routing to speed up lookups
   220  func (a APIDefinitionLoader) MakeSpec(def *apidef.APIDefinition, logger *logrus.Entry) *APISpec {
   221  	spec := &APISpec{}
   222  
   223  	if logger == nil {
   224  		logger = logrus.NewEntry(log)
   225  	}
   226  
   227  	// parse version expiration time stamps
   228  	for key, ver := range def.VersionData.Versions {
   229  		if ver.Expires == "" || ver.Expires == "-1" {
   230  			continue
   231  		}
   232  		// calculate the time
   233  		if t, err := time.Parse(expiredTimeFormat, ver.Expires); err != nil {
   234  			logger.WithError(err).WithField("Expires", ver.Expires).Error("Could not parse expiry date for API")
   235  		} else {
   236  			ver.ExpiresTs = t
   237  			def.VersionData.Versions[key] = ver
   238  		}
   239  	}
   240  
   241  	spec.APIDefinition = def
   242  
   243  	// We'll push the default HealthChecker:
   244  	spec.Health = &DefaultHealthChecker{
   245  		APIID: spec.APIID,
   246  	}
   247  
   248  	// Add any new session managers or auth handlers here
   249  	spec.AuthManager = &DefaultAuthorisationManager{}
   250  
   251  	spec.SessionManager = &DefaultSessionManager{}
   252  	spec.OrgSessionManager = &DefaultSessionManager{}
   253  
   254  	spec.GlobalConfig = config.Global()
   255  
   256  	// Create and init the virtual Machine
   257  	if config.Global().EnableJSVM {
   258  		spec.JSVM.Init(spec, logger)
   259  	}
   260  
   261  	// Set up Event Handlers
   262  	if len(def.EventHandlers.Events) > 0 {
   263  		logger.Debug("Initializing event handlers")
   264  	}
   265  	spec.EventPaths = make(map[apidef.TykEvent][]config.TykEventHandler)
   266  	for eventName, eventHandlerConfs := range def.EventHandlers.Events {
   267  		logger.Debug("FOUND EVENTS TO INIT")
   268  		for _, handlerConf := range eventHandlerConfs {
   269  			logger.Debug("CREATING EVENT HANDLERS")
   270  			eventHandlerInstance, err := EventHandlerByName(handlerConf, spec)
   271  
   272  			if err != nil {
   273  				logger.Error("Failed to init event handler: ", err)
   274  			} else {
   275  				logger.Debug("Init Event Handler: ", eventName)
   276  				spec.EventPaths[eventName] = append(spec.EventPaths[eventName], eventHandlerInstance)
   277  			}
   278  
   279  		}
   280  	}
   281  
   282  	spec.RxPaths = make(map[string][]URLSpec, len(def.VersionData.Versions))
   283  	spec.WhiteListEnabled = make(map[string]bool, len(def.VersionData.Versions))
   284  	for _, v := range def.VersionData.Versions {
   285  		var pathSpecs []URLSpec
   286  		var whiteListSpecs bool
   287  
   288  		// If we have transitioned to extended path specifications, we should use these now
   289  		if v.UseExtendedPaths {
   290  			pathSpecs, whiteListSpecs = a.getExtendedPathSpecs(v, spec)
   291  		} else {
   292  			logger.Warning("Legacy path detected! Upgrade to extended.")
   293  			pathSpecs, whiteListSpecs = a.getPathSpecs(v)
   294  		}
   295  		spec.RxPaths[v.Name] = pathSpecs
   296  		spec.WhiteListEnabled[v.Name] = whiteListSpecs
   297  	}
   298  
   299  	return spec
   300  }
   301  
   302  // FromDashboardService will connect and download ApiDefintions from a Tyk Dashboard instance.
   303  func (a APIDefinitionLoader) FromDashboardService(endpoint, secret string) ([]*APISpec, error) {
   304  	// Get the definitions
   305  	log.Debug("Calling: ", endpoint)
   306  	newRequest, err := http.NewRequest("GET", endpoint, nil)
   307  	if err != nil {
   308  		log.Error("Failed to create request: ", err)
   309  	}
   310  
   311  	newRequest.Header.Set("authorization", secret)
   312  	log.Debug("Using: NodeID: ", getNodeID())
   313  	newRequest.Header.Set(headers.XTykNodeID, getNodeID())
   314  
   315  	newRequest.Header.Set(headers.XTykNonce, ServiceNonce)
   316  
   317  	c := initialiseClient(120 * time.Second)
   318  	resp, err := c.Do(newRequest)
   319  	if err != nil {
   320  		return nil, err
   321  	}
   322  	defer resp.Body.Close()
   323  
   324  	if resp.StatusCode == http.StatusForbidden {
   325  		body, _ := ioutil.ReadAll(resp.Body)
   326  		reLogin()
   327  		return nil, fmt.Errorf("login failure, Response was: %v", string(body))
   328  	}
   329  
   330  	if resp.StatusCode != http.StatusOK {
   331  		body, _ := ioutil.ReadAll(resp.Body)
   332  		reLogin()
   333  		return nil, fmt.Errorf("dashboard API error, response was: %v", string(body))
   334  	}
   335  
   336  	// Extract tagged APIs#
   337  	var list struct {
   338  		Message []struct {
   339  			ApiDefinition *apidef.APIDefinition `bson:"api_definition" json:"api_definition"`
   340  		}
   341  		Nonce string
   342  	}
   343  	if err := json.NewDecoder(resp.Body).Decode(&list); err != nil {
   344  		body, _ := ioutil.ReadAll(resp.Body)
   345  		return nil, fmt.Errorf("failed to decode body: %v body was: %v", err, string(body))
   346  	}
   347  
   348  	// Extract tagged entries only
   349  	apiDefs := make([]*apidef.APIDefinition, 0)
   350  
   351  	if config.Global().DBAppConfOptions.NodeIsSegmented {
   352  		tagList := make(map[string]bool, len(config.Global().DBAppConfOptions.Tags))
   353  		toLoad := make(map[string]*apidef.APIDefinition)
   354  
   355  		for _, mt := range config.Global().DBAppConfOptions.Tags {
   356  			tagList[mt] = true
   357  		}
   358  
   359  		for _, apiEntry := range list.Message {
   360  			for _, t := range apiEntry.ApiDefinition.Tags {
   361  				if tagList[t] {
   362  					toLoad[apiEntry.ApiDefinition.APIID] = apiEntry.ApiDefinition
   363  				}
   364  			}
   365  		}
   366  
   367  		for _, apiDef := range toLoad {
   368  			apiDefs = append(apiDefs, apiDef)
   369  		}
   370  	} else {
   371  		for _, apiEntry := range list.Message {
   372  			apiDefs = append(apiDefs, apiEntry.ApiDefinition)
   373  		}
   374  	}
   375  
   376  	// Process
   377  	var specs []*APISpec
   378  	for _, def := range apiDefs {
   379  		spec := a.MakeSpec(def, nil)
   380  		specs = append(specs, spec)
   381  	}
   382  
   383  	// Set the nonce
   384  	ServiceNonce = list.Nonce
   385  	log.Debug("Loading APIS Finished: Nonce Set: ", ServiceNonce)
   386  
   387  	return specs, nil
   388  }
   389  
   390  // FromCloud will connect and download ApiDefintions from a Mongo DB instance.
   391  func (a APIDefinitionLoader) FromRPC(orgId string) ([]*APISpec, error) {
   392  	if rpc.IsEmergencyMode() {
   393  		return LoadDefinitionsFromRPCBackup()
   394  	}
   395  
   396  	store := RPCStorageHandler{}
   397  	if !store.Connect() {
   398  		return nil, errors.New("Can't connect RPC layer")
   399  	}
   400  
   401  	// enable segments
   402  	var tags []string
   403  	if config.Global().DBAppConfOptions.NodeIsSegmented {
   404  		log.Info("Segmented node, loading: ", config.Global().DBAppConfOptions.Tags)
   405  		tags = config.Global().DBAppConfOptions.Tags
   406  	}
   407  
   408  	apiCollection := store.GetApiDefinitions(orgId, tags)
   409  
   410  	//store.Disconnect()
   411  
   412  	if rpc.LoadCount() > 0 {
   413  		if err := saveRPCDefinitionsBackup(apiCollection); err != nil {
   414  			return nil, err
   415  		}
   416  	}
   417  
   418  	return a.processRPCDefinitions(apiCollection)
   419  }
   420  
   421  // FromCloud will connect and download ApiDefintions from a Mongo DB instance.
   422  func (a APIDefinitionLoader) FromRedis(db config.RedisDBAppConfOptionsConfig) ([]*APISpec, error) {
   423  	var specs []*APISpec
   424  	c := RedisPool.Get()
   425  	defer c.Close()
   426  	log.Info("Loading API Specification from redis")
   427  
   428  	// Load API Definition from Redis DB
   429  	apiKeys, err := redis.Strings(c.Do("KEYS", "*"))
   430  	if err != nil {
   431  		log.Error("Couldn't get api definition from redis db: ", err)
   432  	}
   433  	for _, v := range apiKeys {
   434  		apiDefinition, _ := redis.String(c.Do("GET", v))
   435  		def := a.ParseDefinition(strings.NewReader(apiDefinition))
   436  		spec := a.MakeSpec(def, nil)
   437  		specs = append(specs, spec)
   438  	}
   439  	return specs, nil
   440  }
   441  
   442  func (a APIDefinitionLoader) processRPCDefinitions(apiCollection string) ([]*APISpec, error) {
   443  
   444  	var apiDefs []*apidef.APIDefinition
   445  	if err := json.Unmarshal([]byte(apiCollection), &apiDefs); err != nil {
   446  		return nil, err
   447  	}
   448  
   449  	var specs []*APISpec
   450  	for _, def := range apiDefs {
   451  		def.DecodeFromDB()
   452  
   453  		if config.Global().SlaveOptions.BindToSlugsInsteadOfListenPaths {
   454  			newListenPath := "/" + def.Slug //+ "/"
   455  			log.Warning("Binding to ",
   456  				newListenPath,
   457  				" instead of ",
   458  				def.Proxy.ListenPath)
   459  
   460  			def.Proxy.ListenPath = newListenPath
   461  		}
   462  
   463  		spec := a.MakeSpec(def, nil)
   464  		specs = append(specs, spec)
   465  	}
   466  
   467  	return specs, nil
   468  }
   469  
   470  func (a APIDefinitionLoader) ParseDefinition(r io.Reader) *apidef.APIDefinition {
   471  	def := &apidef.APIDefinition{}
   472  	if err := json.NewDecoder(r).Decode(def); err != nil {
   473  		log.Error("[RPC] --> Couldn't unmarshal api configuration: ", err)
   474  	}
   475  	return def
   476  }
   477  
   478  // FromDir will load APIDefinitions from a directory on the filesystem. Definitions need
   479  // to be the JSON representation of APIDefinition object
   480  func (a APIDefinitionLoader) FromDir(dir string) []*APISpec {
   481  	var specs []*APISpec
   482  	// Grab json files from directory
   483  	paths, _ := filepath.Glob(filepath.Join(dir, "*.json"))
   484  	for _, path := range paths {
   485  		log.Info("Loading API Specification from ", path)
   486  		f, err := os.Open(path)
   487  		if err != nil {
   488  			log.Error("Couldn't open api configuration file: ", err)
   489  			continue
   490  		}
   491  		def := a.ParseDefinition(f)
   492  		f.Close()
   493  		spec := a.MakeSpec(def, nil)
   494  		specs = append(specs, spec)
   495  	}
   496  	return specs
   497  }
   498  
   499  func (a APIDefinitionLoader) getPathSpecs(apiVersionDef apidef.VersionInfo) ([]URLSpec, bool) {
   500  	ignoredPaths := a.compilePathSpec(apiVersionDef.Paths.Ignored, Ignored)
   501  	blackListPaths := a.compilePathSpec(apiVersionDef.Paths.BlackList, BlackList)
   502  	whiteListPaths := a.compilePathSpec(apiVersionDef.Paths.WhiteList, WhiteList)
   503  
   504  	combinedPath := []URLSpec{}
   505  	combinedPath = append(combinedPath, ignoredPaths...)
   506  	combinedPath = append(combinedPath, blackListPaths...)
   507  	combinedPath = append(combinedPath, whiteListPaths...)
   508  
   509  	return combinedPath, len(whiteListPaths) > 0
   510  }
   511  
   512  func (a APIDefinitionLoader) generateRegex(stringSpec string, newSpec *URLSpec, specType URLStatus) {
   513  	apiLangIDsRegex := regexp.MustCompile(`{([^}]*)}`)
   514  	asRegexStr := apiLangIDsRegex.ReplaceAllString(stringSpec, `([^/]*)`)
   515  	asRegex, _ := regexp.Compile(asRegexStr)
   516  	newSpec.Status = specType
   517  	newSpec.Spec = asRegex
   518  }
   519  
   520  func (a APIDefinitionLoader) compilePathSpec(paths []string, specType URLStatus) []URLSpec {
   521  	// transform a configuration URL into an array of URLSpecs
   522  	// This way we can iterate the whole array once, on match we break with status
   523  	urlSpec := []URLSpec{}
   524  
   525  	for _, stringSpec := range paths {
   526  		newSpec := URLSpec{}
   527  		a.generateRegex(stringSpec, &newSpec, specType)
   528  		urlSpec = append(urlSpec, newSpec)
   529  	}
   530  
   531  	return urlSpec
   532  }
   533  
   534  func (a APIDefinitionLoader) compileExtendedPathSpec(paths []apidef.EndPointMeta, specType URLStatus) []URLSpec {
   535  	// transform an extended configuration URL into an array of URLSpecs
   536  	// This way we can iterate the whole array once, on match we break with status
   537  	urlSpec := []URLSpec{}
   538  
   539  	for _, stringSpec := range paths {
   540  		newSpec := URLSpec{}
   541  		a.generateRegex(stringSpec.Path, &newSpec, specType)
   542  
   543  		// Extend with method actions
   544  		newSpec.MethodActions = stringSpec.MethodActions
   545  		urlSpec = append(urlSpec, newSpec)
   546  	}
   547  
   548  	return urlSpec
   549  }
   550  
   551  func (a APIDefinitionLoader) compileCachedPathSpec(oldpaths []string, newpaths []apidef.CacheMeta) []URLSpec {
   552  	// transform an extended configuration URL into an array of URLSpecs
   553  	// This way we can iterate the whole array once, on match we break with status
   554  	urlSpec := []URLSpec{}
   555  
   556  	for _, stringSpec := range oldpaths {
   557  		newSpec := URLSpec{}
   558  		a.generateRegex(stringSpec, &newSpec, Cached)
   559  		newSpec.CacheConfig.Method = SAFE_METHODS
   560  		newSpec.CacheConfig.CacheKeyRegex = ""
   561  		// Extend with method actions
   562  		urlSpec = append(urlSpec, newSpec)
   563  	}
   564  
   565  	for _, spec := range newpaths {
   566  		newSpec := URLSpec{}
   567  		a.generateRegex(spec.Path, &newSpec, Cached)
   568  		newSpec.CacheConfig.Method = spec.Method
   569  		newSpec.CacheConfig.CacheKeyRegex = spec.CacheKeyRegex
   570  		// Extend with method actions
   571  		urlSpec = append(urlSpec, newSpec)
   572  	}
   573  
   574  	return urlSpec
   575  }
   576  
   577  func (a APIDefinitionLoader) filterSprigFuncs() template.FuncMap {
   578  	tmp := sprig.GenericFuncMap()
   579  	delete(tmp, "env")
   580  	delete(tmp, "expandenv")
   581  
   582  	return template.FuncMap(tmp)
   583  }
   584  
   585  func (a APIDefinitionLoader) loadFileTemplate(path string) (*template.Template, error) {
   586  	log.Debug("-- Loading template: ", path)
   587  	return apidef.Template.New("").Funcs(a.filterSprigFuncs()).ParseFiles(path)
   588  }
   589  
   590  func (a APIDefinitionLoader) loadBlobTemplate(blob string) (*template.Template, error) {
   591  	log.Debug("-- Loading blob")
   592  	uDec, err := base64.StdEncoding.DecodeString(blob)
   593  	if err != nil {
   594  		return nil, err
   595  	}
   596  	return apidef.Template.New("").Funcs(a.filterSprigFuncs()).Parse(string(uDec))
   597  }
   598  
   599  func (a APIDefinitionLoader) compileTransformPathSpec(paths []apidef.TemplateMeta, stat URLStatus) []URLSpec {
   600  	// transform an extended configuration URL into an array of URLSpecs
   601  	// This way we can iterate the whole array once, on match we break with status
   602  	urlSpec := []URLSpec{}
   603  
   604  	log.Debug("Checking for transform paths...")
   605  	for _, stringSpec := range paths {
   606  		log.Debug("-- Generating path")
   607  		newSpec := URLSpec{}
   608  		a.generateRegex(stringSpec.Path, &newSpec, stat)
   609  		// Extend with template actions
   610  
   611  		newTransformSpec := TransformSpec{TemplateMeta: stringSpec}
   612  
   613  		// Load the templates
   614  		var err error
   615  
   616  		switch stringSpec.TemplateData.Mode {
   617  		case apidef.UseFile:
   618  			log.Debug("-- Using File mode")
   619  			newTransformSpec.Template, err = a.loadFileTemplate(stringSpec.TemplateData.TemplateSource)
   620  		case apidef.UseBlob:
   621  			log.Debug("-- Blob mode")
   622  			newTransformSpec.Template, err = a.loadBlobTemplate(stringSpec.TemplateData.TemplateSource)
   623  		default:
   624  			log.Warning("[Transform Templates] No template mode defined! Found: ", stringSpec.TemplateData.Mode)
   625  			err = errors.New("No valid template mode defined, must be either 'file' or 'blob'")
   626  		}
   627  
   628  		if stat == Transformed {
   629  			newSpec.TransformAction = newTransformSpec
   630  		} else {
   631  			newSpec.TransformResponseAction = newTransformSpec
   632  		}
   633  
   634  		if err == nil {
   635  			urlSpec = append(urlSpec, newSpec)
   636  			log.Debug("-- Loaded")
   637  		} else {
   638  			log.Error("Template load failure! Skipping transformation: ", err)
   639  		}
   640  
   641  	}
   642  
   643  	return urlSpec
   644  }
   645  
   646  func (a APIDefinitionLoader) compileInjectedHeaderSpec(paths []apidef.HeaderInjectionMeta, stat URLStatus) []URLSpec {
   647  	// transform an extended configuration URL into an array of URLSpecs
   648  	// This way we can iterate the whole array once, on match we break with status
   649  	urlSpec := []URLSpec{}
   650  
   651  	for _, stringSpec := range paths {
   652  		newSpec := URLSpec{}
   653  		a.generateRegex(stringSpec.Path, &newSpec, stat)
   654  		// Extend with method actions
   655  		if stat == HeaderInjected {
   656  			newSpec.InjectHeaders = stringSpec
   657  		} else {
   658  			newSpec.InjectHeadersResponse = stringSpec
   659  		}
   660  
   661  		urlSpec = append(urlSpec, newSpec)
   662  	}
   663  
   664  	return urlSpec
   665  }
   666  
   667  func (a APIDefinitionLoader) compileMethodTransformSpec(paths []apidef.MethodTransformMeta, stat URLStatus) []URLSpec {
   668  	// transform an extended configuration URL into an array of URLSpecs
   669  	// This way we can iterate the whole array once, on match we break with status
   670  	urlSpec := []URLSpec{}
   671  
   672  	for _, stringSpec := range paths {
   673  		newSpec := URLSpec{}
   674  		a.generateRegex(stringSpec.Path, &newSpec, stat)
   675  		newSpec.MethodTransform = stringSpec
   676  
   677  		urlSpec = append(urlSpec, newSpec)
   678  	}
   679  
   680  	return urlSpec
   681  }
   682  
   683  func (a APIDefinitionLoader) compileTimeoutPathSpec(paths []apidef.HardTimeoutMeta, stat URLStatus) []URLSpec {
   684  	// transform an extended configuration URL into an array of URLSpecs
   685  	// This way we can iterate the whole array once, on match we break with status
   686  	urlSpec := []URLSpec{}
   687  
   688  	for _, stringSpec := range paths {
   689  		newSpec := URLSpec{}
   690  		a.generateRegex(stringSpec.Path, &newSpec, stat)
   691  		// Extend with method actions
   692  		newSpec.HardTimeout = stringSpec
   693  
   694  		urlSpec = append(urlSpec, newSpec)
   695  	}
   696  
   697  	return urlSpec
   698  }
   699  
   700  func (a APIDefinitionLoader) compileRequestSizePathSpec(paths []apidef.RequestSizeMeta, stat URLStatus) []URLSpec {
   701  	// transform an extended configuration URL into an array of URLSpecs
   702  	// This way we can iterate the whole array once, on match we break with status
   703  	urlSpec := []URLSpec{}
   704  
   705  	for _, stringSpec := range paths {
   706  		newSpec := URLSpec{}
   707  		a.generateRegex(stringSpec.Path, &newSpec, stat)
   708  		// Extend with method actions
   709  		newSpec.RequestSize = stringSpec
   710  
   711  		urlSpec = append(urlSpec, newSpec)
   712  	}
   713  
   714  	return urlSpec
   715  }
   716  
   717  func (a APIDefinitionLoader) compileCircuitBreakerPathSpec(paths []apidef.CircuitBreakerMeta, stat URLStatus, apiSpec *APISpec) []URLSpec {
   718  	// transform an extended configuration URL into an array of URLSpecs
   719  	// This way we can iterate the whole array once, on match we break with status
   720  	urlSpec := []URLSpec{}
   721  
   722  	for _, stringSpec := range paths {
   723  		newSpec := URLSpec{}
   724  		a.generateRegex(stringSpec.Path, &newSpec, stat)
   725  		// Extend with method actions
   726  		newSpec.CircuitBreaker = ExtendedCircuitBreakerMeta{CircuitBreakerMeta: stringSpec}
   727  		log.Debug("Initialising circuit breaker for: ", stringSpec.Path)
   728  		newSpec.CircuitBreaker.CB = circuit.NewRateBreaker(stringSpec.ThresholdPercent, stringSpec.Samples)
   729  		events := newSpec.CircuitBreaker.CB.Subscribe()
   730  		go func(path string, spec *APISpec, breakerPtr *circuit.Breaker) {
   731  			timerActive := false
   732  			for e := range events {
   733  				switch e {
   734  				case circuit.BreakerTripped:
   735  					log.Warning("[PROXY] [CIRCUIT BREAKER] Breaker tripped for path: ", path)
   736  					log.Debug("Breaker tripped: ", e)
   737  					// Start a timer function
   738  
   739  					if !timerActive {
   740  						go func(timeout int, breaker *circuit.Breaker) {
   741  							log.Debug("-- Sleeping for (s): ", timeout)
   742  							time.Sleep(time.Duration(timeout) * time.Second)
   743  							log.Debug("-- Resetting breaker")
   744  							breaker.Reset()
   745  							timerActive = false
   746  						}(newSpec.CircuitBreaker.ReturnToServiceAfter, breakerPtr)
   747  						timerActive = true
   748  					}
   749  
   750  					if spec.Proxy.ServiceDiscovery.UseDiscoveryService {
   751  						if ServiceCache != nil {
   752  							log.Warning("[PROXY] [CIRCUIT BREAKER] Refreshing host list")
   753  							ServiceCache.Delete(spec.APIID)
   754  						}
   755  					}
   756  
   757  					spec.FireEvent(EventBreakerTriggered, EventCurcuitBreakerMeta{
   758  						EventMetaDefault: EventMetaDefault{Message: "Breaker Tripped"},
   759  						CircuitEvent:     e,
   760  						Path:             path,
   761  						APIID:            spec.APIID,
   762  					})
   763  
   764  				case circuit.BreakerReset:
   765  					// check if this spec is set to release resources
   766  					if spec.shouldRelease {
   767  						// time to stop this Go-routine
   768  						return
   769  					}
   770  
   771  					spec.FireEvent(EventBreakerTriggered, EventCurcuitBreakerMeta{
   772  						EventMetaDefault: EventMetaDefault{Message: "Breaker Reset"},
   773  						CircuitEvent:     e,
   774  						Path:             path,
   775  						APIID:            spec.APIID,
   776  					})
   777  
   778  				}
   779  			}
   780  		}(stringSpec.Path, apiSpec, newSpec.CircuitBreaker.CB)
   781  
   782  		urlSpec = append(urlSpec, newSpec)
   783  	}
   784  
   785  	return urlSpec
   786  }
   787  
   788  func (a APIDefinitionLoader) compileURLRewritesPathSpec(paths []apidef.URLRewriteMeta, stat URLStatus) []URLSpec {
   789  	// transform an extended configuration URL into an array of URLSpecs
   790  	// This way we can iterate the whole array once, on match we break with status
   791  	urlSpec := []URLSpec{}
   792  
   793  	for _, stringSpec := range paths {
   794  		curStringSpec := stringSpec
   795  		newSpec := URLSpec{}
   796  		a.generateRegex(curStringSpec.Path, &newSpec, stat)
   797  		// Extend with method actions
   798  		newSpec.URLRewrite = &curStringSpec
   799  
   800  		urlSpec = append(urlSpec, newSpec)
   801  	}
   802  
   803  	return urlSpec
   804  }
   805  
   806  func (a APIDefinitionLoader) compileVirtualPathspathSpec(paths []apidef.VirtualMeta, stat URLStatus, apiSpec *APISpec) []URLSpec {
   807  	if !config.Global().EnableJSVM {
   808  		return nil
   809  	}
   810  
   811  	// transform an extended configuration URL into an array of URLSpecs
   812  	// This way we can iterate the whole array once, on match we break with status
   813  	urlSpec := []URLSpec{}
   814  	for _, stringSpec := range paths {
   815  		newSpec := URLSpec{}
   816  		a.generateRegex(stringSpec.Path, &newSpec, stat)
   817  		// Extend with method actions
   818  		newSpec.VirtualPathSpec = stringSpec
   819  
   820  		preLoadVirtualMetaCode(&newSpec.VirtualPathSpec, &apiSpec.JSVM)
   821  
   822  		urlSpec = append(urlSpec, newSpec)
   823  	}
   824  
   825  	return urlSpec
   826  }
   827  
   828  func (a APIDefinitionLoader) compileTrackedEndpointPathspathSpec(paths []apidef.TrackEndpointMeta, stat URLStatus) []URLSpec {
   829  	urlSpec := []URLSpec{}
   830  
   831  	for _, stringSpec := range paths {
   832  		newSpec := URLSpec{}
   833  		a.generateRegex(stringSpec.Path, &newSpec, stat)
   834  
   835  		// set Path if it wasn't set
   836  		if stringSpec.Path == "" {
   837  			// even if it is empty (and regex matches everything) some middlewares expect to be value here
   838  			stringSpec.Path = "/"
   839  		}
   840  
   841  		// Extend with method actions
   842  		newSpec.TrackEndpoint = stringSpec
   843  		urlSpec = append(urlSpec, newSpec)
   844  	}
   845  
   846  	return urlSpec
   847  }
   848  
   849  func (a APIDefinitionLoader) compileValidateJSONPathspathSpec(paths []apidef.ValidatePathMeta, stat URLStatus) []URLSpec {
   850  	urlSpec := make([]URLSpec, len(paths))
   851  
   852  	for i, stringSpec := range paths {
   853  		newSpec := URLSpec{}
   854  		a.generateRegex(stringSpec.Path, &newSpec, stat)
   855  		// Extend with method actions
   856  
   857  		stringSpec.SchemaCache = gojsonschema.NewGoLoader(stringSpec.Schema)
   858  		newSpec.ValidatePathMeta = stringSpec
   859  		urlSpec[i] = newSpec
   860  	}
   861  
   862  	return urlSpec
   863  }
   864  
   865  func (a APIDefinitionLoader) compileUnTrackedEndpointPathspathSpec(paths []apidef.TrackEndpointMeta, stat URLStatus) []URLSpec {
   866  	urlSpec := []URLSpec{}
   867  
   868  	for _, stringSpec := range paths {
   869  		newSpec := URLSpec{}
   870  		a.generateRegex(stringSpec.Path, &newSpec, stat)
   871  		// Extend with method actions
   872  		newSpec.DoNotTrackEndpoint = stringSpec
   873  		urlSpec = append(urlSpec, newSpec)
   874  	}
   875  
   876  	return urlSpec
   877  }
   878  
   879  func (a APIDefinitionLoader) compileInternalPathspathSpec(paths []apidef.InternalMeta, stat URLStatus) []URLSpec {
   880  	urlSpec := []URLSpec{}
   881  
   882  	for _, stringSpec := range paths {
   883  		newSpec := URLSpec{}
   884  		a.generateRegex(stringSpec.Path, &newSpec, stat)
   885  		// Extend with method actions
   886  		newSpec.Internal = stringSpec
   887  		urlSpec = append(urlSpec, newSpec)
   888  	}
   889  
   890  	return urlSpec
   891  }
   892  
   893  func (a APIDefinitionLoader) getExtendedPathSpecs(apiVersionDef apidef.VersionInfo, apiSpec *APISpec) ([]URLSpec, bool) {
   894  	// TODO: New compiler here, needs to put data into a different structure
   895  
   896  	ignoredPaths := a.compileExtendedPathSpec(apiVersionDef.ExtendedPaths.Ignored, Ignored)
   897  	blackListPaths := a.compileExtendedPathSpec(apiVersionDef.ExtendedPaths.BlackList, BlackList)
   898  	whiteListPaths := a.compileExtendedPathSpec(apiVersionDef.ExtendedPaths.WhiteList, WhiteList)
   899  	cachedPaths := a.compileCachedPathSpec(apiVersionDef.ExtendedPaths.Cached, apiVersionDef.ExtendedPaths.AdvanceCacheConfig)
   900  	transformPaths := a.compileTransformPathSpec(apiVersionDef.ExtendedPaths.Transform, Transformed)
   901  	transformResponsePaths := a.compileTransformPathSpec(apiVersionDef.ExtendedPaths.TransformResponse, TransformedResponse)
   902  	transformJQPaths := a.compileTransformJQPathSpec(apiVersionDef.ExtendedPaths.TransformJQ, TransformedJQ)
   903  	transformJQResponsePaths := a.compileTransformJQPathSpec(apiVersionDef.ExtendedPaths.TransformJQResponse, TransformedJQResponse)
   904  	headerTransformPaths := a.compileInjectedHeaderSpec(apiVersionDef.ExtendedPaths.TransformHeader, HeaderInjected)
   905  	headerTransformPathsOnResponse := a.compileInjectedHeaderSpec(apiVersionDef.ExtendedPaths.TransformResponseHeader, HeaderInjectedResponse)
   906  	hardTimeouts := a.compileTimeoutPathSpec(apiVersionDef.ExtendedPaths.HardTimeouts, HardTimeout)
   907  	circuitBreakers := a.compileCircuitBreakerPathSpec(apiVersionDef.ExtendedPaths.CircuitBreaker, CircuitBreaker, apiSpec)
   908  	urlRewrites := a.compileURLRewritesPathSpec(apiVersionDef.ExtendedPaths.URLRewrite, URLRewrite)
   909  	virtualPaths := a.compileVirtualPathspathSpec(apiVersionDef.ExtendedPaths.Virtual, VirtualPath, apiSpec)
   910  	requestSizes := a.compileRequestSizePathSpec(apiVersionDef.ExtendedPaths.SizeLimit, RequestSizeLimit)
   911  	methodTransforms := a.compileMethodTransformSpec(apiVersionDef.ExtendedPaths.MethodTransforms, MethodTransformed)
   912  	trackedPaths := a.compileTrackedEndpointPathspathSpec(apiVersionDef.ExtendedPaths.TrackEndpoints, RequestTracked)
   913  	unTrackedPaths := a.compileUnTrackedEndpointPathspathSpec(apiVersionDef.ExtendedPaths.DoNotTrackEndpoints, RequestNotTracked)
   914  	validateJSON := a.compileValidateJSONPathspathSpec(apiVersionDef.ExtendedPaths.ValidateJSON, ValidateJSONRequest)
   915  	internalPaths := a.compileInternalPathspathSpec(apiVersionDef.ExtendedPaths.Internal, Internal)
   916  
   917  	combinedPath := []URLSpec{}
   918  	combinedPath = append(combinedPath, ignoredPaths...)
   919  	combinedPath = append(combinedPath, blackListPaths...)
   920  	combinedPath = append(combinedPath, whiteListPaths...)
   921  	combinedPath = append(combinedPath, cachedPaths...)
   922  	combinedPath = append(combinedPath, transformPaths...)
   923  	combinedPath = append(combinedPath, transformResponsePaths...)
   924  	combinedPath = append(combinedPath, transformJQPaths...)
   925  	combinedPath = append(combinedPath, transformJQResponsePaths...)
   926  	combinedPath = append(combinedPath, headerTransformPaths...)
   927  	combinedPath = append(combinedPath, headerTransformPathsOnResponse...)
   928  	combinedPath = append(combinedPath, hardTimeouts...)
   929  	combinedPath = append(combinedPath, circuitBreakers...)
   930  	combinedPath = append(combinedPath, urlRewrites...)
   931  	combinedPath = append(combinedPath, requestSizes...)
   932  	combinedPath = append(combinedPath, virtualPaths...)
   933  	combinedPath = append(combinedPath, methodTransforms...)
   934  	combinedPath = append(combinedPath, trackedPaths...)
   935  	combinedPath = append(combinedPath, unTrackedPaths...)
   936  	combinedPath = append(combinedPath, validateJSON...)
   937  	combinedPath = append(combinedPath, internalPaths...)
   938  
   939  	return combinedPath, len(whiteListPaths) > 0
   940  }
   941  
   942  func (a *APISpec) Init(authStore, sessionStore, healthStore, orgStore storage.Handler) {
   943  	a.AuthManager.Init(authStore)
   944  	a.SessionManager.Init(sessionStore)
   945  	a.Health.Init(healthStore)
   946  	a.OrgSessionManager.Init(orgStore)
   947  }
   948  
   949  func (a *APISpec) StopSessionManagerPool() {
   950  	a.SessionManager.Stop()
   951  	a.OrgSessionManager.Stop()
   952  }
   953  
   954  func (a *APISpec) getURLStatus(stat URLStatus) RequestStatus {
   955  	switch stat {
   956  	case Ignored:
   957  		return StatusOkAndIgnore
   958  	case BlackList:
   959  		return EndPointNotAllowed
   960  	case WhiteList:
   961  		return StatusOk
   962  	case Cached:
   963  		return StatusCached
   964  	case Transformed:
   965  		return StatusTransform
   966  	case TransformedJQ:
   967  		return StatusTransformJQ
   968  	case HeaderInjected:
   969  		return StatusHeaderInjected
   970  	case HeaderInjectedResponse:
   971  		return StatusHeaderInjectedResponse
   972  	case TransformedResponse:
   973  		return StatusTransformResponse
   974  	case TransformedJQResponse:
   975  		return StatusTransformJQResponse
   976  	case HardTimeout:
   977  		return StatusHardTimeout
   978  	case CircuitBreaker:
   979  		return StatusCircuitBreaker
   980  	case URLRewrite:
   981  		return StatusURLRewrite
   982  	case VirtualPath:
   983  		return StatusVirtualPath
   984  	case RequestSizeLimit:
   985  		return StatusRequestSizeControlled
   986  	case MethodTransformed:
   987  		return StatusMethodTransformed
   988  	case RequestTracked:
   989  		return StatusRequestTracked
   990  	case RequestNotTracked:
   991  		return StatusRequestNotTracked
   992  	case ValidateJSONRequest:
   993  		return StatusValidateJSON
   994  	case Internal:
   995  		return StatusInternal
   996  
   997  	default:
   998  		log.Error("URL Status was not one of Ignored, Blacklist or WhiteList! Blocking.")
   999  		return EndPointNotAllowed
  1000  	}
  1001  }
  1002  
  1003  // URLAllowedAndIgnored checks if a url is allowed and ignored.
  1004  func (a *APISpec) URLAllowedAndIgnored(r *http.Request, rxPaths []URLSpec, whiteListStatus bool) (RequestStatus, interface{}) {
  1005  	// Check if ignored
  1006  	for _, v := range rxPaths {
  1007  		if !v.Spec.MatchString(r.URL.Path) {
  1008  			continue
  1009  		}
  1010  
  1011  		if v.MethodActions != nil {
  1012  			// We are using an extended path set, check for the method
  1013  			methodMeta, matchMethodOk := v.MethodActions[r.Method]
  1014  			if !matchMethodOk {
  1015  				continue
  1016  			}
  1017  
  1018  			// Matched the method, check what status it is
  1019  			// TODO: Extend here for additional reply options
  1020  			switch methodMeta.Action {
  1021  			case apidef.NoAction:
  1022  				// NoAction status means we're not treating this request in any special or exceptional way
  1023  				return a.getURLStatus(v.Status), nil
  1024  			case apidef.Reply:
  1025  				return StatusRedirectFlowByReply, &methodMeta
  1026  			default:
  1027  				log.Error("URL Method Action was not set to NoAction, blocking.")
  1028  				return EndPointNotAllowed, nil
  1029  			}
  1030  		}
  1031  
  1032  		if r.Method == v.Internal.Method && v.Status == Internal && !ctxLoopingEnabled(r) {
  1033  			return EndPointNotAllowed, nil
  1034  		}
  1035  
  1036  		if whiteListStatus {
  1037  			// We have a whitelist, nothing gets through unless specifically defined
  1038  			switch v.Status {
  1039  			case WhiteList, BlackList, Ignored:
  1040  			default:
  1041  				if v.Status == Internal && r.Method == v.Internal.Method && ctxLoopingEnabled(r) {
  1042  					return a.getURLStatus(v.Status), nil
  1043  				} else {
  1044  					return EndPointNotAllowed, nil
  1045  				}
  1046  			}
  1047  		}
  1048  
  1049  		if v.TransformAction.Template != nil {
  1050  			return a.getURLStatus(v.Status), &v.TransformAction
  1051  		}
  1052  
  1053  		if v.TransformJQAction.Filter != "" {
  1054  			return a.getURLStatus(v.Status), &v.TransformJQAction
  1055  		}
  1056  
  1057  		// TODO: Fix, Not a great detection method
  1058  		if len(v.InjectHeaders.Path) > 0 {
  1059  			return a.getURLStatus(v.Status), &v.InjectHeaders
  1060  		}
  1061  
  1062  		// Using a legacy path, handle it raw.
  1063  		return a.getURLStatus(v.Status), nil
  1064  	}
  1065  
  1066  	// Nothing matched - should we still let it through?
  1067  	if whiteListStatus {
  1068  		// We have a whitelist, nothing gets through unless specifically defined
  1069  		return EndPointNotAllowed, nil
  1070  	}
  1071  
  1072  	// No whitelist, but also not in any of the other lists, let it through and filter
  1073  	return StatusOk, nil
  1074  }
  1075  
  1076  // CheckSpecMatchesStatus checks if a url spec has a specific status
  1077  func (a *APISpec) CheckSpecMatchesStatus(r *http.Request, rxPaths []URLSpec, mode URLStatus) (bool, interface{}) {
  1078  	var matchPath, method string
  1079  
  1080  	//If url-rewrite middleware was used, call response middleware of original path and not of rewritten path
  1081  	// context variable UrlRewritePath is set by rewrite middleware
  1082  	if mode == TransformedJQResponse || mode == HeaderInjectedResponse || mode == TransformedResponse {
  1083  		matchPath = ctxGetUrlRewritePath(r)
  1084  		method = ctxGetRequestMethod(r)
  1085  		if matchPath == "" {
  1086  			matchPath = r.URL.Path
  1087  		}
  1088  	} else {
  1089  		matchPath = r.URL.Path
  1090  		method = r.Method
  1091  	}
  1092  
  1093  	if a.Proxy.ListenPath != "/" {
  1094  		matchPath = strings.TrimPrefix(matchPath, a.Proxy.ListenPath)
  1095  	}
  1096  
  1097  	if !strings.HasPrefix(matchPath, "/") {
  1098  		matchPath = "/" + matchPath
  1099  	}
  1100  
  1101  	// Check if ignored
  1102  	for _, v := range rxPaths {
  1103  		if mode != v.Status {
  1104  			continue
  1105  		}
  1106  		if !v.Spec.MatchString(matchPath) {
  1107  			continue
  1108  		}
  1109  
  1110  		switch v.Status {
  1111  		case Ignored, BlackList, WhiteList:
  1112  			return true, nil
  1113  		case Cached:
  1114  			if method == v.CacheConfig.Method || (v.CacheConfig.Method == SAFE_METHODS && (method == "GET" || method == "HEADERS" || method == "OPTIONS")) {
  1115  				return true, &v.CacheConfig
  1116  			}
  1117  		case Transformed:
  1118  			if method == v.TransformAction.Method {
  1119  				return true, &v.TransformAction
  1120  			}
  1121  		case TransformedJQ:
  1122  			if method == v.TransformJQAction.Method {
  1123  				return true, &v.TransformJQAction
  1124  			}
  1125  		case HeaderInjected:
  1126  			if method == v.InjectHeaders.Method {
  1127  				return true, &v.InjectHeaders
  1128  			}
  1129  		case HeaderInjectedResponse:
  1130  			if method == v.InjectHeadersResponse.Method {
  1131  				return true, &v.InjectHeadersResponse
  1132  			}
  1133  		case TransformedResponse:
  1134  			if method == v.TransformResponseAction.Method {
  1135  				return true, &v.TransformResponseAction
  1136  			}
  1137  		case TransformedJQResponse:
  1138  			if method == v.TransformJQResponseAction.Method {
  1139  				return true, &v.TransformJQResponseAction
  1140  			}
  1141  		case HardTimeout:
  1142  			if r.Method == v.HardTimeout.Method {
  1143  				return true, &v.HardTimeout.TimeOut
  1144  			}
  1145  		case CircuitBreaker:
  1146  			if method == v.CircuitBreaker.Method {
  1147  				return true, &v.CircuitBreaker
  1148  			}
  1149  		case URLRewrite:
  1150  			if method == v.URLRewrite.Method {
  1151  				return true, v.URLRewrite
  1152  			}
  1153  		case VirtualPath:
  1154  			if method == v.VirtualPathSpec.Method {
  1155  				return true, &v.VirtualPathSpec
  1156  			}
  1157  		case RequestSizeLimit:
  1158  			if method == v.RequestSize.Method {
  1159  				return true, &v.RequestSize
  1160  			}
  1161  		case MethodTransformed:
  1162  			if method == v.MethodTransform.Method {
  1163  				return true, &v.MethodTransform
  1164  			}
  1165  		case RequestTracked:
  1166  			if method == v.TrackEndpoint.Method {
  1167  				return true, &v.TrackEndpoint
  1168  			}
  1169  		case RequestNotTracked:
  1170  			if method == v.DoNotTrackEndpoint.Method {
  1171  				return true, &v.DoNotTrackEndpoint
  1172  			}
  1173  		case ValidateJSONRequest:
  1174  			if method == v.ValidatePathMeta.Method {
  1175  				return true, &v.ValidatePathMeta
  1176  			}
  1177  		case Internal:
  1178  			if method == v.Internal.Method {
  1179  				return true, &v.Internal
  1180  			}
  1181  		}
  1182  	}
  1183  	return false, nil
  1184  }
  1185  
  1186  func (a *APISpec) getVersionFromRequest(r *http.Request) string {
  1187  	if a.VersionData.NotVersioned {
  1188  		return ""
  1189  	}
  1190  
  1191  	switch a.VersionDefinition.Location {
  1192  	case headerLocation:
  1193  		return r.Header.Get(a.VersionDefinition.Key)
  1194  
  1195  	case urlParamLocation:
  1196  		return r.URL.Query().Get(a.VersionDefinition.Key)
  1197  
  1198  	case urlLocation:
  1199  		uPath := strings.TrimPrefix(r.URL.Path, a.Proxy.ListenPath)
  1200  		uPath = strings.TrimPrefix(uPath, "/"+a.Slug)
  1201  
  1202  		// First non-empty part of the path is the version ID
  1203  		for _, part := range strings.Split(uPath, "/") {
  1204  			if part != "" {
  1205  				return part
  1206  			}
  1207  		}
  1208  	}
  1209  	return ""
  1210  }
  1211  
  1212  // VersionExpired checks if an API version (during a proxied
  1213  // request) is expired. If it isn't and the configured time was valid,
  1214  // it also returns the expiration time.
  1215  func (a *APISpec) VersionExpired(versionDef *apidef.VersionInfo) (bool, *time.Time) {
  1216  	if a.VersionData.NotVersioned {
  1217  		return false, nil
  1218  	}
  1219  
  1220  	// Never expires
  1221  	if versionDef.Expires == "" || versionDef.Expires == "-1" {
  1222  		return false, nil
  1223  	}
  1224  
  1225  	// otherwise use parsed timestamp
  1226  	if versionDef.ExpiresTs.IsZero() {
  1227  		log.Error("Could not parse expiry date for API, disallow")
  1228  		return true, nil
  1229  	}
  1230  
  1231  	// It's in the past, expire
  1232  	// It's in the future, keep going
  1233  	return time.Since(versionDef.ExpiresTs) >= 0, &versionDef.ExpiresTs
  1234  }
  1235  
  1236  // RequestValid will check if an incoming request has valid version
  1237  // data and return a RequestStatus that describes the status of the
  1238  // request
  1239  func (a *APISpec) RequestValid(r *http.Request) (bool, RequestStatus, interface{}) {
  1240  	versionMetaData, versionPaths, whiteListStatus, vstat := a.Version(r)
  1241  
  1242  	// Screwed up version info - fail and pass through
  1243  	if vstat != StatusOk {
  1244  		return false, vstat, nil
  1245  	}
  1246  
  1247  	// Is the API version expired?
  1248  	// TODO: Don't abuse the interface{} return value for both
  1249  	// *apidef.EndpointMethodMeta and *time.Time. Probably need to
  1250  	// redesign or entirely remove RequestValid. See discussion on
  1251  	// https://github.com/TykTechnologies/tyk/pull/776
  1252  	expired, expTime := a.VersionExpired(versionMetaData)
  1253  	if expired {
  1254  		return false, VersionExpired, nil
  1255  	}
  1256  
  1257  	// not expired, let's check path info
  1258  	status, meta := a.URLAllowedAndIgnored(r, versionPaths, whiteListStatus)
  1259  	switch status {
  1260  	case EndPointNotAllowed:
  1261  		return false, status, expTime
  1262  	case StatusRedirectFlowByReply:
  1263  		return true, status, meta
  1264  	case StatusOkAndIgnore, StatusCached, StatusTransform,
  1265  		StatusHeaderInjected, StatusMethodTransformed:
  1266  		return true, status, expTime
  1267  	default:
  1268  		return true, StatusOk, expTime
  1269  	}
  1270  }
  1271  
  1272  // Version attempts to extract the version data from a request, depending on where it is stored in the
  1273  // request (currently only "header" is supported)
  1274  func (a *APISpec) Version(r *http.Request) (*apidef.VersionInfo, []URLSpec, bool, RequestStatus) {
  1275  	var version apidef.VersionInfo
  1276  
  1277  	// try the context first
  1278  	if v := ctxGetVersionInfo(r); v != nil {
  1279  		version = *v
  1280  	} else {
  1281  		// Are we versioned?
  1282  		if a.VersionData.NotVersioned {
  1283  			// Get the first one in the list
  1284  			for _, v := range a.VersionData.Versions {
  1285  				version = v
  1286  				break
  1287  			}
  1288  		} else {
  1289  			// Extract Version Info
  1290  			// First checking for if default version is set
  1291  			vName := a.getVersionFromRequest(r)
  1292  			if vName == "" {
  1293  				if a.VersionData.DefaultVersion == "" {
  1294  					return &version, nil, false, VersionNotFound
  1295  				}
  1296  				vName = a.VersionData.DefaultVersion
  1297  				ctxSetDefaultVersion(r)
  1298  			}
  1299  			// Load Version Data - General
  1300  			var ok bool
  1301  			if version, ok = a.VersionData.Versions[vName]; !ok {
  1302  				return &version, nil, false, VersionDoesNotExist
  1303  			}
  1304  		}
  1305  
  1306  		// cache for the future
  1307  		ctxSetVersionInfo(r, &version)
  1308  	}
  1309  
  1310  	// Load path data and whitelist data for version
  1311  	rxPaths, rxOk := a.RxPaths[version.Name]
  1312  	if !rxOk {
  1313  		log.Error("no RX Paths found for version ", version.Name)
  1314  		return &version, nil, false, VersionDoesNotExist
  1315  	}
  1316  
  1317  	whiteListStatus, wlOk := a.WhiteListEnabled[version.Name]
  1318  	if !wlOk {
  1319  		log.Error("No whitelist data found")
  1320  		return &version, nil, false, VersionWhiteListStatusNotFound
  1321  	}
  1322  
  1323  	return &version, rxPaths, whiteListStatus, StatusOk
  1324  }
  1325  
  1326  type RoundRobin struct {
  1327  	pos uint32
  1328  }
  1329  
  1330  func (r *RoundRobin) WithLen(len int) int {
  1331  	if len < 1 {
  1332  		return 0
  1333  	}
  1334  	// -1 to start at 0, not 1
  1335  	cur := atomic.AddUint32(&r.pos, 1) - 1
  1336  	return int(cur) % len
  1337  }