Feature/websockets (#273)

* #212 - hacked websockets proxy together

* faffing around

* #212 hacking away :(

* #212 websockets proxy middleware working

* #212 map when for webockets working

* #212 some test refactor

* #212 temp commit

* #212 websockets proxy working, tests passing...need to do some tidying and write docs

* #212 more code coverage

* #212 docs for websockets

* #212 updated readme

* #212 tidying up after websockets refactoring

* #212 tidying up after websockets refactoring

* #212 tidying up after websockets refactoring

* stuck a warning in about logging levels into docs!
This commit is contained in:
Tom Pallister
2018-03-23 18:01:02 +00:00
committed by GitHub
parent 4493b22d0d
commit 463a7bdab4
80 changed files with 1539 additions and 369 deletions

View File

@ -1,8 +1,6 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using Consul;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
@ -28,7 +26,7 @@ namespace Ocelot.AcceptanceTests
}
[Fact]
public void should_use_service_discovery_and_load_balance_request()
public void should_load_balance_request()
{
var downstreamServiceOneUrl = "http://localhost:50881";
var downstreamServiceTwoUrl = "http://localhost:50892";
@ -74,18 +72,6 @@ namespace Ocelot.AcceptanceTests
.BDDfy();
}
private void ThenOnlyOneServiceHasBeenCalled()
{
_counterOne.ShouldBe(10);
_counterTwo.ShouldBe(0);
}
private void GivenIResetCounters()
{
_counterOne = 0;
_counterTwo = 0;
}
private void ThenBothServicesCalledRealisticAmountOfTimes(int bottom, int top)
{
_counterOne.ShouldBeInRange(bottom, top);
@ -121,7 +107,7 @@ namespace Ocelot.AcceptanceTests
context.Response.StatusCode = statusCode;
await context.Response.WriteAsync(response);
}
catch (System.Exception exception)
catch (Exception exception)
{
await context.Response.WriteAsync(exception.StackTrace);
}

View File

