mirror of
https://github.com/nsnail/Ocelot.git
synced 2025-04-22 14:02:49 +08:00
Merged into master
This commit is contained in:
commit
6817943b0a
@ -1,13 +1,24 @@
|
||||
using Ocelot.Configuration.File;
|
||||
using Butterfly.Client.Tracing;
|
||||
using Ocelot.Configuration.File;
|
||||
using Ocelot.Requester;
|
||||
|
||||
namespace Ocelot.Configuration.Creator
|
||||
{
|
||||
public class HttpHandlerOptionsCreator : IHttpHandlerOptionsCreator
|
||||
{
|
||||
private IServiceTracer _tracer;
|
||||
|
||||
public HttpHandlerOptionsCreator(IServiceTracer tracer)
|
||||
{
|
||||
_tracer = tracer;
|
||||
}
|
||||
|
||||
public HttpHandlerOptions Create(FileReRoute fileReRoute)
|
||||
{
|
||||
var useTracing = _tracer.GetType() != typeof(FakeServiceTracer) ? fileReRoute.HttpHandlerOptions.UseTracing : false;
|
||||
|
||||
return new HttpHandlerOptions(fileReRoute.HttpHandlerOptions.AllowAutoRedirect,
|
||||
fileReRoute.HttpHandlerOptions.UseCookieContainer, fileReRoute.HttpHandlerOptions.UseTracing);
|
||||
fileReRoute.HttpHandlerOptions.UseCookieContainer, useTracing);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,17 @@
|
||||
using Microsoft.Extensions.Primitives;
|
||||
using System.Linq;
|
||||
|
||||
namespace Ocelot.Infrastructure.Extensions
|
||||
{
|
||||
internal static class StringValueExtensions
|
||||
{
|
||||
public static string GetValue(this StringValues stringValues)
|
||||
{
|
||||
if (stringValues.Count == 1)
|
||||
{
|
||||
return stringValues;
|
||||
}
|
||||
return stringValues.ToArray().LastOrDefault();
|
||||
}
|
||||
}
|
||||
}
|
22
src/Ocelot/Logging/IOcelotLogger.cs
Normal file
22
src/Ocelot/Logging/IOcelotLogger.cs
Normal file
@ -0,0 +1,22 @@
|
||||
using System;
|
||||
|
||||
namespace Ocelot.Logging
|
||||
{
|
||||
/// <summary>
|
||||
/// Thin wrapper around the DotNet core logging framework, used to allow the scopedDataRepository to be injected giving access to the Ocelot RequestId
|
||||
/// </summary>
|
||||
public interface IOcelotLogger
|
||||
{
|
||||
void LogTrace(string message, params object[] args);
|
||||
void LogDebug(string message, params object[] args);
|
||||
void LogInformation(string message, params object[] args);
|
||||
void LogError(string message, Exception exception);
|
||||
void LogError(string message, params object[] args);
|
||||
void LogCritical(string message, Exception exception);
|
||||
|
||||
/// <summary>
|
||||
/// The name of the type the logger has been built for.
|
||||
/// </summary>
|
||||
string Name { get; }
|
||||
}
|
||||
}
|
@ -1,27 +1,7 @@
|
||||
using System;
|
||||
|
||||
namespace Ocelot.Logging
|
||||
namespace Ocelot.Logging
|
||||
{
|
||||
public interface IOcelotLoggerFactory
|
||||
{
|
||||
IOcelotLogger CreateLogger<T>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Thin wrapper around the DotNet core logging framework, used to allow the scopedDataRepository to be injected giving access to the Ocelot RequestId
|
||||
/// </summary>
|
||||
public interface IOcelotLogger
|
||||
{
|
||||
void LogTrace(string message, params object[] args);
|
||||
void LogDebug(string message, params object[] args);
|
||||
void LogInformation(string message, params object[] args);
|
||||
void LogError(string message, Exception exception);
|
||||
void LogError(string message, params object[] args);
|
||||
void LogCritical(string message, Exception exception);
|
||||
|
||||
/// <summary>
|
||||
/// The name of the type the logger has been built for.
|
||||
/// </summary>
|
||||
string Name { get; }
|
||||
}
|
||||
}
|
||||
|
@ -3,18 +3,48 @@ using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.DiagnosticAdapter;
|
||||
using Butterfly.Client.AspNetCore;
|
||||
using Butterfly.OpenTracing;
|
||||
using Ocelot.Middleware;
|
||||
using Butterfly.Client.Tracing;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using Ocelot.Infrastructure.Extensions;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Ocelot.Requester;
|
||||
|
||||
namespace Ocelot.Logging
|
||||
{
|
||||
public class OcelotDiagnosticListener
|
||||
{
|
||||
private IServiceTracer _tracer;
|
||||
private IOcelotLogger _logger;
|
||||
|
||||
public OcelotDiagnosticListener(IOcelotLoggerFactory factory)
|
||||
public OcelotDiagnosticListener(IOcelotLoggerFactory factory, IServiceTracer tracer)
|
||||
{
|
||||
_tracer = tracer;
|
||||
_logger = factory.CreateLogger<OcelotDiagnosticListener>();
|
||||
}
|
||||
|
||||
[DiagnosticName("Ocelot.MiddlewareException")]
|
||||
public virtual void OcelotMiddlewareException(Exception exception, DownstreamContext context, string name)
|
||||
{
|
||||
_logger.LogTrace($"Ocelot.MiddlewareException: {name}; {exception.Message}");
|
||||
Event(context.HttpContext, $"Ocelot.MiddlewareStarted: {name}; {context.HttpContext.Request.Path}");
|
||||
}
|
||||
|
||||
[DiagnosticName("Ocelot.MiddlewareStarted")]
|
||||
public virtual void OcelotMiddlewareStarted(DownstreamContext context, string name)
|
||||
{
|
||||
_logger.LogTrace($"Ocelot.MiddlewareStarted: {name}; {context.HttpContext.Request.Path}");
|
||||
Event(context.HttpContext, $"Ocelot.MiddlewareStarted: {name}; {context.HttpContext.Request.Path}");
|
||||
}
|
||||
|
||||
[DiagnosticName("Ocelot.MiddlewareFinished")]
|
||||
public virtual void OcelotMiddlewareFinished(DownstreamContext context, string name)
|
||||
{
|
||||
_logger.LogTrace($"OcelotMiddlewareFinished: {name}; {context.HttpContext.Request.Path}");
|
||||
Event(context.HttpContext, $"OcelotMiddlewareFinished: {name}; {context.HttpContext.Request.Path}");
|
||||
}
|
||||
|
||||
[DiagnosticName("Microsoft.AspNetCore.MiddlewareAnalysis.MiddlewareStarting")]
|
||||
public virtual void OnMiddlewareStarting(HttpContext httpContext, string name)
|
||||
{
|
||||
@ -37,7 +67,27 @@ namespace Ocelot.Logging
|
||||
|
||||
private void Event(HttpContext httpContext, string @event)
|
||||
{
|
||||
// Hack - if the user isnt using tracing the code gets here and will blow up on
|
||||
// _tracer.Tracer.TryExtract. We already use the fake tracer for another scenario
|
||||
// so sticking it here as well..I guess we need a factory for this but no idea
|
||||
// how to hook that into the diagnostic framework at the moment.
|
||||
if(_tracer.GetType() == typeof(FakeServiceTracer))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var span = httpContext.GetSpan();
|
||||
if(span == null)
|
||||
{
|
||||
var spanBuilder = new SpanBuilder($"server {httpContext.Request.Method} {httpContext.Request.Path}");
|
||||
if (_tracer.Tracer.TryExtract(out var spanContext, httpContext.Request.Headers, (c, k) => c[k].GetValue(),
|
||||
c => c.Select(x => new KeyValuePair<string, string>(x.Key, x.Value.GetValue())).GetEnumerator()))
|
||||
{
|
||||
spanBuilder.AsChildOf(spanContext);
|
||||
};
|
||||
span = _tracer.Start(spanBuilder);
|
||||
httpContext.SetSpan(span);
|
||||
}
|
||||
span?.Log(LogField.CreateNew().Event(@event));
|
||||
}
|
||||
}
|
||||
|
@ -65,6 +65,8 @@
|
||||
rest of asp.net..
|
||||
*/
|
||||
|
||||
builder.Properties["analysis.NextMiddlewareName"] = "TransitionToOcelotMiddleware";
|
||||
|
||||
builder.Use(async (context, task) =>
|
||||
{
|
||||
var downstreamContext = new DownstreamContext(context);
|
||||
|
@ -3,6 +3,7 @@
|
||||
// Removed code and changed RequestDelete to OcelotRequestDelete, HttpContext to DownstreamContext, removed some exception handling messages
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
@ -75,7 +76,28 @@ namespace Ocelot.Middleware.Pipeline
|
||||
var instance = ActivatorUtilities.CreateInstance(app.ApplicationServices, middleware, ctorArgs);
|
||||
if (parameters.Length == 1)
|
||||
{
|
||||
return (OcelotRequestDelegate)methodinfo.CreateDelegate(typeof(OcelotRequestDelegate), instance);
|
||||
var ocelotDelegate = (OcelotRequestDelegate)methodinfo.CreateDelegate(typeof(OcelotRequestDelegate), instance);
|
||||
var diagnosticListener = (DiagnosticListener)app.ApplicationServices.GetService(typeof(DiagnosticListener));
|
||||
var middlewareName = ocelotDelegate.Target.GetType().Name;
|
||||
|
||||
OcelotRequestDelegate wrapped = context => {
|
||||
try
|
||||
{
|
||||
Write(diagnosticListener, "Ocelot.MiddlewareStarted", middlewareName, context);
|
||||
return ocelotDelegate(context);
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
Write(diagnosticListener, "Ocelot.MiddlewareException", middlewareName, context);
|
||||
throw ex;
|
||||
}
|
||||
finally
|
||||
{
|
||||
Write(diagnosticListener, "Ocelot.MiddlewareFinished", middlewareName, context);
|
||||
}
|
||||
};
|
||||
|
||||
return wrapped;
|
||||
}
|
||||
|
||||
var factory = Compile<object>(methodinfo, parameters);
|
||||
@ -93,6 +115,14 @@ namespace Ocelot.Middleware.Pipeline
|
||||
});
|
||||
}
|
||||
|
||||
private static void Write(DiagnosticListener diagnosticListener, string message, string middlewareName, DownstreamContext context)
|
||||
{
|
||||
if(diagnosticListener != null)
|
||||
{
|
||||
diagnosticListener.Write(message, new { name = middlewareName, context = context });
|
||||
}
|
||||
}
|
||||
|
||||
public static IOcelotPipelineBuilder MapWhen(this IOcelotPipelineBuilder app, Predicate predicate, Action<IOcelotPipelineBuilder> configuration)
|
||||
{
|
||||
if (app == null)
|
||||
|
@ -2,6 +2,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using Butterfly.Client.Tracing;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Ocelot.Configuration;
|
||||
using Ocelot.Logging;
|
||||
|
17
src/Ocelot/Requester/FakeServiceTracer.cs
Normal file
17
src/Ocelot/Requester/FakeServiceTracer.cs
Normal file
@ -0,0 +1,17 @@
|
||||
using Butterfly.Client.Tracing;
|
||||
using Butterfly.OpenTracing;
|
||||
|
||||
namespace Ocelot.Requester
|
||||
{
|
||||
public class FakeServiceTracer : IServiceTracer
|
||||
{
|
||||
public ITracer Tracer { get; }
|
||||
public string ServiceName { get; }
|
||||
public string Environment { get; }
|
||||
public string Identity { get; }
|
||||
public ISpan Start(ISpanBuilder spanBuilder)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +1,4 @@
|
||||
using Butterfly.Client.Tracing;
|
||||
using Butterfly.OpenTracing;
|
||||
using Ocelot.Infrastructure.RequestData;
|
||||
|
||||
namespace Ocelot.Requester
|
||||
@ -22,16 +21,4 @@ namespace Ocelot.Requester
|
||||
return new OcelotHttpTracingHandler(_tracer, _repo);
|
||||
}
|
||||
}
|
||||
|
||||
public class FakeServiceTracer : IServiceTracer
|
||||
{
|
||||
public ITracer Tracer { get; }
|
||||
public string ServiceName { get; }
|
||||
public string Environment { get; }
|
||||
public string Identity { get; }
|
||||
public ISpan Start(ISpanBuilder spanBuilder)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
"Logging": {
|
||||
"IncludeScopes": false,
|
||||
"LogLevel": {
|
||||
"Default": "Debug",
|
||||
"Default": "Error",
|
||||
"System": "Error",
|
||||
"Microsoft": "Error"
|
||||
}
|
||||
|
@ -99,7 +99,7 @@
|
||||
"HttpHandlerOptions": {
|
||||
"AllowAutoRedirect": true,
|
||||
"UseCookieContainer": true,
|
||||
"UseTracing": false
|
||||
"UseTracing": true
|
||||
},
|
||||
"QoSOptions": {
|
||||
"ExceptionsAllowedBeforeBreaking": 3,
|
||||
|
@ -1,6 +1,10 @@
|
||||
using Ocelot.Configuration;
|
||||
using System;
|
||||
using Butterfly.Client.Tracing;
|
||||
using Butterfly.OpenTracing;
|
||||
using Ocelot.Configuration;
|
||||
using Ocelot.Configuration.Creator;
|
||||
using Ocelot.Configuration.File;
|
||||
using Ocelot.Requester;
|
||||
using Shouldly;
|
||||
using TestStack.BDDfy;
|
||||
using Xunit;
|
||||
@ -9,13 +13,54 @@ namespace Ocelot.UnitTests.Configuration
|
||||
{
|
||||
public class HttpHandlerOptionsCreatorTests
|
||||
{
|
||||
private readonly IHttpHandlerOptionsCreator _httpHandlerOptionsCreator;
|
||||
private IHttpHandlerOptionsCreator _httpHandlerOptionsCreator;
|
||||
private FileReRoute _fileReRoute;
|
||||
private HttpHandlerOptions _httpHandlerOptions;
|
||||
private IServiceTracer _serviceTracer;
|
||||
|
||||
public HttpHandlerOptionsCreatorTests()
|
||||
{
|
||||
_httpHandlerOptionsCreator = new HttpHandlerOptionsCreator();
|
||||
_serviceTracer = new FakeServiceTracer();
|
||||
_httpHandlerOptionsCreator = new HttpHandlerOptionsCreator(_serviceTracer);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_not_use_tracing_if_fake_tracer_registered()
|
||||
{
|
||||
var fileReRoute = new FileReRoute
|
||||
{
|
||||
HttpHandlerOptions = new FileHttpHandlerOptions
|
||||
{
|
||||
UseTracing = true
|
||||
}
|
||||
};
|
||||
|
||||
var expectedOptions = new HttpHandlerOptions(false, false, false);
|
||||
|
||||
this.Given(x => GivenTheFollowing(fileReRoute))
|
||||
.When(x => WhenICreateHttpHandlerOptions())
|
||||
.Then(x => ThenTheFollowingOptionsReturned(expectedOptions))
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_use_tracing_if_real_tracer_registered()
|
||||
{
|
||||
var fileReRoute = new FileReRoute
|
||||
{
|
||||
HttpHandlerOptions = new FileHttpHandlerOptions
|
||||
{
|
||||
UseTracing = true
|
||||
}
|
||||
};
|
||||
|
||||
var expectedOptions = new HttpHandlerOptions(false, false, true);
|
||||
|
||||
this.Given(x => GivenTheFollowing(fileReRoute))
|
||||
.And(x => GivenARealTracer())
|
||||
.When(x => WhenICreateHttpHandlerOptions())
|
||||
.Then(x => ThenTheFollowingOptionsReturned(expectedOptions))
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@ -68,5 +113,27 @@ namespace Ocelot.UnitTests.Configuration
|
||||
_httpHandlerOptions.UseCookieContainer.ShouldBe(expected.UseCookieContainer);
|
||||
_httpHandlerOptions.UseTracing.ShouldBe(expected.UseTracing);
|
||||
}
|
||||
|
||||
private void GivenARealTracer()
|
||||
{
|
||||
var tracer = new RealTracer();
|
||||
_httpHandlerOptionsCreator = new HttpHandlerOptionsCreator(tracer);
|
||||
}
|
||||
|
||||
class RealTracer : IServiceTracer
|
||||
{
|
||||
public ITracer Tracer => throw new NotImplementedException();
|
||||
|
||||
public string ServiceName => throw new NotImplementedException();
|
||||
|
||||
public string Environment => throw new NotImplementedException();
|
||||
|
||||
public string Identity => throw new NotImplementedException();
|
||||
|
||||
public ISpan Start(ISpanBuilder spanBuilder)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,239 +0,0 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user