Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ application {
}

group = "de.craftsblock"
version = "3.7.0"
version = "3.7.1"

repositories {
mavenCentral()
Expand All @@ -29,7 +29,7 @@ dependencies {
// CraftsBlock related dependencies ---------------------------------------------------------------------------------------

// https://repo.craftsblock.de/#/releases/de/craftsblock/craftscore/bom
implementation platform("de.craftsblock.craftscore:bom:3.8.13-pre8")
implementation platform("de.craftsblock.craftscore:bom:3.8.13-pre9")

// https://repo.craftsblock.de/#/releases/de/craftsblock/craftscore/buffer
api "de.craftsblock.craftscore:buffer"
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/de/craftsblock/craftsnet/CraftsNet.java
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public class CraftsNet {
/**
* The current version of CraftsNet.
*/
public static final String version = "3.7.0";
public static final String version = "3.7.1";

private static CraftsNet instance;

Expand Down
73 changes: 47 additions & 26 deletions src/main/java/de/craftsblock/craftsnet/addon/AddonManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,9 @@
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.Unmodifiable;
import org.jetbrains.annotations.UnmodifiableView;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
Expand All @@ -25,7 +24,7 @@
*
* @author CraftsBlock
* @author Philipp Maywald
* @version 1.3.3
* @version 1.3.4
* @see Addon
* @see AddonLoader
* @since 1.0.0-SNAPSHOT
Expand All @@ -37,7 +36,8 @@ public final class AddonManager {

private final CraftsNet craftsNet;
private final Logger logger;
private final ConcurrentHashMap<String, Addon> addons = new ConcurrentHashMap<>();
private final Map<String, Addon> addons = new ConcurrentHashMap<>();
private final Map<String, Addon> addonsView = Collections.unmodifiableMap(addons);

private final AddonLoader addonLoader;

Expand Down Expand Up @@ -120,10 +120,14 @@ public void fromFiles() throws IOException {
* Method to stop the AddonManager. It is called during application shutdown.
*/
public void stop() {
addons.values().forEach(addon -> {
logger.info("Disabling addon %s", addon.getName());
this.unregister(addon);
});
synchronized (addons) {
addons.values().forEach(addon -> {
logger.info("Disabling addon %s", addon.getName());
addon.onDisable();
});

addons.values().forEach(addon -> addons.remove(addon.getName()));
}

craftsNet.getListenerRegistry().call(new AllAddonsDisabledEvent());
addons.clear();
Expand All @@ -135,7 +139,9 @@ public void stop() {
* @param addon The addon to be registered.
*/
public void register(@NotNull Addon addon) {
addons.put(addon.getName(), addon);
synchronized (addons) {
addons.put(addon.getName(), addon);
}
}

/**
Expand All @@ -144,17 +150,19 @@ public void register(@NotNull Addon addon) {
* @param addon The addon to be unregistered.
*/
public void unregister(@NotNull Addon addon) {
addons.remove(addon.getName());
addon.onDisable();
synchronized (addons) {
addon.onDisable();
addons.remove(addon.getName());
}
}

/**
* Returns a read-only view of the registered addons in the AddonManager.
*
* @return A read-only ConcurrentHashMap containing the registered addons.
*/
public @Unmodifiable @NotNull Map<String, Addon> getAddons() {
return Collections.unmodifiableMap(addons);
public @NotNull @UnmodifiableView Map<String, Addon> getAddons() {
return addonsView;
}

/**
Expand All @@ -165,14 +173,17 @@ public void unregister(@NotNull Addon addon) {
* @return An instance of the specified addon type if found, or {@code null} if not present.
*/
public <T extends Addon> @Nullable T getAddon(@NotNull Class<T> addon) {
if (HollowAddon.class.isAssignableFrom(addon))
if (HollowAddon.class.isAssignableFrom(addon)) {
throw new IllegalArgumentException(addon.getSimpleName() + "s cannot be retrieved by class, use the name instead!");
}

return addons.values().stream()
.filter(addon::isInstance)
.map(addon::cast)
.findFirst()
.orElse(null);
synchronized (addons) {
return addons.values().stream()
.filter(addon::isInstance)
.map(addon::cast)
.findFirst()
.orElse(null);
}
}

/**
Expand All @@ -185,10 +196,12 @@ public void unregister(@NotNull Addon addon) {
*/
@SuppressWarnings("unchecked")
public <T extends Addon> @Nullable T getAddon(String name) {
return (T) addons.values().stream()
.filter(addon -> addon.getName().equalsIgnoreCase(name))
.findFirst()
.orElse(null);
synchronized (addons) {
return (T) addons.values().stream()
.filter(addon -> addon.getName().equalsIgnoreCase(name))
.findFirst()
.orElse(null);
}
}

/**
Expand All @@ -199,8 +212,13 @@ public void unregister(@NotNull Addon addon) {
* @since 3.3.5-SNAPSHOT
*/
public boolean isRegistered(@NotNull Class<? extends Addon> addon) {
if (HollowAddon.class.isAssignableFrom(addon)) return false;
return addons.values().stream().anyMatch(addon::isInstance);
if (HollowAddon.class.isAssignableFrom(addon)) {
return false;
}

synchronized (addons) {
return addons.values().stream().anyMatch(addon::isInstance);
}
}

/**
Expand All @@ -211,7 +229,10 @@ public boolean isRegistered(@NotNull Class<? extends Addon> addon) {
* @since 3.3.5-SNAPSHOT
*/
public boolean isRegistered(@NotNull String name) {
return addons.values().stream().anyMatch(addon -> addon.getName().equalsIgnoreCase(name));
synchronized (addons) {
return addons.values().stream()
.anyMatch(addon -> addon.getName().equalsIgnoreCase(name));
}
}

/**
Expand Down
7 changes: 3 additions & 4 deletions src/main/java/de/craftsblock/craftsnet/api/http/Response.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import de.craftsblock.craftsnet.api.http.cookies.Cookie;
import de.craftsblock.craftsnet.api.http.cors.CorsPolicy;
import de.craftsblock.craftsnet.api.http.encoding.StreamEncoder;
import de.craftsblock.craftsnet.logging.Logger;
import de.craftsblock.craftsnet.api.http.status.HttpStatus;
import org.apache.commons.io.IOUtils;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
Expand Down Expand Up @@ -288,11 +288,10 @@ public void redirect(@NotNull String url) {
* <p>The {@code Location} header instructs the client to perform a new request
* to the specified resource.</p>
*
* @param url The target URL to which the client should be redirected.
* Must not be {@code null}.
* @param url The target URL to which the client should be redirected.
* Must not be {@code null}.
* @param redirection the redirection status to use. Must be one of the
* {@link HttpStatus.Redirection} values.
*
* @throws IllegalStateException if the response headers have already been sent
* and the redirect can no longer be applied
*/
Expand Down
87 changes: 69 additions & 18 deletions src/main/java/de/craftsblock/craftsnet/api/http/WebHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,14 @@
import de.craftsblock.craftsnet.api.http.encoding.StreamEncoder;
import de.craftsblock.craftsnet.api.http.encoding.StreamEncoderRegistry;
import de.craftsblock.craftsnet.api.http.encoding.builtin.IdentityStreamEncoder;
import de.craftsblock.craftsnet.api.http.status.HttpStatus;
import de.craftsblock.craftsnet.api.http.status.HttpStatusException;
import de.craftsblock.craftsnet.api.middlewares.Middleware;
import de.craftsblock.craftsnet.api.middlewares.MiddlewareCallbackInfo;
import de.craftsblock.craftsnet.api.session.Session;
import de.craftsblock.craftsnet.api.session.SessionInfo;
import de.craftsblock.craftsnet.api.utils.Context;
import de.craftsblock.craftsnet.api.transformers.TransformerPerformer;
import de.craftsblock.craftsnet.api.utils.Context;
import de.craftsblock.craftsnet.api.utils.ProtocolVersion;
import de.craftsblock.craftsnet.api.utils.Scheme;
import de.craftsblock.craftsnet.events.requests.PostRequestEvent;
Expand All @@ -25,7 +27,6 @@
import de.craftsblock.craftsnet.events.requests.shares.ShareFileLoadedEvent;
import de.craftsblock.craftsnet.events.requests.shares.ShareRequestEvent;
import de.craftsblock.craftsnet.logging.Logger;
import de.craftsblock.craftsnet.utils.Utils;
import de.craftsblock.craftsnet.utils.reflection.ReflectionUtils;

import java.io.IOException;
Expand All @@ -35,6 +36,7 @@
import java.nio.file.Path;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.BiConsumer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

Expand Down Expand Up @@ -137,22 +139,7 @@ public void handle(HttpExchange httpExchange) throws IOException {
craftsNet.getListenerRegistry().call(new PostRequestEvent(exchange, result.getKey(), result.getValue()));
}
} catch (Throwable t) {
if (craftsNet.getLogStream() != null) {
long errorID = craftsNet.getLogStream().createErrorLog(this.craftsNet, t, this.scheme.getName(), url);
logger.error("Error: %s", t, errorID);
if (!response.headersSent()) {
response.setStatus(500);
}

if (!response.sendingFile() && !httpMethod.equals(HttpMethod.HEAD) && !httpMethod.equals(HttpMethod.UNKNOWN)) {
response.print(Json.empty()
.set("status", "500")
.set("message", "An unexpected exception happened whilst processing your request!")
.set("incident", errorID));
}
} else {
logger.error(t);
}
handleThrowable(response, url, httpMethod, t);
} finally {
response.close();
}
Expand All @@ -161,6 +148,70 @@ public void handle(HttpExchange httpExchange) throws IOException {
}
}

/**
* Handles a {@link Throwable}.
*
* @param response The {@link Response} in which context the throwable was thrown.
* @param url The url of the request.
* @param httpMethod The {@link HttpMethod} of the request.
* @param t The {@link Throwable} that has been caught.
* @since 3.7.1
*/
private void handleThrowable(Response response, String url, HttpMethod httpMethod, Throwable t) {
BiConsumer<Json, Integer> responder = (json, code) -> {
if (!response.headersSent()) {
response.setStatus(code);
}

if (!response.sendingFile() && !httpMethod.equals(HttpMethod.HEAD) && !httpMethod.equals(HttpMethod.UNKNOWN)) {
response.print(json);
}
};

if (t instanceof HttpStatusException httpStatusException) {
handleHttpStatusException(responder, httpStatusException);
return;
}

if (t.getCause() instanceof HttpStatusException httpStatusException) {
handleHttpStatusException(responder, httpStatusException);
return;
}

Long errorID;
if (craftsNet.getLogStream() != null) {
errorID = craftsNet.getLogStream().createErrorLog(this.craftsNet, t, this.scheme.getName(), url);
logger.error("Error: %s", t, errorID);
} else {
errorID = null;
logger.error(t);
}

responder.accept(
Json.empty()
.set("code", HttpStatus.ServerError.INTERNAL_SERVER_ERROR.getCode())
.set("message", "An unexpected exception happened whilst processing your request!")
.setIf("incident", errorID, () -> errorID != null),
HttpStatus.ServerError.INTERNAL_SERVER_ERROR.getCode()
);
}

/**
* Handles caught {@link HttpStatusException}'s.
*
* @param responder Responds to the {@link Response}.
* @param statusException The caught {@link HttpStatusException}.
* @since 3.7.1
*/
private void handleHttpStatusException(BiConsumer<Json, Integer> responder, HttpStatusException statusException) {
responder.accept(
Json.empty()
.set("code", statusException.getCode())
.set("message", statusException.getMessage()),
statusException.getCode()
);
}

/**
* Handles a http request and determines whether a valid route or share is available
* for the given exchange. If a matching route or share is found, the appropriate handler
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package de.craftsblock.craftsnet.api.http;
package de.craftsblock.craftsnet.api.http.status;

import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.Range;
Expand Down Expand Up @@ -118,6 +118,16 @@ default boolean isServerError() {
return isCategory(5);
}

/**
* Checks whether this status indicates an error.
*
* @return {@code true} if the status code indicates an error.
* @since 3.7.1
*/
default boolean isError() {
return isClientError() || isServerError();
}

/**
* Checks whether this HTTP status is a custom status.
*
Expand Down
Loading
Loading