@ -40,12 +40,49 @@ namespace Ocelot.AcceptanceTests
public string RequestIdKey = "OcRequestId";
private readonly Random _random;
private IWebHostBuilder _webHostBuilder;
private WebHostBuilder _ocelotBuilder;
private IWebHost _ocelotHost;
public Steps()
{
_random = new Random();
}
public async Task StartFakeOcelotWithWebSockets()
{
_ocelotBuilder = new WebHostBuilder();
_ocelotBuilder.ConfigureServices(s =>
{
s.AddSingleton(_ocelotBuilder);
s.AddOcelot();
});
_ocelotBuilder.UseKestrel()
.UseUrls("http://localhost:5000")
.UseContentRoot(Directory.GetCurrentDirectory())
.ConfigureAppConfiguration((hostingContext, config) =>
{
config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
var env = hostingContext.HostingEnvironment;
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);
config.AddJsonFile("configuration.json");
config.AddEnvironmentVariables();
})
.ConfigureLogging((hostingContext, logging) =>
{
logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
logging.AddConsole();
})
.Configure(app =>
{
app.UseWebSockets();
app.UseOcelot().Wait();
})
.UseIISIntegration();
_ocelotHost = _ocelotBuilder.Build();
await _ocelotHost.StartAsync();
}
public void GivenThereIsAConfiguration(FileConfiguration fileConfiguration)
{
var configurationPath = TestConfiguration.ConfigurationPath;
@ -698,6 +735,7 @@ namespace Ocelot.AcceptanceTests
{
_ocelotClient?.Dispose();
_ocelotServer?.Dispose();
_ocelotHost?.Dispose();
}
public void ThenTheRequestIdIsReturned()

View File

@ -0,0 +1,487 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Consul;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Ocelot.Configuration.File;
using Shouldly;
using TestStack.BDDfy;
using Xunit;
namespace Ocelot.AcceptanceTests
{
public class WebSocketTests : IDisposable
{
private IWebHost _firstDownstreamHost;
private IWebHost _secondDownstreamHost;
private readonly List<string> _secondRecieved;
private readonly List<string> _firstRecieved;
private readonly List<ServiceEntry> _serviceEntries;
private readonly Steps _steps;
private IWebHost _fakeConsulBuilder;
public WebSocketTests()
{
_steps = new Steps();
_firstRecieved = new List<string>();
_secondRecieved = new List<string>();
_serviceEntries = new List<ServiceEntry>();
}
[Fact]
public async Task should_proxy_websocket_input_to_downstream_service()
{
var downstreamPort = 5001;
var downstreamHost = "localhost";
var config = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
UpstreamPathTemplate = "/",
DownstreamPathTemplate = "/ws",
DownstreamScheme = "ws",
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = downstreamHost,
Port = downstreamPort
}
}
}
}
};
this.Given(_ => _steps.GivenThereIsAConfiguration(config))
.And(_ => _steps.StartFakeOcelotWithWebSockets())
.And(_ => StartFakeDownstreamService($"http://{downstreamHost}:{downstreamPort}", "/ws"))
.When(_ => StartClient("ws://localhost:5000/"))
.Then(_ => _firstRecieved.Count.ShouldBe(10))
.BDDfy();
}
[Fact]
public async Task should_proxy_websocket_input_to_downstream_service_and_use_load_balancer()
{
var downstreamPort = 5005;
var downstreamHost = "localhost";
var secondDownstreamPort = 5006;
var secondDownstreamHost = "localhost";
var config = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
UpstreamPathTemplate = "/",
DownstreamPathTemplate = "/ws",
DownstreamScheme = "ws",
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = downstreamHost,
Port = downstreamPort
},
new FileHostAndPort
{
Host = secondDownstreamHost,
Port = secondDownstreamPort
}
},
LoadBalancer = "RoundRobin"
}
}
};
this.Given(_ => _steps.GivenThereIsAConfiguration(config))
.And(_ => _steps.StartFakeOcelotWithWebSockets())
.And(_ => StartFakeDownstreamService($"http://{downstreamHost}:{downstreamPort}", "/ws"))
.And(_ => StartSecondFakeDownstreamService($"http://{secondDownstreamHost}:{secondDownstreamPort}","/ws"))
.When(_ => WhenIStartTheClients())
.Then(_ => ThenBothDownstreamServicesAreCalled())
.BDDfy();
}
[Fact]
public async Task should_proxy_websocket_input_to_downstream_service_and_use_service_discovery_and_load_balancer()
{
var downstreamPort = 5007;
var downstreamHost = "localhost";
var secondDownstreamPort = 5008;
var secondDownstreamHost = "localhost";
var serviceName = "websockets";
var consulPort = 8509;
var fakeConsulServiceDiscoveryUrl = $"http://localhost:{consulPort}";
var serviceEntryOne = new ServiceEntry()
{
Service = new AgentService()
{
Service = serviceName,
Address = downstreamHost,
Port = downstreamPort,
ID = Guid.NewGuid().ToString(),
Tags = new string[0]
},
};
var serviceEntryTwo = new ServiceEntry()
{
Service = new AgentService()
{
Service = serviceName,
Address = secondDownstreamHost,
Port = secondDownstreamPort,
ID = Guid.NewGuid().ToString(),
Tags = new string[0]
},
};
var config = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
UpstreamPathTemplate = "/",
DownstreamPathTemplate = "/ws",
DownstreamScheme = "ws",
LoadBalancer = "RoundRobin",
ServiceName = serviceName,
UseServiceDiscovery = true
}
},
GlobalConfiguration = new FileGlobalConfiguration
{
ServiceDiscoveryProvider = new FileServiceDiscoveryProvider
{
Host = "localhost",
Port = consulPort,
Type = "consul"
}
}
};
this.Given(_ => _steps.GivenThereIsAConfiguration(config))
.And(_ => _steps.StartFakeOcelotWithWebSockets())
.And(_ => GivenThereIsAFakeConsulServiceDiscoveryProvider(fakeConsulServiceDiscoveryUrl, serviceName))
.And(_ => GivenTheServicesAreRegisteredWithConsul(serviceEntryOne, serviceEntryTwo))
.And(_ => StartFakeDownstreamService($"http://{downstreamHost}:{downstreamPort}", "/ws"))
.And(_ => StartSecondFakeDownstreamService($"http://{secondDownstreamHost}:{secondDownstreamPort}", "/ws"))
.When(_ => WhenIStartTheClients())
.Then(_ => ThenBothDownstreamServicesAreCalled())
.BDDfy();
}
private void ThenBothDownstreamServicesAreCalled()
{
_firstRecieved.Count.ShouldBe(10);
_firstRecieved.ForEach(x =>
{
x.ShouldBe("test");
});
_secondRecieved.Count.ShouldBe(10);
_secondRecieved.ForEach(x =>
{
x.ShouldBe("chocolate");
});
}
private void GivenTheServicesAreRegisteredWithConsul(params ServiceEntry[] serviceEntries)
{
foreach (var serviceEntry in serviceEntries)
{
_serviceEntries.Add(serviceEntry);
}
}
private void GivenThereIsAFakeConsulServiceDiscoveryProvider(string url, string serviceName)
{
_fakeConsulBuilder = new WebHostBuilder()
.UseUrls(url)
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseUrls(url)
.Configure(app =>
{
app.Run(async context =>
{
if (context.Request.Path.Value == $"/v1/health/service/{serviceName}")
{
await context.Response.WriteJsonAsync(_serviceEntries);
}
});
})
.Build();
_fakeConsulBuilder.Start();
}
private async Task WhenIStartTheClients()
{
var firstClient = StartClient("ws://localhost:5000/");
var secondClient = StartSecondClient("ws://localhost:5000/");
await Task.WhenAll(firstClient, secondClient);
}
private async Task StartClient(string url)
{
var client = new ClientWebSocket();
await client.ConnectAsync(new Uri(url), CancellationToken.None);
var sending = Task.Run(async () =>
{
string line = "test";
for (int i = 0; i < 10; i++)
{
var bytes = Encoding.UTF8.GetBytes(line);
await client.SendAsync(new ArraySegment<byte>(bytes), WebSocketMessageType.Text, true,
CancellationToken.None);
await Task.Delay(10);
}
await client.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, "", CancellationToken.None);
});
var receiving = Task.Run(async () =>
{
var buffer = new byte[1024 * 4];
while (true)
{
var result = await client.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
if (result.MessageType == WebSocketMessageType.Text)
{
_firstRecieved.Add(Encoding.UTF8.GetString(buffer, 0, result.Count));
}
else if (result.MessageType == WebSocketMessageType.Close)
{
await client.CloseAsync(WebSocketCloseStatus.NormalClosure, "", CancellationToken.None);
break;
}
}
});
await Task.WhenAll(sending, receiving);
}
private async Task StartSecondClient(string url)
{
await Task.Delay(500);
var client = new ClientWebSocket();
await client.ConnectAsync(new Uri(url), CancellationToken.None);
var sending = Task.Run(async () =>
{
string line = "test";
for (int i = 0; i < 10; i++)
{
var bytes = Encoding.UTF8.GetBytes(line);
await client.SendAsync(new ArraySegment<byte>(bytes), WebSocketMessageType.Text, true,
CancellationToken.None);
await Task.Delay(10);
}
await client.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, "", CancellationToken.None);
});
var receiving = Task.Run(async () =>
{
var buffer = new byte[1024 * 4];
while (true)
{
var result = await client.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
if (result.MessageType == WebSocketMessageType.Text)
{
_secondRecieved.Add(Encoding.UTF8.GetString(buffer, 0, result.Count));
}
else if (result.MessageType == WebSocketMessageType.Close)
{
await client.CloseAsync(WebSocketCloseStatus.NormalClosure, "", CancellationToken.None);
break;
}
}
});
await Task.WhenAll(sending, receiving);
}
private async Task StartFakeDownstreamService(string url, string path)
{
_firstDownstreamHost = new WebHostBuilder()
.ConfigureServices(s => { }).UseKestrel()
.UseUrls(url)
.UseContentRoot(Directory.GetCurrentDirectory())
.ConfigureAppConfiguration((hostingContext, config) =>
{
config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
var env = hostingContext.HostingEnvironment;
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);
config.AddEnvironmentVariables();
})
.ConfigureLogging((hostingContext, logging) =>
{
logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
logging.AddConsole();
})
.Configure(app =>
{
app.UseWebSockets();
app.Use(async (context, next) =>
{
if (context.Request.Path == path)
{
if (context.WebSockets.IsWebSocketRequest)
{
WebSocket webSocket = await context.WebSockets.AcceptWebSocketAsync();
await Echo(webSocket);
}
else
{
context.Response.StatusCode = 400;
}
}
else
{
await next();
}
});
})
.UseIISIntegration().Build();
await _firstDownstreamHost.StartAsync();
}
private async Task StartSecondFakeDownstreamService(string url, string path)
{
_secondDownstreamHost = new WebHostBuilder()
.ConfigureServices(s => { }).UseKestrel()
.UseUrls(url)
.UseContentRoot(Directory.GetCurrentDirectory())
.ConfigureAppConfiguration((hostingContext, config) =>
{
config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
var env = hostingContext.HostingEnvironment;
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);
config.AddEnvironmentVariables();
})
.ConfigureLogging((hostingContext, logging) =>
{
logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
logging.AddConsole();
})
.Configure(app =>
{
app.UseWebSockets();
app.Use(async (context, next) =>
{
if (context.Request.Path == path)
{
if (context.WebSockets.IsWebSocketRequest)
{
WebSocket webSocket = await context.WebSockets.AcceptWebSocketAsync();
await Message(webSocket);
}
else
{
context.Response.StatusCode = 400;
}
}
else
{
await next();
}
});
})
.UseIISIntegration().Build();
await _secondDownstreamHost.StartAsync();
}
private async Task Echo(WebSocket webSocket)
{
try
{
var buffer = new byte[1024 * 4];
var result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
while (!result.CloseStatus.HasValue)
{
await webSocket.SendAsync(new ArraySegment<byte>(buffer, 0, result.Count), result.MessageType, result.EndOfMessage, CancellationToken.None);
result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
}
await webSocket.CloseAsync(result.CloseStatus.Value, result.CloseStatusDescription, CancellationToken.None);
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
private async Task Message(WebSocket webSocket)
{
try
{
var buffer = new byte[1024 * 4];
var bytes = Encoding.UTF8.GetBytes("chocolate");
var result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
while (!result.CloseStatus.HasValue)
{
await webSocket.SendAsync(new ArraySegment<byte>(bytes), result.MessageType, result.EndOfMessage, CancellationToken.None);
result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
}
await webSocket.CloseAsync(result.CloseStatus.Value, result.CloseStatusDescription, CancellationToken.None);
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
public void Dispose()
{
_steps.Dispose();
_firstDownstreamHost?.Dispose();
_secondDownstreamHost?.Dispose();
_fakeConsulBuilder?.Dispose();
}
}
}

View File

@ -43,7 +43,7 @@ namespace Ocelot.UnitTests.Cache
});
_cacheManager = new OcelotCacheManagerCache<CachedResponse>(cacheManagerOutputCache);
_downstreamContext = new DownstreamContext(new DefaultHttpContext());
_downstreamContext.DownstreamRequest = new HttpRequestMessage(HttpMethod.Get, "https://some.url/blah?abcd=123");
_downstreamContext.DownstreamRequest = new Ocelot.Request.Middleware.DownstreamRequest(new HttpRequestMessage(HttpMethod.Get, "https://some.url/blah?abcd=123"));
_next = context => Task.CompletedTask;
_middleware = new OutputCacheMiddleware(_next, _loggerFactory.Object, _cacheManager, _regionCreator);
}

