aboutsummaryrefslogtreecommitdiff
path: root/back-end/src/Endpoints/SiteLookupEndpoint.cs
diff options
context:
space:
mode:
authorLibravatar vnugent <public@vaughnnugent.com>2024-03-13 16:19:50 -0400
committerLibravatar vnugent <public@vaughnnugent.com>2024-03-13 16:19:50 -0400
commite326736021be8ff5af4208d16f59d5e3e4f22b3e (patch)
treec6ed7dfefd5b9c8965cfc185ad3f89436301df1a /back-end/src/Endpoints/SiteLookupEndpoint.cs
parent3883de080e263d2f076f65b4600a5021d3d64a21 (diff)
Squashed commit of the following:v0.1.1
commit 1e08c6d2112459dc02a0ab873123c4a363b01d21 Author: vnugent <public@vaughnnugent.com> Date: Wed Mar 13 16:17:58 2024 -0400 ci: verified container build ready for next release commit 85a1e5b7cc5c99e97a2d4e99bbceb0d2139742ff Author: vnugent <public@vaughnnugent.com> Date: Tue Mar 12 22:05:16 2024 -0400 ci: exciting bare-metal build process, os support, smaller packages commit 748cdbf4880d830fd794e92856e8c35a46e4f884 Author: vnugent <public@vaughnnugent.com> Date: Mon Mar 11 21:21:18 2024 -0400 feat(app): #1 update libs & add curl support
Diffstat (limited to 'back-end/src/Endpoints/SiteLookupEndpoint.cs')
-rw-r--r--back-end/src/Endpoints/SiteLookupEndpoint.cs156
1 files changed, 156 insertions, 0 deletions
diff --git a/back-end/src/Endpoints/SiteLookupEndpoint.cs b/back-end/src/Endpoints/SiteLookupEndpoint.cs
new file mode 100644
index 0000000..effe6aa
--- /dev/null
+++ b/back-end/src/Endpoints/SiteLookupEndpoint.cs
@@ -0,0 +1,156 @@
+// Copyright (C) 2024 Vaughn Nugent
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+using System;
+using System.Net;
+using System.Text;
+using System.Linq;
+using System.Threading.Tasks;
+using System.Collections.Generic;
+
+using VNLib.Utils;
+using VNLib.Utils.Memory;
+using VNLib.Plugins;
+using VNLib.Plugins.Essentials;
+using VNLib.Plugins.Essentials.Endpoints;
+using VNLib.Plugins.Essentials.Extensions;
+using VNLib.Plugins.Extensions.Loading;
+using VNLib.Plugins.Extensions.Validation;
+
+using SimpleBookmark.PlatformFeatures.Curl;
+
+namespace SimpleBookmark.Endpoints
+{
+ [ConfigurationName("curl")]
+ internal sealed class SiteLookupEndpoint : ProtectedWebEndpoint
+ {
+ const string DefaultCurlExecName = "curl";
+ const int MaxTimeoutValue = 30000;
+
+ private readonly SystemCurlApp _curl;
+ private readonly IAsyncLazy<bool> _isSupported;
+
+ public SiteLookupEndpoint(PluginBase plugin, IConfigScope config)
+ {
+ string path = config.GetRequiredProperty("path", p => p.GetString()!);
+ InitPathAndLog(path, plugin.Log);
+
+ string exePath = config.GetValueOrDefault("exe_path", p => p.GetString(), DefaultCurlExecName);
+ bool httspOnly = config.GetValueOrDefault("https_only", p => p.GetBoolean(), false);
+
+ //Optional extra arguments
+ string[] extrArgs = config.GetValueOrDefault(
+ "extra_args",
+ p => p.EnumerateArray().Select(s => s.GetString()!).ToArray(),
+ Array.Empty<string>()
+ );
+
+ _curl = new SystemCurlApp(exePath, httspOnly, extrArgs);
+
+ //Immediately check if curl is supported
+ _isSupported = _curl.TestIsAvailable(plugin.UnloadToken).AsLazy();
+ }
+
+ protected override async ValueTask<VfReturnType> GetAsync(HttpEntity entity)
+ {
+ WebMessage webm = new();
+
+ bool isEnabled = await _isSupported;
+
+ //Allow site to cache if curl is supported on the platform
+ if (entity.QueryArgs.ContainsKey("support"))
+ {
+ webm.Success = isEnabled;
+ return VirtualOk(entity, webm);
+ }
+
+ //Assert supported value as curl is required for a normal url lookup
+ if(webm.Assert(isEnabled, "Curl is not supported on the current platform"))
+ {
+ return VirtualClose(entity, webm, HttpStatusCode.NotImplemented);
+ }
+
+ string? url = entity.QueryArgs.GetValueOrDefault("url");
+
+ if(webm.Assert(!string.IsNullOrWhiteSpace(url), "No url provided"))
+ {
+ return VirtualClose(entity, webm, HttpStatusCode.BadRequest);
+ }
+
+ if(webm.Assert(UrlFromBase64Url(url!, out Uri? uri), "Invalid url provided"))
+ {
+ return VirtualClose(entity, webm, HttpStatusCode.UnprocessableEntity);
+ }
+
+ int? timeoutMs = null;
+
+ //Allow clients to specify a timeout for the request
+ string? timeoutMsS = entity.QueryArgs.GetValueOrDefault("timeout");
+ if (timeoutMsS is not null && int.TryParse(timeoutMsS, out int _timeoutMs))
+ {
+ //Miniumum timeout must be greater than 1 second because curl is timed in seconds
+ timeoutMs = Math.Clamp(_timeoutMs, 1000, MaxTimeoutValue);
+ }
+
+ try
+ {
+ //Exec curl on the url
+ CurlResult result = await _curl.ExecLookupAsync(uri!, timeoutMs, entity.EventCancellation);
+
+ if(webm.Assert(result.IsError == false, result.ErrorMessage!))
+ {
+ return VirtualClose(entity, webm, HttpStatusCode.InternalServerError);
+ }
+
+ webm.Success = true;
+ webm.Result = result.Result; //Set curl lookup result as the response
+
+ return VirtualOk(entity, webm);
+ }
+ catch (TimeoutException)
+ {
+ webm.Result = "Request timed out";
+ return VirtualClose(entity, webm, HttpStatusCode.InternalServerError);
+ }
+ catch (OperationCanceledException)
+ {
+ webm.Result = "Request timed out";
+ return VirtualClose(entity, webm, HttpStatusCode.InternalServerError);
+ }
+ }
+
+ /*
+ * Reads in a base64url encoded string which is the user's search url and
+ * attempts to parse it into a uri. If the url is invalid, the function
+ */
+ private static bool UrlFromBase64Url(string base64Url, out Uri? uri)
+ {
+ uri = null;
+
+ //Alloc output buffer for decoded data
+ using UnsafeMemoryHandle<byte> output = MemoryUtil.UnsafeAllocNearestPage(base64Url.Length, true);
+
+ ERRNO decoded = VnEncoding.Base64UrlDecode(base64Url, output.Span, Encoding.UTF8);
+ if(decoded < 1)
+ {
+ return false;
+ }
+
+ //Recover the url string from its binary representation and try to parse it into a uri
+ string urlstring = Encoding.UTF8.GetString(output.Span[..(int)decoded]);
+ return Uri.TryCreate(urlstring, UriKind.Absolute, out uri);
+ }
+ }
+}