Launcher/LaunchServer/src/main/java/pro/gravit/launchserver/socket/WebSocketService.java

247 lines
11 KiB
Java
Raw Normal View History

package pro.gravit.launchserver.socket;
2018-10-01 11:07:47 +03:00
import com.google.gson.Gson;
2018-10-01 13:01:10 +03:00
import io.netty.channel.Channel;
2018-10-01 14:15:39 +03:00
import io.netty.channel.ChannelFutureListener;
2018-10-01 11:07:47 +03:00
import io.netty.channel.ChannelHandlerContext;
2018-10-01 13:01:10 +03:00
import io.netty.channel.group.ChannelGroup;
2019-08-13 21:02:33 +03:00
import io.netty.channel.group.ChannelMatchers;
2018-10-01 11:07:47 +03:00
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import pro.gravit.launcher.Launcher;
import pro.gravit.launcher.events.ExceptionEvent;
import pro.gravit.launcher.events.RequestEvent;
import pro.gravit.launcher.events.request.ErrorRequestEvent;
import pro.gravit.launcher.request.WebSocketEvent;
import pro.gravit.launchserver.LaunchServer;
2020-08-27 17:25:14 +03:00
import pro.gravit.launchserver.socket.handlers.WebSocketFrameHandler;
import pro.gravit.launchserver.socket.response.SimpleResponse;
import pro.gravit.launchserver.socket.response.WebSocketServerResponse;
import pro.gravit.launchserver.socket.response.auth.*;
import pro.gravit.launchserver.socket.response.management.PingServerReportResponse;
2020-07-03 03:32:38 +03:00
import pro.gravit.launchserver.socket.response.management.PingServerResponse;
import pro.gravit.launchserver.socket.response.management.ServerStatusResponse;
import pro.gravit.launchserver.socket.response.profile.BatchProfileByUsername;
import pro.gravit.launchserver.socket.response.profile.ProfileByUUIDResponse;
import pro.gravit.launchserver.socket.response.profile.ProfileByUsername;
2020-03-20 04:44:24 +03:00
import pro.gravit.launchserver.socket.response.secure.GetSecureLevelInfoResponse;
import pro.gravit.launchserver.socket.response.secure.HardwareReportResponse;
2020-03-20 05:23:09 +03:00
import pro.gravit.launchserver.socket.response.secure.SecurityReportResponse;
2020-03-20 04:44:24 +03:00
import pro.gravit.launchserver.socket.response.secure.VerifySecureLevelKeyResponse;
import pro.gravit.launchserver.socket.response.update.LauncherResponse;
import pro.gravit.launchserver.socket.response.update.UpdateListResponse;
import pro.gravit.launchserver.socket.response.update.UpdateResponse;
2019-08-22 07:34:12 +03:00
import pro.gravit.utils.BiHookSet;
import pro.gravit.utils.ProviderMap;
import pro.gravit.utils.helper.IOHelper;
import pro.gravit.utils.helper.LogHelper;
2018-10-01 11:07:47 +03:00
2019-10-19 19:46:04 +03:00
import java.lang.reflect.Type;
import java.util.concurrent.atomic.AtomicLong;
2020-08-27 17:25:14 +03:00
import java.util.function.BiConsumer;
2019-10-19 19:46:04 +03:00
2018-10-01 11:07:47 +03:00
public class WebSocketService {
2019-10-19 19:43:25 +03:00
public static final ProviderMap<WebSocketServerResponse> providers = new ProviderMap<>();
2020-04-05 10:27:04 +03:00
public final ChannelGroup channels;
2019-08-22 07:34:12 +03:00
public final BiHookSet<WebSocketRequestContext, ChannelHandlerContext> hook = new BiHookSet<>();
2020-04-05 10:27:04 +03:00
private final LaunchServer server;
private final Gson gson;
2018-11-08 15:30:16 +03:00
//Statistic data
2020-09-25 18:44:39 +03:00
public final AtomicLong shortRequestLatency = new AtomicLong();
public final AtomicLong shortRequestCounter = new AtomicLong();
2020-09-25 18:44:39 +03:00
public final AtomicLong middleRequestLatency = new AtomicLong();
public final AtomicLong middleRequestCounter = new AtomicLong();
2020-09-25 18:44:39 +03:00
public final AtomicLong longRequestLatency = new AtomicLong();
public final AtomicLong longRequestCounter = new AtomicLong();
2020-09-25 18:44:39 +03:00
public final AtomicLong lastRequestTime = new AtomicLong();
public WebSocketService(ChannelGroup channels, LaunchServer server) {
2018-10-01 13:01:10 +03:00
this.channels = channels;
2018-10-01 11:07:47 +03:00
this.server = server;
//this.gsonBuiler.registerTypeAdapter(WebSocketServerResponse.class, new JsonResponseAdapter(this));
//this.gsonBuiler.registerTypeAdapter(WebSocketEvent.class, new JsonResultSerializeAdapter());
//this.gsonBuiler.registerTypeAdapter(HashedEntry.class, new HashedEntryAdapter());
this.gson = Launcher.gsonManager.gson;
2018-10-01 11:07:47 +03:00
}
2020-09-25 18:48:33 +03:00
public void forEachActiveChannels(BiConsumer<Channel, WebSocketFrameHandler> callback) {
2020-08-27 17:25:14 +03:00
channels.forEach((channel) -> {
if (channel == null || channel.pipeline() == null) return;
WebSocketFrameHandler wsHandler = channel.pipeline().get(WebSocketFrameHandler.class);
if (wsHandler == null) return;
callback.accept(channel, wsHandler);
});
}
2020-04-05 10:27:04 +03:00
public static void registerResponses() {
providers.register("auth", AuthResponse.class);
providers.register("checkServer", CheckServerResponse.class);
providers.register("joinServer", JoinServerResponse.class);
providers.register("profiles", ProfilesResponse.class);
providers.register("launcher", LauncherResponse.class);
providers.register("updateList", UpdateListResponse.class);
providers.register("setProfile", SetProfileResponse.class);
providers.register("update", UpdateResponse.class);
providers.register("restoreSession", RestoreSessionResponse.class);
providers.register("batchProfileByUsername", BatchProfileByUsername.class);
providers.register("profileByUsername", ProfileByUsername.class);
providers.register("profileByUUID", ProfileByUUIDResponse.class);
providers.register("getAvailabilityAuth", GetAvailabilityAuthResponse.class);
providers.register("exit", ExitResponse.class);
providers.register("getSecureLevelInfo", GetSecureLevelInfoResponse.class);
providers.register("verifySecureLevelKey", VerifySecureLevelKeyResponse.class);
providers.register("securityReport", SecurityReportResponse.class);
providers.register("hardwareReport", HardwareReportResponse.class);
providers.register("serverStatus", ServerStatusResponse.class);
providers.register("pingServerReport", PingServerReportResponse.class);
2020-07-03 03:32:38 +03:00
providers.register("pingServer", PingServerResponse.class);
2020-08-27 17:25:14 +03:00
providers.register("currentUser", CurrentUserResponse.class);
2020-04-05 10:27:04 +03:00
}
2018-10-01 11:07:47 +03:00
public void process(ChannelHandlerContext ctx, TextWebSocketFrame frame, Client client, String ip) {
long startTimeNanos = System.nanoTime();
2018-10-01 11:07:47 +03:00
String request = frame.text();
WebSocketServerResponse response = gson.fromJson(request, WebSocketServerResponse.class);
2020-04-05 10:27:04 +03:00
if (response == null) {
RequestEvent event = new ErrorRequestEvent("This type of request is not supported");
2020-03-01 10:18:54 +03:00
sendObject(ctx, event);
return;
2020-03-01 10:18:54 +03:00
}
2019-05-15 14:11:22 +03:00
process(ctx, response, client, ip);
long executeTime = System.nanoTime() - startTimeNanos;
2020-09-25 18:48:33 +03:00
if (executeTime > 0) {
addRequestTimeToStats(executeTime);
}
}
2020-09-25 18:48:33 +03:00
public void addRequestTimeToStats(long nanos) {
if (nanos < 100_000_000L) // < 100 millis
{
shortRequestCounter.getAndIncrement();
shortRequestLatency.getAndAdd(nanos);
2020-09-25 18:48:33 +03:00
} else if (nanos < 1_000_000_000L) // > 100 millis and < 1 second
{
middleRequestCounter.getAndIncrement();
middleRequestLatency.getAndAdd(nanos);
2020-09-25 18:48:33 +03:00
} else // > 1 second
{
longRequestCounter.getAndIncrement();
longRequestLatency.getAndAdd(nanos);
}
long lastTime = lastRequestTime.get();
long currentTime = System.currentTimeMillis();
2020-09-25 18:48:33 +03:00
if (currentTime - lastTime > 60 * 1000) //1 minute
{
lastRequestTime.set(currentTime);
shortRequestLatency.set(0);
shortRequestCounter.set(0);
middleRequestCounter.set(0);
middleRequestLatency.set(0);
longRequestCounter.set(0);
longRequestLatency.set(0);
}
}
2019-05-15 14:11:22 +03:00
void process(ChannelHandlerContext ctx, WebSocketServerResponse response, Client client, String ip) {
2019-08-22 07:34:12 +03:00
WebSocketRequestContext context = new WebSocketRequestContext(response, client, ip);
2019-10-19 19:46:04 +03:00
if (hook.hook(context, ctx)) {
2019-08-22 07:34:12 +03:00
return;
}
2019-05-15 14:11:22 +03:00
if (response instanceof SimpleResponse) {
SimpleResponse simpleResponse = (SimpleResponse) response;
simpleResponse.server = server;
simpleResponse.service = this;
simpleResponse.ctx = ctx;
2019-05-15 14:11:22 +03:00
if (ip != null) simpleResponse.ip = ip;
2019-05-09 16:05:53 +03:00
else simpleResponse.ip = IOHelper.getIP(ctx.channel().remoteAddress());
}
2018-10-01 11:07:47 +03:00
try {
response.execute(ctx, client);
2018-11-08 15:30:16 +03:00
} catch (Exception e) {
2018-10-01 11:07:47 +03:00
LogHelper.error(e);
RequestEvent event;
2019-05-15 14:11:22 +03:00
if (server.config.netty.sendExceptionEnabled) {
event = new ExceptionEvent(e);
2019-05-15 14:11:22 +03:00
} else {
event = new ErrorRequestEvent("Fatal server error. Contact administrator");
}
2019-05-15 14:11:22 +03:00
if (response instanceof SimpleResponse) {
event.requestUUID = ((SimpleResponse) response).requestUUID;
}
sendObject(ctx, event);
2018-10-01 11:07:47 +03:00
}
}
2018-11-08 15:30:16 +03:00
public void registerClient(Channel channel) {
2018-10-01 13:01:10 +03:00
channels.add(channel);
}
2018-11-08 15:30:16 +03:00
public void sendObject(ChannelHandlerContext ctx, Object obj) {
2019-08-13 21:02:33 +03:00
ctx.writeAndFlush(new TextWebSocketFrame(gson.toJson(obj, WebSocketEvent.class)), ctx.voidPromise());
}
2019-04-03 16:27:40 +03:00
public void sendObject(ChannelHandlerContext ctx, Object obj, Type type) {
2019-08-13 21:02:33 +03:00
ctx.writeAndFlush(new TextWebSocketFrame(gson.toJson(obj, type)), ctx.voidPromise());
2018-10-01 11:07:47 +03:00
}
2018-11-08 15:30:16 +03:00
public void sendObject(Channel channel, Object obj) {
channel.writeAndFlush(new TextWebSocketFrame(gson.toJson(obj, WebSocketEvent.class)), channel.voidPromise());
}
public void sendObject(Channel channel, Object obj, Type type) {
channel.writeAndFlush(new TextWebSocketFrame(gson.toJson(obj, type)), channel.voidPromise());
}
public void sendObjectAll(Object obj) {
2019-05-15 14:11:22 +03:00
for (Channel ch : channels) {
2019-08-13 21:02:33 +03:00
ch.writeAndFlush(new TextWebSocketFrame(gson.toJson(obj, WebSocketEvent.class)), ch.voidPromise());
}
}
public void sendObjectAll(Object obj, Type type) {
2019-05-15 14:11:22 +03:00
for (Channel ch : channels) {
2019-08-13 21:02:33 +03:00
ch.writeAndFlush(new TextWebSocketFrame(gson.toJson(obj, type)), ch.voidPromise());
}
}
2018-11-08 15:30:16 +03:00
public void sendObjectAndClose(ChannelHandlerContext ctx, Object obj) {
2019-07-01 14:34:03 +03:00
ctx.writeAndFlush(new TextWebSocketFrame(gson.toJson(obj, WebSocketEvent.class))).addListener(ChannelFutureListener.CLOSE);
}
public void sendObjectAndClose(ChannelHandlerContext ctx, Object obj, Type type) {
2019-07-01 14:34:03 +03:00
ctx.writeAndFlush(new TextWebSocketFrame(gson.toJson(obj, type))).addListener(ChannelFutureListener.CLOSE);
2018-10-01 14:15:39 +03:00
}
2018-11-08 15:30:16 +03:00
public void sendEvent(EventResult obj) {
2019-08-13 21:02:33 +03:00
channels.writeAndFlush(new TextWebSocketFrame(gson.toJson(obj)), ChannelMatchers.all(), true);
2018-10-01 13:01:10 +03:00
}
2018-11-08 15:30:16 +03:00
2020-04-05 10:27:04 +03:00
public static class WebSocketRequestContext {
public final WebSocketServerResponse response;
public final Client client;
public final String ip;
public WebSocketRequestContext(WebSocketServerResponse response, Client client, String ip) {
this.response = response;
this.client = client;
this.ip = ip;
}
}
public static class EventResult implements WebSocketEvent {
2018-10-01 13:01:10 +03:00
public EventResult() {
2018-10-01 13:01:10 +03:00
}
2018-11-08 15:30:16 +03:00
@Override
public String getType() {
return "event";
}
2018-10-01 13:01:10 +03:00
}
2018-11-08 15:30:16 +03:00
2018-10-01 11:07:47 +03:00
}