View File

@ -40,7 +40,7 @@ namespace Ocelot.UnitTests.Cache
_logger = new Mock<IOcelotLogger>();
_loggerFactory.Setup(x => x.CreateLogger<OutputCacheMiddleware>()).Returns(_logger.Object);
_next = context => Task.CompletedTask;
_downstreamContext.DownstreamRequest = new HttpRequestMessage(HttpMethod.Get, "https://some.url/blah?abcd=123");
_downstreamContext.DownstreamRequest = new Ocelot.Request.Middleware.DownstreamRequest(new HttpRequestMessage(HttpMethod.Get, "https://some.url/blah?abcd=123"));
}
[Fact]

View File

@ -20,6 +20,7 @@ namespace Ocelot.UnitTests.DownstreamUrlCreator
using Xunit;
using Shouldly;
using Microsoft.AspNetCore.Http;
using Ocelot.Request.Middleware;
public class DownstreamUrlCreatorMiddlewareTests
{
@ -30,6 +31,7 @@ namespace Ocelot.UnitTests.DownstreamUrlCreator
private DownstreamUrlCreatorMiddleware _middleware;
private DownstreamContext _downstreamContext;
private OcelotRequestDelegate _next;
private HttpRequestMessage _request;
public DownstreamUrlCreatorMiddlewareTests()
{
@ -38,7 +40,8 @@ namespace Ocelot.UnitTests.DownstreamUrlCreator
_logger = new Mock<IOcelotLogger>();
_loggerFactory.Setup(x => x.CreateLogger<DownstreamUrlCreatorMiddleware>()).Returns(_logger.Object);
_downstreamUrlTemplateVariableReplacer = new Mock<IDownstreamPathPlaceholderReplacer>();
_downstreamContext.DownstreamRequest = new HttpRequestMessage(HttpMethod.Get, "https://my.url/abc/?q=123");
_request = new HttpRequestMessage(HttpMethod.Get, "https://my.url/abc/?q=123");
_downstreamContext.DownstreamRequest = new DownstreamRequest(_request);
_next = context => Task.CompletedTask;
}
@ -208,7 +211,9 @@ namespace Ocelot.UnitTests.DownstreamUrlCreator
private void GivenTheDownstreamRequestUriIs(string uri)
{
_downstreamContext.DownstreamRequest.RequestUri = new Uri(uri);
_request.RequestUri = new Uri(uri);
//todo - not sure if needed
_downstreamContext.DownstreamRequest = new DownstreamRequest(_request);
}
private void GivenTheUrlReplacerWillReturn(string path)
@ -221,7 +226,7 @@ namespace Ocelot.UnitTests.DownstreamUrlCreator
private void ThenTheDownstreamRequestUriIs(string expectedUri)
{
_downstreamContext.DownstreamRequest.RequestUri.OriginalString.ShouldBe(expectedUri);
_downstreamContext.DownstreamRequest.ToHttpRequestMessage().RequestUri.OriginalString.ShouldBe(expectedUri);
}
}
}

