diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..98b27ad
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,45 @@
+# Folders
+artifacts/
+bin/
+obj/
+.dotnet/
+.nuget/
+.packages/
+.tools/
+.vs/
+.vscode/
+node_modules/
+BenchmarkDotNet.Artifacts/
+.gradle/
+src/SignalR/clients/**/dist/
+modules/
+.ionide/
+
+# File extensions
+*.aps
+*.binlog
+*.dll
+*.DS_Store
+*.exe
+*.idb
+*.lib
+*.log
+*.pch
+*.pdb
+*.pidb
+*.psess
+*.res
+*.snk
+*.so
+*.suo
+*.tlog
+*.user
+*.userprefs
+*.vspx
+
+# Specific files, typically generated by tools
+launchSettings.json
+msbuild.ProjectImports.zip
+StyleCop.Cache
+UpgradeLog.htm
+.idea
\ No newline at end of file
diff --git a/AiRepro.sln b/AiRepro.sln
new file mode 100644
index 0000000..9a283c2
--- /dev/null
+++ b/AiRepro.sln
@@ -0,0 +1,28 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Origin1", "Origin1\Origin1.csproj", "{985F15D9-F3D3-40D7-97B2-082A9DEB9ECF}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "App2", "App2\App2.csproj", "{B5A1FDF7-37A4-4947-B627-D6A9F8123586}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "App3", "App3\App3.csproj", "{447E4F97-F345-4A5F-9B11-103AF14CB58E}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {985F15D9-F3D3-40D7-97B2-082A9DEB9ECF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {985F15D9-F3D3-40D7-97B2-082A9DEB9ECF}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {985F15D9-F3D3-40D7-97B2-082A9DEB9ECF}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {985F15D9-F3D3-40D7-97B2-082A9DEB9ECF}.Release|Any CPU.Build.0 = Release|Any CPU
+ {B5A1FDF7-37A4-4947-B627-D6A9F8123586}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B5A1FDF7-37A4-4947-B627-D6A9F8123586}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B5A1FDF7-37A4-4947-B627-D6A9F8123586}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B5A1FDF7-37A4-4947-B627-D6A9F8123586}.Release|Any CPU.Build.0 = Release|Any CPU
+ {447E4F97-F345-4A5F-9B11-103AF14CB58E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {447E4F97-F345-4A5F-9B11-103AF14CB58E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {447E4F97-F345-4A5F-9B11-103AF14CB58E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {447E4F97-F345-4A5F-9B11-103AF14CB58E}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+EndGlobal
diff --git a/App2/App2.csproj b/App2/App2.csproj
new file mode 100644
index 0000000..119cc72
--- /dev/null
+++ b/App2/App2.csproj
@@ -0,0 +1,12 @@
+
+
+
+ netcoreapp3.1
+
+
+
+
+
+
+
+
diff --git a/App2/App3Client.cs b/App2/App3Client.cs
new file mode 100644
index 0000000..9ba1d51
--- /dev/null
+++ b/App2/App3Client.cs
@@ -0,0 +1,21 @@
+using System.Net.Http;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace App2
+{
+ public class App3Client
+ {
+ private readonly HttpClient _client;
+
+ public App3Client(HttpClient client)
+ {
+ _client = client;
+ }
+
+ public async Task SendAsync(string message)
+ {
+ await _client.PostAsync("/ping", new StringContent(message, Encoding.UTF8, "application/json"));
+ }
+ }
+}
\ No newline at end of file
diff --git a/App2/Controllers/App2Controller.cs b/App2/Controllers/App2Controller.cs
new file mode 100644
index 0000000..571aa1a
--- /dev/null
+++ b/App2/Controllers/App2Controller.cs
@@ -0,0 +1,18 @@
+using System.Threading.Tasks;
+using App2;
+using Microsoft.AspNetCore.Mvc;
+
+namespace App3.Controllers
+{
+ [ApiController]
+ [Route("")]
+ public class App2Controller : ControllerBase
+ {
+ [HttpPost("ping")]
+ public async Task Ping([FromBody] object message, [FromServices] App3Client client)
+ {
+ await client.SendAsync(message.ToString());
+ return Ok();
+ }
+ }
+}
\ No newline at end of file
diff --git a/App2/Program.cs b/App2/Program.cs
new file mode 100644
index 0000000..93bca6e
--- /dev/null
+++ b/App2/Program.cs
@@ -0,0 +1,24 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.Hosting;
+using Microsoft.Extensions.Logging;
+
+namespace App2
+{
+ public class Program
+ {
+ public static void Main(string[] args)
+ {
+ CreateHostBuilder(args).Build().Run();
+ }
+
+ public static IHostBuilder CreateHostBuilder(string[] args) =>
+ Host.CreateDefaultBuilder(args)
+ .ConfigureLogging(l => l.AddConsole(o => o.IncludeScopes = true))
+ .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup(); });
+ }
+}
\ No newline at end of file
diff --git a/App2/Startup.cs b/App2/Startup.cs
new file mode 100644
index 0000000..3fcf2d6
--- /dev/null
+++ b/App2/Startup.cs
@@ -0,0 +1,52 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.AspNetCore.HttpsPolicy;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
+using Microsoft.Extensions.Logging;
+
+namespace App2
+{
+ public class Startup
+ {
+ public Startup(IConfiguration configuration)
+ {
+ Configuration = configuration;
+ }
+
+ public IConfiguration Configuration { get; }
+
+ // This method gets called by the runtime. Use this method to add services to the container.
+ public void ConfigureServices(IServiceCollection services)
+ {
+ services.AddApplicationInsightsTelemetry();
+
+ services.AddControllers();
+ services.AddHttpClient(client =>
+ {
+ client.BaseAddress = new Uri(Configuration["Dependency"]);
+ });
+ }
+
+ // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
+ public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
+ {
+ if (env.IsDevelopment())
+ {
+ app.UseDeveloperExceptionPage();
+ }
+
+ app.UseRouting();
+
+ app.UseAuthorization();
+
+ app.UseEndpoints(endpoints => { endpoints.MapControllers(); });
+ }
+ }
+}
\ No newline at end of file
diff --git a/App2/appsettings.Development.json b/App2/appsettings.Development.json
new file mode 100644
index 0000000..8983e0f
--- /dev/null
+++ b/App2/appsettings.Development.json
@@ -0,0 +1,9 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft": "Warning",
+ "Microsoft.Hosting.Lifetime": "Information"
+ }
+ }
+}
diff --git a/App2/appsettings.json b/App2/appsettings.json
new file mode 100644
index 0000000..7d2720a
--- /dev/null
+++ b/App2/appsettings.json
@@ -0,0 +1,12 @@
+{
+ "Logging": {
+ "IncludeScopes": true,
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft": "Warning",
+ "Microsoft.Hosting.Lifetime": "Information"
+ }
+ },
+ "AllowedHosts": "*",
+ "Dependency": "http://localhost:5002/app3"
+}
diff --git a/App3/App3.csproj b/App3/App3.csproj
new file mode 100644
index 0000000..119cc72
--- /dev/null
+++ b/App3/App3.csproj
@@ -0,0 +1,12 @@
+
+
+
+ netcoreapp3.1
+
+
+
+
+
+
+
+
diff --git a/App3/Controllers/App3Controller.cs b/App3/Controllers/App3Controller.cs
new file mode 100644
index 0000000..693f75a
--- /dev/null
+++ b/App3/Controllers/App3Controller.cs
@@ -0,0 +1,19 @@
+using System.Text.Json;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.Extensions.Logging;
+
+namespace App3.Controllers
+{
+ [ApiController]
+ [Route("")]
+ public class App3Controller : ControllerBase
+ {
+ [HttpPost("ping")]
+ public async Task Ping([FromBody] object message, [FromServices] ILogger logger)
+ {
+ logger.LogInformation(message.ToString());
+ return Ok();
+ }
+ }
+}
\ No newline at end of file
diff --git a/App3/Program.cs b/App3/Program.cs
new file mode 100644
index 0000000..ada1140
--- /dev/null
+++ b/App3/Program.cs
@@ -0,0 +1,24 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.Hosting;
+using Microsoft.Extensions.Logging;
+
+namespace App3
+{
+ public class Program
+ {
+ public static void Main(string[] args)
+ {
+ CreateHostBuilder(args).Build().Run();
+ }
+
+ public static IHostBuilder CreateHostBuilder(string[] args) =>
+ Host.CreateDefaultBuilder(args)
+ .ConfigureLogging(l => l.AddConsole(o => o.IncludeScopes = true))
+ .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup(); });
+ }
+}
\ No newline at end of file
diff --git a/App3/Startup.cs b/App3/Startup.cs
new file mode 100644
index 0000000..f23ad44
--- /dev/null
+++ b/App3/Startup.cs
@@ -0,0 +1,48 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.AspNetCore.HttpsPolicy;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
+using Microsoft.Extensions.Logging;
+
+namespace App3
+{
+ public class Startup
+ {
+ public Startup(IConfiguration configuration)
+ {
+ Configuration = configuration;
+ }
+
+ public IConfiguration Configuration { get; }
+
+ // This method gets called by the runtime. Use this method to add services to the container.
+ public void ConfigureServices(IServiceCollection services)
+ {
+ services.AddApplicationInsightsTelemetry();
+
+ services.AddControllers();
+ }
+
+ // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
+ public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
+ {
+ if (env.IsDevelopment())
+ {
+ app.UseDeveloperExceptionPage();
+ }
+
+ app.UseRouting();
+
+ app.UseAuthorization();
+
+ app.UseEndpoints(endpoints => { endpoints.MapControllers(); });
+ }
+ }
+}
\ No newline at end of file
diff --git a/App3/appsettings.Development.json b/App3/appsettings.Development.json
new file mode 100644
index 0000000..8983e0f
--- /dev/null
+++ b/App3/appsettings.Development.json
@@ -0,0 +1,9 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft": "Warning",
+ "Microsoft.Hosting.Lifetime": "Information"
+ }
+ }
+}
diff --git a/App3/appsettings.json b/App3/appsettings.json
new file mode 100644
index 0000000..bcd5e52
--- /dev/null
+++ b/App3/appsettings.json
@@ -0,0 +1,11 @@
+{
+ "Logging": {
+ "IncludeScopes": true,
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft": "Warning",
+ "Microsoft.Hosting.Lifetime": "Information"
+ }
+ },
+ "AllowedHosts": "*"
+}
diff --git a/Origin1/App2Client.cs b/Origin1/App2Client.cs
new file mode 100644
index 0000000..6c4c7fa
--- /dev/null
+++ b/Origin1/App2Client.cs
@@ -0,0 +1,23 @@
+using System;
+using System.Net.Http;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Origin1
+{
+ public class App2Client
+ {
+ private readonly HttpClient _client;
+
+ public App2Client(HttpClient client)
+ {
+ _client = client;
+ }
+
+ public async Task SendAsync(string message)
+ {
+ var resp = await _client.PostAsync("/ping", new StringContent(message, Encoding.UTF8, "application/json"));
+ Console.WriteLine(await resp.Content.ReadAsStringAsync());
+ }
+ }
+}
\ No newline at end of file
diff --git a/Origin1/Controllers/OriginController.cs b/Origin1/Controllers/OriginController.cs
new file mode 100644
index 0000000..d0da38b
--- /dev/null
+++ b/Origin1/Controllers/OriginController.cs
@@ -0,0 +1,18 @@
+using System;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Mvc;
+
+namespace Origin1.Controllers
+{
+ [ApiController]
+ [Route("")]
+ public class OriginController : ControllerBase
+ {
+ [HttpPost("rest")]
+ public async Task Rest([FromBody] object message, [FromServices] App2Client client)
+ {
+ await client.SendAsync(message.ToString());
+ return Ok();
+ }
+ }
+}
\ No newline at end of file
diff --git a/Origin1/Origin1.csproj b/Origin1/Origin1.csproj
new file mode 100644
index 0000000..df21503
--- /dev/null
+++ b/Origin1/Origin1.csproj
@@ -0,0 +1,19 @@
+
+
+
+ netcoreapp3.1
+
+
+
+
+ true
+ PreserveNewest
+
+
+
+
+
+
+
+
+
diff --git a/Origin1/Program.cs b/Origin1/Program.cs
new file mode 100644
index 0000000..5771386
--- /dev/null
+++ b/Origin1/Program.cs
@@ -0,0 +1,24 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.Hosting;
+using Microsoft.Extensions.Logging;
+
+namespace Origin1
+{
+ public class Program
+ {
+ public static void Main(string[] args)
+ {
+ CreateHostBuilder(args).Build().Run();
+ }
+
+ public static IHostBuilder CreateHostBuilder(string[] args) =>
+ Host.CreateDefaultBuilder(args)
+ .ConfigureLogging(l => l.AddConsole(o => o.IncludeScopes = true))
+ .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup(); });
+ }
+}
\ No newline at end of file
diff --git a/Origin1/Startup.cs b/Origin1/Startup.cs
new file mode 100644
index 0000000..aa8f1cc
--- /dev/null
+++ b/Origin1/Startup.cs
@@ -0,0 +1,63 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.AspNetCore.HttpsPolicy;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
+using Microsoft.Extensions.Logging;
+using Origin1.WebSockets;
+
+namespace Origin1
+{
+ public class Startup
+ {
+ public Startup(IConfiguration configuration)
+ {
+ Configuration = configuration;
+ }
+
+ public IConfiguration Configuration { get; }
+
+ // This method gets called by the runtime. Use this method to add services to the container.
+ public void ConfigureServices(IServiceCollection services)
+ {
+ services.AddApplicationInsightsTelemetry();
+
+ services.AddControllers();
+ services.AddHttpClient(client =>
+ {
+ client.BaseAddress = new Uri(Configuration["Dependency"]);
+ });
+ services.AddSingleton();
+ }
+
+ // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
+ public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
+ {
+ if (env.IsDevelopment())
+ {
+ app.UseDeveloperExceptionPage();
+ }
+
+ app.UseRouting();
+
+ app.UseDefaultFiles()
+ .UseStaticFiles();
+
+ app.UseAuthorization();
+
+ app.UseWebSockets();
+ app.Map("/ws", b => b.UseMiddleware());
+
+ app.UseEndpoints(endpoints =>
+ {
+ endpoints.MapControllers();
+ });
+ }
+ }
+}
\ No newline at end of file
diff --git a/Origin1/WebSockets/ReproWebSocketHandler.cs b/Origin1/WebSockets/ReproWebSocketHandler.cs
new file mode 100644
index 0000000..8f5550b
--- /dev/null
+++ b/Origin1/WebSockets/ReproWebSocketHandler.cs
@@ -0,0 +1,17 @@
+using System.Threading.Tasks;
+
+namespace Origin1.WebSockets
+{
+ public class ReproWebSocketHandler
+ {
+ private readonly App2Client _client;
+
+ public ReproWebSocketHandler(App2Client client)
+ {
+ _client = client;
+ }
+
+ public async Task HandleAsync(string message)
+ => await _client.SendAsync(message);
+ }
+}
\ No newline at end of file
diff --git a/Origin1/WebSockets/ReproWebSocketMiddleware.cs b/Origin1/WebSockets/ReproWebSocketMiddleware.cs
new file mode 100644
index 0000000..bac567b
--- /dev/null
+++ b/Origin1/WebSockets/ReproWebSocketMiddleware.cs
@@ -0,0 +1,90 @@
+using System;
+using System.Buffers;
+using System.IO;
+using System.Net.WebSockets;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.Logging;
+
+namespace Origin1.WebSockets
+{
+ public class ReproWebSocketMiddleware
+ {
+ private readonly ILogger _logger;
+ private readonly ReproWebSocketHandler _handler;
+ private readonly RequestDelegate _next;
+
+ public ReproWebSocketMiddleware(
+ ILogger logger,
+ ReproWebSocketHandler handler,
+ RequestDelegate next)
+ {
+ _logger = logger;
+ _handler = handler;
+ _next = next;
+ }
+
+ public async Task Invoke(HttpContext context)
+ {
+ try
+ {
+ if (!context.WebSockets.IsWebSocketRequest)
+ {
+ await _next.Invoke(context);
+ return;
+ }
+
+ WebSocket socket = await context.WebSockets.AcceptWebSocketAsync().ConfigureAwait(false);
+ await Receive(socket, context.RequestAborted);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, ex.Message);
+ }
+ }
+
+
+ private async Task Receive(WebSocket socket, CancellationToken contextRequestAborted) //, Action handleMessage)
+ {
+ while (socket.State == WebSocketState.Open)
+ {
+ string serializedMessage = null;
+ WebSocketReceiveResult result = null;
+ byte[] bytes = ArrayPool.Shared.Rent(16 * 1024);
+ try
+ {
+ var buffer = new ArraySegment(bytes);
+
+ using MemoryStream ms = new MemoryStream();
+ do
+ {
+ try
+ {
+ result = await socket.ReceiveAsync(buffer, contextRequestAborted).ConfigureAwait(false);
+ }
+ catch (OperationCanceledException e)
+ {
+ _logger.LogWarning(e, "Request aborted");
+ return;
+ }
+
+ ms.Write(buffer.Array, buffer.Offset, result.Count);
+ } while (!result.EndOfMessage);
+
+ ms.Seek(0, SeekOrigin.Begin);
+
+ using var reader = new StreamReader(ms, Encoding.UTF8);
+ serializedMessage = await reader.ReadToEndAsync().ConfigureAwait(false);
+ }
+ finally
+ {
+ ArrayPool.Shared.Return(bytes);
+ }
+
+ await _handler.HandleAsync(serializedMessage);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Origin1/appsettings.Development.json b/Origin1/appsettings.Development.json
new file mode 100644
index 0000000..8983e0f
--- /dev/null
+++ b/Origin1/appsettings.Development.json
@@ -0,0 +1,9 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft": "Warning",
+ "Microsoft.Hosting.Lifetime": "Information"
+ }
+ }
+}
diff --git a/Origin1/appsettings.json b/Origin1/appsettings.json
new file mode 100644
index 0000000..3b8d7c7
--- /dev/null
+++ b/Origin1/appsettings.json
@@ -0,0 +1,12 @@
+{
+ "Logging": {
+ "IncludeScopes": true,
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft": "Warning",
+ "Microsoft.Hosting.Lifetime": "Information"
+ }
+ },
+ "AllowedHosts": "*",
+ "Dependency": "http://localhost:5001/app2"
+}
diff --git a/Origin1/wwwroot/index.html b/Origin1/wwwroot/index.html
new file mode 100644
index 0000000..0727833
--- /dev/null
+++ b/Origin1/wwwroot/index.html
@@ -0,0 +1,61 @@
+
+
+
+
+ Title
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file