Skip to content

Commit 71cf405

Browse files
committed
Web sockets
1 parent 162641c commit 71cf405

12 files changed

Lines changed: 284 additions & 2 deletions

File tree

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>Exe</OutputType>
5+
<TargetFramework>netcoreapp3.1</TargetFramework>
6+
<StartupObject>EmailService.LogsViewer.Program</StartupObject>
7+
</PropertyGroup>
8+
9+
<ItemGroup>
10+
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
11+
</ItemGroup>
12+
13+
<ItemGroup>
14+
<ProjectReference Include="..\Models\Models.csproj" />
15+
</ItemGroup>
16+
17+
</Project>
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
using Models.Logs;
2+
using Newtonsoft.Json;
3+
using System;
4+
using System.Net.WebSockets;
5+
using System.Text;
6+
using System.Threading;
7+
using System.Threading.Tasks;
8+
9+
namespace EmailService.LogsViewer
10+
{
11+
class Program
12+
{
13+
public const string URL = "";
14+
public const string KEY = "";
15+
static async Task Main(string[] args)
16+
{
17+
var wsClient = new ClientWebSocket();
18+
19+
wsClient.Options.SetRequestHeader("Authorization", KEY);
20+
await wsClient.ConnectAsync(new Uri(URL), CancellationToken.None);
21+
22+
var array = new byte[4096];
23+
Console.WriteLine($"Ready to listen\t\t{URL}\n");
24+
while (true)
25+
{
26+
var result = await wsClient.ReceiveAsync(array, CancellationToken.None);
27+
if (result.MessageType != WebSocketMessageType.Text)
28+
{
29+
Console.WriteLine("Not text received");
30+
continue;
31+
}
32+
var text = Encoding.UTF8.GetString(array, 0, result.Count);
33+
var logMessage = JsonConvert.DeserializeObject<LogMessage>(text);
34+
WriteReport(logMessage);
35+
}
36+
}
37+
38+
private static void WriteReport(LogMessage logMessage)
39+
{
40+
Console.ForegroundColor = logMessage.ForegroundColor;
41+
Console.BackgroundColor = ConsoleColor.Black;
42+
Console.Write($"{logMessage.LogLevel}:");
43+
Console.ResetColor();
44+
Console.ForegroundColor = ConsoleColor.DarkGray;
45+
Console.Write(' ' + logMessage.DateTimeNormalized);
46+
Console.ResetColor();
47+
Console.WriteLine($" {logMessage.Category}[{logMessage.EventId.Id}]");
48+
Console.WriteLine('\t' + logMessage.Message.Replace("\n", "\n\t"));
49+
}
50+
}
51+
}