View File

@ -11,6 +11,7 @@ using Shouldly;
using TestStack.BDDfy;
using Xunit;
using System.Net.Http;
using Ocelot.Request.Middleware;
namespace Ocelot.UnitTests.Headers
{
@ -18,7 +19,7 @@ namespace Ocelot.UnitTests.Headers
{
private readonly AddHeadersToRequest _addHeadersToRequest;
private readonly Mock<IClaimsParser> _parser;
private readonly HttpRequestMessage _downstreamRequest;
private readonly DownstreamRequest _downstreamRequest;
private List<Claim> _claims;
private List<ClaimToThing> _configuration;
private Response _result;
@ -28,7 +29,7 @@ namespace Ocelot.UnitTests.Headers
{
_parser = new Mock<IClaimsParser>();
_addHeadersToRequest = new AddHeadersToRequest(_parser.Object);
_downstreamRequest = new HttpRequestMessage();
_downstreamRequest = new DownstreamRequest(new HttpRequestMessage(HttpMethod.Get, "http://test.com"));
}
[Fact]

View File

@ -16,6 +16,7 @@ using Ocelot.Middleware;
namespace Ocelot.UnitTests.Headers
{
using System.Threading.Tasks;
using Ocelot.Request.Middleware;
public class HttpHeadersTransformationMiddlewareTests
{
@ -68,7 +69,7 @@ namespace Ocelot.UnitTests.Headers
private void GivenTheDownstreamRequestIs()
{
_downstreamContext.DownstreamRequest = new HttpRequestMessage();
_downstreamContext.DownstreamRequest = new DownstreamRequest(new HttpRequestMessage(HttpMethod.Get, "http://test.com"));
}
private void GivenTheHttpResponseMessageIs()
@ -97,7 +98,7 @@ namespace Ocelot.UnitTests.Headers
private void ThenTheIHttpResponseHeaderReplacerIsCalledCorrectly()
{
_postReplacer.Verify(x => x.Replace(It.IsAny<HttpResponseMessage>(), It.IsAny<List<HeaderFindAndReplace>>(), It.IsAny<HttpRequestMessage>()), Times.Once);
_postReplacer.Verify(x => x.Replace(It.IsAny<HttpResponseMessage>(), It.IsAny<List<HeaderFindAndReplace>>(), It.IsAny<DownstreamRequest>()), Times.Once);
}
private void GivenTheFollowingRequest()

View File

@ -14,6 +14,7 @@ namespace Ocelot.UnitTests.Headers
using Ocelot.Headers;
using Ocelot.Headers.Middleware;
using Ocelot.Logging;
using Ocelot.Request.Middleware;
using Ocelot.Responses;
using TestStack.BDDfy;
using Xunit;
@ -37,7 +38,7 @@ namespace Ocelot.UnitTests.Headers
_loggerFactory.Setup(x => x.CreateLogger<HttpRequestHeadersBuilderMiddleware>()).Returns(_logger.Object);
_next = context => Task.CompletedTask;
_middleware = new HttpRequestHeadersBuilderMiddleware(_next, _loggerFactory.Object, _addHeaders.Object);
_downstreamContext.DownstreamRequest = new HttpRequestMessage();
_downstreamContext.DownstreamRequest = new DownstreamRequest(new HttpRequestMessage(HttpMethod.Get, "http://test.com"));
}
[Fact]
@ -81,7 +82,7 @@ namespace Ocelot.UnitTests.Headers
.Setup(x => x.SetHeadersOnDownstreamRequest(
It.IsAny<List<ClaimToThing>>(),
It.IsAny<IEnumerable<System.Security.Claims.Claim>>(),
It.IsAny<HttpRequestMessage>()))
It.IsAny<DownstreamRequest>()))
.Returns(new OkResponse());
}

View File

