github.com/anuvu/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 }