Use QueryParser for redirect and expose method for new clients
This commit is contained in:
@@ -16,7 +16,7 @@ public class Processing
|
||||
Plugin = plugin;
|
||||
}
|
||||
|
||||
public string ReadChannelName(Chunk[] channelName)
|
||||
internal string ReadChannelName(Chunk[] channelName)
|
||||
{
|
||||
return string.Join("", channelName.Select(chunk => ProcessChunk(chunk, noColor: true)));
|
||||
}
|
||||
@@ -44,6 +44,17 @@ public class Processing
|
||||
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)
|
||||
{
|
||||
if (chunk is IconChunk { } icon)
|
||||
|
||||
@@ -125,14 +125,14 @@ public class RouteController
|
||||
{
|
||||
var currentTick = Environment.TickCount64;
|
||||
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
|
||||
RateLimit[ctx.Request.Source.IpAddress] = currentTick + 10_000;
|
||||
|
||||
var authcode = HttpUtility.ParseQueryString(ctx.Request.DataAsString ?? "").Get("authcode");
|
||||
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();
|
||||
SessionTokens.TryAdd(token, true);
|
||||
@@ -208,16 +208,9 @@ public class RouteController
|
||||
Plugin.Log.Information($"Client connected: {ctx.Guid}");
|
||||
|
||||
var sse = new SSEConnection(Core.TokenSource.Token);
|
||||
await Core.Processing.PrepareNewClient(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);
|
||||
|
||||
// It should always be done after return
|
||||
@@ -232,13 +225,13 @@ public class RouteController
|
||||
#endregion
|
||||
|
||||
#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 = "?";
|
||||
foreach (var (content, index) in parameter.WithIndex())
|
||||
query += index % 2 == 0 ? $"{content}=" : Uri.EscapeDataString(content);
|
||||
var query = HttpUtility.ParseQueryString(string.Empty);
|
||||
foreach (var (key, value) in parameter)
|
||||
query.Add(key, value);
|
||||
|
||||
ctx.Response.Headers.Add("Location", $"{location}{query}");
|
||||
ctx.Response.Headers.Add("Location", $"{location}?{query}");
|
||||
ctx.Response.StatusCode = 302;
|
||||
return await ctx.Response.Send();
|
||||
}
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
using System.Text;
|
||||
using ChatTwo.Http.MessageProtocol;
|
||||
using ChatTwo.Http.MessageProtocol;
|
||||
using WatsonWebserver.Core;
|
||||
|
||||
namespace ChatTwo.Http;
|
||||
|
||||
public class SSEConnection
|
||||
{
|
||||
private long Index;
|
||||
private bool Stopping;
|
||||
private readonly CancellationToken Token;
|
||||
|
||||
@@ -42,8 +40,6 @@ public class SSEConnection
|
||||
Plugin.Log.Information($"Client disconnected: {ctx.Guid}");
|
||||
return;
|
||||
}
|
||||
|
||||
Index++;
|
||||
}
|
||||
}
|
||||
catch (TaskCanceledException)
|
||||
|
||||
@@ -124,13 +124,13 @@ public class ServerCore : IAsyncDisposable
|
||||
{
|
||||
if (RouteController.SessionTokens.IsEmpty)
|
||||
{
|
||||
await RouteController.Redirect(ctx, "/", "message", "Invalid session token.");
|
||||
await RouteController.Redirect(ctx, "/", ("message", "Invalid session token."));
|
||||
return;
|
||||
}
|
||||
|
||||
var cookies = WebserverUtil.GetCookieData(ctx.Request.Headers.Get("Cookie") ?? "");
|
||||
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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user