mirror of
https://github.com/JustArchiNET/ArchiSteamFarm.git
synced 2026-01-09 21:24:34 +00:00
Remove EmbedIO dependency, #667
After second thought it looks like EmbedIO offers absolutely nothing we can't do with a bit of coding. It brings a lot of dependencies (mainly yet another logging library) for absolutely no reason. I'd rather write my own API controllers.
This commit is contained in:
@@ -31,13 +31,15 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="EmbedIO" Version="1.10.0" />
|
||||
<PackageReference Include="HtmlAgilityPack" Version="1.6.1" />
|
||||
<PackageReference Include="Humanizer" Version="2.2.0" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="10.0.3" />
|
||||
<PackageReference Include="NLog" Version="5.0.0-beta11" />
|
||||
<PackageReference Include="SteamKit2" Version="2.0.0-Beta.1" />
|
||||
<PackageReference Include="System.Net.NameResolution" Version="4.3.0" />
|
||||
<PackageReference Include="System.Security.Cryptography.ProtectedData" Version="4.4.0" />
|
||||
<PackageReference Include="System.Security.Principal" Version="4.3.0" />
|
||||
<PackageReference Include="System.Threading.ThreadPool" Version="4.3.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -23,66 +23,32 @@
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using ArchiSteamFarm.Localization;
|
||||
using Unosquare.Labs.EmbedIO;
|
||||
using Unosquare.Labs.EmbedIO.Constants;
|
||||
using Unosquare.Labs.EmbedIO.Modules;
|
||||
using Unosquare.Swan;
|
||||
|
||||
namespace ArchiSteamFarm {
|
||||
[SuppressMessage("ReSharper", "UnusedMember.Global")]
|
||||
internal sealed class IPCWebApiController : WebApiController {
|
||||
[WebApiHandler(HttpVerbs.Get, "/ipc")]
|
||||
public async Task<bool> ExecuteCommandObsolete(WebServer server, HttpListenerContext context) {
|
||||
if ((server == null) || (context == null)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(server) + " || " + nameof(context));
|
||||
return false;
|
||||
}
|
||||
|
||||
string command = context.QueryString("command");
|
||||
if (string.IsNullOrEmpty(command)) {
|
||||
await context.PlainTextResponse(string.Format(Strings.ErrorIsEmpty, nameof(command)), HttpStatusCode.BadRequest).ConfigureAwait(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
Bot targetBot = Bot.Bots.OrderBy(bot => bot.Key).Select(bot => bot.Value).FirstOrDefault();
|
||||
if (targetBot == null) {
|
||||
await context.PlainTextResponse(Strings.ErrorNoBotsDefined, HttpStatusCode.BadRequest).ConfigureAwait(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (command[0] != '!') {
|
||||
command = "!" + command;
|
||||
}
|
||||
|
||||
string content = await targetBot.Response(Program.GlobalConfig.SteamOwnerID, command).ConfigureAwait(false);
|
||||
|
||||
ASF.ArchiLogger.LogGenericInfo(string.Format(Strings.IPCAnswered, command, content));
|
||||
|
||||
await context.PlainTextResponse(content).ConfigureAwait(false);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
internal static class IPC {
|
||||
internal static bool IsRunning => WebServerCancellationToken?.IsCancellationRequested == false;
|
||||
internal static bool IsRunning => HttpListener?.IsListening == true;
|
||||
|
||||
private static WebServer WebServer;
|
||||
private static CancellationTokenSource WebServerCancellationToken;
|
||||
private static HttpListener HttpListener;
|
||||
|
||||
internal static void Initialize(string host, ushort port) {
|
||||
internal static void Init(string host, ushort port) {
|
||||
if (string.IsNullOrEmpty(host) || (port == 0)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(host) + " || " + nameof(port));
|
||||
return;
|
||||
}
|
||||
|
||||
if (WebServer != null) {
|
||||
if (HttpListener != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!HttpListener.IsSupported) {
|
||||
ASF.ArchiLogger.LogGenericError(string.Format(Strings.WarningFailedWithError, "!HttpListener.IsSupported"));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -96,19 +62,49 @@ namespace ArchiSteamFarm {
|
||||
|
||||
string url = "http://" + host + ":" + port + "/";
|
||||
|
||||
Terminal.Settings.DisplayLoggingMessageType = LogMessageType.None;
|
||||
try {
|
||||
HttpListener = new HttpListener {
|
||||
IgnoreWriteExceptions = true
|
||||
};
|
||||
|
||||
Terminal.OnLogMessageReceived += OnLogMessageReceived;
|
||||
|
||||
WebServer = new WebServer(url);
|
||||
WebServer.RegisterModule(new WebApiModule());
|
||||
WebServer.Module<WebApiModule>().RegisterController<IPCWebApiController>();
|
||||
HttpListener.Prefixes.Add(url);
|
||||
} catch (Exception e) {
|
||||
ASF.ArchiLogger.LogGenericException(e);
|
||||
HttpListener = null;
|
||||
}
|
||||
}
|
||||
|
||||
internal static async Task<bool> PlainTextResponse(this HttpListenerContext context, string content, HttpStatusCode statusCode = HttpStatusCode.OK) {
|
||||
if ((context == null) || string.IsNullOrEmpty(content)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(context) + " || " + nameof(content));
|
||||
return false;
|
||||
internal static void Start() {
|
||||
if ((HttpListener?.IsListening != false) || (HttpListener.Prefixes.Count == 0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ASF.ArchiLogger.LogGenericInfo(string.Format(Strings.IPCStarting, HttpListener.Prefixes.First()));
|
||||
|
||||
try {
|
||||
HttpListener.Start();
|
||||
} catch (HttpListenerException e) {
|
||||
ASF.ArchiLogger.LogGenericException(e);
|
||||
return;
|
||||
}
|
||||
|
||||
Utilities.StartBackgroundFunction(Run);
|
||||
|
||||
ASF.ArchiLogger.LogGenericInfo(Strings.IPCReady);
|
||||
}
|
||||
|
||||
internal static void Stop() {
|
||||
if (HttpListener?.IsListening != true) {
|
||||
return;
|
||||
}
|
||||
|
||||
HttpListener.Stop();
|
||||
}
|
||||
|
||||
private static async Task BaseResponse(this HttpListenerContext context, byte[] response, HttpStatusCode statusCode = HttpStatusCode.OK) {
|
||||
if ((context == null) || (response == null) || (response.Length == 0)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(context) + " || " + nameof(response));
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -118,113 +114,145 @@ namespace ArchiSteamFarm {
|
||||
|
||||
context.Response.AppendHeader("Access-Control-Allow-Origin", "null");
|
||||
|
||||
Encoding encoding = Encoding.UTF8;
|
||||
string acceptEncoding = context.Request.Headers["Accept-Encoding"];
|
||||
|
||||
context.Response.ContentEncoding = encoding;
|
||||
context.Response.ContentType = "text/plain; charset=" + encoding.WebName;
|
||||
if (!string.IsNullOrEmpty(acceptEncoding)) {
|
||||
if (acceptEncoding.Contains("gzip")) {
|
||||
context.Response.AddHeader("Content-Encoding", "gzip");
|
||||
using (MemoryStream ms = new MemoryStream()) {
|
||||
using (GZipStream stream = new GZipStream(ms, CompressionMode.Compress)) {
|
||||
await stream.WriteAsync(response, 0, response.Length).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
byte[] buffer = encoding.GetBytes(content + Environment.NewLine);
|
||||
context.Response.ContentLength64 = buffer.Length;
|
||||
response = ms.ToArray();
|
||||
}
|
||||
} else if (acceptEncoding.Contains("deflate")) {
|
||||
context.Response.AddHeader("Content-Encoding", "deflate");
|
||||
using (MemoryStream ms = new MemoryStream()) {
|
||||
using (DeflateStream stream = new DeflateStream(ms, CompressionMode.Compress)) {
|
||||
await stream.WriteAsync(response, 0, response.Length).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
await context.Response.OutputStream.WriteAsync(buffer, 0, buffer.Length).ConfigureAwait(false);
|
||||
} catch (Exception e) {
|
||||
ASF.ArchiLogger.LogGenericDebugException(e);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
internal static void Start() {
|
||||
if (IsRunning || (WebServer == null) || (WebServer.UrlPrefixes.Count == 0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ASF.ArchiLogger.LogGenericInfo(string.Format(Strings.IPCStarting, WebServer.UrlPrefixes.First()));
|
||||
|
||||
// Fail early if we're not able to start our listener
|
||||
|
||||
try {
|
||||
WebServer.Listener.Start();
|
||||
} catch (HttpListenerException e) {
|
||||
ASF.ArchiLogger.LogGenericException(e);
|
||||
return;
|
||||
}
|
||||
|
||||
WebServerCancellationToken = new CancellationTokenSource();
|
||||
Utilities.StartBackgroundFunction(() => Run(WebServerCancellationToken));
|
||||
|
||||
ASF.ArchiLogger.LogGenericInfo(Strings.IPCReady);
|
||||
}
|
||||
|
||||
internal static void Stop() {
|
||||
if (WebServerCancellationToken == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Release(WebServerCancellationToken);
|
||||
}
|
||||
|
||||
private static void OnLogMessageReceived(object sender, LogMessageReceivedEventArgs e) {
|
||||
// Note: it's valid for sender to be null in this function
|
||||
if (e == null) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(e));
|
||||
return;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(e.Message)) {
|
||||
return;
|
||||
}
|
||||
|
||||
string message = e.Source + " | " + e.Message;
|
||||
|
||||
switch (e.MessageType) {
|
||||
case LogMessageType.Error:
|
||||
case LogMessageType.Warning:
|
||||
ASF.ArchiLogger.LogGenericWarning(message);
|
||||
break;
|
||||
case LogMessageType.Info:
|
||||
ASF.ArchiLogger.LogGenericDebug(message);
|
||||
break;
|
||||
default:
|
||||
ASF.ArchiLogger.LogGenericTrace(message);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static void Release(CancellationTokenSource cts) {
|
||||
if (cts == null) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(cts));
|
||||
return;
|
||||
}
|
||||
|
||||
if (cts == WebServerCancellationToken) {
|
||||
WebServerCancellationToken = null;
|
||||
}
|
||||
|
||||
if (!cts.IsCancellationRequested) {
|
||||
cts.Cancel();
|
||||
}
|
||||
|
||||
WebServer.Listener.Stop();
|
||||
}
|
||||
|
||||
private static async Task Run(CancellationTokenSource cts) {
|
||||
if (cts == null) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(cts));
|
||||
return;
|
||||
}
|
||||
|
||||
using (cts) {
|
||||
while ((cts == WebServerCancellationToken) && !cts.IsCancellationRequested) {
|
||||
try {
|
||||
await WebServer.RunAsync(cts.Token).ConfigureAwait(false);
|
||||
} catch (Exception e) {
|
||||
ASF.ArchiLogger.LogGenericException(e);
|
||||
Release(cts);
|
||||
response = ms.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
context.Response.ContentLength64 = response.Length;
|
||||
await context.Response.OutputStream.WriteAsync(response, 0, response.Length).ConfigureAwait(false);
|
||||
} catch (Exception e) {
|
||||
ASF.ArchiLogger.LogGenericDebugException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task ExecuteCommand(HttpListenerContext context) {
|
||||
if (context == null) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(context));
|
||||
return;
|
||||
}
|
||||
|
||||
string command = context.Request.GetQueryStringValue("command");
|
||||
if (string.IsNullOrEmpty(command)) {
|
||||
await context.StringResponse(string.Format(Strings.ErrorIsEmpty, nameof(command)), statusCode: HttpStatusCode.BadRequest).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
Bot targetBot = Bot.Bots.OrderBy(bot => bot.Key).Select(bot => bot.Value).FirstOrDefault();
|
||||
if (targetBot == null) {
|
||||
await context.StringResponse(Strings.ErrorNoBotsDefined, statusCode: HttpStatusCode.BadRequest).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (command[0] != '!') {
|
||||
command = "!" + command;
|
||||
}
|
||||
|
||||
string content = await targetBot.Response(Program.GlobalConfig.SteamOwnerID, command).ConfigureAwait(false);
|
||||
|
||||
ASF.ArchiLogger.LogGenericInfo(string.Format(Strings.IPCAnswered, command, content));
|
||||
|
||||
await context.StringResponse(content).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private static string GetQueryStringValue(this HttpListenerRequest request, string requestKey) {
|
||||
if ((request == null) || string.IsNullOrEmpty(requestKey)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(request) + " || " + nameof(requestKey));
|
||||
return null;
|
||||
}
|
||||
|
||||
string result = (from string key in request.QueryString where requestKey.Equals(key) select request.QueryString[key]).FirstOrDefault();
|
||||
return result;
|
||||
}
|
||||
|
||||
private static async Task HandleGetRequest(HttpListenerContext context) {
|
||||
if (context == null) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(context));
|
||||
return;
|
||||
}
|
||||
|
||||
switch (context.Request.Url.LocalPath.ToUpperInvariant()) {
|
||||
case "/IPC":
|
||||
await ExecuteCommand(context).ConfigureAwait(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task HandleRequest(HttpListenerContext context) {
|
||||
if (context == null) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(context));
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if (Program.GlobalConfig.SteamOwnerID == 0) {
|
||||
ASF.ArchiLogger.LogGenericWarning(Strings.ErrorIPCAccessDenied);
|
||||
await context.StringResponse(Strings.ErrorIPCAccessDenied, statusCode: HttpStatusCode.Forbidden).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (context.Request.HttpMethod) {
|
||||
case WebRequestMethods.Http.Get:
|
||||
await HandleGetRequest(context).ConfigureAwait(false);
|
||||
break;
|
||||
}
|
||||
|
||||
if (context.Response.ContentLength64 == 0) {
|
||||
await context.StringResponse("404 - Not Found", statusCode: HttpStatusCode.NotFound);
|
||||
}
|
||||
} finally {
|
||||
context.Response.Close();
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task Run() {
|
||||
while (HttpListener.IsListening) {
|
||||
HttpListenerContext context;
|
||||
|
||||
try {
|
||||
context = await HttpListener.GetContextAsync().ConfigureAwait(false);
|
||||
} catch (Exception e) {
|
||||
ASF.ArchiLogger.LogGenericException(e);
|
||||
return;
|
||||
}
|
||||
|
||||
Utilities.StartBackgroundFunction(() => HandleRequest(context), false);
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task StringResponse(this HttpListenerContext context, string content, string textType = "text/plain", HttpStatusCode statusCode = HttpStatusCode.OK) {
|
||||
if ((context == null) || string.IsNullOrEmpty(content) || string.IsNullOrEmpty(textType)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(context) + " || " + nameof(content) + " || " + nameof(textType));
|
||||
return;
|
||||
}
|
||||
|
||||
if (context.Response.ContentEncoding == null) {
|
||||
context.Response.ContentEncoding = Encoding.UTF8;
|
||||
}
|
||||
|
||||
context.Response.ContentType = textType + "; charset=" + context.Response.ContentEncoding.WebName;
|
||||
|
||||
byte[] response = context.Response.ContentEncoding.GetBytes(content + Environment.NewLine);
|
||||
await BaseResponse(context, response, statusCode).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -336,7 +336,6 @@ namespace ArchiSteamFarm {
|
||||
return;
|
||||
}
|
||||
|
||||
IPC.Initialize(GlobalConfig.IPCHost, GlobalConfig.IPCPort);
|
||||
OS.Init(GlobalConfig.Headless);
|
||||
WebBrowser.Init();
|
||||
|
||||
@@ -437,6 +436,7 @@ namespace ArchiSteamFarm {
|
||||
goto default;
|
||||
}
|
||||
|
||||
IPC.Init(GlobalConfig.IPCHost, GlobalConfig.IPCPort);
|
||||
IPC.Start();
|
||||
break;
|
||||
case "--service":
|
||||
|
||||
Reference in New Issue
Block a user