LibJF/libjf-web-v0/src/main/java/io/gitlab/jfronny/libjf/web/impl/JfWebServer.java

207 lines
7.9 KiB
Java

package io.gitlab.jfronny.libjf.web.impl;
import io.gitlab.jfronny.libjf.LibJf;
import io.gitlab.jfronny.libjf.web.api.*;
import io.gitlab.jfronny.libjf.web.impl.util.WebPaths;
import io.gitlab.jfronny.libjf.web.impl.util.bluemapcore.*;
import net.fabricmc.loader.api.FabricLoader;
import javax.management.openmbean.KeyAlreadyExistsException;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Objects;
import java.util.stream.Stream;
public class JfWebServer implements WebServer {
private HttpServer server = null;
private final RequestHandler handler = new RequestHandler();
private final int port;
private final int maxConnections;
private final DefaultFileHost dfh = new DefaultFileHost();
public JfWebServer(int port, int maxConnections) {
this.port = port;
this.maxConnections = maxConnections;
}
@Override
public String register(String webPath, ContentProvider provider) {
webPath = WebPaths.simplify(webPath);
if (handler.contentProviders.containsKey(webPath))
throw new KeyAlreadyExistsException("A ContentProvider already exists at that address (" + handler.contentProviders.get(webPath).getClass() + ")");
handler.contentProviders.put(webPath, provider);
return WebPaths.concat(getServerRoot(), webPath);
}
@Override
public String registerFile(String webPath, Path file, Boolean readOnSend) throws IOException {
if (readOnSend) {
if (!Files.exists(file))
throw new FileNotFoundException();
register(webPath, s -> {
HttpResponse resp = new HttpResponse(HttpStatusCode.OK);
resp.addHeader("Content-Type", Files.probeContentType(file));
resp.addHeader("Content-Length", String.valueOf(Files.size(file)));
FileInputStream fs = new FileInputStream(file.toFile());
resp.setData(fs);
return resp;
});
return WebPaths.concat(getServerRoot(), webPath);
}
else {
return registerFile(webPath, Files.readAllBytes(file), Files.probeContentType(file));
}
}
@Override
public String registerFile(String webPath, byte[] data, String contentType) {
return register(webPath, s -> {
HttpResponse resp = new HttpResponse(HttpStatusCode.OK);
resp.addHeader("Content-Type", contentType);
resp.addHeader("Content-Length", String.valueOf(data.length));
ByteArrayInputStream fs = new ByteArrayInputStream(data);
resp.setData(fs);
return resp;
});
}
@Override
public String registerDir(String webPath, Path dir, Boolean readOnSend) throws IOException {
try (Stream<Path> contentPath = Files.walk(dir)) {
if (readOnSend) {
return registerSubServer(webPath, (s, t) -> {
final boolean[] c = {false};
final Path[] p_f = new Path[1];
contentPath.filter(Files::isRegularFile)
.filter(Files::isReadable)
.forEach(q -> {
if (c[0])
return;
Path p = dir.toAbsolutePath().relativize(q.toAbsolutePath());
String wp = webPath;
for (Path path : p) {
wp = WebPaths.concat(wp, path.toString());
}
if (Objects.equals(WebPaths.simplify(wp), WebPaths.simplify(WebPaths.concat(t)))) {
p_f[0] = q;
c[0] = true;
}
});
HttpResponse resp;
if (c[0]) {
resp = new HttpResponse(HttpStatusCode.OK);
resp.addHeader("Content-Type", Files.probeContentType(p_f[0]));
resp.addHeader("Content-Length", String.valueOf(Files.size(p_f[0])));
FileInputStream fs = new FileInputStream(p_f[0].toFile());
resp.setData(fs);
} else {
resp = new HttpResponse(HttpStatusCode.NOT_FOUND);
}
return resp;
});
} else {
contentPath.filter(Files::isRegularFile)
.filter(Files::isReadable)
.forEach(s -> {
Path p = dir.toAbsolutePath().relativize(s.toAbsolutePath());
String wp = webPath;
for (Path path : p) wp = WebPaths.concat(wp, path.toString());
try {
registerFile(wp, s, false);
} catch (IOException e) {
LibJf.LOGGER.error("Could not register static file", e);
}
});
}
return WebPaths.concat(getServerRoot(), webPath);
}
}
@Override
public String registerSubServer(String webPath, SubServer subServer) {
return registerSubServer(webPath, new AdvancedSubServer() {
@Override
public void onStop() {
}
@Override
public void onStart() {
}
@Override
public HttpResponse handle(HttpRequest request, String[] segments) throws IOException {
return subServer.handle(request, segments);
}
});
}
@Override
public String registerSubServer(String webPath, AdvancedSubServer subServer) {
webPath = WebPaths.simplify(webPath);
if (handler.subServers.containsKey(webPath))
throw new KeyAlreadyExistsException("A Subserver already exists at that address (" + handler.subServers.get(webPath).getClass() + ")");
handler.subServers.put(webPath, subServer);
return WebPaths.concat(getServerRoot(), webPath);
}
@Override
public String getServerRoot() {
String ip = JfWebConfig.serverIp;
if (!ip.startsWith("http"))
ip = "http://" + ip;
if (JfWebConfig.portOverride != -1) {
return WebPaths.simplify(ip) + ":" + JfWebConfig.portOverride;
}
return WebPaths.simplify(ip) + ":" + JfWebConfig.port;
}
@Override
public synchronized void stop() {
for (AdvancedSubServer subServer : handler.subServers.values()) subServer.onStop();
if (server != null) {
try {
server.close();
server.join();
}
catch (InterruptedException e) {
//It is most likely already dead
}
}
}
@Override
public synchronized void restart() {
JfWebConfig.ensureValidPort();
int tmpPort = port;
if (server != null) {
tmpPort = server.getPort();
stop();
}
handler.clear();
server = new HttpServer(null, tmpPort, maxConnections, handler, () -> {
if (JfWebConfig.enableFileHost) dfh.register(this);
FabricLoader.getInstance().getEntrypointContainers(LibJf.MOD_ID + ":web", WebInit.class).forEach(entrypoint -> {
WebInit init = entrypoint.getEntrypoint();
init.register(this);
});
});
server.start();
try {
server.waitUntilReady();
} catch (InterruptedException e) {
stop();
LibJf.LOGGER.error("Server could not be readied", e);
}
for (AdvancedSubServer subServer : handler.subServers.values()) {
subServer.onStart();
}
}
@Override
public boolean isActive() {
return server != null && server.isAlive();
}
}