From 8af5fa7157be6bfec041cbfebb55f4503892f4ab Mon Sep 17 00:00:00 2001 From: Trumeet Date: Wed, 13 Jul 2022 12:44:49 -0700 Subject: Add error codes to distinguish invalid requests and server errors API:ADDITION Signed-off-by: Trumeet --- README.md | 6 ++++++ src/main/java/moe/ymc/acron/net/WSFrameHandler.java | 17 ++++++++++++++--- .../java/moe/ymc/acron/s2c/response/EventError.java | 10 ++++++++++ .../java/moe/ymc/acron/serialization/Serializer.java | 10 +++++----- 4 files changed, 35 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index c68122f..e1284a6 100644 --- a/README.md +++ b/README.md @@ -219,10 +219,16 @@ codes, all faulty WebSocket requests will receive error in the following format: { "type": "error", "id": 1, + "code": 500, "message": "Error message. Not machine-readable." } ``` +Parameters: + +* `.code` (int, HTTP status codes, always present): The machine-readable error code (e. g. 400 for Bad Request). +* `.message` (string, any, always present): The human-readable error message. + **`.type` and `.id` are included in every request / response, except for further noticed. Thus, this document excludes them from the parameter lists.** diff --git a/src/main/java/moe/ymc/acron/net/WSFrameHandler.java b/src/main/java/moe/ymc/acron/net/WSFrameHandler.java index c578f2e..713d433 100644 --- a/src/main/java/moe/ymc/acron/net/WSFrameHandler.java +++ b/src/main/java/moe/ymc/acron/net/WSFrameHandler.java @@ -1,5 +1,6 @@ package moe.ymc.acron.net; +import com.google.gson.JsonParseException; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; import io.netty.handler.codec.http.websocketx.*; @@ -57,10 +58,18 @@ public class WSFrameHandler extends SimpleChannelInboundHandler final ClientIdentification identification = ctx.channel().attr(Attributes.ID).get(); final ClientConfiguration configuration = ctx.channel().attr(Attributes.CONFIGURATION).get(); - int id = -2; + int id; + final Request request; try { - final Request request = Serializer.deserialize(frame); + request = Serializer.deserialize(frame); id = request.getId(); + } catch (JsonParseException | IllegalArgumentException | IllegalStateException e) { + ctx.channel().writeAndFlush( + Serializer.serialize(new EventError(-2, EventError.Code.BAD_REQUEST.value, e.getMessage())) + ); + return; + } + try { if (request instanceof final ReqCmd reqCmd) { LOGGER.info("Client {} executed a command: `{}`.", identification.client().id(), @@ -90,7 +99,9 @@ public class WSFrameHandler extends SimpleChannelInboundHandler "This may just be a malformed request. " + "It is reported to the client.", e); - ctx.channel().writeAndFlush(Serializer.serialize(new EventError(id, e.getMessage()))); + ctx.channel().writeAndFlush( + Serializer.serialize(new EventError(id, EventError.Code.SERVER_ERROR.value, e.getMessage())) + ); } } diff --git a/src/main/java/moe/ymc/acron/s2c/response/EventError.java b/src/main/java/moe/ymc/acron/s2c/response/EventError.java index 48d2683..d2a03c3 100644 --- a/src/main/java/moe/ymc/acron/s2c/response/EventError.java +++ b/src/main/java/moe/ymc/acron/s2c/response/EventError.java @@ -5,6 +5,16 @@ import moe.ymc.acron.s2c.Event; import org.jetbrains.annotations.Nullable; public record EventError(@SerializedName("id") int id, + @SerializedName("code") int code, @SerializedName("message") @Nullable String message) implements Event { + public enum Code { + SERVER_ERROR(500), + BAD_REQUEST(400); + + public final int value; + Code(int value) { + this.value = value; + } + } } diff --git a/src/main/java/moe/ymc/acron/serialization/Serializer.java b/src/main/java/moe/ymc/acron/serialization/Serializer.java index 900d6fc..6d06388 100644 --- a/src/main/java/moe/ymc/acron/serialization/Serializer.java +++ b/src/main/java/moe/ymc/acron/serialization/Serializer.java @@ -2,6 +2,7 @@ package moe.ymc.acron.serialization; import com.google.gson.Gson; import com.google.gson.GsonBuilder; +import com.google.gson.JsonParseException; import com.google.gson.typeadapters.RuntimeTypeAdapterFactory; import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; import moe.ymc.acron.c2s.ReqCmd; @@ -14,13 +15,13 @@ import org.jetbrains.annotations.NotNull; public class Serializer { @NotNull - public static Request deserialize(@NotNull TextWebSocketFrame frame) { + public static Request deserialize(@NotNull TextWebSocketFrame frame) + throws JsonParseException, IllegalArgumentException, IllegalStateException { final String text = frame.text(); final RuntimeTypeAdapterFactory adapter = RuntimeTypeAdapterFactory.of(Request.class, "type") .registerSubtype(ReqSetConfig.class, "set_config") - .registerSubtype(ReqCmd.class, "cmd") - ; + .registerSubtype(ReqCmd.class, "cmd"); final Gson gson = new GsonBuilder() .registerTypeAdapter(ReqSetConfig.class, new ReqSetConfig.ReqSetConfigDeserializer()) .registerTypeAdapter(ReqSetConfig.Vec3d.class, new ReqSetConfig.Vec3d.Vec3dDeserializer()) @@ -46,8 +47,7 @@ public class Serializer { .registerSubtype(EventLagging.class, "lagging") .registerSubtype(EventCmdDenied.class, "cmd_denied") .registerSubtype(EventError.class, "error") - .registerSubtype(EventOk.class, "ok") - ; + .registerSubtype(EventOk.class, "ok"); final Gson gson = new GsonBuilder() .registerTypeAdapterFactory(adapter) .create(); -- cgit v1.2.3