@ -11,6 +11,7 @@ using Moq;
using Ocelot.Infrastructure;
using Ocelot.Middleware;
using Ocelot.Infrastructure.RequestData;
using Ocelot.Request.Middleware;
namespace Ocelot.UnitTests.Headers
{
@ -21,7 +22,7 @@ namespace Ocelot.UnitTests.Headers
private HttpResponseHeaderReplacer _replacer;
private List<HeaderFindAndReplace> _headerFindAndReplaces;
private Response _result;
private HttpRequestMessage _request;
private DownstreamRequest _request;
private Mock<IBaseUrlFinder> _finder;
private Mock<IRequestScopedDataRepository> _repo;
@ -69,7 +70,7 @@ namespace Ocelot.UnitTests.Headers
{
var downstreamUrl = "http://downstream.com/";
var request = new HttpRequestMessage();
var request = new HttpRequestMessage(HttpMethod.Get, "http://test.com");
request.RequestUri = new System.Uri(downstreamUrl);
var response = new HttpResponseMessage();
@ -91,7 +92,7 @@ namespace Ocelot.UnitTests.Headers
{
var downstreamUrl = "http://downstream.com/";
var request = new HttpRequestMessage();
var request = new HttpRequestMessage(HttpMethod.Get, "http://test.com");
request.RequestUri = new System.Uri(downstreamUrl);
var response = new HttpResponseMessage();
@ -113,7 +114,7 @@ namespace Ocelot.UnitTests.Headers
{
var downstreamUrl = "http://downstream.com/test/product";
var request = new HttpRequestMessage();
var request = new HttpRequestMessage(HttpMethod.Get, "http://test.com");
request.RequestUri = new System.Uri(downstreamUrl);
var response = new HttpResponseMessage();
@ -135,7 +136,7 @@ namespace Ocelot.UnitTests.Headers
{
var downstreamUrl = "http://downstream.com/test/product";
var request = new HttpRequestMessage();
var request = new HttpRequestMessage(HttpMethod.Get, "http://test.com");
request.RequestUri = new System.Uri(downstreamUrl);
var response = new HttpResponseMessage();
@ -157,7 +158,7 @@ namespace Ocelot.UnitTests.Headers
{
var downstreamUrl = "http://downstream.com:123/test/product";
var request = new HttpRequestMessage();
var request = new HttpRequestMessage(HttpMethod.Get, "http://test.com");
request.RequestUri = new System.Uri(downstreamUrl);
var response = new HttpResponseMessage();
@ -179,7 +180,7 @@ namespace Ocelot.UnitTests.Headers
{
var downstreamUrl = "http://downstream.com:123/test/product";
var request = new HttpRequestMessage();
var request = new HttpRequestMessage(HttpMethod.Get, "http://test.com");
request.RequestUri = new System.Uri(downstreamUrl);
var response = new HttpResponseMessage();
@ -198,7 +199,7 @@ namespace Ocelot.UnitTests.Headers
private void GivenTheRequestIs(HttpRequestMessage request)
{
_request = request;
_request = new DownstreamRequest(request);
}
private void ThenTheHeadersAreNotReplaced()

View File

@ -4,6 +4,7 @@ using Moq;
using Ocelot.Infrastructure;
using Ocelot.Infrastructure.RequestData;
using Ocelot.Middleware;
using Ocelot.Request.Middleware;
using Ocelot.Responses;
using Shouldly;
using Xunit;
@ -43,8 +44,9 @@ namespace Ocelot.UnitTests.Infrastructure
[Fact]
public void should_return_downstream_base_url_when_port_is_not_80_or_443()
{
var request = new HttpRequestMessage();
request.RequestUri = new Uri("http://www.bbc.co.uk");
var httpRequest = new HttpRequestMessage();
httpRequest.RequestUri = new Uri("http://www.bbc.co.uk");
var request = new DownstreamRequest(httpRequest);
var result = _placeholders.Get("{DownstreamBaseUrl}", request);
result.Data.ShouldBe("http://www.bbc.co.uk/");
}
@ -53,8 +55,9 @@ namespace Ocelot.UnitTests.Infrastructure
[Fact]
public void should_return_downstream_base_url_when_port_is_80_or_443()
{
var request = new HttpRequestMessage();
request.RequestUri = new Uri("http://www.bbc.co.uk:123");
var httpRequest = new HttpRequestMessage();
httpRequest.RequestUri = new Uri("http://www.bbc.co.uk:123");
var request = new DownstreamRequest(httpRequest);
var result = _placeholders.Get("{DownstreamBaseUrl}", request);
result.Data.ShouldBe("http://www.bbc.co.uk:123/");
}
@ -62,7 +65,8 @@ namespace Ocelot.UnitTests.Infrastructure
[Fact]
public void should_return_key_does_not_exist_for_http_request_message()
{
var result = _placeholders.Get("{Test}", new System.Net.Http.HttpRequestMessage());
var request = new DownstreamRequest(new HttpRequestMessage(HttpMethod.Get, "http://west.com"));
var result = _placeholders.Get("{Test}", request);
result.IsError.ShouldBeTrue();
result.Errors[0].Message.ShouldBe("Unable to find placeholder called {Test}");
}

View File

@ -0,0 +1,29 @@
using Xunit;
using Ocelot.Infrastructure.Extensions;
using Shouldly;
namespace Ocelot.UnitTests.Infrastructure
{
public class StringExtensionsTests
{
[Fact]
public void should_trim_start()
{
var test = "/string";
test = test.TrimStart("/");
test.ShouldBe("string");
}
[Fact]
public void should_return_source()
{
var test = "string";
test = test.LastCharAsForwardSlash();
test.ShouldBe("string/");
}
}
}

View File

@ -5,6 +5,7 @@ using Ocelot.LoadBalancer.LoadBalancers;
using Ocelot.ServiceDiscovery;
using Shouldly;
using System.Collections.Generic;
using Ocelot.ServiceDiscovery.Providers;
using TestStack.BDDfy;
using Xunit;

View File

@ -13,6 +13,7 @@ namespace Ocelot.UnitTests.LoadBalancer
using Ocelot.LoadBalancer.LoadBalancers;
using Ocelot.LoadBalancer.Middleware;
using Ocelot.Logging;
using Ocelot.Request.Middleware;
using Ocelot.Responses;
using Ocelot.Values;
using Shouldly;
@ -39,13 +40,13 @@ namespace Ocelot.UnitTests.LoadBalancer
_loadBalancerHouse = new Mock<ILoadBalancerHouse>();
_loadBalancer = new Mock<ILoadBalancer>();
_loadBalancerHouse = new Mock<ILoadBalancerHouse>();
_downstreamRequest = new HttpRequestMessage(HttpMethod.Get, "");
_downstreamRequest = new HttpRequestMessage(HttpMethod.Get, "http://test.com/");
_downstreamContext = new DownstreamContext(new DefaultHttpContext());
_loggerFactory = new Mock<IOcelotLoggerFactory>();
_logger = new Mock<IOcelotLogger>();
_loggerFactory.Setup(x => x.CreateLogger<LoadBalancingMiddleware>()).Returns(_logger.Object);
_next = context => Task.CompletedTask;
_downstreamContext.DownstreamRequest = _downstreamRequest;
_downstreamContext.DownstreamRequest = new DownstreamRequest(_downstreamRequest);
}
[Fact]
@ -122,6 +123,7 @@ namespace Ocelot.UnitTests.LoadBalancer
private void GivenTheDownStreamUrlIs(string downstreamUrl)
{
_downstreamRequest.RequestUri = new System.Uri(downstreamUrl);
_downstreamContext.DownstreamRequest = new DownstreamRequest(_downstreamRequest);
}
private void GivenTheLoadBalancerReturnsAnError()
@ -185,7 +187,7 @@ namespace Ocelot.UnitTests.LoadBalancer
private void ThenTheDownstreamUrlIsReplacedWith(string expectedUri)
{
_downstreamContext.DownstreamRequest.RequestUri.OriginalString.ShouldBe(expectedUri);
_downstreamContext.DownstreamRequest.ToHttpRequestMessage().RequestUri.OriginalString.ShouldBe(expectedUri);
}
}
}

View File

@ -9,6 +9,7 @@ using Ocelot.Configuration.Builder;
using Ocelot.Errors;
using Ocelot.Middleware;
using Ocelot.Middleware.Multiplexer;
using Ocelot.Request.Middleware;
using Ocelot.UnitTests.Responder;
using Shouldly;
using TestStack.BDDfy;
@ -48,7 +49,7 @@ namespace Ocelot.UnitTests.Middleware
new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent("Bill says hi") },
DownstreamReRoute = billDownstreamReRoute,
Errors = new List<Error> { new AnyError() },
DownstreamRequest = new HttpRequestMessage(HttpMethod.Get, new Uri("http://www.bbc.co.uk")),
DownstreamRequest = new DownstreamRequest(new HttpRequestMessage(HttpMethod.Get, new Uri("http://www.bbc.co.uk"))),
};
var downstreamContexts = new List<DownstreamContext> { billDownstreamContext };

View File

@ -12,24 +12,27 @@ using TestStack.BDDfy;
using Xunit;
using System.Net.Http;
using System;
using Ocelot.Request.Middleware;
namespace Ocelot.UnitTests.QueryStrings
{
public class AddQueriesToRequestTests
{
private readonly AddQueriesToRequest _addQueriesToRequest;
private HttpRequestMessage _downstreamRequest;
private DownstreamRequest _downstreamRequest;
private readonly Mock<IClaimsParser> _parser;
private List<ClaimToThing> _configuration;
private List<Claim> _claims;
private Response _result;
private Response<string> _claimValue;
private HttpRequestMessage _request;
public AddQueriesToRequestTests()
{
_request = new HttpRequestMessage(HttpMethod.Post, "http://my.url/abc?q=123");
_parser = new Mock<IClaimsParser>();
_addQueriesToRequest = new AddQueriesToRequest(_parser.Object);
_downstreamRequest = new HttpRequestMessage(HttpMethod.Post, "http://my.url/abc?q=123");
_downstreamRequest = new DownstreamRequest(_request);
}
[Fact]
@ -78,7 +81,7 @@ namespace Ocelot.UnitTests.QueryStrings
private void TheTheQueryStringIs(string expected)
{
_downstreamRequest.RequestUri.Query.ShouldBe(expected);
_downstreamRequest.Query.ShouldBe(expected);
}
[Fact]
@ -123,7 +126,7 @@ namespace Ocelot.UnitTests.QueryStrings
private void ThenTheQueryIsAdded()
{
var queries = Microsoft.AspNetCore.WebUtilities.QueryHelpers.ParseQuery(_downstreamRequest.RequestUri.OriginalString);
var queries = Microsoft.AspNetCore.WebUtilities.QueryHelpers.ParseQuery(_downstreamRequest.ToHttpRequestMessage().RequestUri.OriginalString);
var query = queries.First(x => x.Key == "query-key");
query.Value.First().ShouldBe(_claimValue.Data);
}
@ -140,15 +143,18 @@ namespace Ocelot.UnitTests.QueryStrings
private void GivenTheDownstreamRequestHasQueryString(string queryString)
{
_downstreamRequest = new HttpRequestMessage(HttpMethod.Post, $"http://my.url/abc{queryString}");
_request = new HttpRequestMessage(HttpMethod.Post, $"http://my.url/abc{queryString}");
_downstreamRequest = new DownstreamRequest(_request);
}
private void GivenTheDownstreamRequestHasQueryString(string key, string value)
{
var newUri = Microsoft.AspNetCore.WebUtilities.QueryHelpers
.AddQueryString(_downstreamRequest.RequestUri.OriginalString, key, value);
.AddQueryString(_downstreamRequest.ToHttpRequestMessage().RequestUri.OriginalString, key, value);
_downstreamRequest.RequestUri = new Uri(newUri);
_request.RequestUri = new Uri(newUri);
//todo - might not need to instanciate
_downstreamRequest = new DownstreamRequest(_request);
}
private void GivenTheClaimParserReturns(Response<string> claimValue)

View File

@ -18,6 +18,7 @@ namespace Ocelot.UnitTests.QueryStrings
using System.Security.Claims;
using Microsoft.AspNetCore.Http;
using System.Threading.Tasks;
using Ocelot.Request.Middleware;
public class QueryStringBuilderMiddlewareTests
{
@ -36,7 +37,7 @@ namespace Ocelot.UnitTests.QueryStrings
_loggerFactory.Setup(x => x.CreateLogger<QueryStringBuilderMiddleware>()).Returns(_logger.Object);
_next = context => Task.CompletedTask;
_addQueries = new Mock<IAddQueriesToRequest>();
_downstreamContext.DownstreamRequest = new HttpRequestMessage();
_downstreamContext.DownstreamRequest = new DownstreamRequest(new HttpRequestMessage(HttpMethod.Get, "http://test.com"));
_middleware = new QueryStringBuilderMiddleware(_next, _loggerFactory.Object, _addQueries.Object);
}
@ -74,7 +75,7 @@ namespace Ocelot.UnitTests.QueryStrings
.Setup(x => x.SetQueriesOnDownstreamRequest(
It.IsAny<List<ClaimToThing>>(),
It.IsAny<IEnumerable<Claim>>(),
It.IsAny<HttpRequestMessage>()))
It.IsAny<DownstreamRequest>()))
.Returns(new OkResponse());
}

View File

@ -18,6 +18,7 @@ namespace Ocelot.UnitTests.RateLimit
using Microsoft.Extensions.Caching.Memory;
using System.IO;
using System.Threading.Tasks;
using Ocelot.Request.Middleware;
public class ClientRateLimitMiddlewareTests
{
@ -100,7 +101,7 @@ namespace Ocelot.UnitTests.RateLimit
{
var request = new HttpRequestMessage(new HttpMethod("GET"), _url);
request.Headers.Add("ClientId", clientId);
_downstreamContext.DownstreamRequest = request;
_downstreamContext.DownstreamRequest = new DownstreamRequest(request);
_middleware.Invoke(_downstreamContext).GetAwaiter().GetResult();
_responseStatusCode = (int)_downstreamContext.HttpContext.Response.StatusCode;
@ -115,7 +116,7 @@ namespace Ocelot.UnitTests.RateLimit
{
var request = new HttpRequestMessage(new HttpMethod("GET"), _url);
request.Headers.Add("ClientId", clientId);
_downstreamContext.DownstreamRequest = request;
_downstreamContext.DownstreamRequest = new DownstreamRequest(request);
_downstreamContext.HttpContext.Request.Headers.TryAdd("ClientId", clientId);
_middleware.Invoke(_downstreamContext).GetAwaiter().GetResult();

View File

@ -88,7 +88,7 @@ namespace Ocelot.UnitTests.Request
private void GivenTheMapperWillReturnAMappedRequest()
{
_mappedRequest = new OkResponse<HttpRequestMessage>(new HttpRequestMessage());
_mappedRequest = new OkResponse<HttpRequestMessage>(new HttpRequestMessage(HttpMethod.Get, "http://www.bbc.co.uk"));
_requestMapper
.Setup(rm => rm.Map(It.IsAny<HttpRequest>()))

View File

@ -20,6 +20,7 @@ namespace Ocelot.UnitTests.RequestId
using Shouldly;
using TestStack.BDDfy;
using Xunit;
using Ocelot.Request.Middleware;
public class ReRouteRequestIdMiddlewareTests
{
@ -35,7 +36,7 @@ namespace Ocelot.UnitTests.RequestId
public ReRouteRequestIdMiddlewareTests()
{
_downstreamRequest = new HttpRequestMessage();
_downstreamRequest = new HttpRequestMessage(HttpMethod.Get, "http://test.com");
_repo = new Mock<IRequestScopedDataRepository>();
_downstreamContext = new DownstreamContext(new DefaultHttpContext());
_loggerFactory = new Mock<IOcelotLoggerFactory>();
@ -47,7 +48,7 @@ namespace Ocelot.UnitTests.RequestId
return Task.CompletedTask;
};
_middleware = new ReRouteRequestIdMiddleware(_next, _loggerFactory.Object, _repo.Object);
_downstreamContext.DownstreamRequest = _downstreamRequest;
_downstreamContext.DownstreamRequest = new DownstreamRequest(_downstreamRequest);
}
[Fact]

View File

@ -14,6 +14,7 @@ using Ocelot.Configuration;
using Ocelot.Configuration.Builder;
using Ocelot.Logging;
using Ocelot.Middleware;
using Ocelot.Request.Middleware;
using Ocelot.Requester;
using Ocelot.Responses;
using Shouldly;
@ -170,7 +171,7 @@ namespace Ocelot.UnitTests.Requester
var context = new DownstreamContext(new DefaultHttpContext())
{
DownstreamReRoute = downstream,
DownstreamRequest = new HttpRequestMessage() { RequestUri = new Uri("http://localhost:5003") },
DownstreamRequest = new DownstreamRequest(new HttpRequestMessage() { RequestUri = new Uri("http://localhost:5003") }),
};
_context = context;

View File

@ -12,13 +12,16 @@ using Ocelot.Middleware;
using TestStack.BDDfy;
using Xunit;
using Shouldly;
using Ocelot.Request.Middleware;
using System.Threading.Tasks;
using System.Threading;
namespace Ocelot.UnitTests.Requester
{
public class HttpClientHttpRequesterTest
{
private readonly Mock<IHttpClientCache> _cacheHandlers;
private Mock<IDelegatingHandlerHandlerFactory> _house;
private Mock<IDelegatingHandlerHandlerFactory> _factory;
private Response<HttpResponseMessage> _response;
private readonly HttpClientHttpRequester _httpClientRequester;
private DownstreamContext _request;
@ -27,8 +30,8 @@ namespace Ocelot.UnitTests.Requester
public HttpClientHttpRequesterTest()
{
_house = new Mock<IDelegatingHandlerHandlerFactory>();
_house.Setup(x => x.Get(It.IsAny<DownstreamReRoute>())).Returns(new OkResponse<List<Func<DelegatingHandler>>>(new List<Func<DelegatingHandler>>()));
_factory = new Mock<IDelegatingHandlerHandlerFactory>();
_factory.Setup(x => x.Get(It.IsAny<DownstreamReRoute>())).Returns(new OkResponse<List<Func<DelegatingHandler>>>(new List<Func<DelegatingHandler>>()));
_logger = new Mock<IOcelotLogger>();
_loggerFactory = new Mock<IOcelotLoggerFactory>();
_loggerFactory
@ -38,7 +41,7 @@ namespace Ocelot.UnitTests.Requester
_httpClientRequester = new HttpClientHttpRequester(
_loggerFactory.Object,
_cacheHandlers.Object,
_house.Object);
_factory.Object);
}
[Fact]
@ -50,10 +53,11 @@ namespace Ocelot.UnitTests.Requester
var context = new DownstreamContext(new DefaultHttpContext())
{
DownstreamReRoute = reRoute,
DownstreamRequest = new HttpRequestMessage() { RequestUri = new Uri("http://www.bbc.co.uk") },
DownstreamRequest = new DownstreamRequest(new HttpRequestMessage() { RequestUri = new Uri("http://www.bbc.co.uk") }),
};
this.Given(x=>x.GivenTheRequestIs(context))
.And(x => GivenTheHouseReturnsOkHandler())
.When(x=>x.WhenIGetResponse())
.Then(x => x.ThenTheResponseIsCalledCorrectly())
.BDDfy();
@ -68,7 +72,7 @@ namespace Ocelot.UnitTests.Requester
var context = new DownstreamContext(new DefaultHttpContext())
{
DownstreamReRoute = reRoute,
DownstreamRequest = new HttpRequestMessage() { RequestUri = new Uri("http://localhost:60080") },
DownstreamRequest = new DownstreamRequest(new HttpRequestMessage() { RequestUri = new Uri("http://localhost:60080") }),
};
this.Given(x => x.GivenTheRequestIs(context))
@ -96,5 +100,23 @@ namespace Ocelot.UnitTests.Requester
{
_response.IsError.ShouldBeTrue();
}
private void GivenTheHouseReturnsOkHandler()
{
var handlers = new List<Func<DelegatingHandler>>
{
() => new OkDelegatingHandler()
};
_factory.Setup(x => x.Get(It.IsAny<DownstreamReRoute>())).Returns(new OkResponse<List<Func<DelegatingHandler>>>(handlers));
}
class OkDelegatingHandler : DelegatingHandler
{
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
return Task.FromResult(new HttpResponseMessage());
}
}
}
}

View File

@ -1,5 +1,6 @@
using System.Collections.Generic;
using Ocelot.ServiceDiscovery;
using Ocelot.ServiceDiscovery.Providers;
using Ocelot.Values;
using Shouldly;
using TestStack.BDDfy;

View File

@ -9,6 +9,8 @@ using Microsoft.AspNetCore.Http;
using Moq;
using Ocelot.Logging;
using Ocelot.ServiceDiscovery;
using Ocelot.ServiceDiscovery.Configuration;
using Ocelot.ServiceDiscovery.Providers;
using Ocelot.Values;
using Xunit;
using TestStack.BDDfy;

View File

@ -1,4 +1,7 @@
namespace Ocelot.UnitTests.ServiceDiscovery
using Ocelot.ServiceDiscovery.Configuration;
using Ocelot.ServiceDiscovery.Providers;
namespace Ocelot.UnitTests.ServiceDiscovery
{
using System;
using System.Collections.Generic;

View File

@ -5,6 +5,7 @@ using Ocelot.Configuration;
using Ocelot.Configuration.Builder;
using Ocelot.Logging;
using Ocelot.ServiceDiscovery;
using Ocelot.ServiceDiscovery.Providers;
using Shouldly;
using TestStack.BDDfy;
using Xunit;

View File

@ -0,0 +1,239 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Consul;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Ocelot.Configuration.File;
using Ocelot.DependencyInjection;
using Ocelot.Middleware;
using Shouldly;
using TestStack.BDDfy;
using Xunit;
namespace Ocelot.UnitTests.Websockets
{
public class WebSocketsProxyMiddlewareTests : IDisposable
{
private IWebHost _firstDownstreamHost;
private readonly List<string> _firstRecieved;
private WebHostBuilder _ocelotBuilder;
private IWebHost _ocelotHost;
public WebSocketsProxyMiddlewareTests()
{
_firstRecieved = new List<string>();
}
[Fact]
public async Task should_proxy_websocket_input_to_downstream_service()
{
var downstreamPort = 5001;
var downstreamHost = "localhost";
var config = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
UpstreamPathTemplate = "/",
DownstreamPathTemplate = "/ws",
DownstreamScheme = "ws",
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = downstreamHost,
Port = downstreamPort
}
}
}
}
};
this.Given(_ => GivenThereIsAConfiguration(config))
.And(_ => StartFakeOcelotWithWebSockets())
.And(_ => StartFakeDownstreamService($"http://{downstreamHost}:{downstreamPort}", "/ws"))
.When(_ => StartClient("ws://localhost:5000/"))
.Then(_ => _firstRecieved.Count.ShouldBe(10))
.BDDfy();
}
public void Dispose()
{
_firstDownstreamHost?.Dispose();
}
public async Task StartFakeOcelotWithWebSockets()
{
_ocelotBuilder = new WebHostBuilder();
_ocelotBuilder.ConfigureServices(s =>
{
s.AddSingleton(_ocelotBuilder);
s.AddOcelot();
});
_ocelotBuilder.UseKestrel()
.UseUrls("http://localhost:5000")
.UseContentRoot(Directory.GetCurrentDirectory())
.ConfigureAppConfiguration((hostingContext, config) =>
{
config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
var env = hostingContext.HostingEnvironment;
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);
config.AddJsonFile("configuration.json");
config.AddEnvironmentVariables();
})
.ConfigureLogging((hostingContext, logging) =>
{
logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
logging.AddConsole();
})
.Configure(app =>
{
app.UseWebSockets();
app.UseOcelot().Wait();
})
.UseIISIntegration();
_ocelotHost = _ocelotBuilder.Build();
await _ocelotHost.StartAsync();
}
public void GivenThereIsAConfiguration(FileConfiguration fileConfiguration)
{
var configurationPath = Path.Combine(AppContext.BaseDirectory, "configuration.json");
var jsonConfiguration = JsonConvert.SerializeObject(fileConfiguration);
if (File.Exists(configurationPath))
{
File.Delete(configurationPath);
}
File.WriteAllText(configurationPath, jsonConfiguration);
}
private async Task StartFakeDownstreamService(string url, string path)
{
_firstDownstreamHost = new WebHostBuilder()
.ConfigureServices(s => { }).UseKestrel()
.UseUrls(url)
.UseContentRoot(Directory.GetCurrentDirectory())
.ConfigureAppConfiguration((hostingContext, config) =>
{
config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
var env = hostingContext.HostingEnvironment;
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);
config.AddEnvironmentVariables();
})
.ConfigureLogging((hostingContext, logging) =>
{
logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
logging.AddConsole();
})
.Configure(app =>
{
app.UseWebSockets();
app.Use(async (context, next) =>
{
if (context.Request.Path == path)
{
if (context.WebSockets.IsWebSocketRequest)
{
WebSocket webSocket = await context.WebSockets.AcceptWebSocketAsync();
await Echo(webSocket);
}
else
{
context.Response.StatusCode = 400;
}
}
else
{
await next();
}
});
})
.UseIISIntegration().Build();
await _firstDownstreamHost.StartAsync();
}
private async Task StartClient(string url)
{
var client = new ClientWebSocket();
await client.ConnectAsync(new Uri(url), CancellationToken.None);
var sending = Task.Run(async () =>
{
string line = "test";
for (int i = 0; i < 10; i++)
{
var bytes = Encoding.UTF8.GetBytes(line);
await client.SendAsync(new ArraySegment<byte>(bytes), WebSocketMessageType.Text, true,
CancellationToken.None);
await Task.Delay(10);
}
await client.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, "", CancellationToken.None);
});
var receiving = Task.Run(async () =>
{
var buffer = new byte[1024 * 4];
while (true)
{
var result = await client.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
if (result.MessageType == WebSocketMessageType.Text)
{
_firstRecieved.Add(Encoding.UTF8.GetString(buffer, 0, result.Count));
}
else if (result.MessageType == WebSocketMessageType.Close)
{
await client.CloseAsync(WebSocketCloseStatus.NormalClosure, "", CancellationToken.None);
break;
}
}
});
await Task.WhenAll(sending, receiving);
}
private async Task Echo(WebSocket webSocket)
{
try
{
var buffer = new byte[1024 * 4];
var result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
while (!result.CloseStatus.HasValue)
{
await webSocket.SendAsync(new ArraySegment<byte>(buffer, 0, result.Count), result.MessageType, result.EndOfMessage, CancellationToken.None);
result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
}
await webSocket.CloseAsync(result.CloseStatus.Value, result.CloseStatusDescription, CancellationToken.None);
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
}
}