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

     1  package gateway
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"io/ioutil"
     7  	"net"
     8  	"net/http"
     9  	"net/http/httptest"
    10  	"net/url"
    11  	"os"
    12  	"runtime"
    13  
    14  	"strings"
    15  	"sync"
    16  	"testing"
    17  	"time"
    18  
    19  	"github.com/garyburd/redigo/redis"
    20  	"github.com/gorilla/websocket"
    21  	msgpack "gopkg.in/vmihailenco/msgpack.v2"
    22  
    23  	"github.com/TykTechnologies/tyk/apidef"
    24  	"github.com/TykTechnologies/tyk/cli"
    25  	"github.com/TykTechnologies/tyk/config"
    26  	"github.com/TykTechnologies/tyk/storage"
    27  	"github.com/TykTechnologies/tyk/test"
    28  	"github.com/TykTechnologies/tyk/user"
    29  )
    30  
    31  const defaultListenPort = 8080
    32  
    33  func TestMain(m *testing.M) {
    34  	os.Exit(InitTestMain(m))
    35  }
    36  
    37  func createNonThrottledSession() *user.SessionState {
    38  	session := new(user.SessionState)
    39  	session.Rate = 100.0
    40  	session.Allowance = session.Rate
    41  	session.LastCheck = time.Now().Unix()
    42  	session.Per = 1.0
    43  	session.QuotaRenewalRate = 300 // 5 minutes
    44  	session.QuotaRenews = time.Now().Unix()
    45  	session.QuotaRemaining = 10
    46  	session.QuotaMax = 10
    47  	session.Alias = "TEST-ALIAS"
    48  	return session
    49  }
    50  
    51  func TestAA(t *testing.T) {
    52  	ts := StartTest()
    53  
    54  	ts.Start()
    55  	defer ts.Close()
    56  
    57  	BuildAndLoadAPI(func(spec *APISpec) {
    58  		spec.Proxy.ListenPath = "/"
    59  	})
    60  
    61  	ts.Run(t, []test.TestCase{
    62  		{Code: 200},
    63  	}...)
    64  
    65  }
    66  
    67  type tykErrorResponse struct {
    68  	Error string
    69  }
    70  
    71  func testKey(testName string, name string) string {
    72  	return fmt.Sprintf("%s-%s", testName, name)
    73  }
    74  
    75  func TestParambasedAuth(t *testing.T) {
    76  	ts := StartTest()
    77  	defer ts.Close()
    78  
    79  	BuildAndLoadAPI(func(spec *APISpec) {
    80  		spec.Auth.UseParam = true
    81  		spec.UseKeylessAccess = false
    82  		spec.Proxy.ListenPath = "/"
    83  	})
    84  
    85  	key := CreateSession(func(s *user.SessionState) {
    86  		s.AccessRights = map[string]user.AccessDefinition{"test": {
    87  			APIID: "test", Versions: []string{"v1"},
    88  		}}
    89  	})
    90  
    91  	form := url.Values{}
    92  	form.Add("foo", "swiggetty")
    93  	form.Add("bar", "swoggetty")
    94  	form.Add("baz", "swoogetty")
    95  
    96  	expectedBody := `"Form":{"authorization":"` + key + `","bar":"swoggetty","baz":"swoogetty","foo":"swiggetty"}`
    97  
    98  	ts.Run(t, test.TestCase{
    99  		Method:    "POST",
   100  		Path:      "/?authorization=" + key,
   101  		Headers:   map[string]string{"Content-Type": "application/x-www-form-urlencoded"},
   102  		Data:      string(form.Encode()),
   103  		Code:      200,
   104  		BodyMatch: expectedBody,
   105  	})
   106  }
   107  
   108  func TestStripPathWithURLRewrite(t *testing.T) {
   109  	ts := StartTest()
   110  	defer ts.Close()
   111  	defer ResetTestConfig()
   112  
   113  	t.Run("rewrite URL containing listen path", func(t *testing.T) {
   114  		BuildAndLoadAPI(func(spec *APISpec) {
   115  			version := spec.VersionData.Versions["v1"]
   116  			json.Unmarshal([]byte(`{
   117                  "use_extended_paths": true,
   118                  "extended_paths": {
   119                          "url_rewrites": [{
   120                                  "path": "/anything/",
   121                                  "match_pattern": "/anything/(.*)",
   122                                  "method": "GET",
   123  				"rewrite_to":"/something/$1"
   124                          }]
   125                  }
   126              }`), &version)
   127  			spec.VersionData.Versions["v1"] = version
   128  			spec.Proxy.ListenPath = "/myapi/"
   129  			spec.Proxy.StripListenPath = true
   130  
   131  		})
   132  
   133  		ts.Run(t, []test.TestCase{
   134  			{Path: "/myapi/anything/a/myapi/b/c", BodyMatch: `"Url":"/something/a/myapi/b/c"`},
   135  		}...)
   136  	})
   137  }
   138  
   139  func TestSkipTargetPassEscapingOff(t *testing.T) {
   140  	ts := StartTest()
   141  	defer ts.Close()
   142  	defer ResetTestConfig()
   143  
   144  	t.Run("With escaping, default", func(t *testing.T) {
   145  		globalConf := config.Global()
   146  		globalConf.HttpServerOptions.SkipTargetPathEscaping = false
   147  		config.SetGlobal(globalConf)
   148  
   149  		BuildAndLoadAPI(func(spec *APISpec) {
   150  			spec.Proxy.ListenPath = "/"
   151  		})
   152  
   153  		ts.Run(t, []test.TestCase{
   154  			{Path: "/(abc,xyz)?arg=val", BodyMatch: `"Url":"/%28abc,xyz%29?arg=val`},
   155  			{Path: "/%28abc,xyz%29?arg=val", BodyMatch: `"Url":"/%28abc,xyz%29?arg=val`},
   156  		}...)
   157  	})
   158  
   159  	t.Run("Without escaping", func(t *testing.T) {
   160  		globalConf := config.Global()
   161  		globalConf.HttpServerOptions.SkipTargetPathEscaping = true
   162  		config.SetGlobal(globalConf)
   163  
   164  		BuildAndLoadAPI(func(spec *APISpec) {
   165  			spec.Proxy.ListenPath = "/"
   166  		})
   167  
   168  		ts.Run(t, []test.TestCase{
   169  			{Path: "/(abc,xyz)?arg=val", BodyMatch: `"Url":"/(abc,xyz)?arg=val"`},
   170  			{Path: "/%28abc,xyz%29?arg=val", BodyMatch: `"Url":"/%28abc,xyz%29?arg=val"`},
   171  		}...)
   172  	})
   173  
   174  	t.Run("With escaping, listen path and target URL are set, StripListenPath is OFF", func(t *testing.T) {
   175  		globalConf := config.Global()
   176  		globalConf.HttpServerOptions.SkipTargetPathEscaping = false
   177  		config.SetGlobal(globalConf)
   178  
   179  		BuildAndLoadAPI(func(spec *APISpec) {
   180  			spec.Proxy.StripListenPath = false
   181  			spec.Proxy.ListenPath = "/listen_me"
   182  			spec.Proxy.TargetURL = testHttpAny + "/sent_to_me"
   183  		})
   184  
   185  		ts.Run(t, []test.TestCase{
   186  			{Path: "/listen_me/(abc,xyz)?arg=val", BodyMatch: `"Url":"/sent_to_me/listen_me/%28abc,xyz%29?arg=val"`},
   187  			{Path: "/listen_me/%28abc,xyz%29?arg=val", BodyMatch: `"Url":"/sent_to_me/listen_me/%28abc,xyz%29?arg=val"`},
   188  		}...)
   189  	})
   190  
   191  	t.Run("Without escaping, listen path and target URL are set, StripListenPath is OFF", func(t *testing.T) {
   192  		globalConf := config.Global()
   193  		globalConf.HttpServerOptions.SkipTargetPathEscaping = true
   194  		config.SetGlobal(globalConf)
   195  
   196  		BuildAndLoadAPI(func(spec *APISpec) {
   197  			spec.Proxy.StripListenPath = false
   198  			spec.Proxy.ListenPath = "/listen_me"
   199  			spec.Proxy.TargetURL = testHttpAny + "/sent_to_me"
   200  		})
   201  
   202  		ts.Run(t, []test.TestCase{
   203  			{Path: "/listen_me/(abc,xyz)?arg=val", BodyMatch: `"Url":"/sent_to_me/listen_me/(abc,xyz)?arg=val"`},
   204  			{Path: "/listen_me/%28abc,xyz%29?arg=val", BodyMatch: `"Url":"/sent_to_me/listen_me/%28abc,xyz%29?arg=val"`},
   205  		}...)
   206  	})
   207  
   208  	t.Run("With escaping, listen path and target URL are set, StripListenPath is ON", func(t *testing.T) {
   209  		globalConf := config.Global()
   210  		globalConf.HttpServerOptions.SkipTargetPathEscaping = false
   211  		config.SetGlobal(globalConf)
   212  
   213  		BuildAndLoadAPI(func(spec *APISpec) {
   214  			spec.Proxy.StripListenPath = true
   215  			spec.Proxy.ListenPath = "/listen_me"
   216  			spec.Proxy.TargetURL = testHttpAny + "/sent_to_me"
   217  		})
   218  
   219  		ts.Run(t, []test.TestCase{
   220  			{Path: "/listen_me/(abc,xyz)?arg=val", BodyMatch: `"Url":"/sent_to_me/%28abc,xyz%29?arg=val"`},
   221  			{Path: "/listen_me/%28abc,xyz%29?arg=val", BodyMatch: `"Url":"/sent_to_me/%28abc,xyz%29?arg=val"`},
   222  		}...)
   223  	})
   224  
   225  	t.Run("Without escaping, listen path and target URL are set, StripListenPath is ON", func(t *testing.T) {
   226  		globalConf := config.Global()
   227  		globalConf.HttpServerOptions.SkipTargetPathEscaping = true
   228  		config.SetGlobal(globalConf)
   229  
   230  		BuildAndLoadAPI(func(spec *APISpec) {
   231  			spec.Proxy.StripListenPath = true
   232  			spec.Proxy.ListenPath = "/listen_me"
   233  			spec.Proxy.TargetURL = testHttpAny + "/sent_to_me"
   234  		})
   235  
   236  		ts.Run(t, []test.TestCase{
   237  			{Path: "/listen_me/(abc,xyz)?arg=val", BodyMatch: `"Url":"/sent_to_me/(abc,xyz)?arg=val"`},
   238  			{Path: "/listen_me/%28abc,xyz%29?arg=val", BodyMatch: `"Url":"/sent_to_me/%28abc,xyz%29?arg=val"`},
   239  		}...)
   240  	})
   241  }
   242  
   243  func TestSkipTargetPassEscapingOffWithSkipURLCleaningTrue(t *testing.T) {
   244  	globalConf := config.Global()
   245  	globalConf.HttpServerOptions.OverrideDefaults = true
   246  	globalConf.HttpServerOptions.SkipURLCleaning = true
   247  	config.SetGlobal(globalConf)
   248  	defer ResetTestConfig()
   249  
   250  	// here we expect that test gateway will be sending to test upstream requests with not cleaned URI
   251  	// so test upstream shouldn't reply with 301 and process them as well
   252  	prevSkipClean := defaultTestConfig.HttpServerOptions.OverrideDefaults &&
   253  		defaultTestConfig.HttpServerOptions.SkipURLCleaning
   254  	testServerRouter.SkipClean(true)
   255  	defer testServerRouter.SkipClean(prevSkipClean)
   256  
   257  	ts := StartTest()
   258  	defer ts.Close()
   259  
   260  	t.Run("With escaping, default", func(t *testing.T) {
   261  		globalConf := config.Global()
   262  		globalConf.HttpServerOptions.SkipTargetPathEscaping = false
   263  		config.SetGlobal(globalConf)
   264  
   265  		BuildAndLoadAPI(func(spec *APISpec) {
   266  			spec.Proxy.ListenPath = "/"
   267  		})
   268  
   269  		ts.Run(t, []test.TestCase{
   270  			{Path: "/abc/xyz/http%3A%2F%2Ftest.com?arg=val", BodyMatch: `"Url":"/abc/xyz/http%3A%2F%2Ftest.com?arg=val`},
   271  		}...)
   272  	})
   273  
   274  	t.Run("Without escaping, default", func(t *testing.T) {
   275  		globalConf := config.Global()
   276  		globalConf.HttpServerOptions.SkipTargetPathEscaping = true
   277  		config.SetGlobal(globalConf)
   278  
   279  		BuildAndLoadAPI(func(spec *APISpec) {
   280  			spec.Proxy.ListenPath = "/"
   281  		})
   282  
   283  		ts.Run(t, []test.TestCase{
   284  			{Path: "/abc/xyz/http%3A%2F%2Ftest.com?arg=val", BodyMatch: `"Url":"/abc/xyz/http%3A%2F%2Ftest.com?arg=val`},
   285  		}...)
   286  	})
   287  
   288  	t.Run("With escaping, listen path and target URL are set, StripListenPath is OFF", func(t *testing.T) {
   289  		globalConf := config.Global()
   290  		globalConf.HttpServerOptions.SkipTargetPathEscaping = false
   291  		config.SetGlobal(globalConf)
   292  
   293  		BuildAndLoadAPI(func(spec *APISpec) {
   294  			spec.Proxy.StripListenPath = false
   295  			spec.Proxy.ListenPath = "/listen_me"
   296  			spec.Proxy.TargetURL = testHttpAny + "/sent_to_me"
   297  		})
   298  
   299  		ts.Run(t, []test.TestCase{
   300  			{Path: "/listen_me/(abc,xyz)?arg=val", BodyMatch: `"Url":"/sent_to_me/listen_me/%28abc,xyz%29?arg=val"`},
   301  			{Path: "/listen_me/%28abc,xyz%29?arg=val", BodyMatch: `"Url":"/sent_to_me/listen_me/%28abc,xyz%29?arg=val"`},
   302  			{Path: "/listen_me/http%3A%2F%2Ftest.com?arg=val", BodyMatch: `"Url":"/sent_to_me/listen_me/http%3A%2F%2Ftest.com?arg=val`},
   303  		}...)
   304  	})
   305  
   306  	t.Run("Without escaping, listen path and target URL are set, StripListenPath is OFF", func(t *testing.T) {
   307  		globalConf := config.Global()
   308  		globalConf.HttpServerOptions.SkipTargetPathEscaping = true
   309  		config.SetGlobal(globalConf)
   310  
   311  		BuildAndLoadAPI(func(spec *APISpec) {
   312  			spec.Proxy.StripListenPath = false
   313  			spec.Proxy.ListenPath = "/listen_me"
   314  			spec.Proxy.TargetURL = testHttpAny + "/sent_to_me"
   315  		})
   316  
   317  		ts.Run(t, []test.TestCase{
   318  			{Path: "/listen_me/(abc,xyz)?arg=val", BodyMatch: `"Url":"/sent_to_me/listen_me/(abc,xyz)?arg=val"`},
   319  			{Path: "/listen_me/%28abc,xyz%29?arg=val", BodyMatch: `"Url":"/sent_to_me/listen_me/%28abc,xyz%29?arg=val"`},
   320  			{Path: "/listen_me/http%3A%2F%2Ftest.com?arg=val", BodyMatch: `"Url":"/sent_to_me/listen_me/http%3A%2F%2Ftest.com?arg=val`},
   321  		}...)
   322  	})
   323  
   324  	t.Run("With escaping, listen path and target URL are set, StripListenPath is ON", func(t *testing.T) {
   325  		globalConf := config.Global()
   326  		globalConf.HttpServerOptions.SkipTargetPathEscaping = false
   327  		config.SetGlobal(globalConf)
   328  
   329  		BuildAndLoadAPI(func(spec *APISpec) {
   330  			spec.Proxy.StripListenPath = true
   331  			spec.Proxy.ListenPath = "/listen_me"
   332  			spec.Proxy.TargetURL = testHttpAny + "/sent_to_me"
   333  		})
   334  
   335  		ts.Run(t, []test.TestCase{
   336  			{Path: "/listen_me/(abc,xyz)?arg=val", BodyMatch: `"Url":"/sent_to_me/%28abc,xyz%29?arg=val"`},
   337  			{Path: "/listen_me/%28abc,xyz%29?arg=val", BodyMatch: `"Url":"/sent_to_me/%28abc,xyz%29?arg=val"`},
   338  			{Path: "/listen_me/http%3A%2F%2Ftest.com?arg=val", BodyMatch: `"Url":"/sent_to_me/http%3A%2F%2Ftest.com?arg=val`},
   339  		}...)
   340  	})
   341  
   342  	t.Run("Without escaping, listen path and target URL are set, StripListenPath is ON", func(t *testing.T) {
   343  		globalConf := config.Global()
   344  		globalConf.HttpServerOptions.SkipTargetPathEscaping = true
   345  		config.SetGlobal(globalConf)
   346  
   347  		BuildAndLoadAPI(func(spec *APISpec) {
   348  			spec.Proxy.StripListenPath = true
   349  			spec.Proxy.ListenPath = "/listen_me"
   350  			spec.Proxy.TargetURL = testHttpAny + "/sent_to_me"
   351  		})
   352  
   353  		ts.Run(t, []test.TestCase{
   354  			{Path: "/listen_me/(abc,xyz)?arg=val", BodyMatch: `"Url":"/sent_to_me/(abc,xyz)?arg=val"`},
   355  			{Path: "/listen_me/%28abc,xyz%29?arg=val", BodyMatch: `"Url":"/sent_to_me/%28abc,xyz%29?arg=val"`},
   356  			{Path: "/listen_me/http%3A%2F%2Ftest.com?arg=val", BodyMatch: `"Url":"/sent_to_me/http%3A%2F%2Ftest.com?arg=val`},
   357  		}...)
   358  	})
   359  
   360  }
   361  
   362  func TestQuota(t *testing.T) {
   363  	ts := StartTest()
   364  	defer ts.Close()
   365  
   366  	var keyID string
   367  
   368  	var webhookWG sync.WaitGroup
   369  	webhook := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   370  		if r.Header.Get("X-Tyk-Test-Header") != "Tyk v1.BANANA" {
   371  			t.Error("Custom webhook header not set", r.Header)
   372  		}
   373  
   374  		var data map[string]string
   375  		body, _ := ioutil.ReadAll(r.Body)
   376  		json.Unmarshal(body, &data)
   377  
   378  		if data["event"] != "QuotaExceeded" || data["message"] != "Key Quota Limit Exceeded" || data["key"] != keyID {
   379  			t.Error("Webhook payload not match", data)
   380  		}
   381  
   382  		webhookWG.Done()
   383  	}))
   384  	defer webhook.Close()
   385  
   386  	BuildAndLoadAPI(func(spec *APISpec) {
   387  		spec.UseKeylessAccess = false
   388  		spec.Proxy.ListenPath = "/"
   389  
   390  		version := spec.VersionData.Versions["v1"]
   391  		json.Unmarshal([]byte(`{
   392  			"use_extended_paths": true,
   393  			"extended_paths": {
   394  				"ignored":[{
   395  					"path": "/get",
   396  					"method_actions": {"GET": {"action": "no_action"}}
   397  				}]
   398  			}
   399  		}`), &version)
   400  		spec.VersionData.Versions["v1"] = version
   401  
   402  		json.Unmarshal([]byte(`
   403  		{ "events": { "QuotaExceeded":
   404  			[{
   405  				"handler_name":"eh_log_handler",
   406  				"handler_meta": {
   407  					"prefix": "LOG-HANDLER-PREFIX"
   408  				}
   409  			},
   410  			{
   411  				"handler_name":"eh_web_hook_handler",
   412  				"handler_meta": {
   413  					"method": "POST",
   414  					"target_path": "`+webhook.URL+`",
   415  					"template_path": "templates/default_webhook.json",
   416  					"header_map": {"X-Tyk-Test-Header": "Tyk v1.BANANA"},
   417  					"event_timeout": 10
   418  				}
   419  			}]
   420  		}}`), &spec.EventHandlers)
   421  	})
   422  
   423  	// Create session with Quota = 2
   424  	keyID = CreateSession(func(s *user.SessionState) {
   425  		s.QuotaMax = 2
   426  	})
   427  
   428  	authHeaders := map[string]string{
   429  		"authorization": keyID,
   430  	}
   431  
   432  	webhookWG.Add(1)
   433  	ts.Run(t, []test.TestCase{
   434  		{Path: "/", Headers: authHeaders, Code: 200},
   435  		// Ignored path should not affect quota
   436  		{Path: "/get", Headers: authHeaders, Code: 200},
   437  		{Path: "/", Headers: authHeaders, Code: 200},
   438  		{Path: "/", Headers: authHeaders, Code: 403, BodyMatch: `"error": "Quota exceeded"`},
   439  		// Ignored path works without auth
   440  		{Path: "/get", Code: 200},
   441  	}...)
   442  	webhookWG.Wait()
   443  }
   444  
   445  func TestAnalytics(t *testing.T) {
   446  	ts := StartTest(TestConfig{
   447  		Delay: 20 * time.Millisecond,
   448  	})
   449  	defer ts.Close()
   450  
   451  	BuildAndLoadAPI(func(spec *APISpec) {
   452  		spec.UseKeylessAccess = false
   453  		spec.Proxy.ListenPath = "/"
   454  	})
   455  
   456  	// Cleanup before test
   457  	// let records to to be sent
   458  	time.Sleep(recordsBufferFlushInterval + 50)
   459  	analytics.Store.GetAndDeleteSet(analyticsKeyName)
   460  
   461  	t.Run("Log errors", func(t *testing.T) {
   462  		ts.Run(t, []test.TestCase{
   463  			{Path: "/", Code: 401},
   464  			{Path: "/", Code: 401},
   465  		}...)
   466  
   467  		// let records to to be sent
   468  		time.Sleep(recordsBufferFlushInterval + 50)
   469  
   470  		results := analytics.Store.GetAndDeleteSet(analyticsKeyName)
   471  		if len(results) != 2 {
   472  			t.Error("Should return 2 record", len(results))
   473  		}
   474  
   475  		var record AnalyticsRecord
   476  		msgpack.Unmarshal(results[0].([]byte), &record)
   477  		if record.ResponseCode != 401 {
   478  			t.Error("Analytics record do not match: ", record)
   479  		}
   480  	})
   481  
   482  	t.Run("Log success", func(t *testing.T) {
   483  		key := CreateSession()
   484  
   485  		authHeaders := map[string]string{
   486  			"authorization": key,
   487  		}
   488  
   489  		ts.Run(t, test.TestCase{
   490  			Path: "/", Headers: authHeaders, Code: 200,
   491  		})
   492  
   493  		// let records to to be sent
   494  		time.Sleep(recordsBufferFlushInterval + 50)
   495  
   496  		results := analytics.Store.GetAndDeleteSet(analyticsKeyName)
   497  		if len(results) != 1 {
   498  			t.Error("Should return 1 record: ", len(results))
   499  		}
   500  
   501  		var record AnalyticsRecord
   502  		msgpack.Unmarshal(results[0].([]byte), &record)
   503  		if record.ResponseCode != 200 {
   504  			t.Error("Analytics record do not match", record)
   505  		}
   506  	})
   507  
   508  	t.Run("Detailed analytics", func(t *testing.T) {
   509  		defer ResetTestConfig()
   510  		globalConf := config.Global()
   511  		globalConf.AnalyticsConfig.EnableDetailedRecording = true
   512  		config.SetGlobal(globalConf)
   513  
   514  		BuildAndLoadAPI(func(spec *APISpec) {
   515  			spec.UseKeylessAccess = false
   516  			spec.Proxy.ListenPath = "/"
   517  		})
   518  
   519  		key := CreateSession()
   520  
   521  		authHeaders := map[string]string{
   522  			"authorization": key,
   523  		}
   524  
   525  		ts.Run(t, test.TestCase{
   526  			Path: "/", Headers: authHeaders, Code: 200,
   527  		})
   528  
   529  		// let records to to be sent
   530  		time.Sleep(recordsBufferFlushInterval + 50)
   531  
   532  		results := analytics.Store.GetAndDeleteSet(analyticsKeyName)
   533  		if len(results) != 1 {
   534  			t.Error("Should return 1 record: ", len(results))
   535  		}
   536  
   537  		var record AnalyticsRecord
   538  		msgpack.Unmarshal(results[0].([]byte), &record)
   539  		if record.ResponseCode != 200 {
   540  			t.Error("Analytics record do not match", record)
   541  		}
   542  
   543  		if record.RawRequest == "" {
   544  			t.Error("Detailed request info not found", record)
   545  		}
   546  
   547  		if record.RawResponse == "" {
   548  			t.Error("Detailed response info not found", record)
   549  		}
   550  	})
   551  
   552  	t.Run("Detailed analytics with cache", func(t *testing.T) {
   553  		defer ResetTestConfig()
   554  		globalConf := config.Global()
   555  		globalConf.AnalyticsConfig.EnableDetailedRecording = true
   556  		config.SetGlobal(globalConf)
   557  
   558  		BuildAndLoadAPI(func(spec *APISpec) {
   559  			spec.UseKeylessAccess = false
   560  			spec.Proxy.ListenPath = "/"
   561  			spec.CacheOptions = apidef.CacheOptions{
   562  				CacheTimeout:         120,
   563  				EnableCache:          true,
   564  				CacheAllSafeRequests: true,
   565  			}
   566  		})
   567  
   568  		key := CreateSession()
   569  
   570  		authHeaders := map[string]string{
   571  			"authorization": key,
   572  		}
   573  
   574  		ts.Run(t, []test.TestCase{
   575  			{Path: "/", Headers: authHeaders, Code: 200},
   576  			{Path: "/", Headers: authHeaders, Code: 200},
   577  		}...)
   578  
   579  		// let records to to be sent
   580  		time.Sleep(recordsBufferFlushInterval + 50)
   581  
   582  		results := analytics.Store.GetAndDeleteSet(analyticsKeyName)
   583  		if len(results) != 2 {
   584  			t.Fatal("Should return 1 record: ", len(results))
   585  		}
   586  
   587  		// Take second cached request
   588  		var record AnalyticsRecord
   589  		msgpack.Unmarshal(results[1].([]byte), &record)
   590  		if record.ResponseCode != 200 {
   591  			t.Error("Analytics record do not match", record)
   592  		}
   593  
   594  		if record.RawRequest == "" {
   595  			t.Error("Detailed request info not found", record)
   596  		}
   597  
   598  		if record.RawResponse == "" {
   599  			t.Error("Detailed response info not found", record)
   600  		}
   601  	})
   602  }
   603  
   604  func TestListener(t *testing.T) {
   605  	// Trick to get spec JSON, without loading API
   606  	// Specs will be reseted when we do `StartTest`
   607  	specs := BuildAndLoadAPI()
   608  	specJSON, _ := json.Marshal(specs[0].APIDefinition)
   609  	listJSON := fmt.Sprintf("[%s]", string(specJSON))
   610  
   611  	ts := StartTest()
   612  	defer ts.Close()
   613  
   614  	tests := []test.TestCase{
   615  		// Cleanup before tests
   616  		{Method: "DELETE", Path: "/tyk/apis/test", AdminAuth: true},
   617  		{Method: "GET", Path: "/tyk/reload/?block=true", AdminAuth: true, Code: 200},
   618  
   619  		{Method: "GET", Path: "/sample", Code: 404},
   620  		{Method: "GET", Path: "/tyk/apis/", Code: 403},
   621  		{Method: "GET", Path: "/tyk/apis/", AdminAuth: true, Code: 200, BodyMatch: "[]"},
   622  		{Method: "GET", Path: "/tyk/apis", Code: 403},
   623  		{Method: "GET", Path: "/tyk/apis", AdminAuth: true, Code: 200},
   624  		{Method: "POST", Path: "/tyk/apis", Data: sampleAPI, AdminAuth: true, Code: 200},
   625  		{Method: "GET", Path: "/tyk/apis/", AdminAuth: true, Code: 200, BodyMatch: "[]"},
   626  		{Method: "POST", Path: "/tyk/apis/mismatch", AdminAuth: true, Code: 400},
   627  		{Method: "GET", Path: "/tyk/apis/test", AdminAuth: true, Code: 404},
   628  		// API definitions not reloaded yet
   629  		{Method: "GET", Path: "/sample", Code: 404},
   630  		{Method: "GET", Path: "/tyk/reload/?block=true", AdminAuth: true, Code: 200},
   631  		{Method: "GET", Path: "/tyk/apis/test", AdminAuth: true, Code: 200, BodyMatch: string(specJSON)},
   632  		{Method: "GET", Path: "/tyk/apis/", AdminAuth: true, Code: 200, BodyMatch: listJSON},
   633  		{Method: "GET", Path: "/sample", Code: 200},
   634  		{Method: "GET", Path: "/samplefoo", Code: 200},
   635  		{Method: "GET", Path: "/sample/", Code: 200},
   636  		{Method: "GET", Path: "/sample/foo", Code: 200},
   637  	}
   638  
   639  	// have all needed reload ticks ready
   640  	go func() {
   641  		for i := 0; i < 4*4; i++ {
   642  			ReloadTick <- time.Time{}
   643  		}
   644  	}()
   645  
   646  	ts.RunExt(t, tests...)
   647  }
   648  
   649  // Admin api located on separate port
   650  func TestControlListener(t *testing.T) {
   651  	ts := StartTest(TestConfig{
   652  		sepatateControlAPI: true,
   653  	})
   654  	defer ts.Close()
   655  
   656  	tests := []test.TestCase{
   657  		{Method: "GET", Path: "/", Code: 404},
   658  		{Method: "GET", Path: "/tyk/apis", Code: 404},
   659  
   660  		// Querying control API
   661  		{Method: "GET", Path: "/", Code: 404, ControlRequest: true},
   662  		{Method: "GET", Path: "/tyk/apis", Code: 403, ControlRequest: true},
   663  		{Method: "GET", Path: "/tyk/apis/", Code: 200, AdminAuth: true, ControlRequest: true},
   664  	}
   665  
   666  	ts.RunExt(t, tests...)
   667  	doReload()
   668  	ts.RunExt(t, tests...)
   669  }
   670  
   671  func TestHttpPprof(t *testing.T) {
   672  	old := cli.HTTPProfile
   673  	defer func() { cli.HTTPProfile = old }()
   674  
   675  	ts := StartTest(TestConfig{
   676  		sepatateControlAPI: true,
   677  	})
   678  
   679  	ts.Run(t, []test.TestCase{
   680  		{Path: "/debug/pprof/", Code: 404},
   681  		{Path: "/debug/pprof/", Code: 404, ControlRequest: true},
   682  	}...)
   683  	ts.Close()
   684  
   685  	*cli.HTTPProfile = true
   686  
   687  	ts.Start()
   688  	ts.Run(t, []test.TestCase{
   689  		{Path: "/debug/pprof/", Code: 404},
   690  		{Path: "/debug/pprof/", Code: 200, ControlRequest: true},
   691  		{Path: "/debug/pprof/heap", Code: 200, ControlRequest: true},
   692  	}...)
   693  	ts.Close()
   694  }
   695  
   696  func TestManagementNodeRedisEvents(t *testing.T) {
   697  	defer ResetTestConfig()
   698  	globalConf := config.Global()
   699  	globalConf.ManagementNode = false
   700  	config.SetGlobal(globalConf)
   701  	msg := redis.Message{
   702  		Data: []byte(`{"Command": "NoticeGatewayDRLNotification"}`),
   703  	}
   704  	shouldHandle := func(got NotificationCommand) {
   705  		if want := NoticeGatewayDRLNotification; got != want {
   706  			t.Fatalf("want %q, got %q", want, got)
   707  		}
   708  	}
   709  	handleRedisEvent(msg, shouldHandle, nil)
   710  	globalConf.ManagementNode = true
   711  	config.SetGlobal(globalConf)
   712  	notHandle := func(got NotificationCommand) {
   713  		t.Fatalf("should have not handled redis event")
   714  	}
   715  	handleRedisEvent(msg, notHandle, nil)
   716  }
   717  
   718  func TestListenPathTykPrefix(t *testing.T) {
   719  	ts := StartTest()
   720  	defer ts.Close()
   721  
   722  	BuildAndLoadAPI(func(spec *APISpec) {
   723  		spec.Proxy.ListenPath = "/tyk-foo/"
   724  	})
   725  
   726  	ts.Run(t, test.TestCase{
   727  		Path: "/tyk-foo/",
   728  		Code: 200,
   729  	})
   730  }
   731  
   732  func TestReloadGoroutineLeakWithAsyncWrites(t *testing.T) {
   733  	ts := StartTest()
   734  	defer ts.Close()
   735  
   736  	globalConf := config.Global()
   737  	globalConf.UseAsyncSessionWrite = true
   738  	globalConf.EnableJSVM = false
   739  	config.SetGlobal(globalConf)
   740  	defer ResetTestConfig()
   741  
   742  	specs := BuildAndLoadAPI(func(spec *APISpec) {
   743  		spec.Proxy.ListenPath = "/"
   744  	})
   745  
   746  	before := runtime.NumGoroutine()
   747  
   748  	LoadAPI(specs...) // just doing doReload() doesn't load anything as BuildAndLoadAPI cleans up folder with API specs
   749  
   750  	time.Sleep(100 * time.Millisecond)
   751  
   752  	after := runtime.NumGoroutine()
   753  
   754  	if before < after {
   755  		t.Errorf("Goroutine leak, was: %d, after reload: %d", before, after)
   756  	}
   757  }
   758  
   759  func TestReloadGoroutineLeakWithCircuitBreaker(t *testing.T) {
   760  	ts := StartTest()
   761  	defer ts.Close()
   762  
   763  	globalConf := config.Global()
   764  	globalConf.EnableJSVM = false
   765  	config.SetGlobal(globalConf)
   766  	defer ResetTestConfig()
   767  
   768  	specs := BuildAndLoadAPI(func(spec *APISpec) {
   769  		spec.Proxy.ListenPath = "/"
   770  		UpdateAPIVersion(spec, "v1", func(version *apidef.VersionInfo) {
   771  			version.ExtendedPaths = apidef.ExtendedPathsSet{
   772  				CircuitBreaker: []apidef.CircuitBreakerMeta{
   773  					{
   774  						Path:                 "/",
   775  						Method:               http.MethodGet,
   776  						ThresholdPercent:     0.5,
   777  						Samples:              5,
   778  						ReturnToServiceAfter: 10,
   779  					},
   780  				},
   781  			}
   782  		})
   783  	})
   784  
   785  	before := runtime.NumGoroutine()
   786  
   787  	LoadAPI(specs...) // just doing doReload() doesn't load anything as BuildAndLoadAPI cleans up folder with API specs
   788  
   789  	time.Sleep(100 * time.Millisecond)
   790  
   791  	after := runtime.NumGoroutine()
   792  
   793  	if before < after-1 { // -1 because there is one will be running until we fix circuitbreaker Subscribe() method
   794  		t.Errorf("Goroutine leak, was: %d, after reload: %d", before, after)
   795  	}
   796  }
   797  
   798  func TestProxyUserAgent(t *testing.T) {
   799  	ts := StartTest()
   800  	defer ts.Close()
   801  
   802  	BuildAndLoadAPI(func(spec *APISpec) {
   803  		spec.Proxy.ListenPath = "/"
   804  	})
   805  
   806  	ts.Run(t, []test.TestCase{
   807  		{
   808  			Headers:   map[string]string{"User-Agent": ""},
   809  			BodyMatch: fmt.Sprintf(`"User-Agent":"%s"`, defaultUserAgent),
   810  		},
   811  		{
   812  			Headers:   map[string]string{"User-Agent": "SomeAgent"},
   813  			BodyMatch: `"User-Agent":"SomeAgent"`,
   814  		},
   815  	}...)
   816  }
   817  
   818  func TestSkipUrlCleaning(t *testing.T) {
   819  	globalConf := config.Global()
   820  	globalConf.HttpServerOptions.OverrideDefaults = true
   821  	globalConf.HttpServerOptions.SkipURLCleaning = true
   822  	config.SetGlobal(globalConf)
   823  	defer ResetTestConfig()
   824  
   825  	ts := StartTest()
   826  	defer ts.Close()
   827  
   828  	s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   829  		w.Write([]byte(r.URL.Path))
   830  	}))
   831  	defer s.Close()
   832  
   833  	BuildAndLoadAPI(func(spec *APISpec) {
   834  		spec.Proxy.ListenPath = "/"
   835  		spec.Proxy.TargetURL = s.URL
   836  	})
   837  
   838  	ts.Run(t, test.TestCase{
   839  		Path: "/http://example.com", BodyMatch: "/http://example.com", Code: 200,
   840  	})
   841  }
   842  
   843  func TestMultiTargetProxy(t *testing.T) {
   844  	ts := StartTest()
   845  	defer ts.Close()
   846  
   847  	BuildAndLoadAPI(func(spec *APISpec) {
   848  		spec.VersionData.NotVersioned = false
   849  		spec.VersionData.Versions = map[string]apidef.VersionInfo{
   850  			"vdef": {Name: "vdef"},
   851  			"vother": {
   852  				Name:           "vother",
   853  				OverrideTarget: testHttpAny + "/vother",
   854  			},
   855  		}
   856  		spec.Proxy.ListenPath = "/"
   857  	})
   858  
   859  	ts.Run(t, []test.TestCase{
   860  		{
   861  			Headers:   map[string]string{"version": "vdef"},
   862  			JSONMatch: map[string]string{"Url": `"/"`},
   863  			Code:      200,
   864  		},
   865  		{
   866  			Headers:   map[string]string{"version": "vother"},
   867  			JSONMatch: map[string]string{"Url": `"/vother"`},
   868  			Code:      200,
   869  		},
   870  	}...)
   871  }
   872  
   873  func TestCustomDomain(t *testing.T) {
   874  	t.Run("With custom domain support", func(t *testing.T) {
   875  		globalConf := config.Global()
   876  		globalConf.EnableCustomDomains = true
   877  		config.SetGlobal(globalConf)
   878  		defer ResetTestConfig()
   879  
   880  		BuildAndLoadAPI(
   881  			func(spec *APISpec) {
   882  				spec.Domain = "localhost"
   883  			},
   884  			func(spec *APISpec) {
   885  				spec.Domain = ""
   886  			},
   887  		)
   888  	})
   889  
   890  	t.Run("Without custom domain support", func(t *testing.T) {
   891  		BuildAndLoadAPI(
   892  			func(spec *APISpec) {
   893  				spec.Domain = "localhost"
   894  			},
   895  			func(spec *APISpec) {
   896  				spec.Domain = ""
   897  			},
   898  		)
   899  	})
   900  }
   901  
   902  func TestHelloHealthcheck(t *testing.T) {
   903  	ts := StartTest()
   904  	defer ts.Close()
   905  
   906  	t.Run("Without APIs", func(t *testing.T) {
   907  		ts.Run(t, []test.TestCase{
   908  			{Method: "GET", Path: "/hello", Code: 200},
   909  		}...)
   910  	})
   911  
   912  	t.Run("With APIs", func(t *testing.T) {
   913  		BuildAndLoadAPI(func(spec *APISpec) {
   914  			spec.Proxy.ListenPath = "/sample"
   915  		})
   916  
   917  		ts.Run(t, []test.TestCase{
   918  			{Method: "GET", Path: "/hello", Code: 200},
   919  			{Method: "GET", Path: "/sample/hello", Code: 200},
   920  		}...)
   921  	})
   922  }
   923  
   924  func TestCacheAllSafeRequests(t *testing.T) {
   925  	ts := StartTest()
   926  	defer ts.Close()
   927  	cache := storage.RedisCluster{KeyPrefix: "cache-"}
   928  	defer cache.DeleteScanMatch("*")
   929  
   930  	BuildAndLoadAPI(func(spec *APISpec) {
   931  		spec.CacheOptions = apidef.CacheOptions{
   932  			CacheTimeout:         120,
   933  			EnableCache:          true,
   934  			CacheAllSafeRequests: true,
   935  		}
   936  		spec.Proxy.ListenPath = "/"
   937  	})
   938  
   939  	headerCache := map[string]string{"x-tyk-cached-response": "1"}
   940  
   941  	ts.Run(t, []test.TestCase{
   942  		{Method: "GET", Path: "/", HeadersNotMatch: headerCache, Delay: 10 * time.Millisecond},
   943  		{Method: "GET", Path: "/", HeadersMatch: headerCache},
   944  		{Method: "POST", Path: "/", HeadersNotMatch: headerCache},
   945  		{Method: "POST", Path: "/", HeadersNotMatch: headerCache},
   946  		{Method: "GET", Path: "/", HeadersMatch: headerCache},
   947  	}...)
   948  }
   949  
   950  func TestCachePostRequest(t *testing.T) {
   951  	ts := StartTest()
   952  	defer ts.Close()
   953  	cache := storage.RedisCluster{KeyPrefix: "cache-"}
   954  	defer cache.DeleteScanMatch("*")
   955  
   956  	BuildAndLoadAPI(func(spec *APISpec) {
   957  		spec.CacheOptions = apidef.CacheOptions{
   958  			CacheTimeout:         120,
   959  			EnableCache:          true,
   960  			CacheAllSafeRequests: false,
   961  		}
   962  
   963  		UpdateAPIVersion(spec, "v1", func(v *apidef.VersionInfo) {
   964  			json.Unmarshal([]byte(`[{
   965  						"method":"POST",
   966  						"path":"/",
   967  						"cache_key_regex":"\"id\":[^,]*"
   968  					}
   969                                  ]`), &v.ExtendedPaths.AdvanceCacheConfig)
   970  		})
   971  
   972  		spec.Proxy.ListenPath = "/"
   973  	})
   974  
   975  	headerCache := map[string]string{"x-tyk-cached-response": "1"}
   976  
   977  	ts.Run(t, []test.TestCase{
   978  		{Method: "POST", Path: "/", Data: "{\"id\":\"1\",\"name\":\"test\"}", HeadersNotMatch: headerCache, Delay: 10 * time.Millisecond},
   979  		{Method: "POST", Path: "/", Data: "{\"id\":\"1\",\"name\":\"test\"}", HeadersMatch: headerCache, Delay: 10 * time.Millisecond},
   980  		{Method: "POST", Path: "/", Data: "{\"id\":\"2\",\"name\":\"test\"}", HeadersNotMatch: headerCache, Delay: 10 * time.Millisecond},
   981  		// if regex match returns nil, then request body is ignored while generating cache key
   982  		{Method: "POST", Path: "/", Data: "{\"name\":\"test\"}", HeadersNotMatch: headerCache, Delay: 10 * time.Millisecond},
   983  		{Method: "POST", Path: "/", Data: "{\"name\":\"test2\"}", HeadersMatch: headerCache, Delay: 10 * time.Millisecond},
   984  	}...)
   985  }
   986  
   987  func TestCacheEtag(t *testing.T) {
   988  	ts := StartTest()
   989  	defer ts.Close()
   990  	cache := storage.RedisCluster{KeyPrefix: "cache-"}
   991  	defer cache.DeleteScanMatch("*")
   992  
   993  	upstream := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   994  		w.Header().Set("Etag", "12345")
   995  		w.Write([]byte("body"))
   996  	}))
   997  
   998  	BuildAndLoadAPI(func(spec *APISpec) {
   999  		spec.CacheOptions = apidef.CacheOptions{
  1000  			CacheTimeout:         120,
  1001  			EnableCache:          true,
  1002  			CacheAllSafeRequests: true,
  1003  		}
  1004  		spec.Proxy.ListenPath = "/"
  1005  		spec.Proxy.TargetURL = upstream.URL
  1006  	})
  1007  
  1008  	headerCache := map[string]string{"x-tyk-cached-response": "1"}
  1009  	invalidEtag := map[string]string{"If-None-Match": "invalid"}
  1010  	validEtag := map[string]string{"If-None-Match": "12345"}
  1011  
  1012  	ts.Run(t, []test.TestCase{
  1013  		{Method: "GET", Path: "/", HeadersNotMatch: headerCache, Delay: 100 * time.Millisecond},
  1014  		{Method: "GET", Path: "/", HeadersMatch: headerCache, BodyMatch: "body"},
  1015  		{Method: "GET", Path: "/", Headers: invalidEtag, HeadersMatch: headerCache, BodyMatch: "body"},
  1016  		{Method: "GET", Path: "/", Headers: validEtag, HeadersMatch: headerCache, BodyNotMatch: "body"},
  1017  	}...)
  1018  }
  1019  
  1020  // func TestWebsocketsUpstreamUpgradeRequest(t *testing.T) {
  1021  // 	// setup spec and do test HTTP upgrade-request
  1022  // 	globalConf := config.Global()
  1023  // 	globalConf.HttpServerOptions.EnableWebSockets = true
  1024  // 	config.SetGlobal(globalConf)
  1025  // 	defer ResetTestConfig()
  1026  
  1027  // 	ts := StartTest()
  1028  // 	defer ts.Close()
  1029  
  1030  // 	BuildAndLoadAPI(func(spec *APISpec) {
  1031  // 		spec.Proxy.ListenPath = "/"
  1032  // 	})
  1033  
  1034  // 	ts.Run(t, test.TestCase{
  1035  // 		Path: "/ws",
  1036  // 		Headers: map[string]string{
  1037  // 			"Connection":            "Upgrade",
  1038  // 			"Upgrade":               "websocket",
  1039  // 			"Sec-Websocket-Version": "13",
  1040  // 			"Sec-Websocket-Key":     "abc",
  1041  // 		},
  1042  // 		Code: http.StatusSwitchingProtocols,
  1043  // 	})
  1044  // }
  1045  
  1046  func TestWebsocketsSeveralOpenClose(t *testing.T) {
  1047  	globalConf := config.Global()
  1048  	globalConf.HttpServerOptions.EnableWebSockets = true
  1049  	config.SetGlobal(globalConf)
  1050  	defer ResetTestConfig()
  1051  
  1052  	ts := StartTest()
  1053  	defer ts.Close()
  1054  
  1055  	BuildAndLoadAPI(func(spec *APISpec) {
  1056  		spec.Proxy.ListenPath = "/"
  1057  	})
  1058  
  1059  	baseURL := strings.Replace(ts.URL, "http://", "ws://", -1)
  1060  
  1061  	// connect 1st time, send and read message, close connection
  1062  	conn1, _, err := websocket.DefaultDialer.Dial(baseURL+"/ws", nil)
  1063  	if err != nil {
  1064  		t.Fatalf("cannot make websocket connection: %v", err)
  1065  	}
  1066  	err = conn1.WriteMessage(websocket.BinaryMessage, []byte("test message 1"))
  1067  	if err != nil {
  1068  		t.Fatalf("cannot write message: %v", err)
  1069  	}
  1070  	_, p, err := conn1.ReadMessage()
  1071  	if err != nil {
  1072  		t.Fatalf("cannot read message: %v", err)
  1073  	}
  1074  	if string(p) != "reply to message: test message 1" {
  1075  		t.Error("Unexpected reply:", string(p))
  1076  	}
  1077  	conn1.Close()
  1078  
  1079  	// connect 2nd time, send and read message, but don't close yet
  1080  	conn2, _, err := websocket.DefaultDialer.Dial(baseURL+"/ws", nil)
  1081  	if err != nil {
  1082  		t.Fatalf("cannot make websocket connection: %v", err)
  1083  	}
  1084  	err = conn2.WriteMessage(websocket.BinaryMessage, []byte("test message 2"))
  1085  	if err != nil {
  1086  		t.Fatalf("cannot write message: %v", err)
  1087  	}
  1088  	_, p, err = conn2.ReadMessage()
  1089  	if err != nil {
  1090  		t.Fatalf("cannot read message: %v", err)
  1091  	}
  1092  	if string(p) != "reply to message: test message 2" {
  1093  		t.Error("Unexpected reply:", string(p))
  1094  	}
  1095  
  1096  	// connect 3d time having one connection already open before, send and read message
  1097  	conn3, _, err := websocket.DefaultDialer.Dial(baseURL+"/ws", nil)
  1098  	if err != nil {
  1099  		t.Fatalf("cannot make websocket connection: %v", err)
  1100  	}
  1101  	err = conn3.WriteMessage(websocket.BinaryMessage, []byte("test message 3"))
  1102  	if err != nil {
  1103  		t.Fatalf("cannot write message: %v", err)
  1104  	}
  1105  	_, p, err = conn3.ReadMessage()
  1106  	if err != nil {
  1107  		t.Fatalf("cannot read message: %v", err)
  1108  	}
  1109  	if string(p) != "reply to message: test message 3" {
  1110  		t.Error("Unexpected reply:", string(p))
  1111  	}
  1112  
  1113  	// check that we still can interact via 2nd connection we did before
  1114  	err = conn2.WriteMessage(websocket.BinaryMessage, []byte("new test message 2"))
  1115  	if err != nil {
  1116  		t.Fatalf("cannot write message: %v", err)
  1117  	}
  1118  	_, p, err = conn2.ReadMessage()
  1119  	if err != nil {
  1120  		t.Fatalf("cannot read message: %v", err)
  1121  	}
  1122  	if string(p) != "reply to message: new test message 2" {
  1123  		t.Error("Unexpected reply:", string(p))
  1124  	}
  1125  
  1126  	// check that we still can interact via 3d connection we did before
  1127  	err = conn3.WriteMessage(websocket.BinaryMessage, []byte("new test message 3"))
  1128  	if err != nil {
  1129  		t.Fatalf("cannot write message: %v", err)
  1130  	}
  1131  	_, p, err = conn3.ReadMessage()
  1132  	if err != nil {
  1133  		t.Fatalf("cannot read message: %v", err)
  1134  	}
  1135  	if string(p) != "reply to message: new test message 3" {
  1136  		t.Error("Unexpected reply:", string(p))
  1137  	}
  1138  
  1139  	// clean up connections
  1140  	conn2.Close()
  1141  	conn3.Close()
  1142  }
  1143  
  1144  func TestWebsocketsAndHTTPEndpointMatch(t *testing.T) {
  1145  	globalConf := config.Global()
  1146  	globalConf.HttpServerOptions.EnableWebSockets = true
  1147  	config.SetGlobal(globalConf)
  1148  	defer ResetTestConfig()
  1149  
  1150  	ts := StartTest()
  1151  	defer ts.Close()
  1152  
  1153  	BuildAndLoadAPI(func(spec *APISpec) {
  1154  		spec.Proxy.ListenPath = "/"
  1155  	})
  1156  
  1157  	baseURL := strings.Replace(ts.URL, "http://", "ws://", -1)
  1158  
  1159  	// connect to ws, send 1st message and check reply
  1160  	wsConn, _, err := websocket.DefaultDialer.Dial(baseURL+"/ws", nil)
  1161  	if err != nil {
  1162  		t.Fatalf("cannot make websocket connection: %v", err)
  1163  	}
  1164  	err = wsConn.WriteMessage(websocket.BinaryMessage, []byte("test message 1"))
  1165  	if err != nil {
  1166  		t.Fatalf("cannot write message: %v", err)
  1167  	}
  1168  	_, p, err := wsConn.ReadMessage()
  1169  	if err != nil {
  1170  		t.Fatalf("cannot read message: %v", err)
  1171  	}
  1172  	if string(p) != "reply to message: test message 1" {
  1173  		t.Error("Unexpected reply:", string(p))
  1174  	}
  1175  
  1176  	// make 1st http request
  1177  	ts.Run(t, test.TestCase{
  1178  		Method: "GET",
  1179  		Path:   "/abc",
  1180  		Code:   http.StatusOK,
  1181  	})
  1182  
  1183  	// send second WS connection upgrade request
  1184  	// connect to ws, send 1st message and check reply
  1185  	wsConn2, _, err := websocket.DefaultDialer.Dial(baseURL+"/ws", nil)
  1186  	if err != nil {
  1187  		t.Fatalf("cannot make websocket connection: %v", err)
  1188  	}
  1189  	err = wsConn2.WriteMessage(websocket.BinaryMessage, []byte("test message 1 to ws 2"))
  1190  	if err != nil {
  1191  		t.Fatalf("cannot write message: %v", err)
  1192  	}
  1193  	_, p, err = wsConn2.ReadMessage()
  1194  	if err != nil {
  1195  		t.Fatalf("cannot read message: %v", err)
  1196  	}
  1197  	if string(p) != "reply to message: test message 1 to ws 2" {
  1198  		t.Error("Unexpected reply:", string(p))
  1199  	}
  1200  	wsConn2.Close()
  1201  
  1202  	// send second message to WS and check reply
  1203  	err = wsConn.WriteMessage(websocket.BinaryMessage, []byte("test message 2"))
  1204  	if err != nil {
  1205  		t.Fatalf("cannot write message: %v", err)
  1206  	}
  1207  	_, p, err = wsConn.ReadMessage()
  1208  	if err != nil {
  1209  		t.Fatalf("cannot read message: %v", err)
  1210  	}
  1211  	if string(p) != "reply to message: test message 2" {
  1212  		t.Error("Unexpected reply:", string(p))
  1213  	}
  1214  
  1215  	// make 2nd http request
  1216  	ts.Run(t, test.TestCase{
  1217  		Method: "GET",
  1218  		Path:   "/abc",
  1219  		Code:   http.StatusOK,
  1220  	})
  1221  
  1222  	wsConn.Close()
  1223  
  1224  	// make 3d http request after closing WS connection
  1225  	ts.Run(t, test.TestCase{
  1226  		Method: "GET",
  1227  		Path:   "/abc",
  1228  		Code:   http.StatusOK,
  1229  	})
  1230  }
  1231  
  1232  func createTestUptream(t *testing.T, allowedConns int, readsPerConn int) net.Listener {
  1233  	l, _ := net.Listen("tcp", "127.0.0.1:0")
  1234  	go func() {
  1235  		conns := 0
  1236  
  1237  		for {
  1238  			conn, err := l.Accept()
  1239  			if err != nil {
  1240  				return
  1241  			}
  1242  			conns++
  1243  
  1244  			if conns > allowedConns {
  1245  				t.Fatal("Too many connections")
  1246  				conn.Close()
  1247  				return
  1248  			}
  1249  
  1250  			reads := 0
  1251  			go func() {
  1252  				for {
  1253  					buf := make([]byte, 1024)
  1254  					conn.SetDeadline(time.Now().Add(50 * time.Millisecond))
  1255  					_, err := conn.Read(buf)
  1256  					if err != nil {
  1257  						conn.Close()
  1258  						return
  1259  					}
  1260  					reads++
  1261  
  1262  					if reads > readsPerConn {
  1263  						t.Error("Too many reads per conn")
  1264  						conn.Close()
  1265  						return
  1266  					}
  1267  
  1268  					conn.SetDeadline(time.Now().Add(50 * time.Millisecond))
  1269  					conn.Write([]byte("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"))
  1270  				}
  1271  			}()
  1272  		}
  1273  	}()
  1274  
  1275  	return l
  1276  }
  1277  
  1278  func TestKeepAliveConns(t *testing.T) {
  1279  	ts := StartTest()
  1280  	defer ts.Close()
  1281  	defer ResetTestConfig()
  1282  
  1283  	t.Run("Should use same connection", func(t *testing.T) {
  1284  		// set keep alive option
  1285  		globalConf := config.Global()
  1286  		globalConf.ProxyCloseConnections = false
  1287  		config.SetGlobal(globalConf)
  1288  
  1289  		// Allow 1 connection with 3 reads
  1290  		upstream := createTestUptream(t, 1, 3)
  1291  		defer upstream.Close()
  1292  
  1293  		BuildAndLoadAPI(func(spec *APISpec) {
  1294  			spec.Proxy.ListenPath = "/"
  1295  			spec.Proxy.TargetURL = "http://" + upstream.Addr().String()
  1296  		})
  1297  
  1298  		ts.Run(t, []test.TestCase{
  1299  			{Code: 200},
  1300  			{Code: 200},
  1301  			{Code: 200},
  1302  		}...)
  1303  	})
  1304  
  1305  	t.Run("Should use separate connection", func(t *testing.T) {
  1306  		globalConf := config.Global()
  1307  		globalConf.ProxyCloseConnections = true
  1308  		config.SetGlobal(globalConf)
  1309  
  1310  		// Allow 3 connections with 1 read
  1311  		upstream := createTestUptream(t, 3, 1)
  1312  		defer upstream.Close()
  1313  
  1314  		BuildAndLoadAPI(func(spec *APISpec) {
  1315  			spec.Proxy.ListenPath = "/"
  1316  			spec.Proxy.TargetURL = "http://" + upstream.Addr().String()
  1317  		})
  1318  
  1319  		ts.Run(t, []test.TestCase{
  1320  			{Code: 200},
  1321  			{Code: 200},
  1322  			{Code: 200},
  1323  		}...)
  1324  	})
  1325  
  1326  	t.Run("Should respect max_conn_time", func(t *testing.T) {
  1327  		globalConf := config.Global()
  1328  		globalConf.ProxyCloseConnections = false
  1329  		globalConf.MaxConnTime = 1
  1330  		config.SetGlobal(globalConf)
  1331  
  1332  		// Allow 2 connection with 2 reads
  1333  		upstream := createTestUptream(t, 2, 2)
  1334  		defer upstream.Close()
  1335  
  1336  		spec := BuildAndLoadAPI(func(spec *APISpec) {
  1337  			spec.Proxy.ListenPath = "/"
  1338  			spec.Proxy.TargetURL = "http://" + upstream.Addr().String()
  1339  		})[0]
  1340  
  1341  		ts.Run(t, []test.TestCase{
  1342  			{Code: 200},
  1343  			{Code: 200},
  1344  		}...)
  1345  
  1346  		// Set in past to re-create transport
  1347  		spec.HTTPTransportCreated = time.Now().Add(-time.Minute)
  1348  
  1349  		// Should be called in new connection
  1350  		// We already made 2 requests above, so 3th in same not allowed
  1351  		ts.Run(t, test.TestCase{Code: 200})
  1352  	})
  1353  }
  1354  
  1355  // TestRateLimitForAPIAndRateLimitAndQuotaCheck ensures that the Rate Limit for the key is applied before the rate limit
  1356  // for the API. Meaning that a single token cannot reduce service availability for other tokens by simply going over the
  1357  // API's global rate limit.
  1358  func TestRateLimitForAPIAndRateLimitAndQuotaCheck(t *testing.T) {
  1359  	globalCfg := config.Global()
  1360  	globalCfg.EnableNonTransactionalRateLimiter = false
  1361  	globalCfg.EnableSentinelRateLimiter = true
  1362  	config.SetGlobal(globalCfg)
  1363  
  1364  	defer ResetTestConfig()
  1365  
  1366  	ts := StartTest()
  1367  	defer ts.Close()
  1368  
  1369  	BuildAndLoadAPI(func(spec *APISpec) {
  1370  		spec.APIID += "_" + time.Now().String()
  1371  		spec.UseKeylessAccess = false
  1372  		spec.DisableRateLimit = false
  1373  		spec.OrgID = "default"
  1374  		spec.GlobalRateLimit = apidef.GlobalRateLimit{
  1375  			Rate: 2,
  1376  			Per:  60,
  1377  		}
  1378  		spec.Proxy.ListenPath = "/"
  1379  	})
  1380  
  1381  	sess1token := CreateSession(func(s *user.SessionState) {
  1382  		s.Rate = 1
  1383  		s.Per = 60
  1384  	})
  1385  	defer FallbackKeySesionManager.RemoveSession(sess1token, false)
  1386  
  1387  	sess2token := CreateSession(func(s *user.SessionState) {
  1388  		s.Rate = 1
  1389  		s.Per = 60
  1390  	})
  1391  	defer FallbackKeySesionManager.RemoveSession(sess2token, false)
  1392  
  1393  	ts.Run(t, []test.TestCase{
  1394  		{Headers: map[string]string{"Authorization": sess1token}, Code: http.StatusOK, Path: "/", Delay: 100 * time.Millisecond},
  1395  		{Headers: map[string]string{"Authorization": sess1token}, Code: http.StatusTooManyRequests, Path: "/"},
  1396  		{Headers: map[string]string{"Authorization": sess2token}, Code: http.StatusOK, Path: "/", Delay: 100 * time.Millisecond},
  1397  		{Headers: map[string]string{"Authorization": sess2token}, Code: http.StatusTooManyRequests, Path: "/"},
  1398  	}...)
  1399  }
  1400  
  1401  func TestTracing(t *testing.T) {
  1402  	ts := StartTest()
  1403  	defer ts.Close()
  1404  
  1405  	prepareStorage()
  1406  	spec := BuildAPI(func(spec *APISpec) {
  1407  		spec.UseKeylessAccess = false
  1408  	})[0]
  1409  
  1410  	keyID := CreateSession(func(s *user.SessionState) {})
  1411  	authHeaders := map[string][]string{"Authorization": {keyID}}
  1412  
  1413  	ts.Run(t, []test.TestCase{
  1414  		{Method: "GET", Path: "/tyk/debug", AdminAuth: true, Code: 405},
  1415  		{Method: "POST", Path: "/tyk/debug", AdminAuth: true, Code: 400, BodyMatch: "Request malformed"},
  1416  		{Method: "POST", Path: "/tyk/debug", Data: `{}`, AdminAuth: true, Code: 400, BodyMatch: "Spec field is missing"},
  1417  		{Method: "POST", Path: "/tyk/debug", Data: `{"Spec": {}}`, AdminAuth: true, Code: 400, BodyMatch: "Request field is missing"},
  1418  		{Method: "POST", Path: "/tyk/debug", Data: `{"Spec": {}, "Request": {}}`, AdminAuth: true, Code: 400, BodyMatch: "Spec not valid, skipped!"},
  1419  		{Method: "POST", Path: "/tyk/debug", Data: traceRequest{Spec: spec.APIDefinition, Request: &traceHttpRequest{Method: "GET", Path: "/"}}, AdminAuth: true, Code: 200, BodyMatch: `401 Unauthorized`},
  1420  		{Method: "POST", Path: "/tyk/debug", Data: traceRequest{Spec: spec.APIDefinition, Request: &traceHttpRequest{Path: "/", Headers: authHeaders}}, AdminAuth: true, Code: 200, BodyMatch: `200 OK`},
  1421  	}...)
  1422  }
  1423  
  1424  func TestBrokenClients(t *testing.T) {
  1425  	ts := StartTest()
  1426  	defer ts.Close()
  1427  	defer ResetTestConfig()
  1428  
  1429  	globalConf := config.Global()
  1430  	globalConf.ProxyDefaultTimeout = 1
  1431  	config.SetGlobal(globalConf)
  1432  
  1433  	BuildAndLoadAPI(func(spec *APISpec) {
  1434  		spec.UseKeylessAccess = true
  1435  		spec.Proxy.ListenPath = "/"
  1436  		spec.EnforcedTimeoutEnabled = true
  1437  	})
  1438  
  1439  	buf := make([]byte, 1024)
  1440  
  1441  	t.Run("Valid client", func(t *testing.T) {
  1442  		conn, _ := net.DialTimeout("tcp", ts.ln.Addr().String(), 0)
  1443  		conn.Write([]byte("GET / HTTP/1.1\r\nHost: 127.0.0.1\r\n\r\n"))
  1444  		conn.Read(buf)
  1445  
  1446  		if string(buf[:12]) != "HTTP/1.1 200" {
  1447  			t.Error("Invalid server response:", string(buf))
  1448  		}
  1449  	})
  1450  
  1451  	t.Run("Invalid client: close without read", func(t *testing.T) {
  1452  		time.Sleep(recordsBufferFlushInterval + 50*time.Millisecond)
  1453  		analytics.Store.GetAndDeleteSet(analyticsKeyName)
  1454  
  1455  		conn, _ := net.DialTimeout("tcp", ts.ln.Addr().String(), 0)
  1456  		conn.Write([]byte("GET / HTTP/1.1\r\nHost: 127.0.0.1\r\n\r\n"))
  1457  		conn.Close()
  1458  		//conn.Read(buf)
  1459  
  1460  		time.Sleep(recordsBufferFlushInterval + 50*time.Millisecond)
  1461  		results := analytics.Store.GetAndDeleteSet(analyticsKeyName)
  1462  
  1463  		var record AnalyticsRecord
  1464  		msgpack.Unmarshal(results[0].([]byte), &record)
  1465  		if record.ResponseCode != 499 {
  1466  			t.Fatal("Analytics record do not match:", record)
  1467  		}
  1468  	})
  1469  }