/// <summary> /// Verify that we can create an HTTP traffic manager rule for a /// site on the proxy port using a specific hostname and then /// verify that that the traffic manager actually works by spinning /// up a [vegomatic] based service to accept the traffic. /// </summary> /// <param name="testName">Simple name (without spaces) used to ensure that URIs cached for different tests won't conflict.</param> /// <param name="proxyPort">The inbound proxy port.</param> /// <param name="network">The proxy network.</param> /// <param name="trafficManager">The traffic manager.</param> /// <param name="useCache">Optionally enable caching and verify.</param> /// <param name="serviceName">Optionally specifies the backend service name (defaults to <b>vegomatic</b>).</param> /// <param name="useIPAddress">Optionally uses an IP addrress rather than a hostname for the rule frontend.</param> /// <returns>The tracking <see cref="Task"/>.</returns> private async Task TestHttpRule(string testName, int proxyPort, string network, TrafficManager trafficManager, bool useCache = false, string serviceName = "vegomatic", bool useIPAddress = false) { // Append a GUID to the test name to ensure that we won't // conflict with what any previous test runs may have loaded // into the cache. testName += "-" + Guid.NewGuid().ToString("D"); // Verify that we can create an HTTP traffic manager rule for a // site on the proxy port using a specific hostname and then // verify that that the traffic manager actually works by spinning // up a [vegomatic] based service to accept the traffic. var queryCount = 100; var manager = hive.GetReachableManager(); var hostname = useIPAddress ? manager.PrivateAddress.ToString() : testHostname; manager.Connect(); using (var client = new TestHttpClient(disableConnectionReuse: true)) { // Setup the client to query the [vegomatic] service through the // proxy without needing to configure a hive DNS entry. client.BaseAddress = new Uri($"http://{manager.PrivateAddress}:{proxyPort}/"); client.DefaultRequestHeaders.Host = testHostname; // Configure the traffic manager rule. var rule = new TrafficHttpRule() { Name = "vegomatic", CheckExpect = "status 200", CheckSeconds = 1, }; if (useCache) { rule.Cache = new TrafficHttpCache() { Enabled = true }; } rule.Frontends.Add( new TrafficHttpFrontend() { Host = useIPAddress ? null : hostname, ProxyPort = proxyPort }); rule.Backends.Add( new TrafficHttpBackend() { Server = serviceName, Port = 80 }); trafficManager.SetRule(rule); // Spin up a single [vegomatic] service instance. manager.SudoCommand($"docker service create --name vegomatic --network {network} --replicas 1 {vegomaticImage} test-server").EnsureSuccess(); await WaitUntilReadyAsync(client.BaseAddress, hostname); // Query the service several times to verify that we get a response and // also that all of the responses are the same (because we have only // a single [vegomatic] instance returning its UUID). // // We're going to use a different URL for each request so that we // won't see any cache hits. var uniqueResponses = new HashSet <string>(); var viaVarnish = false; var cacheHit = false; for (int i = 0; i < queryCount; i++) { var response = await client.GetAsync($"/{testName}/pass-1/{i}?body=server-id&expires=60"); Assert.Equal(HttpStatusCode.OK, response.StatusCode); if (ViaVarnish(response)) { viaVarnish = true; } if (CacheHit(response)) { cacheHit = true; } var body = await response.Content.ReadAsStringAsync(); if (!uniqueResponses.Contains(body)) { uniqueResponses.Add(body); } } Assert.Single(uniqueResponses); if (useCache) { // [viaVarnish] should be TRUE because we're routing through the cache. Assert.True(viaVarnish); // [cacheHit] should be FALSE because we used a unique URI for each request. Assert.False(cacheHit); } else { // [viaVarnish] and [cacheHit] should both be FALSE because we're not caching. Assert.False(viaVarnish); Assert.False(cacheHit); } // Repeat the test if caching is enabled with the same URLs as last time and verify that // we see cache hits this time. if (useCache) { viaVarnish = false; cacheHit = false; for (int i = 0; i < queryCount; i++) { var response = await client.GetAsync($"/{testName}/pass-1/{i}?body=server-id&expires=60"); Assert.Equal(HttpStatusCode.OK, response.StatusCode); if (ViaVarnish(response)) { viaVarnish = true; } if (CacheHit(response)) { cacheHit = true; } var body = await response.Content.ReadAsStringAsync(); if (!uniqueResponses.Contains(body)) { uniqueResponses.Add(body); } } Assert.True(viaVarnish); Assert.True(cacheHit); } // Spin up a second replica and repeat the query test to verify // that we see two unique responses. // // Note that we're going to pass a new set of URLs to avoid having // any responses cached so we'll end up seeing all of the IDs. // // Note also that we need to perform these requests in parallel // to try to force Varnish to establish more than one connection // to the [vegomatic] service. If we don't do this, Varnish will // establish a single connection to one of the service instances // and keep sending traffic there resulting in us seeing only // one response UUID. manager.SudoCommand($"docker service update --replicas 2 vegomatic").EnsureSuccess(); await WaitUntilReadyAsync(client.BaseAddress, hostname); // Reset the response info and do the requests. uniqueResponses.Clear(); viaVarnish = false; cacheHit = false; var tasks = new List <Task>(); var uris = new List <string>(); for (int i = 0; i < queryCount; i++) { uris.Add($"/{testName}/pass-2/{i}?body=server-id&expires=60&delay=0.250"); } foreach (var uri in uris) { tasks.Add(Task.Run( async() => { var response = await client.GetAsync(uri); var body = await response.Content.ReadAsStringAsync(); Assert.Equal(HttpStatusCode.OK, response.StatusCode); if (ViaVarnish(response)) { viaVarnish = true; } if (CacheHit(response)) { cacheHit = true; } lock (uniqueResponses) { if (!uniqueResponses.Contains(body)) { uniqueResponses.Add(body); } } })); } await NeonHelper.WaitAllAsync(tasks, TimeSpan.FromSeconds(30)); if (useCache) { // [viaVarnish] should be TRUE because we're routing through the cache. Assert.True(viaVarnish); // [cacheHit] should be FALSE because we used a unique URI for each request. Assert.False(cacheHit); } else { // [viaVarnish] and [cacheHit] should both be FALSE because we're not caching. Assert.False(viaVarnish); Assert.False(cacheHit); } Assert.Equal(2, uniqueResponses.Count); } }
public CarTests(ApiApplicationFactory <Startup> factory, CarGenerator carGenerator) { client = factory.CreateTestClient("cars/"); this.carGenerator = carGenerator; }
public LogLevelTests() { _client = new TestHttpClient(); _credentials = new BasicAuthCredentials("http://test:80", "Walter", "White"); }
public static ILineBot CreateBot(TestHttpClient httpClient) { return(new LineBot(new TestConfiguration(), httpClient, new EmptyLineBotLogger())); }
public static ILineBot CreateBot(ILineBotLogger logger) { return(new LineBot(new TestConfiguration(), TestHttpClient.Create(), logger)); }
public RequestUriTests() { _client = new TestHttpClient(); }
public PostContent() { _client = new TestHttpClient(); }
public void RedirectProcessorRedirectsOldUrlWithReplaceType() { var configuration = TestData.TestData.DefaultConfiguration; var urlParser = new UrlParser(); var urlFormatter = new UrlFormatter(); var redirectParser = new RedirectParser( configuration, urlParser, urlFormatter); // create redirect processor var testHttpClient = new TestHttpClient(); var redirectProcessor = new RedirectProcessor( configuration, new UrlHelper( configuration, urlParser, urlFormatter), testHttpClient, urlParser, urlFormatter, new RedirectHelper( configuration, urlParser, urlFormatter)); // parse redirect with old url replace type var parsedRedirects = TestData.TestData.GetParsedRedirects( configuration, new[] { new Redirect { OldUrl = "/url1", NewUrl = "/url2", RedirectType = RedirectType.Replace } }) .ToList(); // parse redirects to verify replace redirect works var parsedRedirects2 = TestData.TestData.GetParsedRedirects( configuration, new[] { new Redirect { OldUrl = "/url1/replace/redirect", NewUrl = "/url2/replace/redirect" } }) .ToList(); // preload parsed redirects redirectProcessor.PreloadParsedRedirects( parsedRedirects); var processedRedirects = new [] { new ProcessedRedirect { ParsedRedirect = parsedRedirects[0] }, new ProcessedRedirect { ParsedRedirect = parsedRedirects2[0] } }; // process redirects foreach (var processedRedirect in processedRedirects) { redirectProcessor.Process( processedRedirect); } // verify var urlResponseResult = processedRedirects[1] .Results .FirstOrDefault(x => x.Type == ResultTypes.UrlResponse); Assert.IsNotNull(urlResponseResult); Assert.AreEqual( "http://www.test.local/url2/replace/redirect", urlResponseResult.Url); }
public void RedirectProcessorHandlesLowercasedRedirects() { var configuration = TestData.TestData.DefaultConfiguration; var urlParser = new UrlParser(); var urlFormatter = new UrlFormatter(); var redirectParser = new RedirectParser( configuration, urlParser, urlFormatter); // create redirect processor var testHttpClient = new TestHttpClient(); var redirectProcessor = new RedirectProcessor( configuration, new UrlHelper( configuration, urlParser, urlFormatter), testHttpClient, urlParser, urlFormatter, new RedirectHelper( configuration, urlParser, urlFormatter)); // parse redirects with uppercased old url var parsedRedirects = TestData.TestData.GetParsedRedirects( configuration, new[] { new Redirect { OldUrl = "/Url1", NewUrl = "/url2" } }) .ToList(); // simulate uppercased url redirects to lowercased url testHttpClient.Responses[ parsedRedirects[0].OldUrl.Formatted] = new HttpResponse { StatusCode = 301, Headers = new Dictionary <string, string> { { "Location", "/url1" } } }; // preload parsed redirects redirectProcessor.PreloadParsedRedirects( parsedRedirects); // process redirects var processedRedirect = new ProcessedRedirect { ParsedRedirect = parsedRedirects[0] }; redirectProcessor.Process( processedRedirect); // verify var urlResponseResult = processedRedirect .Results .FirstOrDefault(x => x.Type == ResultTypes.UrlResponse); Assert.IsNotNull(urlResponseResult); Assert.AreEqual( "http://www.test.local/url2", urlResponseResult.Url); }
public void RedirectProcessorCachesResponse() { var configuration = TestData.TestData.DefaultConfiguration; var urlParser = new UrlParser(); var urlFormatter = new UrlFormatter(); var redirectParser = new RedirectParser( configuration, urlParser, urlFormatter); // create redirect processor var testHttpClient = new TestHttpClient(); var redirectProcessor = new RedirectProcessor( configuration, new UrlHelper( configuration, urlParser, urlFormatter), testHttpClient, urlParser, urlFormatter, new RedirectHelper( configuration, urlParser, urlFormatter)); // create and parse redirects var redirects = new List <IRedirect> { new Redirect { OldUrl = "/url1", NewUrl = "/url3" }, new Redirect { OldUrl = "/url2", NewUrl = "/url3" } }; var parsedRedirects = new List <IParsedRedirect>(); foreach (var redirect in redirects) { parsedRedirects.Add( redirectParser.ParseRedirect( redirect)); } // preload parsed redirects redirectProcessor.PreloadParsedRedirects( parsedRedirects); // verify controlled http client doesn't have any responses Assert.AreEqual( 0, testHttpClient.Responses.Count); // process redirects and verify responses are cached by overriding responses UrlResponseResult urlResponseResult = null; var processedRedirects = new List <IProcessedRedirect>(); foreach (var parsedRedirect in parsedRedirects) { var processedRedirect = new ProcessedRedirect { ParsedRedirect = parsedRedirect }; redirectProcessor.Process( processedRedirect); // get url response result, if url response result is null and // controlled http client has a response for old url if (urlResponseResult == null && testHttpClient.Responses.ContainsKey( parsedRedirect.NewUrl.Formatted)) { urlResponseResult = processedRedirect.Results .FirstOrDefault(r => r.Type.Equals( ResultTypes.UrlResponse)) as UrlResponseResult; } else { // override response with forbidden status code testHttpClient.Responses[ parsedRedirect.NewUrl.Formatted] = new HttpResponse { StatusCode = 401 }; } } // verify url response result for /url3 has status code ok and not forbidden Assert.IsNotNull( urlResponseResult); Assert.AreEqual( "http://www.test.local/url3", urlResponseResult.Url); Assert.AreEqual( 404, urlResponseResult.StatusCode ); }
public void DetectUrlResponse() { // create test http client var testHttpClient = new TestHttpClient(); // parsed redirects var parsedRedirects = TestData.TestData.GetParsedRedirects(); // add moved response for parsed redirects foreach (var redirect in parsedRedirects) { testHttpClient.Responses[ redirect.OldUrl.Formatted] = new HttpResponse { StatusCode = 301, Headers = new Dictionary <string, string> { { "Location", redirect.NewUrl.Formatted } } }; } // override redirect old url with ok response testHttpClient.Responses[ "http://www.test.local/new-url"] = new HttpResponse { StatusCode = 200 }; // create redirect processor var configuration = new Configuration { DefaultUrl = TestData.TestData.DefaultHost, ForceHttpHostPatterns = new[] { "www\\.test\\.local" } }; var urlParser = new UrlParser(); var urlFormatter = new UrlFormatter(); var redirectProcessor = new RedirectProcessor( configuration, new UrlHelper( configuration, urlParser, urlFormatter), testHttpClient, urlParser, urlFormatter, new RedirectHelper( configuration, urlParser, urlFormatter)); // preload redirects redirectProcessor.PreloadParsedRedirects( parsedRedirects); // process redirects var processedRedirects = TestData.TestData.GetProcessedRedirects( parsedRedirects, new[] { redirectProcessor }); // verify redirect processor detects overridden url with response var urlResponse = redirectProcessor.Results .FirstOrDefault(x => x.Type.Equals(ResultTypes.UrlResponse)); Assert.IsNotNull(urlResponse); Assert.AreEqual( "http://www.test.local/new-url", urlResponse.Url); }
public void HttpsRedirectDoesntReturnCyclicRedirect() { // create redirect processor var configuration = new Configuration { DefaultUrl = TestData.TestData.DefaultHost, ForceHttpHostPatterns = new[] { "www\\.test\\.local" } }; // create url and redirect parser var urlFormatter = new UrlFormatter(); var urlParser = new UrlParser(); var urlHelper = new UrlHelper( configuration, urlParser, urlFormatter); var redirectParser = new RedirectParser( configuration, urlParser, urlFormatter); // create redirect processor var testHttpClient = new TestHttpClient(); var redirectProcessor = new RedirectProcessor( configuration, urlHelper, testHttpClient, urlParser, urlFormatter, new RedirectHelper( configuration, urlParser, urlFormatter)); // parse redirects var redirects = new[] { new Redirect { OldUrl = "http://www.test.local/url1", NewUrl = "https://www.test.local/url1" }, new Redirect { OldUrl = "https://www.test.local/url1", NewUrl = "https://www.test.local/url2" }, new Redirect { OldUrl = "https://www.test.local/url2", NewUrl = "https://www.test.local/url3" } }; // parse redirects var parsedRedirects = new List <IParsedRedirect>(); foreach (var redirect in redirects) { parsedRedirects.Add( redirectParser.ParseRedirect( redirect)); } // preload parsed redirects redirectProcessor.PreloadParsedRedirects( parsedRedirects); // process redirects using redirect processor var processedRedirects = TestData.TestData.GetProcessedRedirects( parsedRedirects, new[] { redirectProcessor }); // verify cyclic redirect is not detected var cyclicRedirect = processedRedirects .FirstOrDefault(pr => pr.Results.Any( r => r.Type.Equals(ResultTypes.CyclicRedirect))); Assert.IsNull(cyclicRedirect); // verify optimized redirect is detected var optimizedRedirect = processedRedirects .FirstOrDefault(pr => pr.Results.Any( r => r.Type.Equals(ResultTypes.OptimizedRedirect))); Assert.IsNotNull(optimizedRedirect); }