commit c2ad3fea0544a9d3619c1ad9ebd5cc6e94d52ac5
Author: feyris-tan <4116042+feyris-tan@users.noreply.github.com>
Date: Sun Apr 26 21:28:28 2026 +0200
Basis import
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..5ff6309
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,38 @@
+target/
+!.mvn/wrapper/maven-wrapper.jar
+!**/src/main/**/target/
+!**/src/test/**/target/
+
+### IntelliJ IDEA ###
+.idea/modules.xml
+.idea/jarRepositories.xml
+.idea/compiler.xml
+.idea/libraries/
+*.iws
+*.iml
+*.ipr
+
+### Eclipse ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+build/
+!**/src/main/**/build/
+!**/src/test/**/build/
+
+### VS Code ###
+.vscode/
+
+### Mac OS ###
+.DS_Store
\ No newline at end of file
diff --git a/.idea/encodings.xml b/.idea/encodings.xml
new file mode 100644
index 0000000..aa00ffa
--- /dev/null
+++ b/.idea/encodings.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..df00c07
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/sonarlint.xml b/.idea/sonarlint.xml
new file mode 100644
index 0000000..5b670f2
--- /dev/null
+++ b/.idea/sonarlint.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/uiDesigner.xml b/.idea/uiDesigner.xml
new file mode 100644
index 0000000..2b63946
--- /dev/null
+++ b/.idea/uiDesigner.xml
@@ -0,0 +1,124 @@
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..94a25f7
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/workspace.xml b/.idea/workspace.xml
new file mode 100644
index 0000000..66fe871
--- /dev/null
+++ b/.idea/workspace.xml
@@ -0,0 +1,233 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1777206490693
+
+
+ 1777206490693
+
+
+
+
+
+
+
+
+
+
+
+ file://$PROJECT_DIR$/src/main/java/moe/yo3explorer/dustspeck2/business/nuget/boundary/RegistrationsBaseUrlService.java
+ 44
+
+
+
+ file://$PROJECT_DIR$/src/main/java/moe/yo3explorer/dustspeck2/business/nuget/boundary/ReadmeUriTemplateService.java
+ 41
+
+
+
+ file://$PROJECT_DIR$/src/main/java/moe/yo3explorer/dustspeck2/presentation/Dustspeck2HttpHandler.java
+ 50
+
+
+
+ file://$PROJECT_DIR$/src/main/java/moe/yo3explorer/dustspeck2/business/nuget/boundary/VulnerabilityInfoService.java
+ 41
+
+
+
+ file://$PROJECT_DIR$/src/main/java/moe/yo3explorer/dustspeck2/business/nuget/boundary/SearchQueryService.java
+ 40
+
+
+
+ file://$PROJECT_DIR$/src/main/java/moe/yo3explorer/dustspeck2/business/nuget/boundary/VersionPageService.java
+ 43
+
+
+
+ file://$PROJECT_DIR$/src/main/java/moe/yo3explorer/dustspeck2/business/nuget/boundary/VersionPageService.java
+ 30
+
+
+
+ file://$PROJECT_DIR$/src/main/java/moe/yo3explorer/dustspeck2/business/nuget/boundary/VersionPageService.java
+ 33
+
+
+
+ file://$PROJECT_DIR$/src/main/java/moe/yo3explorer/dustspeck2/business/nuget/boundary/VersionPageService.java
+ 35
+
+
+
+ file://$PROJECT_DIR$/src/main/java/moe/yo3explorer/dustspeck2/business/nuget/boundary/PackageBaseAddressService.java
+ 32
+
+
+
+ file://$PROJECT_DIR$/src/main/java/moe/yo3explorer/dustspeck2/business/nuget/boundary/PackageBaseAddressService.java
+ 35
+
+
+
+ file://$PROJECT_DIR$/src/main/java/moe/yo3explorer/dustspeck2/business/nuget/boundary/PackageBaseAddressService.java
+ 37
+
+
+
+ file://$PROJECT_DIR$/src/main/java/moe/yo3explorer/dustspeck2/business/nuget/boundary/PackageBaseAddressService.java
+ 45
+
+
+
+ file://$PROJECT_DIR$/src/main/java/moe/yo3explorer/dustspeck2/business/nuget/boundary/VulnerabilityBaseService.java
+ 43
+
+
+
+ file://$PROJECT_DIR$/src/main/java/moe/yo3explorer/dustspeck2/business/nuget/boundary/IconService.java
+ 30
+
+
+
+ file://$PROJECT_DIR$/src/main/java/moe/yo3explorer/dustspeck2/business/nuget/boundary/IconService.java
+ 33
+
+
+
+ file://$PROJECT_DIR$/src/main/java/moe/yo3explorer/dustspeck2/business/nuget/boundary/IconService.java
+ 35
+
+
+
+ file://$PROJECT_DIR$/src/main/java/moe/yo3explorer/dustspeck2/business/nuget/boundary/IconService.java
+ 43
+
+
+
+ file://$PROJECT_DIR$/src/main/java/moe/yo3explorer/dustspeck2/business/nuget/boundary/IconService.java
+ 53
+
+
+
+ file://$PROJECT_DIR$/src/main/java/moe/yo3explorer/dustspeck2/business/nuget/boundary/VulnerabilityInfoService.java
+ 31
+
+
+
+
+
+
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..167301d
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,42 @@
+
+
+ 4.0.0
+
+ moe.yo3explorer
+ dustspeck2
+ 1.0-SNAPSHOT
+
+
+ 17
+ 17
+ UTF-8
+
+
+
+
+ io.undertow
+ undertow-core
+ 2.1.0.Final
+
+
+
+ org.apache.logging.log4j
+ log4j-core
+ 2.24.1
+
+
+
+ com.squareup.okhttp3
+ okhttp
+ 4.12.0
+
+
+
+ commons-io
+ commons-io
+ 2.17.0
+
+
+
\ No newline at end of file
diff --git a/src/main/java/moe/yo3explorer/dustspeck2/business/nuget/boundary/Dustspeck2Context.java b/src/main/java/moe/yo3explorer/dustspeck2/business/nuget/boundary/Dustspeck2Context.java
new file mode 100644
index 0000000..b39eeb2
--- /dev/null
+++ b/src/main/java/moe/yo3explorer/dustspeck2/business/nuget/boundary/Dustspeck2Context.java
@@ -0,0 +1,91 @@
+package moe.yo3explorer.dustspeck2.business.nuget.boundary;
+
+import moe.yo3explorer.dustspeck2.business.nuget.control.*;
+import org.apache.commons.io.IOUtils;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+
+public class Dustspeck2Context
+{
+ private static Dustspeck2Context _instance;
+ private static Logger logger;
+ private String dataDirectory;
+
+ private Dustspeck2Context()
+ {
+ logger = LogManager.getLogger(Dustspeck2Context.class);
+ logger.info("Built context!");
+ }
+
+ public static Dustspeck2Context getInstance()
+ {
+ if (_instance == null)
+ {
+ _instance = new Dustspeck2Context();
+ }
+ return _instance;
+ }
+
+ public void setDataDirectory(String dataDirectory)
+ {
+ if (fileStorage != null)
+ throw new FileStorageAlreadyWorkingException();
+
+ this.dataDirectory = dataDirectory;
+ }
+
+ private FileStorage fileStorage;
+ public synchronized FileStorage getFileStorage() throws IOException
+ {
+ if (this.dataDirectory == null)
+ throw new DataDirectoryNotConfiguredException();
+
+ if (fileStorage == null)
+ {
+ fileStorage = new FileStorage(dataDirectory);
+ }
+ return fileStorage;
+ }
+
+ private HttpClient httpClient;
+ public synchronized HttpClient getHttpClient()
+ {
+ if (httpClient == null)
+ httpClient = new HttpClient();
+ return httpClient;
+ }
+
+ private boolean internetConnectionChecked;
+ private boolean isOffline;
+ public synchronized boolean isOffline()
+ {
+ if (!internetConnectionChecked)
+ {
+ isOffline = !InternetConnectionChecker.checkInternetConnection();
+ internetConnectionChecked = true;
+ }
+ return isOffline;
+ }
+
+ @NotNull
+ public byte[] patchJson(byte[] input, String newServer)
+ {
+ String sourceJson = IOUtils.toString(input, String.valueOf(StandardCharsets.UTF_8));
+ sourceJson = sourceJson.replace("https://api.nuget.org/","http://" + newServer + "/");
+ return sourceJson.getBytes(StandardCharsets.UTF_8);
+ }
+
+ public String guessMimeType(@NotNull String fname)
+ {
+ if (fname.endsWith(".nupkg"))
+ return "application/x-nupkg";
+ else if (fname.endsWith(".json"))
+ return "application/json";
+ else
+ return "octet/stream";
+ }
+}
diff --git a/src/main/java/moe/yo3explorer/dustspeck2/business/nuget/boundary/IconService.java b/src/main/java/moe/yo3explorer/dustspeck2/business/nuget/boundary/IconService.java
new file mode 100644
index 0000000..7dc16ff
--- /dev/null
+++ b/src/main/java/moe/yo3explorer/dustspeck2/business/nuget/boundary/IconService.java
@@ -0,0 +1,60 @@
+package moe.yo3explorer.dustspeck2.business.nuget.boundary;
+
+import io.undertow.server.HttpServerExchange;
+import io.undertow.util.HttpString;
+import moe.yo3explorer.dustspeck2.business.nuget.control.FileStorage;
+import moe.yo3explorer.dustspeck2.business.nuget.control.HttpClient;
+import moe.yo3explorer.dustspeck2.presentation.Dustspeck2Service;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+public class IconService implements Dustspeck2Service
+{
+ @Override
+ public boolean canHandle(String s)
+ {
+ return s.startsWith("/v3-flatcontainer/");
+ }
+
+ @Override
+ public void handleRequest(String queryString, String[] vines, HttpServerExchange httpServerExchange) throws IOException
+ {
+ String originalUrl = "https://api.nuget.org" + queryString;
+ Dustspeck2Context dustspeck2 = Dustspeck2Context.getInstance();
+ String mime = dustspeck2.guessMimeType(queryString);
+ String onDiskFilename = queryString;
+
+ FileStorage fileStorage = dustspeck2.getFileStorage();
+ if (fileStorage.testForFile(onDiskFilename))
+ {
+ byte[] buffer = fileStorage.getFile(queryString);
+ if (queryString.endsWith(".json"))
+ {
+ buffer = dustspeck2.patchJson(buffer, httpServerExchange.getHostAndPort());
+ }
+ httpServerExchange.setStatusCode(200);
+ httpServerExchange.getResponseHeaders().put(HttpString.tryFromString("Content-Type"),mime);
+ httpServerExchange.getResponseSender().send(ByteBuffer.wrap(buffer));
+ return;
+ }
+
+ if (dustspeck2.isOffline())
+ {
+ httpServerExchange.setStatusCode(404);
+ httpServerExchange.getResponseSender().send("");
+ return;
+ }
+
+ HttpClient httpClient = dustspeck2.getHttpClient();
+ byte[] file = httpClient.getFile(originalUrl);
+ fileStorage.writeFile(queryString, file);
+ if (queryString.endsWith(".json"))
+ {
+ file = dustspeck2.patchJson(file, httpServerExchange.getHostAndPort());
+ }
+ httpServerExchange.setStatusCode(200);
+ httpServerExchange.getResponseHeaders().put(HttpString.tryFromString("Content-Type"),mime);
+ httpServerExchange.getResponseSender().send(ByteBuffer.wrap(file));
+ }
+}
diff --git a/src/main/java/moe/yo3explorer/dustspeck2/business/nuget/boundary/PackageBaseAddressService.java b/src/main/java/moe/yo3explorer/dustspeck2/business/nuget/boundary/PackageBaseAddressService.java
new file mode 100644
index 0000000..c0f3423
--- /dev/null
+++ b/src/main/java/moe/yo3explorer/dustspeck2/business/nuget/boundary/PackageBaseAddressService.java
@@ -0,0 +1,62 @@
+package moe.yo3explorer.dustspeck2.business.nuget.boundary;
+
+import io.undertow.server.HttpServerExchange;
+import io.undertow.util.HttpString;
+import moe.yo3explorer.dustspeck2.business.nuget.control.FileStorage;
+import moe.yo3explorer.dustspeck2.business.nuget.control.HttpClient;
+import moe.yo3explorer.dustspeck2.presentation.Dustspeck2Service;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+public class PackageBaseAddressService implements Dustspeck2Service
+{
+ @Override
+ public boolean canHandle(@NotNull String s)
+ {
+ return s.startsWith("/PackageBaseAddress/v3-flatcontainer");
+ }
+
+ @Override
+ public void handleRequest(@NotNull String queryString, String[] vines, HttpServerExchange httpServerExchange) throws IOException
+ {
+ String originalUrl = queryString.replace("/PackageBaseAddress","");
+ originalUrl = "https://api.nuget.org" + originalUrl;;
+ Dustspeck2Context dustspeck2 = Dustspeck2Context.getInstance();
+ String mime = dustspeck2.guessMimeType(queryString);
+ String onDiskFilename = queryString.replace("/PackageBaseAddress","");
+
+ FileStorage fileStorage = dustspeck2.getFileStorage();
+ if (fileStorage.testForFile(onDiskFilename))
+ {
+ byte[] buffer = fileStorage.getFile(onDiskFilename);
+ if (queryString.endsWith(".json"))
+ {
+ buffer = dustspeck2.patchJson(buffer, httpServerExchange.getHostAndPort());
+ }
+ httpServerExchange.setStatusCode(200);
+ httpServerExchange.getResponseHeaders().put(HttpString.tryFromString("Content-Type"),mime);
+ httpServerExchange.getResponseSender().send(ByteBuffer.wrap(buffer));
+ return;
+ }
+
+ if (dustspeck2.isOffline())
+ {
+ httpServerExchange.setStatusCode(404);
+ httpServerExchange.getResponseSender().send("");
+ return;
+ }
+
+ HttpClient httpClient = dustspeck2.getHttpClient();
+ byte[] file = httpClient.getFile(originalUrl);
+ fileStorage.writeFile(onDiskFilename, file);
+ if (queryString.endsWith(".json"))
+ {
+ file = dustspeck2.patchJson(file, httpServerExchange.getHostAndPort());
+ }
+ httpServerExchange.setStatusCode(200);
+ httpServerExchange.getResponseHeaders().put(HttpString.tryFromString("Content-Type"),mime);
+ httpServerExchange.getResponseSender().send(ByteBuffer.wrap(file));
+ }
+}
diff --git a/src/main/java/moe/yo3explorer/dustspeck2/business/nuget/boundary/ReadmeUriTemplateService.java b/src/main/java/moe/yo3explorer/dustspeck2/business/nuget/boundary/ReadmeUriTemplateService.java
new file mode 100644
index 0000000..c5844a7
--- /dev/null
+++ b/src/main/java/moe/yo3explorer/dustspeck2/business/nuget/boundary/ReadmeUriTemplateService.java
@@ -0,0 +1,54 @@
+package moe.yo3explorer.dustspeck2.business.nuget.boundary;
+
+import io.undertow.server.HttpServerExchange;
+import io.undertow.util.HttpString;
+import moe.yo3explorer.dustspeck2.business.nuget.control.FileStorage;
+import moe.yo3explorer.dustspeck2.business.nuget.control.HttpClient;
+import moe.yo3explorer.dustspeck2.presentation.Dustspeck2Service;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+public class ReadmeUriTemplateService implements Dustspeck2Service
+{
+ @Override
+ public boolean canHandle(@NotNull String s)
+ {
+ return s.startsWith("/ReadmeUriTemplate/v3-flatcontainer/") && s.endsWith("/readme");
+ }
+
+ @Override
+ public void handleRequest(String queryString, @NotNull String[] vines, HttpServerExchange httpServerExchange) throws IOException
+ {
+ String readme = vines[vines.length - 1];
+ String version = vines[vines.length - 2];
+ String packageName = vines[vines.length - 3];
+ String liveUrl = String.format("https://api.nuget.org/v3-flatcontainer/%s/%s/%s",packageName,version,readme);
+
+ Dustspeck2Context dustspeck2 = Dustspeck2Context.getInstance();
+ FileStorage fileStorage = dustspeck2.getFileStorage();
+ if (fileStorage.testForFile(queryString))
+ {
+ byte[] buffer = fileStorage.getFile(queryString);
+ httpServerExchange.setStatusCode(200);
+ httpServerExchange.getResponseHeaders().put(HttpString.tryFromString("Content-Type"),"text/plain");
+ httpServerExchange.getResponseSender().send(ByteBuffer.wrap(buffer));
+ return;
+ }
+
+ if (dustspeck2.isOffline())
+ {
+ httpServerExchange.setStatusCode(404);
+ httpServerExchange.getResponseSender().send("");
+ return;
+ }
+
+ HttpClient httpClient = dustspeck2.getHttpClient();
+ byte[] file = httpClient.getFile(liveUrl);
+ fileStorage.writeFile(queryString, file);
+ httpServerExchange.setStatusCode(200);
+ httpServerExchange.getResponseHeaders().put(HttpString.tryFromString("Content-Type"),"text/plain");
+ httpServerExchange.getResponseSender().send(ByteBuffer.wrap(file));
+ }
+}
diff --git a/src/main/java/moe/yo3explorer/dustspeck2/business/nuget/boundary/RegistrationsBaseUrlService.java b/src/main/java/moe/yo3explorer/dustspeck2/business/nuget/boundary/RegistrationsBaseUrlService.java
new file mode 100644
index 0000000..9f9532b
--- /dev/null
+++ b/src/main/java/moe/yo3explorer/dustspeck2/business/nuget/boundary/RegistrationsBaseUrlService.java
@@ -0,0 +1,58 @@
+package moe.yo3explorer.dustspeck2.business.nuget.boundary;
+
+import io.undertow.server.HttpServerExchange;
+import io.undertow.util.HttpString;
+import moe.yo3explorer.dustspeck2.business.nuget.control.FileStorage;
+import moe.yo3explorer.dustspeck2.business.nuget.control.HttpClient;
+import moe.yo3explorer.dustspeck2.presentation.Dustspeck2Service;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+public class RegistrationsBaseUrlService implements Dustspeck2Service
+{
+ @Override
+ public boolean canHandle(@NotNull String s)
+ {
+ if (!s.endsWith(".json"))
+ return false;
+
+ return s.startsWith("/RegistrationsBaseUrl/v3/registration5-gz-semver2/");
+ }
+
+ @Override
+ public void handleRequest(String queryString, @NotNull String[] vines, HttpServerExchange httpServerExchange) throws IOException
+ {
+ String indexName = vines[vines.length - 1];
+ String packageName = vines[vines.length - 2];
+ String onDiskFilename = queryString.replace("/RegistrationsBaseUrl","");
+
+ Dustspeck2Context dustspeck2 = Dustspeck2Context.getInstance();
+ FileStorage fileStorage = dustspeck2.getFileStorage();
+ if (fileStorage.testForFile(onDiskFilename))
+ {
+ byte[] buffer = fileStorage.getFile(onDiskFilename);
+ buffer = dustspeck2.patchJson(buffer,httpServerExchange.getHostAndPort());
+ httpServerExchange.setStatusCode(200);
+ httpServerExchange.getResponseHeaders().put(HttpString.tryFromString("Content-Type"),"application/json");
+ httpServerExchange.getResponseSender().send(ByteBuffer.wrap(buffer));
+ return;
+ }
+
+ if (dustspeck2.isOffline())
+ {
+ httpServerExchange.setStatusCode(404);
+ httpServerExchange.getResponseSender().send("");
+ return;
+ }
+
+ HttpClient httpClient = dustspeck2.getHttpClient();
+ byte[] file = httpClient.getFile(String.format("https://api.nuget.org/v3/registration5-gz-semver2/%s/%s", packageName, indexName));
+ fileStorage.writeFile(onDiskFilename, file);
+ file = dustspeck2.patchJson(file,httpServerExchange.getHostAndPort());
+ httpServerExchange.setStatusCode(200);
+ httpServerExchange.getResponseHeaders().put(HttpString.tryFromString("Content-Type"),"application/json");
+ httpServerExchange.getResponseSender().send(ByteBuffer.wrap(file));
+ }
+}
diff --git a/src/main/java/moe/yo3explorer/dustspeck2/business/nuget/boundary/SearchQueryService.java b/src/main/java/moe/yo3explorer/dustspeck2/business/nuget/boundary/SearchQueryService.java
new file mode 100644
index 0000000..a07ea64
--- /dev/null
+++ b/src/main/java/moe/yo3explorer/dustspeck2/business/nuget/boundary/SearchQueryService.java
@@ -0,0 +1,53 @@
+package moe.yo3explorer.dustspeck2.business.nuget.boundary;
+
+import io.undertow.server.HttpServerExchange;
+import io.undertow.util.HttpString;
+import moe.yo3explorer.dustspeck2.business.nuget.control.FileStorage;
+import moe.yo3explorer.dustspeck2.business.nuget.control.HttpClient;
+import moe.yo3explorer.dustspeck2.presentation.Dustspeck2HttpHandler;
+import moe.yo3explorer.dustspeck2.presentation.Dustspeck2Service;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+public class SearchQueryService implements Dustspeck2Service
+{
+ @Override
+ public boolean canHandle(@NotNull String s)
+ {
+ return s.equals("/SearchQueryService/query");
+ }
+
+ @Override
+ public void handleRequest(String queryString, String[] vines, @NotNull HttpServerExchange httpServerExchange) throws IOException
+ {
+ String filename = queryString + "/" + httpServerExchange.getQueryString();
+
+ Dustspeck2Context dustspeck2 = Dustspeck2Context.getInstance();
+ FileStorage fileStorage = dustspeck2.getFileStorage();
+ if (fileStorage.testForFile(filename))
+ {
+ byte[] buffer = fileStorage.getFile(filename);
+ buffer = dustspeck2.patchJson(buffer,httpServerExchange.getHostAndPort());
+ httpServerExchange.setStatusCode(200);
+ httpServerExchange.getResponseHeaders().put(HttpString.tryFromString("Content-Type"),"application/json");
+ httpServerExchange.getResponseSender().send(ByteBuffer.wrap(buffer));
+ return;
+ }
+
+ if (dustspeck2.isOffline())
+ {
+ httpServerExchange.setStatusCode(404);
+ return;
+ }
+
+ HttpClient httpClient = dustspeck2.getHttpClient();
+ byte[] buffer = httpClient.getFile("https://azuresearch-usnc.nuget.org/query", httpServerExchange.getQueryString());
+ fileStorage.writeFile(filename,buffer);
+ buffer = dustspeck2.patchJson(buffer,httpServerExchange.getHostAndPort());
+ httpServerExchange.setStatusCode(200);
+ httpServerExchange.getResponseHeaders().put(HttpString.tryFromString("Content-Type"),"application/json");
+ httpServerExchange.getResponseSender().send(ByteBuffer.wrap(buffer));
+ }
+}
diff --git a/src/main/java/moe/yo3explorer/dustspeck2/business/nuget/boundary/VersionPageService.java b/src/main/java/moe/yo3explorer/dustspeck2/business/nuget/boundary/VersionPageService.java
new file mode 100644
index 0000000..762a3e7
--- /dev/null
+++ b/src/main/java/moe/yo3explorer/dustspeck2/business/nuget/boundary/VersionPageService.java
@@ -0,0 +1,62 @@
+package moe.yo3explorer.dustspeck2.business.nuget.boundary;
+
+import io.undertow.server.HttpServerExchange;
+import io.undertow.util.HttpString;
+import moe.yo3explorer.dustspeck2.business.nuget.control.FileStorage;
+import moe.yo3explorer.dustspeck2.business.nuget.control.HttpClient;
+import moe.yo3explorer.dustspeck2.presentation.Dustspeck2Service;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+public class VersionPageService implements Dustspeck2Service
+{
+ @Override
+ public boolean canHandle(@NotNull String s)
+ {
+ return s.startsWith("/v3/registration5-gz-semver2/");
+ }
+
+ @Override
+ public void handleRequest(String queryString, String[] vines, HttpServerExchange httpServerExchange) throws IOException
+ {
+ String onDiskFilename = String.format("RegistrationsBaseUrl/%s",queryString);
+ Dustspeck2Context dustspeck2 = Dustspeck2Context.getInstance();
+ String mime = dustspeck2.guessMimeType(queryString);
+
+ FileStorage fileStorage = dustspeck2.getFileStorage();
+ if (fileStorage.testForFile(onDiskFilename))
+ {
+ byte[] buffer = fileStorage.getFile(queryString);
+ if (queryString.endsWith(".json"))
+ {
+ buffer = dustspeck2.patchJson(buffer, httpServerExchange.getHostAndPort());
+ }
+ httpServerExchange.setStatusCode(200);
+ httpServerExchange.getResponseHeaders().put(HttpString.tryFromString("Content-Type"),mime);
+ httpServerExchange.getResponseSender().send(ByteBuffer.wrap(buffer));
+ return;
+ }
+
+ if (dustspeck2.isOffline())
+ {
+ httpServerExchange.setStatusCode(404);
+ httpServerExchange.getResponseSender().send("");
+ return;
+ }
+
+ HttpClient httpClient = dustspeck2.getHttpClient();
+ byte[] file = httpClient.getFile(String.format("https://api.nuget.org/%s",queryString));
+ fileStorage.writeFile(queryString, file);
+ if (queryString.endsWith(".json"))
+ {
+ file = dustspeck2.patchJson(file, httpServerExchange.getHostAndPort());
+ }
+ httpServerExchange.setStatusCode(200);
+ httpServerExchange.getResponseHeaders().put(HttpString.tryFromString("Content-Type"),mime);
+ httpServerExchange.getResponseSender().send(ByteBuffer.wrap(file));
+ }
+
+
+}
diff --git a/src/main/java/moe/yo3explorer/dustspeck2/business/nuget/boundary/VulnerabilityBaseService.java b/src/main/java/moe/yo3explorer/dustspeck2/business/nuget/boundary/VulnerabilityBaseService.java
new file mode 100644
index 0000000..002d600
--- /dev/null
+++ b/src/main/java/moe/yo3explorer/dustspeck2/business/nuget/boundary/VulnerabilityBaseService.java
@@ -0,0 +1,60 @@
+package moe.yo3explorer.dustspeck2.business.nuget.boundary;
+
+import io.undertow.server.HttpServerExchange;
+import io.undertow.util.HttpString;
+import moe.yo3explorer.dustspeck2.business.nuget.control.FileStorage;
+import moe.yo3explorer.dustspeck2.business.nuget.control.HttpClient;
+import moe.yo3explorer.dustspeck2.presentation.Dustspeck2Service;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+public class VulnerabilityBaseService implements Dustspeck2Service
+{
+ @Override
+ public boolean canHandle(String s)
+ {
+ return s.startsWith("/v3-vulnerabilities/");
+ }
+
+ @Override
+ public void handleRequest(String queryString, String[] vines, HttpServerExchange httpServerExchange) throws IOException
+ {
+ String originalUrl = "https://api.nuget.org" + queryString;
+ Dustspeck2Context dustspeck2 = Dustspeck2Context.getInstance();
+ String mime = dustspeck2.guessMimeType(queryString);
+ String onDiskFilename = queryString;
+
+ FileStorage fileStorage = dustspeck2.getFileStorage();
+ if (fileStorage.testForFile(onDiskFilename))
+ {
+ byte[] buffer = fileStorage.getFile(queryString);
+ if (queryString.endsWith(".json"))
+ {
+ buffer = dustspeck2.patchJson(buffer, httpServerExchange.getHostAndPort());
+ }
+ httpServerExchange.setStatusCode(200);
+ httpServerExchange.getResponseHeaders().put(HttpString.tryFromString("Content-Type"),mime);
+ httpServerExchange.getResponseSender().send(ByteBuffer.wrap(buffer));
+ return;
+ }
+
+ if (dustspeck2.isOffline())
+ {
+ httpServerExchange.setStatusCode(404);
+ httpServerExchange.getResponseSender().send("");
+ return;
+ }
+
+ HttpClient httpClient = dustspeck2.getHttpClient();
+ byte[] file = httpClient.getFile(originalUrl);
+ fileStorage.writeFile(queryString, file);
+ if (queryString.endsWith(".json"))
+ {
+ file = dustspeck2.patchJson(file, httpServerExchange.getHostAndPort());
+ }
+ httpServerExchange.setStatusCode(200);
+ httpServerExchange.getResponseHeaders().put(HttpString.tryFromString("Content-Type"),mime);
+ httpServerExchange.getResponseSender().send(ByteBuffer.wrap(file));
+ }
+}
diff --git a/src/main/java/moe/yo3explorer/dustspeck2/business/nuget/boundary/VulnerabilityInfoService.java b/src/main/java/moe/yo3explorer/dustspeck2/business/nuget/boundary/VulnerabilityInfoService.java
new file mode 100644
index 0000000..7b922cc
--- /dev/null
+++ b/src/main/java/moe/yo3explorer/dustspeck2/business/nuget/boundary/VulnerabilityInfoService.java
@@ -0,0 +1,57 @@
+package moe.yo3explorer.dustspeck2.business.nuget.boundary;
+
+import io.undertow.server.HttpServerExchange;
+import io.undertow.util.HttpString;
+import moe.yo3explorer.dustspeck2.business.nuget.control.FileStorage;
+import moe.yo3explorer.dustspeck2.business.nuget.control.HttpClient;
+import moe.yo3explorer.dustspeck2.presentation.Dustspeck2Service;
+import org.apache.commons.io.IOUtils;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+
+public class VulnerabilityInfoService implements Dustspeck2Service
+{
+ @Override
+ public boolean canHandle(@NotNull String s)
+ {
+ return s.equals("/VulnerabilityInfo/v3/vulnerabilities/index.json");
+ }
+
+ @Override
+ public void handleRequest(String queryString, String[] vines, HttpServerExchange httpServerExchange) throws IOException
+ {
+ String onDiskFilename = queryString.replace("/VulnerabilityInfo/","");
+
+ Dustspeck2Context dustspeck2 = Dustspeck2Context.getInstance();
+ FileStorage fileStorage = dustspeck2.getFileStorage();
+ if (fileStorage.testForFile(onDiskFilename))
+ {
+ byte[] buffer = fileStorage.getFile(onDiskFilename);
+ buffer = dustspeck2.patchJson(buffer,httpServerExchange.getHostAndPort());
+ httpServerExchange.setStatusCode(200);
+ httpServerExchange.getResponseHeaders().put(HttpString.tryFromString("Content-Type"),"application/json");
+ httpServerExchange.getResponseSender().send(ByteBuffer.wrap(buffer));
+ return;
+ }
+
+ if (dustspeck2.isOffline())
+ {
+ httpServerExchange.setStatusCode(404);
+ httpServerExchange.getResponseSender().send("");
+ return;
+ }
+
+ HttpClient httpClient = dustspeck2.getHttpClient();
+ byte[] file = httpClient.getFile("https://api.nuget.org/v3/vulnerabilities/index.json");
+ fileStorage.writeFile(onDiskFilename, file);
+ file = dustspeck2.patchJson(file,httpServerExchange.getHostAndPort());
+ httpServerExchange.setStatusCode(200);
+ httpServerExchange.getResponseHeaders().put(HttpString.tryFromString("Content-Type"),"application/json");
+ httpServerExchange.getResponseSender().send(ByteBuffer.wrap(file));
+ }
+
+
+}
diff --git a/src/main/java/moe/yo3explorer/dustspeck2/business/nuget/control/DataDirectoryNotConfiguredException.java b/src/main/java/moe/yo3explorer/dustspeck2/business/nuget/control/DataDirectoryNotConfiguredException.java
new file mode 100644
index 0000000..547a528
--- /dev/null
+++ b/src/main/java/moe/yo3explorer/dustspeck2/business/nuget/control/DataDirectoryNotConfiguredException.java
@@ -0,0 +1,5 @@
+package moe.yo3explorer.dustspeck2.business.nuget.control;
+
+public class DataDirectoryNotConfiguredException extends RuntimeException
+{
+}
diff --git a/src/main/java/moe/yo3explorer/dustspeck2/business/nuget/control/FileStorage.java b/src/main/java/moe/yo3explorer/dustspeck2/business/nuget/control/FileStorage.java
new file mode 100644
index 0000000..328a784
--- /dev/null
+++ b/src/main/java/moe/yo3explorer/dustspeck2/business/nuget/control/FileStorage.java
@@ -0,0 +1,58 @@
+package moe.yo3explorer.dustspeck2.business.nuget.control;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+public class FileStorage
+{
+ private final Path outdirAsPath;
+ private final String outdirAsString;
+ private final File outdirAsFile;
+ private final Logger logger;
+
+ public FileStorage(String dataDirectory) throws IOException
+ {
+ logger = LogManager.getLogger(getClass());
+ logger.info("Initialized File Storage");
+
+ outdirAsString = dataDirectory;
+ outdirAsPath = Path.of(dataDirectory);
+ outdirAsFile = outdirAsPath.toFile();
+ if (!outdirAsFile.isDirectory())
+ {
+ Files.createDirectories(outdirAsPath);
+ logger.info(String.format("Created directory: %s", dataDirectory));
+ }
+ }
+
+ public boolean testForFile(String queryString)
+ {
+ Path path = Paths.get(outdirAsString,queryString);
+ File file = path.toFile();
+ return file.isFile();
+ }
+
+ public byte[] getFile(String queryString) throws IOException
+ {
+ Path path = Paths.get(outdirAsString,queryString);
+ FileInputStream fis = new FileInputStream(path.toFile());
+ byte[] result = fis.readAllBytes();
+ fis.close();
+ return result;
+ }
+
+ public void writeFile(String queryString, byte[] file) throws IOException
+ {
+ Path path = Paths.get(outdirAsString,queryString);
+ Files.createDirectories(path.getParent());
+ Files.write(path,file);
+ }
+}
diff --git a/src/main/java/moe/yo3explorer/dustspeck2/business/nuget/control/FileStorageAlreadyWorkingException.java b/src/main/java/moe/yo3explorer/dustspeck2/business/nuget/control/FileStorageAlreadyWorkingException.java
new file mode 100644
index 0000000..62410d3
--- /dev/null
+++ b/src/main/java/moe/yo3explorer/dustspeck2/business/nuget/control/FileStorageAlreadyWorkingException.java
@@ -0,0 +1,5 @@
+package moe.yo3explorer.dustspeck2.business.nuget.control;
+
+public class FileStorageAlreadyWorkingException extends RuntimeException
+{
+}
diff --git a/src/main/java/moe/yo3explorer/dustspeck2/business/nuget/control/HttpClient.java b/src/main/java/moe/yo3explorer/dustspeck2/business/nuget/control/HttpClient.java
new file mode 100644
index 0000000..0a9570c
--- /dev/null
+++ b/src/main/java/moe/yo3explorer/dustspeck2/business/nuget/control/HttpClient.java
@@ -0,0 +1,36 @@
+package moe.yo3explorer.dustspeck2.business.nuget.control;
+
+import okhttp3.Call;
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.Response;
+
+import java.io.IOException;
+
+public class HttpClient
+{
+ private final OkHttpClient client;
+
+ public HttpClient()
+ {
+ this.client = new OkHttpClient();
+ }
+
+ public byte[] getFile(String path) throws IOException
+ {
+ Request request = new Request.Builder()
+ .url(path)
+ .build();
+
+ Call call = client.newCall(request);
+ Response response = call.execute();
+ byte[] bytes = response.body().bytes();
+ return bytes;
+ }
+
+ public byte[] getFile(String path, String queryString) throws IOException
+ {
+ String newPath = String.format("%s?%s",path,queryString);
+ return getFile(newPath);
+ }
+}
diff --git a/src/main/java/moe/yo3explorer/dustspeck2/business/nuget/control/InternetConnectionChecker.java b/src/main/java/moe/yo3explorer/dustspeck2/business/nuget/control/InternetConnectionChecker.java
new file mode 100644
index 0000000..1c444db
--- /dev/null
+++ b/src/main/java/moe/yo3explorer/dustspeck2/business/nuget/control/InternetConnectionChecker.java
@@ -0,0 +1,67 @@
+package moe.yo3explorer.dustspeck2.business.nuget.control;
+
+import okhttp3.Call;
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.Response;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import java.io.IOException;
+import java.util.Random;
+
+public class InternetConnectionChecker
+{
+ private InternetConnectionChecker() {}
+
+ public static boolean checkInternetConnection()
+ {
+ Logger logger = LogManager.getLogger(InternetConnectionChecker.class);
+
+ String[] sites = new String[]
+ {
+ "http://www.google.com", "http://www.youtube.com", "http://www.amazon.com",
+ "http://www.archive.org", "http://store.steampowered.com", "http://www.microsoft.com",
+ "http://www.apple.com", "http://www.facebook.com", "http://www.nintendo.co.jp",
+ "http://www.sony.co.jp", "http://www.discord.com", "http://www.netflix.com",
+ "http://www.spotify.com", "http://www.vw.de", "http://www.nestle.com", "http://www.disney.com",
+ "http://www.toyota.co.jp", "http://www.bp.com", "http://www.shell.com", "http://www.basf.de",
+ "http://www.siemens.de", "http://www.boeing.com", "http://www.ibm.com", "http://www.axa.fr",
+ "http://www.bahn.de", "http://www.intel.com", "http://www.samsung.com", "http://www.tesla.com",
+ "http://de.wikipedia.org", "http://www.debian.org", "http://www.sega.co.jp",
+ "http://www.nuget.org/", "http://www.github.com", "http://www.msftconnecttest.com/ncsi.txt"
+ };
+
+ int fails = 0;
+ OkHttpClient client = new OkHttpClient();
+ Random rng = new Random();
+ while (true)
+ {
+ String site = sites[rng.nextInt(sites.length)];
+
+ Request request = new Request.Builder()
+ .url(site)
+ .build();
+
+ try
+ {
+ Call call = client.newCall(request);
+ Response execute = call.execute();
+ int code = execute.code();
+ logger.info(String.format("GET %s returned %d, so I'm gonna assume I have internet.",site,code));
+ execute.close();
+ return true;
+ }
+ catch (Throwable t)
+ {
+ fails++;
+ }
+ if (fails >= 3)
+ {
+ logger.info("Could not verify the internet connection, so I'm gonna assume I'm offline.");
+ return false;
+ }
+ }
+
+ }
+}
diff --git a/src/main/java/moe/yo3explorer/dustspeck2/presentation/Dustspeck2HttpHandler.java b/src/main/java/moe/yo3explorer/dustspeck2/presentation/Dustspeck2HttpHandler.java
new file mode 100644
index 0000000..a3f20a4
--- /dev/null
+++ b/src/main/java/moe/yo3explorer/dustspeck2/presentation/Dustspeck2HttpHandler.java
@@ -0,0 +1,55 @@
+package moe.yo3explorer.dustspeck2.presentation;
+
+import io.undertow.server.HttpHandler;
+import io.undertow.server.HttpServerExchange;
+import io.undertow.util.HttpString;
+import moe.yo3explorer.dustspeck2.business.nuget.boundary.Dustspeck2Context;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.List;
+import java.util.ServiceLoader;
+
+public class Dustspeck2HttpHandler implements HttpHandler
+{
+ private static Logger logger;
+ private final List services;
+
+ public Dustspeck2HttpHandler()
+ {
+ if (logger == null)
+ {
+ logger = LogManager.getLogger(getClass());
+ logger.info("Loaded the HTTP handler.");
+ }
+
+ ServiceLoader loader = ServiceLoader.load(Dustspeck2Service.class);
+ services = loader.stream().map(ServiceLoader.Provider::get).toList();
+ logger.info(String.format("Discovered %d services",services.size()));
+ }
+
+ @Override
+ public void handleRequest(@NotNull HttpServerExchange httpServerExchange) throws Exception
+ {
+ String queryString = httpServerExchange.getRequestURI();
+ logger.info(String.format("%s %s", httpServerExchange.getRequestMethod().toString(), queryString));
+
+ String[] vines = queryString.split("/");
+
+ httpServerExchange.getResponseHeaders().put(new HttpString("Server"),"sophia.net/Dustspeck2");
+
+ for (Dustspeck2Service serviceCandidate: services)
+ {
+ if (serviceCandidate.canHandle(queryString))
+ {
+ serviceCandidate.handleRequest(queryString, vines, httpServerExchange);
+ return;
+ }
+ }
+
+ httpServerExchange.setStatusCode(404);
+ httpServerExchange.getResponseSender().send("");
+ httpServerExchange.getResponseSender().close();
+ }
+}
diff --git a/src/main/java/moe/yo3explorer/dustspeck2/presentation/Dustspeck2Service.java b/src/main/java/moe/yo3explorer/dustspeck2/presentation/Dustspeck2Service.java
new file mode 100644
index 0000000..8d6df5d
--- /dev/null
+++ b/src/main/java/moe/yo3explorer/dustspeck2/presentation/Dustspeck2Service.java
@@ -0,0 +1,13 @@
+package moe.yo3explorer.dustspeck2.presentation;
+
+import io.undertow.server.HttpHandler;
+import io.undertow.server.HttpServerExchange;
+
+import java.io.IOException;
+
+public interface Dustspeck2Service
+{
+ boolean canHandle(String s);
+
+ void handleRequest(String queryString, String[] vines, HttpServerExchange httpServerExchange) throws IOException;
+}
diff --git a/src/main/java/moe/yo3explorer/dustspeck2/presentation/EnvironmentVariableMissingException.java b/src/main/java/moe/yo3explorer/dustspeck2/presentation/EnvironmentVariableMissingException.java
new file mode 100644
index 0000000..df37020
--- /dev/null
+++ b/src/main/java/moe/yo3explorer/dustspeck2/presentation/EnvironmentVariableMissingException.java
@@ -0,0 +1,9 @@
+package moe.yo3explorer.dustspeck2.presentation;
+
+public class EnvironmentVariableMissingException extends RuntimeException
+{
+ public EnvironmentVariableMissingException(String setting)
+ {
+ super(String.format("Environment vatiable %s is not configured.", setting));
+ }
+}
diff --git a/src/main/java/moe/yo3explorer/dustspeck2/presentation/Main.java b/src/main/java/moe/yo3explorer/dustspeck2/presentation/Main.java
new file mode 100644
index 0000000..1923e47
--- /dev/null
+++ b/src/main/java/moe/yo3explorer/dustspeck2/presentation/Main.java
@@ -0,0 +1,40 @@
+package moe.yo3explorer.dustspeck2.presentation;
+
+import io.undertow.Undertow;
+import io.undertow.server.HttpHandler;
+import moe.yo3explorer.dustspeck2.business.nuget.boundary.Dustspeck2Context;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import java.util.Map;
+
+public class Main
+{
+ public static void main(String[] args)
+ {
+ Logger logger = LogManager.getLogger(Main.class);
+ logger.info("Hello! This is Dustspeck2!");
+
+ Map getenv = System.getenv();
+ String[] env = new String[] {"DATA_DIRECTORY"};
+ for (String setting : env)
+ {
+ if (!getenv.containsKey(setting))
+ {
+ throw new EnvironmentVariableMissingException(setting);
+ }
+ }
+
+ Dustspeck2Context dustspeck2 = Dustspeck2Context.getInstance();
+ dustspeck2.setDataDirectory(getenv.get("DATA_DIRECTORY"));
+
+ Dustspeck2HttpHandler dustspeck2HttpHandler = new Dustspeck2HttpHandler();
+
+ Undertow server = Undertow.builder()
+ .addHttpListener(8080, "0.0.0.0")
+ .setHandler(dustspeck2HttpHandler).build();
+
+ logger.info("Began listening on port 8080...");
+ server.start();
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/moe/yo3explorer/dustspeck2/presentation/ResourceService.java b/src/main/java/moe/yo3explorer/dustspeck2/presentation/ResourceService.java
new file mode 100644
index 0000000..b288e0e
--- /dev/null
+++ b/src/main/java/moe/yo3explorer/dustspeck2/presentation/ResourceService.java
@@ -0,0 +1,63 @@
+package moe.yo3explorer.dustspeck2.presentation;
+
+import io.undertow.server.HttpServerExchange;
+import io.undertow.util.HttpString;
+import org.apache.commons.io.IOUtils;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+
+public class ResourceService implements Dustspeck2Service
+{
+ @Override
+ public boolean canHandle(@NotNull String s)
+ {
+ switch (s)
+ {
+ case "/index.json":
+ case "/favicon.ico":
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ private String indexJson;
+ private byte[] faviconIco;
+
+ @Override
+ public void handleRequest(@NotNull String queryString, String[] vines, @NotNull HttpServerExchange httpServerExchange) throws IOException
+ {
+ String hostAndPort = httpServerExchange.getHostAndPort();
+ hostAndPort = String.format("http://%s",hostAndPort);
+
+ switch (queryString)
+ {
+ case "/index.json":
+ if (indexJson == null)
+ {
+ InputStream indexStream = getClass().getClassLoader().getResourceAsStream("index.json");
+ indexJson = IOUtils.toString(indexStream, StandardCharsets.UTF_8);
+ indexJson = indexJson.replace("{0}", hostAndPort);
+ }
+ httpServerExchange.getResponseHeaders().put(HttpString.tryFromString("Content-Type"),"application/json");
+ httpServerExchange.getResponseSender().send(indexJson);
+ httpServerExchange.getResponseSender().close();
+ return;
+ case "/favicon.ico":
+ if (faviconIco == null)
+ {
+ InputStream faviconStream = getClass().getClassLoader().getResourceAsStream("favicon.ico");
+ faviconIco = IOUtils.toByteArray(faviconStream);
+ }
+ httpServerExchange.getResponseHeaders().put(HttpString.tryFromString("Content-Type"),"application/json");
+ httpServerExchange.getResponseSender().send(ByteBuffer.wrap(faviconIco));
+ httpServerExchange.getResponseSender().close();
+ return;
+ }
+ }
+
+}
diff --git a/src/main/java/moe/yo3explorer/dustspeck2/presentation/RootRedirectService.java b/src/main/java/moe/yo3explorer/dustspeck2/presentation/RootRedirectService.java
new file mode 100644
index 0000000..b2549cd
--- /dev/null
+++ b/src/main/java/moe/yo3explorer/dustspeck2/presentation/RootRedirectService.java
@@ -0,0 +1,25 @@
+package moe.yo3explorer.dustspeck2.presentation;
+
+import io.undertow.server.HttpServerExchange;
+import io.undertow.util.HttpString;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.IOException;
+
+public class RootRedirectService implements Dustspeck2Service
+{
+ @Override
+ public boolean canHandle(String s)
+ {
+ return s.equals("/");
+ }
+
+ @Override
+ public void handleRequest(String queryString, String[] vines, @NotNull HttpServerExchange httpServerExchange) throws IOException
+ {
+ httpServerExchange.setStatusCode(301);
+ httpServerExchange.getResponseHeaders().put(HttpString.tryFromString("Location"),"/index.json");
+ httpServerExchange.getResponseSender().send("");
+ httpServerExchange.getResponseSender().close();
+ }
+}
diff --git a/src/main/resources/META-INF/services/moe.yo3explorer.dustspeck2.presentation.Dustspeck2Service b/src/main/resources/META-INF/services/moe.yo3explorer.dustspeck2.presentation.Dustspeck2Service
new file mode 100644
index 0000000..1f853f6
--- /dev/null
+++ b/src/main/resources/META-INF/services/moe.yo3explorer.dustspeck2.presentation.Dustspeck2Service
@@ -0,0 +1,10 @@
+moe.yo3explorer.dustspeck2.presentation.RootRedirectService
+moe.yo3explorer.dustspeck2.presentation.ResourceService
+moe.yo3explorer.dustspeck2.business.nuget.boundary.RegistrationsBaseUrlService
+moe.yo3explorer.dustspeck2.business.nuget.boundary.ReadmeUriTemplateService
+moe.yo3explorer.dustspeck2.business.nuget.boundary.VulnerabilityInfoService
+moe.yo3explorer.dustspeck2.business.nuget.boundary.SearchQueryService
+moe.yo3explorer.dustspeck2.business.nuget.boundary.VersionPageService
+moe.yo3explorer.dustspeck2.business.nuget.boundary.PackageBaseAddressService
+moe.yo3explorer.dustspeck2.business.nuget.boundary.VulnerabilityBaseService
+moe.yo3explorer.dustspeck2.business.nuget.boundary.IconService
\ No newline at end of file
diff --git a/src/main/resources/favicon.ico b/src/main/resources/favicon.ico
new file mode 100644
index 0000000..fb0980c
Binary files /dev/null and b/src/main/resources/favicon.ico differ
diff --git a/src/main/resources/index.json b/src/main/resources/index.json
new file mode 100644
index 0000000..ec0a12a
--- /dev/null
+++ b/src/main/resources/index.json
@@ -0,0 +1,182 @@
+{
+ "version": "3.0.0",
+ "resources": [
+ {
+ "@id": "{0}/SearchQueryService/query",
+ "@type": "SearchQueryService",
+ "comment": "Query endpoint of NuGet Search service (primary)"
+ },
+ {
+ "@id": "{0}/SearchAutocompleteService/autocomplete",
+ "@type": "SearchAutocompleteService",
+ "comment": "Autocomplete endpoint of NuGet Search service (primary)"
+ },
+ {
+ "@id": "{0}/SearchGalleryQueryService/",
+ "@type": "SearchGalleryQueryService/3.0.0-rc",
+ "comment": "Azure Website based Search Service used by Gallery (primary)"
+ },
+ {
+ "@id": "{0}/RegistrationsBaseUrl/v3/registration5-semver1/",
+ "@type": "RegistrationsBaseUrl",
+ "comment": "Base URL of Azure storage where NuGet package registration info is stored"
+ },
+ {
+ "@id": "{0}/PackageBaseAddress/v3-flatcontainer/",
+ "@type": "PackageBaseAddress/3.0.0",
+ "comment": "Base URL of where NuGet packages are stored, in the format {0}/api.nuget.org/v3-flatcontainer/{id-lower}/{version-lower}/{id-lower}.{version-lower}.nupkg"
+ },
+ {
+ "@id": "{0}/LegacyGallery/api/v2",
+ "@type": "LegacyGallery"
+ },
+ {
+ "@id": "{0}/LegacyGallery/api/v2",
+ "@type": "LegacyGallery/2.0.0"
+ },
+ {
+ "@id": "{0}/PackagePublish/api/v2/package",
+ "@type": "PackagePublish/2.0.0"
+ },
+ {
+ "@id": "{0}/SymbolPackagePublish/api/v2/symbolpackage",
+ "@type": "SymbolPackagePublish/4.9.0",
+ "comment": "The gallery symbol publish endpoint."
+ },
+ {
+ "@id": "{0}/SearchQueryService/query",
+ "@type": "SearchQueryService/3.0.0-rc",
+ "comment": "Query endpoint of NuGet Search service (primary) used by RC clients"
+ },
+ {
+ "@id": "{0}/SearchQueryService/query",
+ "@type": "SearchQueryService/3.0.0-rc",
+ "comment": "Query endpoint of NuGet Search service (secondary) used by RC clients"
+ },
+ {
+ "@id": "{0}/SearchQueryService/query",
+ "@type": "SearchQueryService/3.5.0",
+ "comment": "Query endpoint of NuGet Search service (primary) that supports package type filtering"
+ },
+ {
+ "@id": "{0}/SearchQueryService/query",
+ "@type": "SearchQueryService/3.5.0",
+ "comment": "Query endpoint of NuGet Search service (secondary) that supports package type filtering"
+ },
+ {
+ "@id": "{0}/SearchAutocompleteService/autocomplete",
+ "@type": "SearchAutocompleteService/3.0.0-rc",
+ "comment": "Autocomplete endpoint of NuGet Search service (primary) used by RC clients"
+ },
+ {
+ "@id": "{0}/SearchAutocompleteService/autocomplete",
+ "@type": "SearchAutocompleteService/3.0.0-rc",
+ "comment": "Autocomplete endpoint of NuGet Search service (secondary) used by RC clients"
+ },
+ {
+ "@id": "{0}/SearchAutocompleteService/autocomplete",
+ "@type": "SearchAutocompleteService/3.5.0",
+ "comment": "Autocomplete endpoint of NuGet Search service (primary) that supports package type filtering"
+ },
+ {
+ "@id": "{0}/SearchAutocompleteService/autocomplete",
+ "@type": "SearchAutocompleteService/3.5.0",
+ "comment": "Autocomplete endpoint of NuGet Search service (secondary) that supports package type filtering"
+ },
+ {
+ "@id": "{0}/RegistrationsBaseUrl/v3/registration5-semver1/",
+ "@type": "RegistrationsBaseUrl/3.0.0-rc",
+ "comment": "Base URL of Azure storage where NuGet package registration info is stored used by RC clients. This base URL does not include SemVer 2.0.0 packages."
+ },
+ {
+ "@id": "{0}/ReportAbuseUriTemplate/packages/{id}/{version}/ReportAbuse",
+ "@type": "ReportAbuseUriTemplate/3.0.0-rc",
+ "comment": "URI template used by NuGet Client to construct Report Abuse URL for packages used by RC clients"
+ },
+ {
+ "@id": "{0}/PackageDisplayMetadataUriTemplate/v3/registration5-semver1/{id-lower}/index.json",
+ "@type": "PackageDisplayMetadataUriTemplate/3.0.0-rc",
+ "comment": "URI template used by NuGet Client to construct display metadata for Packages using ID"
+ },
+ {
+ "@id": "{0}/PackageVersionDisplayMetadataUriTemplate/v3/registration5-semver1/{id-lower}/{version-lower}.json",
+ "@type": "PackageVersionDisplayMetadataUriTemplate/3.0.0-rc",
+ "comment": "URI template used by NuGet Client to construct display metadata for Packages using ID, Version"
+ },
+ {
+ "@id": "{0}/SearchQueryService/query",
+ "@type": "SearchQueryService/3.0.0-beta",
+ "comment": "Query endpoint of NuGet Search service (primary) used by beta clients"
+ },
+ {
+ "@id": "{0}/SearchQueryService/query",
+ "@type": "SearchQueryService/3.0.0-beta",
+ "comment": "Query endpoint of NuGet Search service (secondary) used by beta clients"
+ },
+ {
+ "@id": "{0}/SearchAutocompleteService/autocomplete",
+ "@type": "SearchAutocompleteService/3.0.0-beta",
+ "comment": "Autocomplete endpoint of NuGet Search service (primary) used by beta clients"
+ },
+ {
+ "@id": "{0}/SearchAutocompleteService/autocomplete",
+ "@type": "SearchAutocompleteService/3.0.0-beta",
+ "comment": "Autocomplete endpoint of NuGet Search service (secondary) used by beta clients"
+ },
+ {
+ "@id": "{0}/RegistrationsBaseUrl/v3/registration5-semver1/",
+ "@type": "RegistrationsBaseUrl/3.0.0-beta",
+ "comment": "Base URL of Azure storage where NuGet package registration info is stored used by Beta clients. This base URL does not include SemVer 2.0.0 packages."
+ },
+ {
+ "@id": "{0}/ReportAbuseUriTemplate/packages/{id}/{version}/ReportAbuse",
+ "@type": "ReportAbuseUriTemplate/3.0.0-beta",
+ "comment": "URI template used by NuGet Client to construct Report Abuse URL for packages"
+ },
+ {
+ "@id": "{0}/PackageDetailsUriTemplate/packages/{id}/{version}?_src=template",
+ "@type": "PackageDetailsUriTemplate/5.1.0",
+ "comment": "URI template used by NuGet Client to construct details URL for packages"
+ },
+ {
+ "@id": "{0}/OwnerDetailsUriTemplate/profiles/{owner}?_src=template",
+ "@type": "OwnerDetailsUriTemplate/6.11.0",
+ "comment": "URI template used by NuGet Client to construct owner URL for packages"
+ },
+ {
+ "@id": "{0}/RegistrationsBaseUrl/v3/registration5-gz-semver1/",
+ "@type": "RegistrationsBaseUrl/3.4.0",
+ "comment": "Base URL of Azure storage where NuGet package registration info is stored in GZIP format. This base URL does not include SemVer 2.0.0 packages."
+ },
+ {
+ "@id": "{0}/RegistrationsBaseUrl/v3/registration5-gz-semver2/",
+ "@type": "RegistrationsBaseUrl/3.6.0",
+ "comment": "Base URL of Azure storage where NuGet package registration info is stored in GZIP format. This base URL includes SemVer 2.0.0 packages."
+ },
+ {
+ "@id": "{0}/RegistrationsBaseUrl/v3/registration5-gz-semver2/",
+ "@type": "RegistrationsBaseUrl/Versioned",
+ "clientVersion": "4.3.0-alpha",
+ "comment": "Base URL of Azure storage where NuGet package registration info is stored in GZIP format. This base URL includes SemVer 2.0.0 packages."
+ },
+ {
+ "@id": "{0}/VulnerabilityInfo/v3/vulnerabilities/index.json",
+ "@type": "VulnerabilityInfo/6.7.0",
+ "comment": "The endpoint for discovering information about vulnerabilities of packages in this package source."
+ },
+ {
+ "@id": "{0}/Catalog/v3/catalog0/index.json",
+ "@type": "Catalog/3.0.0",
+ "comment": "Index of the NuGet package catalog."
+ },
+ {
+ "@id": "{0}/ReadmeUriTemplate/v3-flatcontainer/{lower_id}/{lower_version}/readme",
+ "@type": "ReadmeUriTemplate/6.13.0",
+ "comment": "URI template used by NuGet Client to construct a URL for downloading a package's README."
+ }
+ ],
+ "@context": {
+ "@vocab": "http://schema.nuget.org/services#",
+ "comment": "http://www.w3.org/2000/01/rdf-schema#comment"
+ }
+}
\ No newline at end of file
diff --git a/src/main/resources/log4j2.xml b/src/main/resources/log4j2.xml
new file mode 100644
index 0000000..d053883
--- /dev/null
+++ b/src/main/resources/log4j2.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file