github.com/Tyktechnologies/tyk@v2.9.5+incompatible/gateway/gateway_test.go (about) 1 package gateway 2 3 import ( 4 "bytes" 5 "context" 6 "encoding/json" 7 "fmt" 8 "io/ioutil" 9 "net" 10 "net/http" 11 "net/http/httptest" 12 "net/url" 13 "os" 14 "runtime" 15 "strconv" 16 17 "strings" 18 "sync" 19 "testing" 20 "time" 21 22 "github.com/go-redis/redis" 23 "github.com/gorilla/websocket" 24 proxyproto "github.com/pires/go-proxyproto" 25 msgpack "gopkg.in/vmihailenco/msgpack.v2" 26 27 "github.com/TykTechnologies/tyk/apidef" 28 "github.com/TykTechnologies/tyk/cli" 29 "github.com/TykTechnologies/tyk/config" 30 "github.com/TykTechnologies/tyk/storage" 31 "github.com/TykTechnologies/tyk/test" 32 "github.com/TykTechnologies/tyk/user" 33 ) 34 35 const defaultListenPort = 8080 36 37 func TestMain(m *testing.M) { 38 os.Exit(InitTestMain(context.Background(), m)) 39 } 40 41 func createNonThrottledSession() *user.SessionState { 42 session := new(user.SessionState) 43 session.Rate = 100.0 44 session.Allowance = session.Rate 45 session.LastCheck = time.Now().Unix() 46 session.Per = 1.0 47 session.QuotaRenewalRate = 300 // 5 minutes 48 session.QuotaRenews = time.Now().Unix() 49 session.QuotaRemaining = 10 50 session.QuotaMax = 10 51 session.Alias = "TEST-ALIAS" 52 session.Mutex = &sync.RWMutex{} 53 return session 54 } 55 56 func TestAA(t *testing.T) { 57 ts := StartTest() 58 59 ts.Start() 60 defer ts.Close() 61 62 BuildAndLoadAPI(func(spec *APISpec) { 63 spec.Proxy.ListenPath = "/" 64 }) 65 66 ts.Run(t, []test.TestCase{ 67 {Code: 200}, 68 }...) 69 70 } 71 72 type tykErrorResponse struct { 73 Error string 74 } 75 76 func testKey(testName string, name string) string { 77 return fmt.Sprintf("%s-%s", testName, name) 78 } 79 80 func TestParambasedAuth(t *testing.T) { 81 ts := StartTest() 82 defer ts.Close() 83 84 BuildAndLoadAPI(func(spec *APISpec) { 85 spec.Auth.UseParam = true 86 spec.UseKeylessAccess = false 87 spec.Proxy.ListenPath = "/" 88 }) 89 90 key := CreateSession(func(s *user.SessionState) { 91 s.SetAccessRights(map[string]user.AccessDefinition{"test": { 92 APIID: "test", Versions: []string{"v1"}, 93 }}) 94 s.Mutex = &sync.RWMutex{} 95 }) 96 97 form := url.Values{} 98 form.Add("foo", "swiggetty") 99 form.Add("bar", "swoggetty") 100 form.Add("baz", "swoogetty") 101 102 expectedBody := `"Form":{"authorization":"` + key + `","bar":"swoggetty","baz":"swoogetty","foo":"swiggetty"}` 103 104 ts.Run(t, test.TestCase{ 105 Method: "POST", 106 Path: "/?authorization=" + key, 107 Headers: map[string]string{"Content-Type": "application/x-www-form-urlencoded"}, 108 Data: string(form.Encode()), 109 Code: 200, 110 BodyMatch: expectedBody, 111 }) 112 } 113 114 func TestStripPathWithURLRewrite(t *testing.T) { 115 ts := StartTest() 116 defer ts.Close() 117 defer ResetTestConfig() 118 119 t.Run("rewrite URL containing listen path", func(t *testing.T) { 120 BuildAndLoadAPI(func(spec *APISpec) { 121 version := spec.VersionData.Versions["v1"] 122 json.Unmarshal([]byte(`{ 123 "use_extended_paths": true, 124 "extended_paths": { 125 "url_rewrites": [{ 126 "path": "/anything/", 127 "match_pattern": "/anything/(.*)", 128 "method": "GET", 129 "rewrite_to":"/something/$1" 130 }] 131 } 132 }`), &version) 133 spec.VersionData.Versions["v1"] = version 134 spec.Proxy.ListenPath = "/myapi/" 135 spec.Proxy.StripListenPath = true 136 137 }) 138 139 ts.Run(t, []test.TestCase{ 140 {Path: "/myapi/anything/a/myapi/b/c", BodyMatch: `"Url":"/something/a/myapi/b/c"`}, 141 }...) 142 }) 143 } 144 145 func TestSkipTargetPassEscapingOff(t *testing.T) { 146 ts := StartTest() 147 defer ts.Close() 148 defer ResetTestConfig() 149 150 t.Run("With escaping, default", func(t *testing.T) { 151 globalConf := config.Global() 152 globalConf.HttpServerOptions.SkipTargetPathEscaping = false 153 config.SetGlobal(globalConf) 154 155 BuildAndLoadAPI(func(spec *APISpec) { 156 spec.Proxy.ListenPath = "/" 157 }) 158 159 ts.Run(t, []test.TestCase{ 160 {Path: "/(abc,xyz)?arg=val", BodyMatch: `"Url":"/%28abc,xyz%29\?arg=val`}, 161 {Path: "/%28abc,xyz%29?arg=val", BodyMatch: `"Url":"/%28abc,xyz%29\?arg=val`}, 162 }...) 163 }) 164 165 t.Run("Without escaping", func(t *testing.T) { 166 globalConf := config.Global() 167 globalConf.HttpServerOptions.SkipTargetPathEscaping = true 168 config.SetGlobal(globalConf) 169 170 BuildAndLoadAPI(func(spec *APISpec) { 171 spec.Proxy.ListenPath = "/" 172 }) 173 174 ts.Run(t, []test.TestCase{ 175 {Path: "/(abc,xyz)?arg=val", BodyMatch: `"Url":"/\(abc,xyz\)\?arg=val"`}, 176 {Path: "/%28abc,xyz%29?arg=val", BodyMatch: `"Url":"/%28abc,xyz%29\?arg=val"`}, 177 }...) 178 }) 179 180 t.Run("With escaping, listen path and target URL are set, StripListenPath is OFF", func(t *testing.T) { 181 globalConf := config.Global() 182 globalConf.HttpServerOptions.SkipTargetPathEscaping = false 183 config.SetGlobal(globalConf) 184 185 BuildAndLoadAPI(func(spec *APISpec) { 186 spec.Proxy.StripListenPath = false 187 spec.Proxy.ListenPath = "/listen_me" 188 spec.Proxy.TargetURL = TestHttpAny + "/sent_to_me" 189 }) 190 191 ts.Run(t, []test.TestCase{ 192 {Path: "/listen_me/(abc,xyz)?arg=val", BodyMatch: `"Url":"/sent_to_me/listen_me/%28abc,xyz%29\?arg=val"`}, 193 {Path: "/listen_me/%28abc,xyz%29?arg=val", BodyMatch: `"Url":"/sent_to_me/listen_me/%28abc,xyz%29\?arg=val"`}, 194 }...) 195 }) 196 197 t.Run("Without escaping, listen path and target URL are set, StripListenPath is OFF", func(t *testing.T) { 198 globalConf := config.Global() 199 globalConf.HttpServerOptions.SkipTargetPathEscaping = true 200 config.SetGlobal(globalConf) 201 202 BuildAndLoadAPI(func(spec *APISpec) { 203 spec.Proxy.StripListenPath = false 204 spec.Proxy.ListenPath = "/listen_me" 205 spec.Proxy.TargetURL = TestHttpAny + "/sent_to_me" 206 }) 207 208 ts.Run(t, []test.TestCase{ 209 {Path: "/listen_me/(abc,xyz)?arg=val", BodyMatch: `"Url":"/sent_to_me/listen_me/\(abc,xyz\)\?arg=val"`}, 210 {Path: "/listen_me/%28abc,xyz%29?arg=val", BodyMatch: `"Url":"/sent_to_me/listen_me/%28abc,xyz%29\?arg=val"`}, 211 }...) 212 }) 213 214 t.Run("With escaping, listen path and target URL are set, StripListenPath is ON", func(t *testing.T) { 215 globalConf := config.Global() 216 globalConf.HttpServerOptions.SkipTargetPathEscaping = false 217 config.SetGlobal(globalConf) 218 219 BuildAndLoadAPI(func(spec *APISpec) { 220 spec.Proxy.StripListenPath = true 221 spec.Proxy.ListenPath = "/listen_me" 222 spec.Proxy.TargetURL = TestHttpAny + "/sent_to_me" 223 }) 224 225 ts.Run(t, []test.TestCase{ 226 {Path: "/listen_me/(abc,xyz)?arg=val", BodyMatch: `"Url":"/sent_to_me/%28abc,xyz%29\?arg=val"`}, 227 {Path: "/listen_me/%28abc,xyz%29?arg=val", BodyMatch: `"Url":"/sent_to_me/%28abc,xyz%29\?arg=val"`}, 228 }...) 229 }) 230 231 t.Run("Without escaping, listen path and target URL are set, StripListenPath is ON", func(t *testing.T) { 232 globalConf := config.Global() 233 globalConf.HttpServerOptions.SkipTargetPathEscaping = true 234 config.SetGlobal(globalConf) 235 236 BuildAndLoadAPI(func(spec *APISpec) { 237 spec.Proxy.StripListenPath = true 238 spec.Proxy.ListenPath = "/listen_me" 239 spec.Proxy.TargetURL = TestHttpAny + "/sent_to_me" 240 }) 241 242 ts.Run(t, []test.TestCase{ 243 {Path: "/listen_me/(abc,xyz)?arg=val", BodyMatch: `"Url":"/sent_to_me/\(abc,xyz\)\?arg=val"`}, 244 {Path: "/listen_me/%28abc,xyz%29?arg=val", BodyMatch: `"Url":"/sent_to_me/%28abc,xyz%29\?arg=val"`}, 245 }...) 246 }) 247 } 248 249 func TestSkipTargetPassEscapingOffWithSkipURLCleaningTrue(t *testing.T) { 250 globalConf := config.Global() 251 globalConf.HttpServerOptions.OverrideDefaults = true 252 globalConf.HttpServerOptions.SkipURLCleaning = true 253 config.SetGlobal(globalConf) 254 defer ResetTestConfig() 255 256 // here we expect that test gateway will be sending to test upstream requests with not cleaned URI 257 // so test upstream shouldn't reply with 301 and process them as well 258 prevSkipClean := defaultTestConfig.HttpServerOptions.OverrideDefaults && 259 defaultTestConfig.HttpServerOptions.SkipURLCleaning 260 testServerRouter.SkipClean(true) 261 defer testServerRouter.SkipClean(prevSkipClean) 262 263 ts := StartTest() 264 defer ts.Close() 265 266 t.Run("With escaping, default", func(t *testing.T) { 267 globalConf := config.Global() 268 globalConf.HttpServerOptions.SkipTargetPathEscaping = false 269 config.SetGlobal(globalConf) 270 271 BuildAndLoadAPI(func(spec *APISpec) { 272 spec.Proxy.ListenPath = "/" 273 }) 274 275 ts.Run(t, []test.TestCase{ 276 {Path: "/abc/xyz/http%3A%2F%2Ftest.com?arg=val", BodyMatch: `"Url":"/abc/xyz/http%3A%2F%2Ftest.com\?arg=val`}, 277 }...) 278 }) 279 280 t.Run("Without escaping, default", func(t *testing.T) { 281 globalConf := config.Global() 282 globalConf.HttpServerOptions.SkipTargetPathEscaping = true 283 config.SetGlobal(globalConf) 284 285 BuildAndLoadAPI(func(spec *APISpec) { 286 spec.Proxy.ListenPath = "/" 287 }) 288 289 ts.Run(t, []test.TestCase{ 290 {Path: "/abc/xyz/http%3A%2F%2Ftest.com?arg=val", BodyMatch: `"Url":"/abc/xyz/http%3A%2F%2Ftest.com\?arg=val`}, 291 }...) 292 }) 293 294 t.Run("With escaping, listen path and target URL are set, StripListenPath is OFF", func(t *testing.T) { 295 globalConf := config.Global() 296 globalConf.HttpServerOptions.SkipTargetPathEscaping = false 297 config.SetGlobal(globalConf) 298 299 BuildAndLoadAPI(func(spec *APISpec) { 300 spec.Proxy.StripListenPath = false 301 spec.Proxy.ListenPath = "/listen_me" 302 spec.Proxy.TargetURL = TestHttpAny + "/sent_to_me" 303 }) 304 305 ts.Run(t, []test.TestCase{ 306 {Path: "/listen_me/(abc,xyz)?arg=val", BodyMatch: `"Url":"/sent_to_me/listen_me/%28abc,xyz%29\?arg=val"`}, 307 {Path: "/listen_me/%28abc,xyz%29?arg=val", BodyMatch: `"Url":"/sent_to_me/listen_me/%28abc,xyz%29\?arg=val"`}, 308 {Path: "/listen_me/http%3A%2F%2Ftest.com?arg=val", BodyMatch: `"Url":"/sent_to_me/listen_me/http%3A%2F%2Ftest.com\?arg=val`}, 309 }...) 310 }) 311 312 t.Run("Without escaping, listen path and target URL are set, StripListenPath is OFF", func(t *testing.T) { 313 globalConf := config.Global() 314 globalConf.HttpServerOptions.SkipTargetPathEscaping = true 315 config.SetGlobal(globalConf) 316 317 BuildAndLoadAPI(func(spec *APISpec) { 318 spec.Proxy.StripListenPath = false 319 spec.Proxy.ListenPath = "/listen_me" 320 spec.Proxy.TargetURL = TestHttpAny + "/sent_to_me" 321 }) 322 323 ts.Run(t, []test.TestCase{ 324 {Path: "/listen_me/(abc,xyz)?arg=val", BodyMatch: `"Url":"/sent_to_me/listen_me/\(abc,xyz\)\?arg=val"`}, 325 {Path: "/listen_me/%28abc,xyz%29?arg=val", BodyMatch: `"Url":"/sent_to_me/listen_me/%28abc,xyz%29\?arg=val"`}, 326 {Path: "/listen_me/http%3A%2F%2Ftest.com?arg=val", BodyMatch: `"Url":"/sent_to_me/listen_me/http%3A%2F%2Ftest.com\?arg=val`}, 327 }...) 328 }) 329 330 t.Run("With escaping, listen path and target URL are set, StripListenPath is ON", func(t *testing.T) { 331 globalConf := config.Global() 332 globalConf.HttpServerOptions.SkipTargetPathEscaping = false 333 config.SetGlobal(globalConf) 334 335 BuildAndLoadAPI(func(spec *APISpec) { 336 spec.Proxy.StripListenPath = true 337 spec.Proxy.ListenPath = "/listen_me" 338 spec.Proxy.TargetURL = TestHttpAny + "/sent_to_me" 339 }) 340 341 ts.Run(t, []test.TestCase{ 342 {Path: "/listen_me/(abc,xyz)?arg=val", BodyMatch: `"Url":"/sent_to_me/%28abc,xyz%29\?arg=val"`}, 343 {Path: "/listen_me/%28abc,xyz%29?arg=val", BodyMatch: `"Url":"/sent_to_me/%28abc,xyz%29\?arg=val"`}, 344 {Path: "/listen_me/http%3A%2F%2Ftest.com?arg=val", BodyMatch: `"Url":"/sent_to_me/http%3A%2F%2Ftest.com\?arg=val`}, 345 }...) 346 }) 347 348 t.Run("Without escaping, listen path and target URL are set, StripListenPath is ON", func(t *testing.T) { 349 globalConf := config.Global() 350 globalConf.HttpServerOptions.SkipTargetPathEscaping = true 351 config.SetGlobal(globalConf) 352 353 BuildAndLoadAPI(func(spec *APISpec) { 354 spec.Proxy.StripListenPath = true 355 spec.Proxy.ListenPath = "/listen_me" 356 spec.Proxy.TargetURL = TestHttpAny + "/sent_to_me" 357 }) 358 359 ts.Run(t, []test.TestCase{ 360 {Path: "/listen_me/(abc,xyz)?arg=val", BodyMatch: `"Url":"/sent_to_me/\(abc,xyz\)\?arg=val"`}, 361 {Path: "/listen_me/%28abc,xyz%29?arg=val", BodyMatch: `"Url":"/sent_to_me/%28abc,xyz%29\?arg=val"`}, 362 {Path: "/listen_me/http%3A%2F%2Ftest.com?arg=val", BodyMatch: `"Url":"/sent_to_me/http%3A%2F%2Ftest.com\?arg=val`}, 363 }...) 364 }) 365 366 } 367 368 func TestQuota(t *testing.T) { 369 ts := StartTest() 370 defer ts.Close() 371 372 var keyID string 373 374 var webhookWG sync.WaitGroup 375 webhook := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 376 if r.Header.Get("X-Tyk-Test-Header") != "Tyk v1.BANANA" { 377 t.Error("Custom webhook header not set", r.Header) 378 } 379 380 var data map[string]string 381 body, _ := ioutil.ReadAll(r.Body) 382 json.Unmarshal(body, &data) 383 384 if data["event"] != "QuotaExceeded" || data["message"] != "Key Quota Limit Exceeded" || data["key"] != keyID { 385 t.Error("Webhook payload not match", data) 386 } 387 388 webhookWG.Done() 389 })) 390 defer webhook.Close() 391 392 BuildAndLoadAPI(func(spec *APISpec) { 393 spec.UseKeylessAccess = false 394 spec.Proxy.ListenPath = "/" 395 396 version := spec.VersionData.Versions["v1"] 397 json.Unmarshal([]byte(`{ 398 "use_extended_paths": true, 399 "extended_paths": { 400 "ignored":[{ 401 "path": "/get", 402 "method_actions": {"GET": {"action": "no_action"}} 403 }] 404 } 405 }`), &version) 406 spec.VersionData.Versions["v1"] = version 407 408 json.Unmarshal([]byte(` 409 { "events": { "QuotaExceeded": 410 [{ 411 "handler_name":"eh_log_handler", 412 "handler_meta": { 413 "prefix": "LOG-HANDLER-PREFIX" 414 } 415 }, 416 { 417 "handler_name":"eh_web_hook_handler", 418 "handler_meta": { 419 "method": "POST", 420 "target_path": "`+webhook.URL+`", 421 "template_path": "templates/default_webhook.json", 422 "header_map": {"X-Tyk-Test-Header": "Tyk v1.BANANA"}, 423 "event_timeout": 10 424 } 425 }] 426 }}`), &spec.EventHandlers) 427 }) 428 429 // Create session with Quota = 2 430 keyID = CreateSession(func(s *user.SessionState) { 431 s.QuotaMax = 2 432 s.Mutex = &sync.RWMutex{} 433 }) 434 435 authHeaders := map[string]string{ 436 "authorization": keyID, 437 } 438 439 webhookWG.Add(1) 440 ts.Run(t, []test.TestCase{ 441 {Path: "/", Headers: authHeaders, Code: 200}, 442 // Ignored path should not affect quota 443 {Path: "/get", Headers: authHeaders, Code: 200}, 444 {Path: "/", Headers: authHeaders, Code: 200}, 445 {Path: "/", Headers: authHeaders, Code: 403, BodyMatch: `"error": "Quota exceeded"`}, 446 // Ignored path works without auth 447 {Path: "/get", Code: 200}, 448 }...) 449 webhookWG.Wait() 450 } 451 452 func TestAnalytics(t *testing.T) { 453 ts := StartTest(TestConfig{ 454 Delay: 20 * time.Millisecond, 455 }) 456 defer ts.Close() 457 base := config.Global() 458 459 BuildAndLoadAPI(func(spec *APISpec) { 460 spec.UseKeylessAccess = false 461 spec.Proxy.ListenPath = "/" 462 }) 463 464 // Cleanup before test 465 // let records to to be sent 466 time.Sleep(recordsBufferFlushInterval + 50) 467 analytics.Store.GetAndDeleteSet(analyticsKeyName) 468 469 t.Run("Log errors", func(t *testing.T) { 470 ts.Run(t, []test.TestCase{ 471 {Path: "/", Code: 401}, 472 {Path: "/", Code: 401}, 473 }...) 474 475 // let records to to be sent 476 time.Sleep(recordsBufferFlushInterval + 50) 477 478 results := analytics.Store.GetAndDeleteSet(analyticsKeyName) 479 if len(results) != 2 { 480 t.Error("Should return 2 record", len(results)) 481 } 482 483 var record AnalyticsRecord 484 msgpack.Unmarshal([]byte(results[0].(string)), &record) 485 if record.ResponseCode != 401 { 486 t.Error("Analytics record do not match: ", record) 487 } 488 }) 489 490 t.Run("Log success", func(t *testing.T) { 491 key := CreateSession() 492 493 authHeaders := map[string]string{ 494 "authorization": key, 495 } 496 497 ts.Run(t, test.TestCase{ 498 Path: "/", Headers: authHeaders, Code: 200, 499 }) 500 501 // let records to to be sent 502 time.Sleep(recordsBufferFlushInterval + 50) 503 504 results := analytics.Store.GetAndDeleteSet(analyticsKeyName) 505 if len(results) != 1 { 506 t.Error("Should return 1 record: ", len(results)) 507 } 508 509 var record AnalyticsRecord 510 msgpack.Unmarshal([]byte(results[0].(string)), &record) 511 if record.ResponseCode != 200 { 512 t.Error("Analytics record do not match", record) 513 } 514 }) 515 516 t.Run("Detailed analytics with api spec config enabled", func(t *testing.T) { 517 defer func() { 518 config.SetGlobal(base) 519 }() 520 globalConf := config.Global() 521 globalConf.AnalyticsConfig.EnableDetailedRecording = false 522 config.SetGlobal(globalConf) 523 524 BuildAndLoadAPI(func(spec *APISpec) { 525 spec.UseKeylessAccess = false 526 spec.Proxy.ListenPath = "/" 527 spec.EnableDetailedRecording = true 528 }) 529 530 key := CreateSession() 531 532 authHeaders := map[string]string{ 533 "authorization": key, 534 } 535 536 ts.Run(t, test.TestCase{ 537 Path: "/", Headers: authHeaders, Code: 200, 538 }) 539 540 // let records to to be sent 541 time.Sleep(recordsBufferFlushInterval + 50) 542 543 results := analytics.Store.GetAndDeleteSet(analyticsKeyName) 544 if len(results) != 1 { 545 t.Error("Should return 1 record: ", len(results)) 546 } 547 548 var record AnalyticsRecord 549 msgpack.Unmarshal([]byte(results[0].(string)), &record) 550 if record.ResponseCode != 200 { 551 t.Error("Analytics record do not match", record) 552 } 553 554 if record.RawRequest == "" { 555 t.Error("Detailed request info not found", record) 556 } 557 558 if record.RawResponse == "" { 559 t.Error("Detailed response info not found", record) 560 } 561 }) 562 563 t.Run("Detailed analytics with only key flag set", func(t *testing.T) { 564 defer func() { 565 config.SetGlobal(base) 566 }() 567 globalConf := config.Global() 568 globalConf.AnalyticsConfig.EnableDetailedRecording = false 569 config.SetGlobal(globalConf) 570 571 BuildAndLoadAPI(func(spec *APISpec) { 572 spec.UseKeylessAccess = false 573 spec.Proxy.ListenPath = "/" 574 spec.EnableDetailedRecording = false 575 }) 576 577 key := CreateSession(func(sess *user.SessionState) { 578 sess.EnableDetailedRecording = true 579 sess.Mutex = &sync.RWMutex{} 580 }) 581 582 authHeaders := map[string]string{ 583 "authorization": key, 584 } 585 586 ts.Run(t, test.TestCase{ 587 Path: "/", Headers: authHeaders, Code: 200, 588 }) 589 590 // let records to to be sent 591 time.Sleep(recordsBufferFlushInterval + 50) 592 593 results := analytics.Store.GetAndDeleteSet(analyticsKeyName) 594 if len(results) != 1 { 595 t.Error("Should return 1 record: ", len(results)) 596 } 597 598 var record AnalyticsRecord 599 msgpack.Unmarshal([]byte(results[0].(string)), &record) 600 if record.ResponseCode != 200 { 601 t.Error("Analytics record do not match", record) 602 } 603 604 if record.RawRequest == "" { 605 t.Error("Detailed request info not found", record) 606 } 607 608 if record.RawResponse == "" { 609 t.Error("Detailed response info not found", record) 610 } 611 }) 612 t.Run("Detailed analytics", func(t *testing.T) { 613 defer func() { 614 config.SetGlobal(base) 615 }() 616 globalConf := config.Global() 617 globalConf.AnalyticsConfig.EnableDetailedRecording = true 618 config.SetGlobal(globalConf) 619 620 BuildAndLoadAPI(func(spec *APISpec) { 621 spec.UseKeylessAccess = false 622 spec.Proxy.ListenPath = "/" 623 }) 624 625 key := CreateSession() 626 627 authHeaders := map[string]string{ 628 "authorization": key, 629 } 630 631 ts.Run(t, test.TestCase{ 632 Path: "/", Headers: authHeaders, Code: 200, 633 }) 634 635 // let records to to be sent 636 time.Sleep(recordsBufferFlushInterval + 50) 637 638 results := analytics.Store.GetAndDeleteSet(analyticsKeyName) 639 if len(results) != 1 { 640 t.Error("Should return 1 record: ", len(results)) 641 } 642 643 var record AnalyticsRecord 644 msgpack.Unmarshal([]byte(results[0].(string)), &record) 645 if record.ResponseCode != 200 { 646 t.Error("Analytics record do not match", record) 647 } 648 649 if record.RawRequest == "" { 650 t.Error("Detailed request info not found", record) 651 } 652 653 if record.RawResponse == "" { 654 t.Error("Detailed response info not found", record) 655 } 656 }) 657 658 t.Run("Detailed analytics with latency", func(t *testing.T) { 659 defer func() { 660 config.SetGlobal(base) 661 }() 662 globalConf := config.Global() 663 globalConf.AnalyticsConfig.EnableDetailedRecording = true 664 config.SetGlobal(globalConf) 665 ls := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 666 // We are delaying the response by 2 ms. This is important because anytime 667 // less than 0 eg 0.2 ms will be round off to 0 which is not good to check if we have 668 // latency correctly set. 669 time.Sleep(2 * time.Millisecond) 670 })) 671 defer ls.Close() 672 BuildAndLoadAPI(func(spec *APISpec) { 673 spec.UseKeylessAccess = false 674 spec.Proxy.ListenPath = "/" 675 spec.Proxy.TargetURL = ls.URL 676 }) 677 678 key := CreateSession() 679 680 authHeaders := map[string]string{ 681 "authorization": key, 682 } 683 684 ts.Run(t, test.TestCase{ 685 Path: "/", Headers: authHeaders, Code: 200, 686 }) 687 688 // let records to to be sent 689 time.Sleep(recordsBufferFlushInterval + 50) 690 691 results := analytics.Store.GetAndDeleteSet(analyticsKeyName) 692 if len(results) != 1 { 693 t.Error("Should return 1 record: ", len(results)) 694 } 695 696 var record AnalyticsRecord 697 msgpack.Unmarshal([]byte(results[0].(string)), &record) 698 if record.ResponseCode != 200 { 699 t.Error("Analytics record do not match", record) 700 } 701 702 if record.RawRequest == "" { 703 t.Error("Detailed request info not found", record) 704 } 705 706 if record.RawResponse == "" { 707 t.Error("Detailed response info not found", record) 708 } 709 if record.Latency.Total == 0 { 710 t.Error("expected total latency to be set") 711 } 712 if record.Latency.Upstream == 0 { 713 t.Error("expected upstream latency to be set") 714 } 715 if record.Latency.Total != record.RequestTime { 716 t.Errorf("expected %d got %d", record.RequestTime, record.Latency.Total) 717 } 718 }) 719 720 t.Run("Detailed analytics with cache", func(t *testing.T) { 721 defer func() { 722 config.SetGlobal(base) 723 }() 724 globalConf := config.Global() 725 globalConf.AnalyticsConfig.EnableDetailedRecording = true 726 config.SetGlobal(globalConf) 727 728 BuildAndLoadAPI(func(spec *APISpec) { 729 spec.UseKeylessAccess = false 730 spec.Proxy.ListenPath = "/" 731 spec.CacheOptions = apidef.CacheOptions{ 732 CacheTimeout: 120, 733 EnableCache: true, 734 CacheAllSafeRequests: true, 735 } 736 }) 737 738 key := CreateSession() 739 740 authHeaders := map[string]string{ 741 "authorization": key, 742 } 743 744 ts.Run(t, []test.TestCase{ 745 {Path: "/", Headers: authHeaders, Code: 200}, 746 {Path: "/", Headers: authHeaders, Code: 200}, 747 }...) 748 749 // let records to to be sent 750 time.Sleep(recordsBufferFlushInterval + 50) 751 752 results := analytics.Store.GetAndDeleteSet(analyticsKeyName) 753 if len(results) != 2 { 754 t.Fatal("Should return 1 record: ", len(results)) 755 } 756 757 // Take second cached request 758 var record AnalyticsRecord 759 msgpack.Unmarshal([]byte(results[1].(string)), &record) 760 if record.ResponseCode != 200 { 761 t.Error("Analytics record do not match", record) 762 } 763 764 if record.RawRequest == "" { 765 t.Error("Detailed request info not found", record) 766 } 767 768 if record.RawResponse == "" { 769 t.Error("Detailed response info not found", record) 770 } 771 }) 772 } 773 774 func TestListener(t *testing.T) { 775 // Trick to get spec JSON, without loading API 776 // Specs will be reseted when we do `StartTest` 777 specs := BuildAndLoadAPI() 778 specJSON, _ := json.Marshal(specs[0].APIDefinition) 779 listJSON := fmt.Sprintf(`\[%s\]`, string(specJSON)) 780 781 ts := StartTest() 782 defer ts.Close() 783 784 tests := []test.TestCase{ 785 // Cleanup before tests 786 {Method: "DELETE", Path: "/tyk/apis/test", AdminAuth: true}, 787 {Method: "GET", Path: "/tyk/reload/?block=true", AdminAuth: true, Code: 200}, 788 789 {Method: "GET", Path: "/sample", Code: 404}, 790 {Method: "GET", Path: "/tyk/apis/", Code: 403}, 791 {Method: "GET", Path: "/tyk/apis/", AdminAuth: true, Code: 200, BodyMatch: `\[\]`}, 792 {Method: "GET", Path: "/tyk/apis", Code: 403}, 793 {Method: "GET", Path: "/tyk/apis", AdminAuth: true, Code: 200}, 794 {Method: "POST", Path: "/tyk/apis", Data: sampleAPI, AdminAuth: true, Code: 200}, 795 {Method: "GET", Path: "/tyk/apis/", AdminAuth: true, Code: 200, BodyMatch: `\[\]`}, 796 {Method: "POST", Path: "/tyk/apis/mismatch", AdminAuth: true, Code: 400}, 797 {Method: "GET", Path: "/tyk/apis/test", AdminAuth: true, Code: 404}, 798 // API definitions not reloaded yet 799 {Method: "GET", Path: "/sample", Code: 404}, 800 {Method: "GET", Path: "/tyk/reload/?block=true", AdminAuth: true, Code: 200}, 801 {Method: "GET", Path: "/tyk/apis/test", AdminAuth: true, Code: 200, BodyMatch: string(specJSON)}, 802 {Method: "GET", Path: "/tyk/apis/", AdminAuth: true, Code: 200, BodyMatch: listJSON}, 803 {Method: "GET", Path: "/sample", Code: 200}, 804 {Method: "GET", Path: "/samplefoo", Code: 200}, 805 {Method: "GET", Path: "/sample/", Code: 200}, 806 {Method: "GET", Path: "/sample/foo", Code: 200}, 807 } 808 809 // have all needed reload ticks ready 810 go func() { 811 for i := 0; i < 4*4; i++ { 812 ReloadTestCase.Tick() 813 } 814 }() 815 816 ts.RunExt(t, tests...) 817 } 818 819 // Admin api located on separate port 820 func TestControlListener(t *testing.T) { 821 ts := StartTest(TestConfig{ 822 sepatateControlAPI: true, 823 }) 824 defer ts.Close() 825 826 tests := []test.TestCase{ 827 {Method: "GET", Path: "/", Code: 404}, 828 {Method: "GET", Path: "/tyk/apis", Code: 404}, 829 830 // Querying control API 831 {Method: "GET", Path: "/", Code: 404, ControlRequest: true}, 832 {Method: "GET", Path: "/tyk/apis", Code: 403, ControlRequest: true}, 833 {Method: "GET", Path: "/tyk/apis/", Code: 200, AdminAuth: true, ControlRequest: true}, 834 } 835 836 ts.RunExt(t, tests...) 837 DoReload() 838 ts.RunExt(t, tests...) 839 } 840 841 func TestHttpPprof(t *testing.T) { 842 old := cli.HTTPProfile 843 defer func() { cli.HTTPProfile = old }() 844 845 ts := StartTest(TestConfig{ 846 sepatateControlAPI: true, 847 }) 848 849 ts.Run(t, []test.TestCase{ 850 {Path: "/debug/pprof/", Code: 404}, 851 {Path: "/debug/pprof/", Code: 404, ControlRequest: true}, 852 }...) 853 ts.Close() 854 855 *cli.HTTPProfile = true 856 857 ts.Start() 858 ts.Run(t, []test.TestCase{ 859 {Path: "/debug/pprof/", Code: 404}, 860 {Path: "/debug/pprof/", Code: 200, ControlRequest: true}, 861 {Path: "/debug/pprof/heap", Code: 200, ControlRequest: true}, 862 }...) 863 ts.Close() 864 } 865 866 func TestManagementNodeRedisEvents(t *testing.T) { 867 defer ResetTestConfig() 868 globalConf := config.Global() 869 globalConf.ManagementNode = false 870 config.SetGlobal(globalConf) 871 872 t.Run("Without signing:", func(t *testing.T) { 873 msg := redis.Message{ 874 Payload: `{"Command": "NoticeGatewayDRLNotification"}`, 875 } 876 877 callbackRun := false 878 shouldHandle := func(got NotificationCommand) { 879 callbackRun = true 880 if want := NoticeGatewayDRLNotification; got != want { 881 t.Fatalf("want %q, got %q", want, got) 882 } 883 } 884 handleRedisEvent(&msg, shouldHandle, nil) 885 if !callbackRun { 886 t.Fatalf("Should run callback") 887 } 888 globalConf.ManagementNode = true 889 config.SetGlobal(globalConf) 890 notHandle := func(got NotificationCommand) { 891 t.Fatalf("should have not handled redis event") 892 } 893 handleRedisEvent(msg, notHandle, nil) 894 }) 895 896 t.Run("With signature", func(t *testing.T) { 897 globalConf := config.Global() 898 globalConf.AllowInsecureConfigs = false 899 config.SetGlobal(globalConf) 900 901 n := Notification{ 902 Command: NoticeGroupReload, 903 Payload: string("test"), 904 } 905 n.Sign() 906 msg := redis.Message{} 907 payload, _ := json.Marshal(n) 908 msg.Payload = string(payload) 909 910 callbackRun := false 911 shouldHandle := func(got NotificationCommand) { 912 callbackRun = true 913 if want := NoticeGroupReload; got != want { 914 t.Fatalf("want %q, got %q", want, got) 915 } 916 } 917 918 handleRedisEvent(&msg, shouldHandle, nil) 919 if !callbackRun { 920 t.Fatalf("Should run callback") 921 } 922 923 n.Signature = "wrong" 924 payload, _ = json.Marshal(n) 925 msg.Payload = string(payload) 926 927 valid := false 928 shouldFail := func(got NotificationCommand) { 929 valid = true 930 } 931 handleRedisEvent(&msg, shouldFail, nil) 932 if valid { 933 t.Fatalf("Should fail validation") 934 } 935 }) 936 } 937 938 func TestListenPathTykPrefix(t *testing.T) { 939 ts := StartTest() 940 defer ts.Close() 941 942 BuildAndLoadAPI(func(spec *APISpec) { 943 spec.Proxy.ListenPath = "/tyk-foo/" 944 }) 945 946 ts.Run(t, test.TestCase{ 947 Path: "/tyk-foo/", 948 Code: 200, 949 }) 950 } 951 952 func TestReloadGoroutineLeakWithCircuitBreaker(t *testing.T) { 953 ts := StartTest() 954 defer ts.Close() 955 956 globalConf := config.Global() 957 globalConf.EnableJSVM = false 958 config.SetGlobal(globalConf) 959 defer ResetTestConfig() 960 961 specs := BuildAndLoadAPI(func(spec *APISpec) { 962 spec.Proxy.ListenPath = "/" 963 UpdateAPIVersion(spec, "v1", func(version *apidef.VersionInfo) { 964 version.ExtendedPaths = apidef.ExtendedPathsSet{ 965 CircuitBreaker: []apidef.CircuitBreakerMeta{ 966 { 967 Path: "/", 968 Method: http.MethodGet, 969 ThresholdPercent: 0.5, 970 Samples: 5, 971 ReturnToServiceAfter: 10, 972 }, 973 }, 974 } 975 }) 976 }) 977 978 before := runtime.NumGoroutine() 979 980 LoadAPI(specs...) // just doing DoReload() doesn't load anything as BuildAndLoadAPI cleans up folder with API specs 981 982 time.Sleep(100 * time.Millisecond) 983 984 after := runtime.NumGoroutine() 985 986 if before < after { 987 t.Errorf("Goroutine leak, was: %d, after reload: %d", before, after) 988 } 989 } 990 991 func listenProxyProto(ls net.Listener) error { 992 pl := &proxyproto.Listener{Listener: ls} 993 for { 994 conn, err := pl.Accept() 995 if err != nil { 996 return err 997 } 998 recv := make([]byte, 4) 999 _, err = conn.Read(recv) 1000 if err != nil { 1001 return err 1002 } 1003 if _, err := conn.Write([]byte("pong")); err != nil { 1004 return err 1005 } 1006 } 1007 } 1008 1009 func TestProxyProtocol(t *testing.T) { 1010 l, err := net.Listen("tcp", "127.0.0.1:0") 1011 if err != nil { 1012 t.Fatal(err) 1013 } 1014 defer l.Close() 1015 go listenProxyProto(l) 1016 ts := StartTest() 1017 defer ts.Close() 1018 rp, err := net.Listen("tcp", "127.0.0.1:0") 1019 if err != nil { 1020 t.Fatal(err) 1021 } 1022 _, port, err := net.SplitHostPort(rp.Addr().String()) 1023 if err != nil { 1024 t.Fatal(err) 1025 } 1026 p, err := strconv.Atoi(port) 1027 if err != nil { 1028 t.Fatal(err) 1029 } 1030 EnablePort(p, "tcp") 1031 defer ResetTestConfig() 1032 1033 proxyAddr := rp.Addr().String() 1034 rp.Close() 1035 BuildAndLoadAPI(func(spec *APISpec) { 1036 spec.Proxy.ListenPath = "/" 1037 spec.Protocol = "tcp" 1038 spec.EnableProxyProtocol = true 1039 spec.ListenPort = p 1040 spec.Proxy.TargetURL = l.Addr().String() 1041 }) 1042 1043 // we want to check if the gateway started listening on the tcp port. 1044 ls, err := net.Dial("tcp", proxyAddr) 1045 if err != nil { 1046 t.Fatalf("expected the proxy to listen on address %s", proxyAddr) 1047 } 1048 defer ls.Close() 1049 ls.Write([]byte("ping")) 1050 recv := make([]byte, 4) 1051 _, err = ls.Read(recv) 1052 if err != nil { 1053 t.Fatalf("err: %v", err) 1054 } 1055 if !bytes.Equal(recv, []byte("pong")) { 1056 t.Fatalf("bad: %v", recv) 1057 } 1058 } 1059 1060 func TestProxyUserAgent(t *testing.T) { 1061 ts := StartTest() 1062 defer ts.Close() 1063 1064 BuildAndLoadAPI(func(spec *APISpec) { 1065 spec.Proxy.ListenPath = "/" 1066 }) 1067 1068 ts.Run(t, []test.TestCase{ 1069 { 1070 Headers: map[string]string{"User-Agent": ""}, 1071 BodyMatch: fmt.Sprintf(`"User-Agent":"%s"`, defaultUserAgent), 1072 }, 1073 { 1074 Headers: map[string]string{"User-Agent": "SomeAgent"}, 1075 BodyMatch: `"User-Agent":"SomeAgent"`, 1076 }, 1077 }...) 1078 } 1079 1080 func TestSkipUrlCleaning(t *testing.T) { 1081 globalConf := config.Global() 1082 globalConf.HttpServerOptions.OverrideDefaults = true 1083 globalConf.HttpServerOptions.SkipURLCleaning = true 1084 config.SetGlobal(globalConf) 1085 defer ResetTestConfig() 1086 1087 ts := StartTest() 1088 defer ts.Close() 1089 1090 s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 1091 w.Write([]byte(r.URL.Path)) 1092 })) 1093 defer s.Close() 1094 1095 BuildAndLoadAPI(func(spec *APISpec) { 1096 spec.Proxy.ListenPath = "/" 1097 spec.Proxy.TargetURL = s.URL 1098 }) 1099 1100 ts.Run(t, test.TestCase{ 1101 Path: "/http://example.com", BodyMatch: "/http://example.com", Code: 200, 1102 }) 1103 } 1104 1105 func TestMultiTargetProxy(t *testing.T) { 1106 ts := StartTest() 1107 defer ts.Close() 1108 1109 BuildAndLoadAPI(func(spec *APISpec) { 1110 spec.VersionData.NotVersioned = false 1111 spec.VersionData.Versions = map[string]apidef.VersionInfo{ 1112 "vdef": {Name: "vdef"}, 1113 "vother": { 1114 Name: "vother", 1115 OverrideTarget: TestHttpAny + "/vother", 1116 }, 1117 } 1118 spec.Proxy.ListenPath = "/" 1119 }) 1120 1121 ts.Run(t, []test.TestCase{ 1122 { 1123 Headers: map[string]string{"version": "vdef"}, 1124 JSONMatch: map[string]string{"Url": `"/"`}, 1125 Code: 200, 1126 }, 1127 { 1128 Headers: map[string]string{"version": "vother"}, 1129 JSONMatch: map[string]string{"Url": `"/vother"`}, 1130 Code: 200, 1131 }, 1132 }...) 1133 } 1134 1135 func TestCustomDomain(t *testing.T) { 1136 t.Run("With custom domain support", func(t *testing.T) { 1137 globalConf := config.Global() 1138 globalConf.EnableCustomDomains = true 1139 config.SetGlobal(globalConf) 1140 defer ResetTestConfig() 1141 1142 ts := StartTest() 1143 defer ts.Close() 1144 1145 BuildAndLoadAPI( 1146 func(spec *APISpec) { 1147 spec.Domain = "host1" 1148 spec.Proxy.ListenPath = "/with_domain" 1149 }, 1150 func(spec *APISpec) { 1151 spec.Domain = "" 1152 spec.Proxy.ListenPath = "/without_domain" 1153 }, 1154 ) 1155 1156 ts.Run(t, []test.TestCase{ 1157 {Code: 200, Path: "/with_domain", Domain: "host1"}, 1158 {Code: 404, Path: "/with_domain"}, 1159 {Code: 200, Path: "/without_domain"}, 1160 {Code: 200, Path: "/tyk/keys", AdminAuth: true}, 1161 }...) 1162 }) 1163 1164 t.Run("Without custom domain support", func(t *testing.T) { 1165 ts := StartTest() 1166 defer ts.Close() 1167 1168 BuildAndLoadAPI( 1169 func(spec *APISpec) { 1170 spec.Domain = "host1.local." 1171 spec.Proxy.ListenPath = "/" 1172 }, 1173 func(spec *APISpec) { 1174 spec.Domain = "" 1175 spec.Proxy.ListenPath = "/" 1176 }, 1177 ) 1178 1179 ts.Run(t, []test.TestCase{ 1180 {Code: 200, Path: "/with_domain", Domain: "host1"}, 1181 {Code: 200, Path: "/with_domain"}, 1182 {Code: 200, Path: "/without_domain"}, 1183 {Code: 200, Path: "/tyk/keys", AdminAuth: true}, 1184 }...) 1185 }) 1186 } 1187 1188 func TestHelloHealthcheck(t *testing.T) { 1189 ts := StartTest() 1190 defer ts.Close() 1191 1192 t.Run("Without APIs", func(t *testing.T) { 1193 ts.Run(t, []test.TestCase{ 1194 {Method: "GET", Path: "/hello", Code: 200}, 1195 }...) 1196 }) 1197 1198 t.Run("With APIs", func(t *testing.T) { 1199 BuildAndLoadAPI(func(spec *APISpec) { 1200 spec.Proxy.ListenPath = "/sample" 1201 }) 1202 1203 ts.Run(t, []test.TestCase{ 1204 {Method: "GET", Path: "/hello", Code: 200}, 1205 {Method: "GET", Path: "/sample/hello", Code: 200}, 1206 }...) 1207 }) 1208 } 1209 1210 func TestCacheAllSafeRequests(t *testing.T) { 1211 ts := StartTest() 1212 defer ts.Close() 1213 cache := storage.RedisCluster{KeyPrefix: "cache-"} 1214 defer cache.DeleteScanMatch("*") 1215 1216 BuildAndLoadAPI(func(spec *APISpec) { 1217 spec.CacheOptions = apidef.CacheOptions{ 1218 CacheTimeout: 120, 1219 EnableCache: true, 1220 CacheAllSafeRequests: true, 1221 } 1222 spec.Proxy.ListenPath = "/" 1223 }) 1224 1225 headerCache := map[string]string{"x-tyk-cached-response": "1"} 1226 1227 ts.Run(t, []test.TestCase{ 1228 {Method: "GET", Path: "/", HeadersNotMatch: headerCache, Delay: 10 * time.Millisecond}, 1229 {Method: "GET", Path: "/", HeadersMatch: headerCache}, 1230 {Method: "POST", Path: "/", HeadersNotMatch: headerCache}, 1231 {Method: "POST", Path: "/", HeadersNotMatch: headerCache}, 1232 {Method: "GET", Path: "/", HeadersMatch: headerCache}, 1233 }...) 1234 } 1235 1236 func TestCacheAllSafeRequestsWithCachedHeaders(t *testing.T) { 1237 ts := StartTest() 1238 defer ts.Close() 1239 cache := storage.RedisCluster{KeyPrefix: "cache-"} 1240 defer cache.DeleteScanMatch("*") 1241 authorization := "authorization" 1242 tenant := "tenant-id" 1243 1244 BuildAndLoadAPI(func(spec *APISpec) { 1245 spec.CacheOptions = apidef.CacheOptions{ 1246 CacheTimeout: 120, 1247 EnableCache: true, 1248 CacheAllSafeRequests: true, 1249 CacheByHeaders: []string{authorization, tenant}, 1250 } 1251 spec.UseKeylessAccess = true 1252 spec.Proxy.ListenPath = "/" 1253 }) 1254 1255 headerCache := map[string]string{"x-tyk-cached-response": "1"} 1256 sess1token := CreateSession(func(s *user.SessionState) { 1257 s.Rate = 1 1258 s.Per = 60 1259 s.Mutex = &sync.RWMutex{} 1260 }) 1261 sess2token := CreateSession(func(s *user.SessionState) { 1262 s.Rate = 1 1263 s.Per = 60 1264 s.Mutex = &sync.RWMutex{} 1265 }) 1266 1267 ts.Run(t, []test.TestCase{ 1268 {Method: http.MethodGet, Path: "/", Headers: map[string]string{authorization: sess1token}, HeadersNotMatch: headerCache, Delay: 10 * time.Millisecond}, 1269 {Method: http.MethodGet, Path: "/", Headers: map[string]string{authorization: sess1token}, HeadersMatch: headerCache}, 1270 {Method: http.MethodGet, Path: "/", Headers: map[string]string{authorization: sess2token}, HeadersNotMatch: headerCache, Delay: 10 * time.Millisecond}, 1271 {Method: http.MethodGet, Path: "/", Headers: map[string]string{tenant: "Some UUID"}, HeadersNotMatch: headerCache, Delay: 10 * time.Millisecond}, 1272 {Method: http.MethodGet, Path: "/", Headers: map[string]string{tenant: "Some UUID"}, HeadersMatch: headerCache}, 1273 {Method: http.MethodGet, Path: "/", HeadersNotMatch: headerCache, Delay: 10 * time.Millisecond}, 1274 {Method: http.MethodGet, Path: "/", HeadersMatch: headerCache}, 1275 {Method: http.MethodGet, Path: "/", Headers: map[string]string{tenant: "Some UUID", authorization: sess2token}, HeadersNotMatch: headerCache}, 1276 }...) 1277 } 1278 1279 func TestCacheWithAdvanceUrlRewrite(t *testing.T) { 1280 ts := StartTest() 1281 defer ts.Close() 1282 cache := storage.RedisCluster{KeyPrefix: "cache-"} 1283 defer cache.DeleteScanMatch("*") 1284 1285 BuildAndLoadAPI(func(spec *APISpec) { 1286 version := spec.VersionData.Versions["v1"] 1287 version.UseExtendedPaths = true 1288 version.ExtendedPaths = apidef.ExtendedPathsSet{ 1289 URLRewrite: []apidef.URLRewriteMeta{ 1290 { 1291 Path: "/test", 1292 Method: http.MethodGet, 1293 MatchPattern: "/test(.*)", 1294 Triggers: []apidef.RoutingTrigger{ 1295 { 1296 On: "all", 1297 Options: apidef.RoutingTriggerOptions{ 1298 HeaderMatches: map[string]apidef.StringRegexMap{ 1299 "rewritePath": { 1300 MatchPattern: "newpath", 1301 }, 1302 }, 1303 }, 1304 RewriteTo: "/newpath", 1305 }, 1306 }, 1307 }, 1308 }, 1309 Cached: []string{"/test"}, 1310 } 1311 spec.CacheOptions = apidef.CacheOptions{ 1312 CacheTimeout: 120, 1313 EnableCache: true, 1314 } 1315 spec.Proxy.ListenPath = "/" 1316 spec.VersionData.Versions["v1"] = version 1317 }) 1318 1319 headerCache := map[string]string{"x-tyk-cached-response": "1"} 1320 matchHeaders := map[string]string{"rewritePath": "newpath"} 1321 randomheaders := map[string]string{"something": "abcd"} 1322 1323 ts.Run(t, []test.TestCase{ 1324 {Method: http.MethodGet, Path: "/test", Headers: matchHeaders, HeadersNotMatch: headerCache, Delay: 10 * time.Millisecond}, 1325 {Method: http.MethodGet, Path: "/test", Headers: matchHeaders, HeadersMatch: headerCache}, 1326 //Even if trigger condition failed, as response is cached 1327 // will still get redirected response 1328 {Method: http.MethodGet, Path: "/test", Headers: randomheaders, HeadersMatch: headerCache, BodyMatch: `"Url":"/newpath"`}, 1329 {Method: http.MethodPost, Path: "/test", HeadersNotMatch: headerCache}, 1330 {Method: http.MethodGet, Path: "/test", HeadersMatch: headerCache}, 1331 }...) 1332 } 1333 1334 func TestCachePostRequest(t *testing.T) { 1335 ts := StartTest() 1336 defer ts.Close() 1337 cache := storage.RedisCluster{KeyPrefix: "cache-"} 1338 defer cache.DeleteScanMatch("*") 1339 tenant := "tenant-id" 1340 1341 BuildAndLoadAPI(func(spec *APISpec) { 1342 spec.CacheOptions = apidef.CacheOptions{ 1343 CacheTimeout: 120, 1344 EnableCache: true, 1345 CacheAllSafeRequests: false, 1346 CacheByHeaders: []string{tenant}, 1347 } 1348 1349 UpdateAPIVersion(spec, "v1", func(v *apidef.VersionInfo) { 1350 v.ExtendedPaths.AdvanceCacheConfig = []apidef.CacheMeta{ 1351 { 1352 Method: http.MethodPost, 1353 Path: "/", 1354 CacheKeyRegex: "\"id\":[^,]*", 1355 }, 1356 } 1357 }) 1358 1359 spec.Proxy.ListenPath = "/" 1360 }) 1361 1362 headerCache := map[string]string{"x-tyk-cached-response": "1"} 1363 1364 ts.Run(t, []test.TestCase{ 1365 {Method: http.MethodPost, Path: "/", Data: "{\"id\":\"1\",\"name\":\"test\"}", HeadersNotMatch: headerCache, Delay: 10 * time.Millisecond}, 1366 {Method: http.MethodPost, Path: "/", Data: "{\"id\":\"1\",\"name\":\"test\"}", HeadersMatch: headerCache, Delay: 10 * time.Millisecond}, 1367 {Method: http.MethodPost, Path: "/", Data: "{\"id\":\"2\",\"name\":\"test\"}", HeadersNotMatch: headerCache, Delay: 10 * time.Millisecond}, 1368 // if regex match returns nil, then request body is ignored while generating cache key 1369 {Method: http.MethodPost, Path: "/", Data: "{\"name\":\"test\"}", HeadersNotMatch: headerCache, Delay: 10 * time.Millisecond}, 1370 {Method: http.MethodPost, Path: "/", Data: "{\"name\":\"test2\"}", HeadersMatch: headerCache, Delay: 10 * time.Millisecond}, 1371 {Method: http.MethodPost, Path: "/", Data: "{\"name\":\"test2\"}", Headers: map[string]string{tenant: "someUUID"}, HeadersNotMatch: headerCache, Delay: 10 * time.Millisecond}, 1372 {Method: http.MethodPost, Path: "/", Data: "{\"name\":\"test2\"}", Headers: map[string]string{tenant: "someUUID"}, HeadersMatch: headerCache}, 1373 }...) 1374 } 1375 1376 func TestAdvanceCachePutRequest(t *testing.T) { 1377 ts := StartTest() 1378 defer ts.Close() 1379 cache := storage.RedisCluster{KeyPrefix: "cache-"} 1380 defer cache.DeleteScanMatch("*") 1381 tenant := "tenant-id" 1382 1383 BuildAndLoadAPI(func(spec *APISpec) { 1384 spec.CacheOptions = apidef.CacheOptions{ 1385 CacheTimeout: 120, 1386 EnableCache: true, 1387 CacheAllSafeRequests: true, 1388 CacheOnlyResponseCodes: []int{404}, // should not influence because of AdvanceCacheConfig.CacheOnlyResponseCodes 1389 CacheByHeaders: []string{tenant}, 1390 } 1391 spec.Proxy.ListenPath = "/" 1392 1393 UpdateAPIVersion(spec, "v1", func(v *apidef.VersionInfo) { 1394 json.Unmarshal([]byte(`[{ 1395 "method":"PUT", 1396 "path":"/put/", 1397 "cache_key_regex":"\"id\":[^,]*", 1398 "cache_response_codes":[200] 1399 },{ 1400 "method":"PATCH", 1401 "path":"/patch/", 1402 "cache_key_regex":"\"id\":[^,]*", 1403 "cache_response_codes":[200] 1404 },{ 1405 "method":"DELETE", 1406 "path":"/delete/", 1407 "cache_response_codes":[200] 1408 },{ 1409 "method":"PUT", 1410 "path":"/full-body-hash/", 1411 "cache_response_codes":[200], 1412 "cache_key_regex":".*" 1413 } 1414 ]`), &v.ExtendedPaths.AdvanceCacheConfig) 1415 }) 1416 spec.Proxy.ListenPath = "/" 1417 }) 1418 1419 headerCache := map[string]string{"x-tyk-cached-response": "1"} 1420 1421 ts.Run(t, []test.TestCase{ 1422 {Method: http.MethodPut, Path: "/put/", Data: "{\"id\":\"1\",\"name\":\"test\"}", HeadersNotMatch: headerCache, Delay: 10 * time.Millisecond}, // 0 1423 {Method: http.MethodPut, Path: "/put/", Data: "{\"id\":\"1\",\"name\":\"test\"}", HeadersMatch: headerCache, Delay: 10 * time.Millisecond}, 1424 {Method: http.MethodPut, Path: "/put/", Data: "{\"id\":\"2\",\"name\":\"test\"}", HeadersNotMatch: headerCache, Delay: 10 * time.Millisecond}, 1425 // if regex match returns nil, then request body is ignored while generating cache key 1426 {Method: http.MethodPut, Path: "/put/", Data: "{\"name\":\"test\"}", HeadersNotMatch: headerCache, Delay: 10 * time.Millisecond}, 1427 {Method: http.MethodPut, Path: "/put/", Data: "{\"name\":\"test2\"}", HeadersMatch: headerCache, Delay: 10 * time.Millisecond}, 1428 {Method: http.MethodPut, Path: "/put/", Data: "{\"name\":\"test2\"}", Headers: map[string]string{"someheader": "someUUID"}, HeadersMatch: headerCache, Delay: 10 * time.Millisecond}, 1429 // test when no body and no headers 1430 {Method: http.MethodPut, Path: "/put/", HeadersMatch: headerCache}, 1431 // test CacheByHeaders change - header added 1432 {Method: http.MethodPut, Path: "/put/", Data: "{\"name\":\"test2\"}", Headers: map[string]string{tenant: "someUUID"}, HeadersNotMatch: headerCache, Delay: 10 * time.Millisecond}, 1433 {Method: http.MethodPut, Path: "/put/", Data: "{\"name\":\"test2\"}", Headers: map[string]string{tenant: "someUUID"}, HeadersMatch: headerCache}, 1434 1435 // PATCH 1436 {Method: http.MethodPatch, Path: "/patch/", Data: "{\"id\":\"1\",\"name\":\"test\"}", HeadersNotMatch: headerCache, Delay: 10 * time.Millisecond}, 1437 {Method: http.MethodPatch, Path: "/patch/", Data: "{\"id\":\"1\",\"name\":\"test\"}", HeadersMatch: headerCache, Delay: 10 * time.Millisecond}, // 10 1438 {Method: http.MethodPatch, Path: "/patch/", Data: "{\"id\":\"2\",\"name\":\"test\"}", HeadersNotMatch: headerCache, Delay: 10 * time.Millisecond}, 1439 // if regex match returns nil, then request body is ignored while generating cache key 1440 {Method: http.MethodPatch, Path: "/patch/", Data: "{\"name\":\"test\"}", HeadersNotMatch: headerCache, Delay: 10 * time.Millisecond}, 1441 {Method: http.MethodPatch, Path: "/patch/", Data: "{\"name\":\"test2\"}", HeadersMatch: headerCache, Delay: 10 * time.Millisecond}, 1442 {Method: http.MethodPatch, Path: "/patch/", Data: "{\"name\":\"test2\"}", Headers: map[string]string{"someheader": "someUUID"}, HeadersMatch: headerCache, Delay: 10 * time.Millisecond}, 1443 // test when no body and no headers 1444 {Method: http.MethodPatch, Path: "/patch/", HeadersMatch: headerCache}, 1445 // test CacheByHeaders change - header added 1446 {Method: http.MethodPatch, Path: "/patch/", Data: "{\"name\":\"test2\"}", Headers: map[string]string{tenant: "someUUID"}, HeadersNotMatch: headerCache, Delay: 10 * time.Millisecond}, 1447 {Method: http.MethodPatch, Path: "/patch/", Data: "{\"name\":\"test2\"}", Headers: map[string]string{tenant: "someUUID"}, HeadersMatch: headerCache}, 1448 1449 // DELETE 1450 {Method: http.MethodDelete, Path: "/delete/", HeadersNotMatch: headerCache, Delay: 10 * time.Millisecond}, 1451 {Method: http.MethodDelete, Path: "/delete/", HeadersMatch: headerCache, Delay: 10 * time.Millisecond}, 1452 {Method: http.MethodDelete, Path: "/delete/", Headers: map[string]string{tenant: "someUUID"}, HeadersNotMatch: headerCache, Delay: 10 * time.Millisecond}, // 20 1453 {Method: http.MethodDelete, Path: "/delete/", Headers: map[string]string{tenant: "someUUID"}, HeadersMatch: headerCache}, 1454 1455 // Put with full body hash 1456 {Method: http.MethodPut, Path: "/full-body-hash/", Data: "{\"id\":\"1\",\"name\":\"test\"}", HeadersNotMatch: headerCache, Delay: 10 * time.Millisecond}, 1457 {Method: http.MethodPut, Path: "/full-body-hash/", Data: "{\"id\":\"1\",\"name\":\"test2\"}", HeadersNotMatch: headerCache, Delay: 10 * time.Millisecond}, 1458 {Method: http.MethodPut, Path: "/full-body-hash/", Data: "{\"id\":\"2\",\"name\":\"test2\"}", HeadersNotMatch: headerCache, Delay: 10 * time.Millisecond}, 1459 {Method: http.MethodPut, Path: "/full-body-hash/", Data: "{\"id\":\"2\",\"name\":\"test2\"}", HeadersMatch: headerCache}, 1460 }...) 1461 } 1462 1463 func TestCacheAllSafeRequestsWithAdvancedCacheEndpoint(t *testing.T) { 1464 ts := StartTest() 1465 defer ts.Close() 1466 cache := storage.RedisCluster{KeyPrefix: "cache-"} 1467 defer cache.DeleteScanMatch("*") 1468 1469 BuildAndLoadAPI(func(spec *APISpec) { 1470 spec.CacheOptions = apidef.CacheOptions{ 1471 CacheTimeout: 120, 1472 EnableCache: true, 1473 CacheAllSafeRequests: true, 1474 CacheOnlyResponseCodes: []int{200}, // should not influence because of AdvanceCacheConfig.CacheOnlyResponseCodes 1475 } 1476 1477 UpdateAPIVersion(spec, "v1", func(v *apidef.VersionInfo) { 1478 json.Unmarshal([]byte(`[{ 1479 "method":"PUT", 1480 "path":"/", 1481 "cache_key_regex":"\"id\":[^,]*", 1482 "cache_response_codes":[404] 1483 } 1484 ]`), &v.ExtendedPaths.AdvanceCacheConfig) 1485 }) 1486 spec.Proxy.ListenPath = "/" 1487 }) 1488 1489 headerCache := map[string]string{"x-tyk-cached-response": "1"} 1490 1491 ts.Run(t, []test.TestCase{ 1492 // Make sure CacheAllSafeRequests is working 1493 {Method: http.MethodGet, Path: "/", HeadersNotMatch: headerCache, Delay: 10 * time.Millisecond}, 1494 {Method: http.MethodGet, Path: "/", HeadersMatch: headerCache}, 1495 }...) 1496 } 1497 1498 func TestCacheEtag(t *testing.T) { 1499 ts := StartTest() 1500 defer ts.Close() 1501 cache := storage.RedisCluster{KeyPrefix: "cache-"} 1502 defer cache.DeleteScanMatch("*") 1503 1504 upstream := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 1505 w.Header().Set("Etag", "12345") 1506 w.Write([]byte("body")) 1507 })) 1508 1509 BuildAndLoadAPI(func(spec *APISpec) { 1510 spec.CacheOptions = apidef.CacheOptions{ 1511 CacheTimeout: 120, 1512 EnableCache: true, 1513 CacheAllSafeRequests: true, 1514 } 1515 spec.Proxy.ListenPath = "/" 1516 spec.Proxy.TargetURL = upstream.URL 1517 }) 1518 1519 headerCache := map[string]string{"x-tyk-cached-response": "1"} 1520 invalidEtag := map[string]string{"If-None-Match": "invalid"} 1521 validEtag := map[string]string{"If-None-Match": "12345"} 1522 1523 ts.Run(t, []test.TestCase{ 1524 {Method: "GET", Path: "/", HeadersNotMatch: headerCache, Delay: 100 * time.Millisecond}, 1525 {Method: "GET", Path: "/", HeadersMatch: headerCache, BodyMatch: "body"}, 1526 {Method: "GET", Path: "/", Headers: invalidEtag, HeadersMatch: headerCache, BodyMatch: "body"}, 1527 {Method: "GET", Path: "/", Headers: validEtag, HeadersMatch: headerCache, BodyNotMatch: "body"}, 1528 }...) 1529 } 1530 1531 // func TestWebsocketsUpstreamUpgradeRequest(t *testing.T) { 1532 // // setup spec and do test HTTP upgrade-request 1533 // globalConf := config.Global() 1534 // globalConf.HttpServerOptions.EnableWebSockets = true 1535 // config.SetGlobal(globalConf) 1536 // defer ResetTestConfig() 1537 1538 // ts := StartTest() 1539 // defer ts.Close() 1540 1541 // BuildAndLoadAPI(func(spec *APISpec) { 1542 // spec.Proxy.ListenPath = "/" 1543 // }) 1544 1545 // ts.Run(t, test.TestCase{ 1546 // Path: "/ws", 1547 // Headers: map[string]string{ 1548 // "Connection": "Upgrade", 1549 // "Upgrade": "websocket", 1550 // "Sec-Websocket-Version": "13", 1551 // "Sec-Websocket-Key": "abc", 1552 // }, 1553 // Code: http.StatusSwitchingProtocols, 1554 // }) 1555 // } 1556 1557 func TestWebsocketsSeveralOpenClose(t *testing.T) { 1558 globalConf := config.Global() 1559 globalConf.HttpServerOptions.EnableWebSockets = true 1560 config.SetGlobal(globalConf) 1561 defer ResetTestConfig() 1562 1563 ts := StartTest() 1564 defer ts.Close() 1565 1566 BuildAndLoadAPI(func(spec *APISpec) { 1567 spec.Proxy.ListenPath = "/" 1568 }) 1569 1570 baseURL := strings.Replace(ts.URL, "http://", "ws://", -1) 1571 1572 // connect 1st time, send and read message, close connection 1573 conn1, _, err := websocket.DefaultDialer.Dial(baseURL+"/ws", nil) 1574 if err != nil { 1575 t.Fatalf("cannot make websocket connection: %v", err) 1576 } 1577 err = conn1.WriteMessage(websocket.BinaryMessage, []byte("test message 1")) 1578 if err != nil { 1579 t.Fatalf("cannot write message: %v", err) 1580 } 1581 _, p, err := conn1.ReadMessage() 1582 if err != nil { 1583 t.Fatalf("cannot read message: %v", err) 1584 } 1585 if string(p) != "reply to message: test message 1" { 1586 t.Error("Unexpected reply:", string(p)) 1587 } 1588 conn1.Close() 1589 1590 // connect 2nd time, send and read message, but don't close yet 1591 conn2, _, err := websocket.DefaultDialer.Dial(baseURL+"/ws", nil) 1592 if err != nil { 1593 t.Fatalf("cannot make websocket connection: %v", err) 1594 } 1595 err = conn2.WriteMessage(websocket.BinaryMessage, []byte("test message 2")) 1596 if err != nil { 1597 t.Fatalf("cannot write message: %v", err) 1598 } 1599 _, p, err = conn2.ReadMessage() 1600 if err != nil { 1601 t.Fatalf("cannot read message: %v", err) 1602 } 1603 if string(p) != "reply to message: test message 2" { 1604 t.Error("Unexpected reply:", string(p)) 1605 } 1606 1607 // connect 3d time having one connection already open before, send and read message 1608 conn3, _, err := websocket.DefaultDialer.Dial(baseURL+"/ws", nil) 1609 if err != nil { 1610 t.Fatalf("cannot make websocket connection: %v", err) 1611 } 1612 err = conn3.WriteMessage(websocket.BinaryMessage, []byte("test message 3")) 1613 if err != nil { 1614 t.Fatalf("cannot write message: %v", err) 1615 } 1616 _, p, err = conn3.ReadMessage() 1617 if err != nil { 1618 t.Fatalf("cannot read message: %v", err) 1619 } 1620 if string(p) != "reply to message: test message 3" { 1621 t.Error("Unexpected reply:", string(p)) 1622 } 1623 1624 // check that we still can interact via 2nd connection we did before 1625 err = conn2.WriteMessage(websocket.BinaryMessage, []byte("new test message 2")) 1626 if err != nil { 1627 t.Fatalf("cannot write message: %v", err) 1628 } 1629 _, p, err = conn2.ReadMessage() 1630 if err != nil { 1631 t.Fatalf("cannot read message: %v", err) 1632 } 1633 if string(p) != "reply to message: new test message 2" { 1634 t.Error("Unexpected reply:", string(p)) 1635 } 1636 1637 // check that we still can interact via 3d connection we did before 1638 err = conn3.WriteMessage(websocket.BinaryMessage, []byte("new test message 3")) 1639 if err != nil { 1640 t.Fatalf("cannot write message: %v", err) 1641 } 1642 _, p, err = conn3.ReadMessage() 1643 if err != nil { 1644 t.Fatalf("cannot read message: %v", err) 1645 } 1646 if string(p) != "reply to message: new test message 3" { 1647 t.Error("Unexpected reply:", string(p)) 1648 } 1649 1650 // clean up connections 1651 conn2.Close() 1652 conn3.Close() 1653 } 1654 1655 func TestWebsocketsAndHTTPEndpointMatch(t *testing.T) { 1656 globalConf := config.Global() 1657 globalConf.HttpServerOptions.EnableWebSockets = true 1658 config.SetGlobal(globalConf) 1659 defer ResetTestConfig() 1660 1661 ts := StartTest() 1662 defer ts.Close() 1663 1664 BuildAndLoadAPI(func(spec *APISpec) { 1665 spec.Proxy.ListenPath = "/" 1666 }) 1667 1668 baseURL := strings.Replace(ts.URL, "http://", "ws://", -1) 1669 1670 // connect to ws, send 1st message and check reply 1671 wsConn, _, err := websocket.DefaultDialer.Dial(baseURL+"/ws", nil) 1672 if err != nil { 1673 t.Fatalf("cannot make websocket connection: %v", err) 1674 } 1675 err = wsConn.WriteMessage(websocket.BinaryMessage, []byte("test message 1")) 1676 if err != nil { 1677 t.Fatalf("cannot write message: %v", err) 1678 } 1679 _, p, err := wsConn.ReadMessage() 1680 if err != nil { 1681 t.Fatalf("cannot read message: %v", err) 1682 } 1683 if string(p) != "reply to message: test message 1" { 1684 t.Error("Unexpected reply:", string(p)) 1685 } 1686 1687 // make 1st http request 1688 ts.Run(t, test.TestCase{ 1689 Method: "GET", 1690 Path: "/abc", 1691 Code: http.StatusOK, 1692 }) 1693 1694 // send second WS connection upgrade request 1695 // connect to ws, send 1st message and check reply 1696 wsConn2, _, err := websocket.DefaultDialer.Dial(baseURL+"/ws", nil) 1697 if err != nil { 1698 t.Fatalf("cannot make websocket connection: %v", err) 1699 } 1700 err = wsConn2.WriteMessage(websocket.BinaryMessage, []byte("test message 1 to ws 2")) 1701 if err != nil { 1702 t.Fatalf("cannot write message: %v", err) 1703 } 1704 _, p, err = wsConn2.ReadMessage() 1705 if err != nil { 1706 t.Fatalf("cannot read message: %v", err) 1707 } 1708 if string(p) != "reply to message: test message 1 to ws 2" { 1709 t.Error("Unexpected reply:", string(p)) 1710 } 1711 wsConn2.Close() 1712 1713 // send second message to WS and check reply 1714 err = wsConn.WriteMessage(websocket.BinaryMessage, []byte("test message 2")) 1715 if err != nil { 1716 t.Fatalf("cannot write message: %v", err) 1717 } 1718 _, p, err = wsConn.ReadMessage() 1719 if err != nil { 1720 t.Fatalf("cannot read message: %v", err) 1721 } 1722 if string(p) != "reply to message: test message 2" { 1723 t.Error("Unexpected reply:", string(p)) 1724 } 1725 1726 // make 2nd http request 1727 ts.Run(t, test.TestCase{ 1728 Method: "GET", 1729 Path: "/abc", 1730 Code: http.StatusOK, 1731 }) 1732 1733 wsConn.Close() 1734 1735 // make 3d http request after closing WS connection 1736 ts.Run(t, test.TestCase{ 1737 Method: "GET", 1738 Path: "/abc", 1739 Code: http.StatusOK, 1740 }) 1741 } 1742 1743 func createTestUptream(t *testing.T, allowedConns int, readsPerConn int) net.Listener { 1744 l, _ := net.Listen("tcp", "127.0.0.1:0") 1745 go func() { 1746 conns := 0 1747 1748 for { 1749 conn, err := l.Accept() 1750 if err != nil { 1751 return 1752 } 1753 conns++ 1754 1755 if conns > allowedConns { 1756 t.Fatal("Too many connections") 1757 conn.Close() 1758 return 1759 } 1760 1761 reads := 0 1762 go func() { 1763 for { 1764 buf := make([]byte, 1024) 1765 conn.SetDeadline(time.Now().Add(50 * time.Millisecond)) 1766 _, err := conn.Read(buf) 1767 if err != nil { 1768 conn.Close() 1769 return 1770 } 1771 reads++ 1772 1773 if reads > readsPerConn { 1774 t.Error("Too many reads per conn") 1775 conn.Close() 1776 return 1777 } 1778 1779 conn.SetDeadline(time.Now().Add(50 * time.Millisecond)) 1780 conn.Write([]byte("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n")) 1781 } 1782 }() 1783 } 1784 }() 1785 1786 return l 1787 } 1788 1789 func TestKeepAliveConns(t *testing.T) { 1790 ts := StartTest() 1791 defer ts.Close() 1792 defer ResetTestConfig() 1793 1794 t.Run("Should use same connection", func(t *testing.T) { 1795 // set keep alive option 1796 globalConf := config.Global() 1797 globalConf.ProxyCloseConnections = false 1798 config.SetGlobal(globalConf) 1799 1800 // Allow 1 connection with 3 reads 1801 upstream := createTestUptream(t, 1, 3) 1802 defer upstream.Close() 1803 1804 BuildAndLoadAPI(func(spec *APISpec) { 1805 spec.Proxy.ListenPath = "/" 1806 spec.Proxy.TargetURL = "http://" + upstream.Addr().String() 1807 }) 1808 1809 ts.Run(t, []test.TestCase{ 1810 {Code: 200}, 1811 {Code: 200}, 1812 {Code: 200}, 1813 }...) 1814 }) 1815 1816 t.Run("Should use separate connection", func(t *testing.T) { 1817 globalConf := config.Global() 1818 globalConf.ProxyCloseConnections = true 1819 config.SetGlobal(globalConf) 1820 1821 // Allow 3 connections with 1 read 1822 upstream := createTestUptream(t, 3, 1) 1823 defer upstream.Close() 1824 1825 BuildAndLoadAPI(func(spec *APISpec) { 1826 spec.Proxy.ListenPath = "/" 1827 spec.Proxy.TargetURL = "http://" + upstream.Addr().String() 1828 }) 1829 1830 ts.Run(t, []test.TestCase{ 1831 {Code: 200}, 1832 {Code: 200}, 1833 {Code: 200}, 1834 }...) 1835 }) 1836 1837 t.Run("Should respect max_conn_time", func(t *testing.T) { 1838 globalConf := config.Global() 1839 globalConf.ProxyCloseConnections = false 1840 globalConf.MaxConnTime = 1 1841 config.SetGlobal(globalConf) 1842 1843 // Allow 2 connection with 2 reads 1844 upstream := createTestUptream(t, 2, 2) 1845 defer upstream.Close() 1846 1847 spec := BuildAndLoadAPI(func(spec *APISpec) { 1848 spec.Proxy.ListenPath = "/" 1849 spec.Proxy.TargetURL = "http://" + upstream.Addr().String() 1850 })[0] 1851 1852 ts.Run(t, []test.TestCase{ 1853 {Code: 200}, 1854 {Code: 200}, 1855 }...) 1856 1857 // Set in past to re-create transport 1858 spec.HTTPTransportCreated = time.Now().Add(-time.Minute) 1859 1860 // Should be called in new connection 1861 // We already made 2 requests above, so 3th in same not allowed 1862 ts.Run(t, test.TestCase{Code: 200}) 1863 }) 1864 } 1865 1866 // TestRateLimitForAPIAndRateLimitAndQuotaCheck ensures that the Rate Limit for the key is applied before the rate limit 1867 // for the API. Meaning that a single token cannot reduce service availability for other tokens by simply going over the 1868 // API's global rate limit. 1869 func TestRateLimitForAPIAndRateLimitAndQuotaCheck(t *testing.T) { 1870 defer ResetTestConfig() 1871 ts := StartTest() 1872 defer ts.Close() 1873 1874 globalCfg := config.Global() 1875 globalCfg.EnableNonTransactionalRateLimiter = false 1876 globalCfg.EnableSentinelRateLimiter = true 1877 config.SetGlobal(globalCfg) 1878 1879 BuildAndLoadAPI(func(spec *APISpec) { 1880 spec.APIID += "_" + time.Now().String() 1881 spec.UseKeylessAccess = false 1882 spec.DisableRateLimit = false 1883 spec.OrgID = "default" 1884 spec.GlobalRateLimit = apidef.GlobalRateLimit{ 1885 Rate: 2, 1886 Per: 60, 1887 } 1888 spec.Proxy.ListenPath = "/" 1889 }) 1890 1891 sess1token := CreateSession(func(s *user.SessionState) { 1892 s.Rate = 1 1893 s.Per = 60 1894 s.Mutex = &sync.RWMutex{} 1895 }) 1896 defer FallbackKeySesionManager.RemoveSession(sess1token, false) 1897 1898 sess2token := CreateSession(func(s *user.SessionState) { 1899 s.Rate = 1 1900 s.Per = 60 1901 s.Mutex = &sync.RWMutex{} 1902 }) 1903 defer FallbackKeySesionManager.RemoveSession(sess2token, false) 1904 1905 ts.Run(t, []test.TestCase{ 1906 {Headers: map[string]string{"Authorization": sess1token}, Code: http.StatusOK, Path: "/", Delay: 100 * time.Millisecond}, 1907 {Headers: map[string]string{"Authorization": sess1token}, Code: http.StatusTooManyRequests, Path: "/"}, 1908 {Headers: map[string]string{"Authorization": sess2token}, Code: http.StatusOK, Path: "/", Delay: 100 * time.Millisecond}, 1909 {Headers: map[string]string{"Authorization": sess2token}, Code: http.StatusTooManyRequests, Path: "/"}, 1910 }...) 1911 } 1912 1913 func TestTracing(t *testing.T) { 1914 ts := StartTest() 1915 defer ts.Close() 1916 1917 prepareStorage() 1918 spec := BuildAPI(func(spec *APISpec) { 1919 spec.UseKeylessAccess = false 1920 })[0] 1921 1922 keyID := CreateSession(func(s *user.SessionState) { s.Mutex = &sync.RWMutex{} }) 1923 authHeaders := map[string][]string{"Authorization": {keyID}} 1924 1925 ts.Run(t, []test.TestCase{ 1926 {Method: "GET", Path: "/tyk/debug", AdminAuth: true, Code: 405}, 1927 {Method: "POST", Path: "/tyk/debug", AdminAuth: true, Code: 400, BodyMatch: "Request malformed"}, 1928 {Method: "POST", Path: "/tyk/debug", Data: `{}`, AdminAuth: true, Code: 400, BodyMatch: "Spec field is missing"}, 1929 {Method: "POST", Path: "/tyk/debug", Data: `{"Spec": {}}`, AdminAuth: true, Code: 400, BodyMatch: "Request field is missing"}, 1930 {Method: "POST", Path: "/tyk/debug", Data: `{"Spec": {}, "Request": {}}`, AdminAuth: true, Code: 400, BodyMatch: "Spec not valid, skipped!"}, 1931 {Method: "POST", Path: "/tyk/debug", Data: traceRequest{Spec: spec.APIDefinition, Request: &traceHttpRequest{Method: "GET", Path: "/"}}, AdminAuth: true, Code: 200, BodyMatch: `401 Unauthorized`}, 1932 {Method: "POST", Path: "/tyk/debug", Data: traceRequest{Spec: spec.APIDefinition, Request: &traceHttpRequest{Path: "/", Headers: authHeaders}}, AdminAuth: true, Code: 200, BodyMatch: `200 OK`}, 1933 }...) 1934 1935 t.Run("Custom auth header", func(t *testing.T) { 1936 spec.AuthConfigs = map[string]apidef.AuthConfig{ 1937 authTokenType: { 1938 AuthHeaderName: "Custom-Auth-Header", 1939 }, 1940 } 1941 1942 customAuthHeaders := map[string][]string{"custom-auth-header": {keyID}} 1943 1944 _, _ = ts.Run(t, []test.TestCase{ 1945 {Method: http.MethodPost, Path: "/tyk/debug", Data: traceRequest{Spec: spec.APIDefinition, 1946 Request: &traceHttpRequest{Path: "/", Headers: authHeaders}}, AdminAuth: true, Code: 200, BodyMatch: `401 Unauthorized`}, 1947 {Method: http.MethodPost, Path: "/tyk/debug", Data: traceRequest{Spec: spec.APIDefinition, 1948 Request: &traceHttpRequest{Path: "/", Headers: customAuthHeaders}}, AdminAuth: true, Code: 200, BodyMatch: `200 OK`}, 1949 }...) 1950 }) 1951 } 1952 1953 func TestBrokenClients(t *testing.T) { 1954 ts := StartTest() 1955 defer ts.Close() 1956 defer ResetTestConfig() 1957 1958 globalConf := config.Global() 1959 globalConf.ProxyDefaultTimeout = 1 1960 config.SetGlobal(globalConf) 1961 1962 BuildAndLoadAPI(func(spec *APISpec) { 1963 spec.UseKeylessAccess = true 1964 spec.Proxy.ListenPath = "/" 1965 spec.EnforcedTimeoutEnabled = true 1966 }) 1967 1968 buf := make([]byte, 1024) 1969 1970 t.Run("Valid client", func(t *testing.T) { 1971 conn, _ := net.DialTimeout("tcp", mainProxy().listener.Addr().String(), 0) 1972 conn.Write([]byte("GET / HTTP/1.1\r\nHost: 127.0.0.1\r\n\r\n")) 1973 conn.Read(buf) 1974 1975 if string(buf[:12]) != "HTTP/1.1 200" { 1976 t.Error("Invalid server response:", string(buf)) 1977 } 1978 }) 1979 1980 t.Run("Invalid client: close without read", func(t *testing.T) { 1981 time.Sleep(recordsBufferFlushInterval + 50*time.Millisecond) 1982 analytics.Store.GetAndDeleteSet(analyticsKeyName) 1983 1984 conn, _ := net.DialTimeout("tcp", mainProxy().listener.Addr().String(), 0) 1985 conn.Write([]byte("GET / HTTP/1.1\r\nHost: 127.0.0.1\r\n\r\n")) 1986 conn.Close() 1987 //conn.Read(buf) 1988 1989 time.Sleep(recordsBufferFlushInterval + 50*time.Millisecond) 1990 results := analytics.Store.GetAndDeleteSet(analyticsKeyName) 1991 1992 var record AnalyticsRecord 1993 msgpack.Unmarshal([]byte(results[0].(string)), &record) 1994 if record.ResponseCode != 499 { 1995 t.Fatal("Analytics record do not match:", record) 1996 } 1997 }) 1998 } 1999 2000 func TestStripRegex(t *testing.T) { 2001 sample := []struct { 2002 strip string 2003 path string 2004 expect string 2005 vars map[string]string 2006 }{ 2007 { 2008 strip: "/base", 2009 path: "/base/path", 2010 expect: "/path", 2011 vars: map[string]string{}, 2012 }, 2013 { 2014 strip: "/base/{key}", 2015 path: "/base/path/path", 2016 expect: "/path", 2017 vars: map[string]string{ 2018 "key": "path", 2019 }, 2020 }, 2021 { 2022 strip: "/taihoe-test/{test:[\\w\\d]+}/id/", 2023 path: "/taihoe-test/asdas234234dad/id/v1/get", 2024 expect: "v1/get", 2025 vars: map[string]string{ 2026 "test": "asdas234234dad", 2027 }, 2028 }, 2029 } 2030 for _, v := range sample { 2031 got := stripListenPath(v.strip, v.path, v.vars) 2032 if got != v.expect { 2033 t.Errorf("expected %s got %s", v.expect, got) 2034 } 2035 } 2036 } 2037 2038 func TestCache_singleErrorResponse(t *testing.T) { 2039 ts := StartTest() 2040 defer ts.Close() 2041 srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})) 2042 defer srv.Close() 2043 BuildAndLoadAPI(func(spec *APISpec) { 2044 spec.UseKeylessAccess = true 2045 spec.Proxy.ListenPath = "/" 2046 spec.Proxy.TargetURL = srv.URL 2047 spec.CacheOptions.CacheTimeout = 1 2048 spec.CacheOptions.EnableCache = true 2049 spec.CacheOptions.CacheAllSafeRequests = true 2050 }) 2051 ts.Run(t, 2052 test.TestCase{Method: http.MethodGet, Path: "/", Code: http.StatusOK}, 2053 ) 2054 time.Sleep(time.Second) 2055 srv.Close() 2056 wantBody := `{ 2057 "error": "There was a problem proxying the request" 2058 }` 2059 ts.Run(t, 2060 test.TestCase{Method: http.MethodGet, Path: "/", Code: http.StatusInternalServerError, BodyMatch: wantBody}, 2061 ) 2062 } 2063 2064 func TestOverrideErrors(t *testing.T) { 2065 assert := func(expectedError string, expectedCode int, actualError error, actualCode int) { 2066 if !(expectedError == actualError.Error() && expectedCode == actualCode) { 2067 t.Fatal("Override failed") 2068 } 2069 } 2070 2071 const message1 = "Message1" 2072 const code1 = 1 2073 const message2 = "Message2" 2074 const code2 = 2 2075 const message3 = "Message3" 2076 const code3 = 3 2077 const message4 = "Message4" 2078 const code4 = 4 2079 const message5 = "Message5" 2080 const code5 = 5 2081 const message6 = "Message6" 2082 const code6 = 6 2083 2084 globalConf := config.Global() 2085 globalConf.OverrideMessages = map[string]config.TykError{ 2086 ErrOAuthAuthorizationFieldMissing: { 2087 Message: message1, 2088 Code: code1, 2089 }, 2090 ErrOAuthAuthorizationFieldMalformed: { 2091 Message: message2, 2092 Code: code2, 2093 }, 2094 ErrOAuthKeyNotFound: { 2095 Message: message3, 2096 Code: code3, 2097 }, 2098 ErrOAuthClientDeleted: { 2099 Message: message4, 2100 Code: code4, 2101 }, 2102 ErrAuthAuthorizationFieldMissing: { 2103 Message: message5, 2104 Code: code5, 2105 }, 2106 ErrAuthKeyNotFound: { 2107 Message: message6, 2108 Code: code6, 2109 }, 2110 } 2111 config.SetGlobal(globalConf) 2112 2113 overrideTykErrors() 2114 2115 e, i := errorAndStatusCode(ErrOAuthAuthorizationFieldMissing) 2116 assert(message1, code1, e, i) 2117 2118 e, i = errorAndStatusCode(ErrOAuthAuthorizationFieldMalformed) 2119 assert(message2, code2, e, i) 2120 2121 e, i = errorAndStatusCode(ErrOAuthKeyNotFound) 2122 assert(message3, code3, e, i) 2123 2124 e, i = errorAndStatusCode(ErrOAuthClientDeleted) 2125 assert(message4, code4, e, i) 2126 2127 e, i = errorAndStatusCode(ErrAuthAuthorizationFieldMissing) 2128 assert(message5, code5, e, i) 2129 2130 e, i = errorAndStatusCode(ErrAuthKeyNotFound) 2131 assert(message6, code6, e, i) 2132 2133 t.Run("Partial override", func(t *testing.T) { 2134 globalConf.OverrideMessages = map[string]config.TykError{ 2135 ErrOAuthAuthorizationFieldMissing: { 2136 Code: code4, 2137 }, 2138 ErrOAuthAuthorizationFieldMalformed: { 2139 Message: message4, 2140 }, 2141 } 2142 config.SetGlobal(globalConf) 2143 2144 overrideTykErrors() 2145 2146 e, i := errorAndStatusCode(ErrOAuthAuthorizationFieldMissing) 2147 assert(message1, code4, e, i) 2148 2149 e, i = errorAndStatusCode(ErrOAuthAuthorizationFieldMalformed) 2150 assert(message4, code2, e, i) 2151 2152 }) 2153 }