Use QueryParser for redirect and expose method for new clients
This commit is contained in:
@@ -16,7 +16,7 @@ public class Processing
|
|||||||
Plugin = plugin;
|
Plugin = plugin;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string ReadChannelName(Chunk[] channelName)
|
internal string ReadChannelName(Chunk[] channelName)
|
||||||
{
|
{
|
||||||
return string.Join("", channelName.Select(chunk => ProcessChunk(chunk, noColor: true)));
|
return string.Join("", channelName.Select(chunk => ProcessChunk(chunk, noColor: true)));
|
||||||
}
|
}
|
||||||
@@ -44,6 +44,17 @@ public class Processing
|
|||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal async Task PrepareNewClient(SSEConnection sse)
|
||||||
|
{
|
||||||
|
var messages = await WebserverUtil.FrameworkWrapper(ReadMessageList);
|
||||||
|
var channels = await Plugin.Framework.RunOnTick(Plugin.ChatLogWindow.GetAvailableChannels);
|
||||||
|
var channelName = await Plugin.Framework.RunOnTick(() => ReadChannelName(Plugin.ChatLogWindow.PreviousChannel));
|
||||||
|
|
||||||
|
sse.OutboundQueue.Enqueue(new NewMessageEvent(new Messages(messages)));
|
||||||
|
sse.OutboundQueue.Enqueue(new SwitchChannelEvent(new SwitchChannel(channelName)));
|
||||||
|
sse.OutboundQueue.Enqueue(new ChannelListEvent(new ChannelList(channels.ToDictionary(pair => pair.Key, pair => (uint)pair.Value))));
|
||||||
|
}
|
||||||
|
|
||||||
private string ProcessChunk(Chunk chunk, bool noColor = false)
|
private string ProcessChunk(Chunk chunk, bool noColor = false)
|
||||||
{
|
{
|
||||||
if (chunk is IconChunk { } icon)
|
if (chunk is IconChunk { } icon)
|
||||||
|
|||||||
@@ -125,14 +125,14 @@ public class RouteController
|
|||||||
{
|
{
|
||||||
var currentTick = Environment.TickCount64;
|
var currentTick = Environment.TickCount64;
|
||||||
if (RateLimit.TryGetValue(ctx.Request.Source.IpAddress, out var timestamp) && timestamp > currentTick)
|
if (RateLimit.TryGetValue(ctx.Request.Source.IpAddress, out var timestamp) && timestamp > currentTick)
|
||||||
return await Redirect(ctx, "/", "message", "Rate limit active.");
|
return await Redirect(ctx, "/", ("message", "Rate limit active."), ("retry", "12345"));
|
||||||
|
|
||||||
// The next request will be rate limited for 10s
|
// The next request will be rate limited for 10s
|
||||||
RateLimit[ctx.Request.Source.IpAddress] = currentTick + 10_000;
|
RateLimit[ctx.Request.Source.IpAddress] = currentTick + 10_000;
|
||||||
|
|
||||||
var authcode = HttpUtility.ParseQueryString(ctx.Request.DataAsString ?? "").Get("authcode");
|
var authcode = HttpUtility.ParseQueryString(ctx.Request.DataAsString ?? "").Get("authcode");
|
||||||
if (authcode == null || authcode != Plugin.Config.WebinterfacePassword)
|
if (authcode == null || authcode != Plugin.Config.WebinterfacePassword)
|
||||||
return await Redirect(ctx, "/", "message", "Authentication failed.");
|
return await Redirect(ctx, "/", ("message", "Authentication failed."));
|
||||||
|
|
||||||
var token = WebinterfaceUtil.GenerateSimpleToken();
|
var token = WebinterfaceUtil.GenerateSimpleToken();
|
||||||
SessionTokens.TryAdd(token, true);
|
SessionTokens.TryAdd(token, true);
|
||||||
@@ -208,16 +208,9 @@ public class RouteController
|
|||||||
Plugin.Log.Information($"Client connected: {ctx.Guid}");
|
Plugin.Log.Information($"Client connected: {ctx.Guid}");
|
||||||
|
|
||||||
var sse = new SSEConnection(Core.TokenSource.Token);
|
var sse = new SSEConnection(Core.TokenSource.Token);
|
||||||
|
await Core.Processing.PrepareNewClient(sse);
|
||||||
Core.EventConnections.Add(sse);
|
Core.EventConnections.Add(sse);
|
||||||
|
|
||||||
// TODO Check if reconnect or new connection
|
|
||||||
var messages = await WebserverUtil.FrameworkWrapper(Core.Processing.ReadMessageList);
|
|
||||||
var channels = await Plugin.Framework.RunOnTick(Plugin.ChatLogWindow.GetAvailableChannels);
|
|
||||||
var channelName = await Plugin.Framework.RunOnTick(() => Core.Processing.ReadChannelName(Plugin.ChatLogWindow.PreviousChannel));
|
|
||||||
sse.OutboundQueue.Enqueue(new NewMessageEvent(new Messages(messages)));
|
|
||||||
sse.OutboundQueue.Enqueue(new SwitchChannelEvent(new SwitchChannel(channelName)));
|
|
||||||
sse.OutboundQueue.Enqueue(new ChannelListEvent(new ChannelList(channels.ToDictionary(pair => pair.Key, pair => (uint)pair.Value))));
|
|
||||||
|
|
||||||
await sse.HandleEventLoop(ctx);
|
await sse.HandleEventLoop(ctx);
|
||||||
|
|
||||||
// It should always be done after return
|
// It should always be done after return
|
||||||
@@ -232,13 +225,13 @@ public class RouteController
|
|||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region RedirectHelper
|
#region RedirectHelper
|
||||||
public static async Task<bool> Redirect(HttpContextBase ctx, string location, params string[] parameter)
|
public static async Task<bool> Redirect(HttpContextBase ctx, string location, params (string, string)[] parameter)
|
||||||
{
|
{
|
||||||
var query = "?";
|
var query = HttpUtility.ParseQueryString(string.Empty);
|
||||||
foreach (var (content, index) in parameter.WithIndex())
|
foreach (var (key, value) in parameter)
|
||||||
query += index % 2 == 0 ? $"{content}=" : Uri.EscapeDataString(content);
|
query.Add(key, value);
|
||||||
|
|
||||||
ctx.Response.Headers.Add("Location", $"{location}{query}");
|
ctx.Response.Headers.Add("Location", $"{location}?{query}");
|
||||||
ctx.Response.StatusCode = 302;
|
ctx.Response.StatusCode = 302;
|
||||||
return await ctx.Response.Send();
|
return await ctx.Response.Send();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
using System.Text;
|
using ChatTwo.Http.MessageProtocol;
|
||||||
using ChatTwo.Http.MessageProtocol;
|
|
||||||
using WatsonWebserver.Core;
|
using WatsonWebserver.Core;
|
||||||
|
|
||||||
namespace ChatTwo.Http;
|
namespace ChatTwo.Http;
|
||||||
|
|
||||||
public class SSEConnection
|
public class SSEConnection
|
||||||
{
|
{
|
||||||
private long Index;
|
|
||||||
private bool Stopping;
|
private bool Stopping;
|
||||||
private readonly CancellationToken Token;
|
private readonly CancellationToken Token;
|
||||||
|
|
||||||
@@ -42,8 +40,6 @@ public class SSEConnection
|
|||||||
Plugin.Log.Information($"Client disconnected: {ctx.Guid}");
|
Plugin.Log.Information($"Client disconnected: {ctx.Guid}");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Index++;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (TaskCanceledException)
|
catch (TaskCanceledException)
|
||||||
|
|||||||
@@ -124,13 +124,13 @@ public class ServerCore : IAsyncDisposable
|
|||||||
{
|
{
|
||||||
if (RouteController.SessionTokens.IsEmpty)
|
if (RouteController.SessionTokens.IsEmpty)
|
||||||
{
|
{
|
||||||
await RouteController.Redirect(ctx, "/", "message", "Invalid session token.");
|
await RouteController.Redirect(ctx, "/", ("message", "Invalid session token."));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var cookies = WebserverUtil.GetCookieData(ctx.Request.Headers.Get("Cookie") ?? "");
|
var cookies = WebserverUtil.GetCookieData(ctx.Request.Headers.Get("Cookie") ?? "");
|
||||||
if (!cookies.TryGetValue("ChatTwo-token", out var token) || !RouteController.SessionTokens.ContainsKey(token))
|
if (!cookies.TryGetValue("ChatTwo-token", out var token) || !RouteController.SessionTokens.ContainsKey(token))
|
||||||
await RouteController.Redirect(ctx, "/", "message", "Invalid session token.");
|
await RouteController.Redirect(ctx, "/", ("message", "Invalid session token."));
|
||||||
|
|
||||||
// Do nothing to let auth pass
|
// Do nothing to let auth pass
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user