src/EmailService.sln

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EmailService", "EmailServic
77
EndProject
88
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Models", "Models\Models.csproj", "{F3C853FD-5509-4803-AEF4-820BF87F640E}"
99
EndProject
10-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Client", "Client\Client.csproj", "{090F3095-5A51-436B-B539-46041D63E1A8}"
10+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Client", "Client\Client.csproj", "{090F3095-5A51-436B-B539-46041D63E1A8}"
1111
EndProject
12-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PublicAPI", "PublicAPI\PublicAPI.csproj", "{D092C687-8707-4D0C-AD8A-F4F1FB1718F2}"
12+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PublicAPI", "PublicAPI\PublicAPI.csproj", "{D092C687-8707-4D0C-AD8A-F4F1FB1718F2}"
13+
EndProject
14+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EmailService.LogsViewer", "EmailService.LogsViewer\EmailService.LogsViewer.csproj", "{5AD09402-E028-4541-B765-9253BE73F3C0}"
1315
EndProject
1416
Global
1517
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -33,6 +35,10 @@ Global
3335
{D092C687-8707-4D0C-AD8A-F4F1FB1718F2}.Debug|Any CPU.Build.0 = Debug|Any CPU
3436
{D092C687-8707-4D0C-AD8A-F4F1FB1718F2}.Release|Any CPU.ActiveCfg = Release|Any CPU
3537
{D092C687-8707-4D0C-AD8A-F4F1FB1718F2}.Release|Any CPU.Build.0 = Release|Any CPU
38+
{5AD09402-E028-4541-B765-9253BE73F3C0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
39+
{5AD09402-E028-4541-B765-9253BE73F3C0}.Debug|Any CPU.Build.0 = Debug|Any CPU
40+
{5AD09402-E028-4541-B765-9253BE73F3C0}.Release|Any CPU.ActiveCfg = Release|Any CPU
41+
{5AD09402-E028-4541-B765-9253BE73F3C0}.Release|Any CPU.Build.0 = Release|Any CPU
3642
EndGlobalSection
3743
GlobalSection(SolutionProperties) = preSolution
3844
HideSolutionNode = FALSE
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
using EmailService.Services.Logs;
2+
using Microsoft.AspNetCore.Builder;
3+
using Microsoft.AspNetCore.Http;
4+
using System.Threading.Tasks;
5+
6+
namespace EmailService.Middlewares
7+
{
8+
public class LogsMiddleware
9+
{
10+
private readonly RequestDelegate next;
11+
private readonly ILogsWebSocketHandler logsHandler;
12+
private readonly string path;
13+
private readonly string secret;
14+
15+
public LogsMiddleware(RequestDelegate next, ILogsWebSocketHandler logsHandler,
16+
string path, string secret)
17+
{
18+
this.next = next;
19+
this.logsHandler = logsHandler;
20+
this.path = path;
21+
this.secret = secret;
22+
}
23+
24+
public async Task Invoke(HttpContext httpContext)
25+
{
26+
if (httpContext.WebSockets.IsWebSocketRequest)
27+
{
28+
if (httpContext.Request.Path.StartsWithSegments(path) && httpContext.Request.Headers.TryGetValue("Authorization", out var authValue) && authValue == secret)
29+
{
30+
await logsHandler.HandleWebSocketAsync(await httpContext.WebSockets.AcceptWebSocketAsync());
31+
}
32+
}
33+
await next(httpContext);
34+
}
35+
}
36+
37+
// Extension method used to add the LogsMiddleware to the HTTP request pipeline.
38+
public static class LogsMiddlewareExtensions
39+
{
40+
public static IApplicationBuilder UseLogsMiddleware(this IApplicationBuilder builder, string path, string secret)
41+
{
42+
return builder.UseMiddleware<LogsMiddleware>(path, secret);
43+
}
44+
}
45+
}

src/EmailService/Program.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1+
using EmailService.Services.Logs;
12
using Microsoft.AspNetCore.Hosting;
23
using Microsoft.Extensions.Configuration;
4+
using Microsoft.Extensions.Logging;
35
using Microsoft.Extensions.Hosting;
46

57
namespace EmailService
@@ -13,9 +15,11 @@ public static void Main(string[] args)
1315

1416
public static IHostBuilder CreateHostBuilder(string[] args) =>
1517
Host.CreateDefaultBuilder(args)
18+
//.ConfigureLogging(cfg => cfg.AddProvider(new WebSocketLoggerProvider()))
1619
.ConfigureWebHostDefaults(webBuilder =>
1720
{
1821
webBuilder.ConfigureAppConfiguration(cfg => cfg.AddJsonFile("appsettings.Secret.json"));
22+
webBuilder.ConfigureLogging(logging => logging.AddProvider(new WebSocketLoggerProvider()));
1923
webBuilder.UseStartup<Startup>();
2024
});
2125
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
using System.Net.WebSockets;
2+
using System.Threading.Tasks;
3+
4+
namespace EmailService.Services.Logs
5+
{
6+
public interface ILogsWebSocketHandler
7+
{
8+
Task HandleWebSocketAsync(WebSocket webSocket);
9+
}
10+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
using Models.Logs;
2+
using Newtonsoft.Json;
3+
using System;
4+
using System.Collections.Generic;
5+
using System.Net.WebSockets;
6+
using System.Text;
7+
using System.Threading;
8+
using System.Threading.Tasks;
9+
10+
namespace EmailService.Services.Logs
11+
{
12+
public class LogsWebSocketHandler : ILogsWebSocketHandler
13+
{
14+
private readonly HashSet<WebSocket> webSockets = new HashSet<WebSocket>();
15+
16+
private static LogsWebSocketHandler instance;
17+
public static LogsWebSocketHandler Instance => instance ?? (instance = new LogsWebSocketHandler());
18+
19+
private LogsWebSocketHandler() { }
20+
21+
public async Task HandleWebSocketAsync(WebSocket webSocket)
22+
{
23+
webSockets.Add(webSocket);
24+
while (webSocket.State == WebSocketState.Open)
25+
{
26+
await Task.Delay(10000);
27+
}
28+
webSockets.Remove(webSocket);
29+
}
30+
31+
public void SendLogMessage(LogMessage logMessage)
32+
{
33+
var strMessage = JsonConvert.SerializeObject(logMessage);
34+
foreach (var socket in webSockets)
35+
{
36+
try
37+
{
38+
socket.SendAsync(Encoding.UTF8.GetBytes(strMessage), WebSocketMessageType.Text, true, CancellationToken.None);
39+
}
40+
catch (Exception) { }
41+
}
42+
}
43+
}
44+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
using Microsoft.Extensions.Logging;
2+
using Models.Logs;
3+
using System;
4+
5+
namespace EmailService.Services.Logs
6+
{
7+
public class WebSocketLogger : ILogger
8+
{
9+
private readonly string category;
10+
11+
public WebSocketLogger(string category)
12+
{
13+
this.category = category;
14+
}
15+
public IDisposable BeginScope<TState>(TState state)
16+
{
17+
return null;
18+
}
19+
20+
public bool IsEnabled(LogLevel logLevel)
21+
{
22+
return true;
23+
}
24+
25+
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
26+
{
27+
LogsWebSocketHandler.Instance.SendLogMessage(new LogMessage
28+
{
29+
LogLevel = logLevel,
30+
EventId = eventId,
31+
Category = category,
32+
Message = formatter(state, exception),
33+
DateTime = DateTime.UtcNow
34+
});
35+
}
36+
}
37+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
using Microsoft.Extensions.Logging;
2+
3+
namespace EmailService.Services.Logs
4+
{
5+
public class WebSocketLoggerProvider : ILoggerProvider
6+
{
7+
public ILogger CreateLogger(string categoryName)
8+
{
9+
return new WebSocketLogger(categoryName);
10+
}
11+
12+
public void Dispose()
13+
{
14+
}
15+
}
16+
}

src/EmailService/Startup.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using EmailService.Middlewares;
2+
using EmailService.Services.Logs;
23
using Microsoft.AspNetCore.Builder;
34
using Microsoft.AspNetCore.Hosting;
45
using Microsoft.Extensions.Configuration;
@@ -28,6 +29,8 @@ public void ConfigureServices(IServiceCollection services)
2829
services.AddEmailSender(Configuration
2930
.GetSection(nameof(EmailSenderOptions))
3031
.Get<EmailSenderOptions>());
32+
33+
services.AddSingleton<ILogsWebSocketHandler>(LogsWebSocketHandler.Instance);
3134
}
3235

3336
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
@@ -43,6 +46,13 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
4346
.Get<HeaderAuthorizationOptions>()
4447
.Key);
4548

49+
app.UseWebSockets();
50+
51+
var emailSender = Configuration
52+
.GetSection(nameof(EmailSenderOptions))
53+
.Get<EmailSenderOptions>();
54+
app.UseLogsMiddleware("/api/logsStream", emailSender.Key);
55+
4656
app.UseRouting();
4757

4858
app.UseAuthorization();

0 commit comments

Comments
 (0)