diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 0000000..3729ff0
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,25 @@
+**/.classpath
+**/.dockerignore
+**/.env
+**/.git
+**/.gitignore
+**/.project
+**/.settings
+**/.toolstarget
+**/.vs
+**/.vscode
+**/*.*proj.user
+**/*.dbmdl
+**/*.jfm
+**/azds.yaml
+**/bin
+**/charts
+**/docker-compose*
+**/Dockerfile*
+**/node_modules
+**/npm-debug.log
+**/obj
+**/secrets.dev.yaml
+**/values.dev.yaml
+LICENSE
+README.md
\ No newline at end of file
diff --git a/CloudFlareReverseProxy.sln b/CloudFlareReverseProxy.sln
new file mode 100644
index 0000000..9c3b5a6
--- /dev/null
+++ b/CloudFlareReverseProxy.sln
@@ -0,0 +1,25 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.31911.196
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CloudFlareReverseProxy", "CloudFlareReverseProxy\CloudFlareReverseProxy.csproj", "{EA43FF3F-964F-4982-8C79-B4DB77AB7FEF}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {EA43FF3F-964F-4982-8C79-B4DB77AB7FEF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {EA43FF3F-964F-4982-8C79-B4DB77AB7FEF}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {EA43FF3F-964F-4982-8C79-B4DB77AB7FEF}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {EA43FF3F-964F-4982-8C79-B4DB77AB7FEF}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {1983598E-E53B-4CDD-A7FA-E15EB8F3DB9A}
+ EndGlobalSection
+EndGlobal
diff --git a/CloudFlareReverseProxy/CloudFlareReverseProxy.csproj b/CloudFlareReverseProxy/CloudFlareReverseProxy.csproj
new file mode 100644
index 0000000..3e61aba
--- /dev/null
+++ b/CloudFlareReverseProxy/CloudFlareReverseProxy.csproj
@@ -0,0 +1,14 @@
+
+
+
+ net5.0
+ fe0043e8-ea3b-4937-b2a1-fb2af7fe92d6
+ Linux
+
+
+
+
+
+
+
+
diff --git a/CloudFlareReverseProxy/Dockerfile b/CloudFlareReverseProxy/Dockerfile
new file mode 100644
index 0000000..74eae50
--- /dev/null
+++ b/CloudFlareReverseProxy/Dockerfile
@@ -0,0 +1,22 @@
+#See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging.
+
+FROM mcr.microsoft.com/dotnet/aspnet:5.0 AS base
+WORKDIR /app
+EXPOSE 80
+EXPOSE 443
+
+FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build
+WORKDIR /src
+COPY ["CloudFlareReverseProxy/CloudFlareReverseProxy.csproj", "CloudFlareReverseProxy/"]
+RUN dotnet restore "CloudFlareReverseProxy/CloudFlareReverseProxy.csproj"
+COPY . .
+WORKDIR "/src/CloudFlareReverseProxy"
+RUN dotnet build "CloudFlareReverseProxy.csproj" -c Release -o /app/build
+
+FROM build AS publish
+RUN dotnet publish "CloudFlareReverseProxy.csproj" -c Release -o /app/publish
+
+FROM base AS final
+WORKDIR /app
+COPY --from=publish /app/publish .
+ENTRYPOINT ["dotnet", "CloudFlareReverseProxy.dll"]
\ No newline at end of file
diff --git a/CloudFlareReverseProxy/Program.cs b/CloudFlareReverseProxy/Program.cs
new file mode 100644
index 0000000..53220b1
--- /dev/null
+++ b/CloudFlareReverseProxy/Program.cs
@@ -0,0 +1,26 @@
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.Hosting;
+using Microsoft.Extensions.Logging;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace CloudFlareReverseProxy
+{
+ public class Program
+ {
+ public static void Main(string[] args)
+ {
+ CreateHostBuilder(args).Build().Run();
+ }
+
+ public static IHostBuilder CreateHostBuilder(string[] args) =>
+ Host.CreateDefaultBuilder(args)
+ .ConfigureWebHostDefaults(webBuilder =>
+ {
+ webBuilder.UseStartup();
+ });
+ }
+}
diff --git a/CloudFlareReverseProxy/Properties/launchSettings.json b/CloudFlareReverseProxy/Properties/launchSettings.json
new file mode 100644
index 0000000..56f42da
--- /dev/null
+++ b/CloudFlareReverseProxy/Properties/launchSettings.json
@@ -0,0 +1,35 @@
+{
+ "iisSettings": {
+ "windowsAuthentication": false,
+ "anonymousAuthentication": true,
+ "iisExpress": {
+ "applicationUrl": "http://localhost:40939",
+ "sslPort": 0
+ }
+ },
+ "profiles": {
+ "IIS Express": {
+ "commandName": "IISExpress",
+ "launchBrowser": true,
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ },
+ "CloudFlareReverseProxy": {
+ "commandName": "Project",
+ "launchBrowser": true,
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ },
+ "dotnetRunMessages": "true",
+ "applicationUrl": "https://localhost:5001;http://localhost:5000"
+ },
+ "Docker": {
+ "commandName": "Docker",
+ "launchBrowser": true,
+ "launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}",
+ "publishAllPorts": true,
+ "useSSL": true
+ }
+ }
+}
\ No newline at end of file
diff --git a/CloudFlareReverseProxy/ReverseProxyMiddleware.cs b/CloudFlareReverseProxy/ReverseProxyMiddleware.cs
new file mode 100644
index 0000000..dc1e197
--- /dev/null
+++ b/CloudFlareReverseProxy/ReverseProxyMiddleware.cs
@@ -0,0 +1,167 @@
+using FlareSolverrSharp;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.WebUtilities;
+using Microsoft.Extensions.Configuration;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net.Http;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace CloudFlareReverseProxy
+{
+ public class ReverseProxyMiddleware
+ {
+ private static HttpClient _httpClient;
+ private readonly RequestDelegate _nextMiddleware;
+
+ public ReverseProxyMiddleware(RequestDelegate nextMiddleware, IConfiguration configuration)
+ {
+ _nextMiddleware = nextMiddleware;
+ var handler = new ClearanceHandler(configuration["FlareSolverrApiUrl"])
+ {
+ MaxTimeout = 60000
+ };
+ _httpClient = new HttpClient(handler);
+ }
+
+ public async Task Invoke(HttpContext context)
+ {
+ var targetUri = BuildTargetUri(context.Request);
+
+ if (targetUri != null)
+ {
+ var targetRequestMessage = CreateTargetMessage(context, targetUri);
+
+ using (var responseMessage = await _httpClient.SendAsync(targetRequestMessage, HttpCompletionOption.ResponseHeadersRead, context.RequestAborted))
+ {
+ context.Response.StatusCode = (int)responseMessage.StatusCode;
+
+ CopyFromTargetResponseHeaders(context, responseMessage);
+
+ await ProcessResponseContent(context, responseMessage);
+ }
+
+ return;
+ }
+
+ await _nextMiddleware(context);
+ }
+
+ private async Task ProcessResponseContent(HttpContext context, HttpResponseMessage responseMessage)
+ {
+ var content = await responseMessage.Content.ReadAsByteArrayAsync();
+
+ if (IsContentOfType(responseMessage, "text/html") || IsContentOfType(responseMessage, "text/javascript"))
+ {
+ var stringContent = Encoding.UTF8.GetString(content);
+ var newContent = stringContent.Replace("https://www.google.com", "/google")
+ .Replace("https://www.gstatic.com", "/googlestatic")
+ .Replace("https://docs.google.com/forms", "/googleforms");
+ await context.Response.WriteAsync(newContent, Encoding.UTF8);
+ }
+ else
+ {
+ await context.Response.Body.WriteAsync(content);
+ }
+ }
+
+ private bool IsContentOfType(HttpResponseMessage responseMessage, string type)
+ {
+ var result = false;
+
+ if (responseMessage.Content?.Headers?.ContentType != null)
+ {
+ result = responseMessage.Content.Headers.ContentType.MediaType == type;
+ }
+
+ return result;
+ }
+
+ private HttpRequestMessage CreateTargetMessage(HttpContext context, Uri targetUri)
+ {
+ var requestMessage = new HttpRequestMessage();
+ CopyFromOriginalRequestContentAndHeaders(context, requestMessage);
+
+ //targetUri = new Uri(QueryHelpers.AddQueryString(targetUri.OriginalString, new Dictionary() { { "entry.1884265043", "John Doe" } }));
+
+ requestMessage.RequestUri = targetUri;
+ requestMessage.Headers.Host = targetUri.Host;
+ requestMessage.Method = GetMethod(context.Request.Method);
+
+ return requestMessage;
+ }
+
+ private void CopyFromOriginalRequestContentAndHeaders(HttpContext context, HttpRequestMessage requestMessage)
+ {
+ var requestMethod = context.Request.Method;
+
+ if (!HttpMethods.IsGet(requestMethod) &&
+ !HttpMethods.IsHead(requestMethod) &&
+ !HttpMethods.IsDelete(requestMethod) &&
+ !HttpMethods.IsTrace(requestMethod))
+ {
+ var streamContent = new StreamContent(context.Request.Body);
+ requestMessage.Content = streamContent;
+ }
+
+ foreach (var header in context.Request.Headers)
+ {
+ requestMessage.Content?.Headers.TryAddWithoutValidation(header.Key, header.Value.ToArray());
+ }
+ }
+
+ private void CopyFromTargetResponseHeaders(HttpContext context, HttpResponseMessage responseMessage)
+ {
+ foreach (var header in responseMessage.Headers)
+ {
+ context.Response.Headers[header.Key] = header.Value.ToArray();
+ }
+
+ foreach (var header in responseMessage.Content.Headers)
+ {
+ context.Response.Headers[header.Key] = header.Value.ToArray();
+ }
+ context.Response.Headers.Remove("transfer-encoding");
+ }
+ private static HttpMethod GetMethod(string method)
+ {
+ if (HttpMethods.IsDelete(method)) return HttpMethod.Delete;
+ if (HttpMethods.IsGet(method)) return HttpMethod.Get;
+ if (HttpMethods.IsHead(method)) return HttpMethod.Head;
+ if (HttpMethods.IsOptions(method)) return HttpMethod.Options;
+ if (HttpMethods.IsPost(method)) return HttpMethod.Post;
+ if (HttpMethods.IsPut(method)) return HttpMethod.Put;
+ if (HttpMethods.IsTrace(method)) return HttpMethod.Trace;
+ return new HttpMethod(method);
+ }
+
+ private Uri BuildTargetUri(HttpRequest request)
+ {
+ Uri targetUri = null;
+ //PathString remainingPath;
+
+ /*
+ if (request.Path.StartsWithSegments("/googleforms", out remainingPath))
+ {
+ targetUri = new Uri("https://docs.google.com/forms" + remainingPath);
+ }
+
+ if (request.Path.StartsWithSegments("/google", out remainingPath))
+ {
+ targetUri = new Uri("https://www.google.com" + remainingPath);
+ }
+
+ if (request.Path.StartsWithSegments("/googlestatic", out remainingPath))
+ {
+ targetUri = new Uri(" https://www.gstatic.com" + remainingPath);
+ }
+ */
+ if (!request.Query["url"].Any())
+ return targetUri;
+ else
+ return new Uri(Uri.UnescapeDataString(request.Query["url"].ToString()));
+ }
+ }
+}
diff --git a/CloudFlareReverseProxy/Startup.cs b/CloudFlareReverseProxy/Startup.cs
new file mode 100644
index 0000000..7ac5349
--- /dev/null
+++ b/CloudFlareReverseProxy/Startup.cs
@@ -0,0 +1,43 @@
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace CloudFlareReverseProxy
+{
+ public class Startup
+ {
+ // This method gets called by the runtime. Use this method to add services to the container.
+ // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
+ public void ConfigureServices(IServiceCollection services)
+ {
+ }
+
+ // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
+ public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
+ {
+ if (env.IsDevelopment())
+ {
+ app.UseDeveloperExceptionPage();
+ }
+
+ //app.UseRouting();
+ app.UseMiddleware();
+
+ /*
+ app.UseEndpoints(endpoints =>
+ {
+ endpoints.MapGet("/", async context =>
+ {
+ await context.Response.WriteAsync("Hello World!");
+ });
+ });
+ */
+ }
+ }
+}
diff --git a/CloudFlareReverseProxy/appsettings.Development.json b/CloudFlareReverseProxy/appsettings.Development.json
new file mode 100644
index 0000000..8983e0f
--- /dev/null
+++ b/CloudFlareReverseProxy/appsettings.Development.json
@@ -0,0 +1,9 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft": "Warning",
+ "Microsoft.Hosting.Lifetime": "Information"
+ }
+ }
+}
diff --git a/CloudFlareReverseProxy/appsettings.json b/CloudFlareReverseProxy/appsettings.json
new file mode 100644
index 0000000..4652911
--- /dev/null
+++ b/CloudFlareReverseProxy/appsettings.json
@@ -0,0 +1,11 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft": "Warning",
+ "Microsoft.Hosting.Lifetime": "Information"
+ }
+ },
+ "AllowedHosts": "*",
+ "FlareSolverrApiUrl": "http://basilisk.chrispr.lan:8191"
+}