[ANY] New Sessions system completed

This commit is contained in:
Gravita 2020-12-15 19:27:38 +07:00
parent acf2d2d9cd
commit c596c30ff6
10 changed files with 171 additions and 158 deletions

View file

@ -10,6 +10,7 @@
import pro.gravit.launcher.modules.events.ClosePhase;
import pro.gravit.launcher.profiles.ClientProfile;
import pro.gravit.launchserver.auth.AuthProviderPair;
import pro.gravit.launchserver.auth.session.MemorySessionStorage;
import pro.gravit.launchserver.binary.*;
import pro.gravit.launchserver.config.LaunchServerConfig;
import pro.gravit.launchserver.config.LaunchServerRuntimeConfig;
@ -128,6 +129,7 @@ public LaunchServer(LaunchServerDirectories directories, LaunchServerEnv env, La
runtime.verify();
config.verify();
if(config.sessions == null) config.sessions = new MemorySessionStorage();
if (config.components != null) {
LogHelper.debug("PreInit components");
config.components.forEach((k, v) -> {
@ -147,7 +149,8 @@ public LaunchServer(LaunchServerDirectories directories, LaunchServerEnv env, La
pingServerManager = new PingServerManager(this);
//Generate or set new Certificate API
certificateManager.orgName = config.projectName;
if (config.certificate != null && config.certificate.enabled) {
/*
if (false) {
if (IOHelper.isFile(caCertFile) && IOHelper.isFile(caKeyFile)) {
certificateManager.ca = certificateManager.readCertificate(caCertFile);
certificateManager.caKey = certificateManager.readPrivateKey(caKeyFile);
@ -175,6 +178,7 @@ public LaunchServer(LaunchServerDirectories directories, LaunchServerEnv env, La
}
}
}
*/
config.init(ReloadType.FULL);
registerObject("launchServer", this);
GarbageManager.registerNeedGC(sessionManager);

View file

@ -0,0 +1,108 @@
package pro.gravit.launchserver.auth.session;
import pro.gravit.launcher.NeedGarbageCollection;
import pro.gravit.launchserver.manangers.SessionManager;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Stream;
public class MemorySessionStorage extends SessionStorage implements NeedGarbageCollection {
private final Map<UUID, Entry> clientSet = new ConcurrentHashMap<>(128);
private final Map<UUID, Set<Entry>> uuidIndex = new ConcurrentHashMap<>(32);
@Override
public byte[] getSessionData(UUID session) {
Entry e = clientSet.get(session);
if(e == null) return null;
return e.data;
}
@Override
public Stream<UUID> getSessionsFromUserUUID(UUID userUUID) {
Set<Entry> set = uuidIndex.get(userUUID);
if(set != null) return set.stream().map((e) -> e.sessionUuid);
return null;
}
@Override
public boolean writeSession(UUID userUUID, UUID sessionUUID, byte[] data) {
deleteSession(sessionUUID);
Entry e = new Entry(data, sessionUUID);
clientSet.put(sessionUUID, e);
if(userUUID != null) {
Set<Entry> uuidSet = uuidIndex.computeIfAbsent(userUUID, k -> ConcurrentHashMap.newKeySet());
uuidSet.add(e);
}
return false;
}
@Override
public boolean deleteSession(UUID sessionUUID) {
Entry e =clientSet.remove(sessionUUID);
if(e != null) {
Set<Entry> set = uuidIndex.get(sessionUUID);
if(set != null) {
removeUuidFromIndexSet(set, e, sessionUUID);
}
return true;
}
return false;
}
@Override
public boolean deleteSessionsByUserUUID(UUID userUUID) {
Set<Entry> set = uuidIndex.get(userUUID);
if(set != null) {
for(Entry e : set) {
clientSet.remove(e.sessionUuid);
}
set.clear();
uuidIndex.remove(userUUID);
}
return true;
}
@Override
public void clear() {
clientSet.clear();
uuidIndex.clear();
}
private void removeUuidFromIndexSet(Set<Entry> set, Entry e, UUID session) {
set.remove(e);
if(set.isEmpty()) {
uuidIndex.remove(session);
}
}
@Override
public void garbageCollection() {
long time = System.currentTimeMillis();
long session_timeout = server.config.netty.performance.sessionLifetimeMs;
Set<UUID> to_delete = new HashSet<>(32);
clientSet.forEach((uuid, entry) -> {
long timestamp = entry.timestamp;
if(timestamp + session_timeout < time)
to_delete.add(uuid);
});
for(UUID session : to_delete) {
deleteSession(session);
}
to_delete.clear();
}
private static class Entry {
public byte[] data;
public UUID sessionUuid;
public long timestamp;
public Entry(byte[] data, UUID sessionUuid) {
this.data = data;
this.sessionUuid = sessionUuid;
this.timestamp = System.currentTimeMillis();
}
}
}

View file

@ -0,0 +1,26 @@
package pro.gravit.launchserver.auth.session;
import pro.gravit.launchserver.LaunchServer;
import pro.gravit.utils.ProviderMap;
import java.util.List;
import java.util.UUID;
import java.util.stream.Stream;
public abstract class SessionStorage {
protected transient LaunchServer server;
public static ProviderMap<SessionStorage> providers = new ProviderMap<>();
public abstract byte[] getSessionData(UUID session);
public abstract Stream<UUID> getSessionsFromUserUUID(UUID userUUID);
public abstract boolean writeSession(UUID userUUID, UUID sessionUUID, byte[] data);
public abstract boolean deleteSession(UUID sessionUUID);
public boolean deleteSessionsByUserUUID(UUID userUUID) {
getSessionsFromUserUUID(userUUID).forEach(this::deleteSession);
return true;
}
public abstract void clear();
public void init(LaunchServer server)
{
this.server = server;
}
}

View file

@ -1,67 +0,0 @@
package pro.gravit.launchserver.command.dump;
import com.google.gson.reflect.TypeToken;
import pro.gravit.launcher.Launcher;
import pro.gravit.launchserver.LaunchServer;
import pro.gravit.launchserver.command.Command;
import pro.gravit.launchserver.socket.Client;
import pro.gravit.utils.command.SubCommand;
import pro.gravit.utils.helper.IOHelper;
import pro.gravit.utils.helper.LogHelper;
import java.io.Reader;
import java.io.Writer;
import java.lang.reflect.Type;
import java.nio.file.Paths;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
public class DumpSessionsCommand extends Command {
public DumpSessionsCommand(LaunchServer server) {
super(server);
childCommands.put("load", new SubCommand() {
@Override
public void invoke(String... args) throws Exception {
verifyArgs(args, 1);
LogHelper.info("Sessions read from %s", args[0]);
int size;
try (Reader reader = IOHelper.newReader(Paths.get(args[0]))) {
Type setType = new TypeToken<HashSet<Client>>() {
}.getType();
Set<Client> clientSet = Launcher.gsonManager.configGson.fromJson(reader, setType);
size = clientSet.size();
server.sessionManager.loadSessions(clientSet);
}
LogHelper.subInfo("Readed %d sessions", size);
}
});
childCommands.put("unload", new SubCommand() {
@Override
public void invoke(String... args) throws Exception {
verifyArgs(args, 1);
LogHelper.info("Sessions write to %s", args[0]);
Collection<Client> clientSet = server.sessionManager.getSessions();
try (Writer writer = IOHelper.newWriter(Paths.get(args[0]))) {
Launcher.gsonManager.configGson.toJson(clientSet, writer);
}
LogHelper.subInfo("Write %d sessions", clientSet.size());
}
});
}
@Override
public String getArgsDescription() {
return "[load/unload] [filename]";
}
@Override
public String getUsageDescription() {
return "Load or unload sessions";
}
@Override
public void invoke(String... args) throws Exception {
invokeSubcommands(args);
}
}

View file

@ -5,7 +5,6 @@
import pro.gravit.launchserver.command.auth.UUIDToUsernameCommand;
import pro.gravit.launchserver.command.auth.UsernameToUUIDCommand;
import pro.gravit.launchserver.command.basic.*;
import pro.gravit.launchserver.command.dump.DumpSessionsCommand;
import pro.gravit.launchserver.command.hash.*;
import pro.gravit.launchserver.command.install.CheckInstallCommand;
import pro.gravit.launchserver.command.install.MultiCommand;
@ -61,12 +60,6 @@ public static void registerCommands(pro.gravit.utils.command.CommandHandler hand
Category authCategory = new Category(auth, "auth", "User Management");
handler.registerCategory(authCategory);
//Register dump commands
BaseCommandCategory dump = new BaseCommandCategory();
dump.registerCommand("dumpSessions", new DumpSessionsCommand(server));
Category dumpCategory = new Category(dump, "dump", "Dump runtime data");
handler.registerCategory(dumpCategory);
//Register service commands
BaseCommandCategory service = new BaseCommandCategory();
service.registerCommand("config", new ConfigCommand(server));

View file

@ -38,7 +38,7 @@ public void invoke(String... args) {
for (CommandHandler.Category category : server.commandHandler.getCategories()) {
commands += category.category.commandsMap().size();
}
LogHelper.info("Sessions: %d | Commands: %d(%d categories)", server.sessionManager.getSessions().size(), commands, server.commandHandler.getCategories().size() + 1);
LogHelper.info("Commands: %d(%d categories)", commands, server.commandHandler.getCategories().size() + 1);
for (AuthProviderPair pair : server.config.auth.values()) {
if (pair.handler instanceof CachedAuthHandler) {
LogHelper.info("AuthHandler %s: EntryCache: %d | usernameCache: %d", pair.name, ((CachedAuthHandler) pair.handler).getEntryCache().size(), ((CachedAuthHandler) pair.handler).getUsernamesCache().size());

View file

@ -10,6 +10,8 @@
import pro.gravit.launchserver.auth.protect.ProtectHandler;
import pro.gravit.launchserver.auth.protect.StdProtectHandler;
import pro.gravit.launchserver.auth.provider.RejectAuthProvider;
import pro.gravit.launchserver.auth.session.MemorySessionStorage;
import pro.gravit.launchserver.auth.session.SessionStorage;
import pro.gravit.launchserver.auth.texture.RequestTextureProvider;
import pro.gravit.launchserver.binary.tasks.exe.Launch4JTask;
import pro.gravit.launchserver.components.AuthLimiterComponent;
@ -33,6 +35,7 @@ public final class LaunchServerConfig {
public LauncherConfig.LauncherEnvironment env;
public Map<String, AuthProviderPair> auth;
public DaoProvider dao;
public SessionStorage sessions;
// Handlers & Providers
public ProtectHandler protectHandler;
@ -40,7 +43,6 @@ public final class LaunchServerConfig {
public ExeConf launch4j;
public NettyConfig netty;
public LauncherConf launcher;
public CertificateConf certificate;
public JarSignerConf sign;
public String startScript;
private transient LaunchServer server = null;
@ -71,6 +73,7 @@ public static LaunchServerConfig getDefault(LaunchServer.LaunchServerEnv env) {
a.displayName = "Default";
newConfig.auth.put("std", a);
newConfig.protectHandler = new StdProtectHandler();
newConfig.sessions = new MemorySessionStorage();
newConfig.binaryName = "Launcher";
newConfig.netty = new NettyConfig();
@ -97,8 +100,6 @@ public static LaunchServerConfig getDefault(LaunchServer.LaunchServerEnv env) {
newConfig.launcher.stripLineNumbers = true;
newConfig.launcher.proguardGenMappings = true;
newConfig.certificate = new LaunchServerConfig.CertificateConf();
newConfig.certificate.enabled = false;
newConfig.sign = new JarSignerConf();
newConfig.components = new HashMap<>();
@ -187,6 +188,10 @@ public void init(LaunchServer.ReloadType type) {
protectHandler.init(server);
protectHandler.checkLaunchServerLicense();
}
if(sessions != null) {
sessions.init(server);
server.registerObject("sessions", sessions);
}
if (components != null) {
components.forEach((k, v) -> server.registerObject("component.".concat(k), v));
}
@ -229,6 +234,9 @@ public void close(LaunchServer.ReloadType type) {
server.unregisterObject("protectHandler", protectHandler);
protectHandler.close();
}
if(sessions != null) {
server.unregisterObject("sessions", sessions);
}
if (dao != null) {
server.unregisterObject("dao", dao);
if (dao instanceof AutoCloseable) {
@ -260,10 +268,6 @@ public static class ExeConf {
public String txtProductVersion;
}
public static class CertificateConf {
public boolean enabled;
}
public static class JarSignerConf {
public boolean enabled = false;
public String keyStore = "pathToKey";

View file

@ -11,6 +11,7 @@
import pro.gravit.launchserver.auth.protect.ProtectHandler;
import pro.gravit.launchserver.auth.protect.hwid.HWIDProvider;
import pro.gravit.launchserver.auth.provider.AuthProvider;
import pro.gravit.launchserver.auth.session.SessionStorage;
import pro.gravit.launchserver.auth.texture.TextureProvider;
import pro.gravit.launchserver.components.Component;
import pro.gravit.launchserver.dao.provider.DaoProvider;
@ -41,6 +42,7 @@ public void registerAdapters(GsonBuilder builder) {
builder.registerTypeAdapter(AuthRequest.AuthPasswordInterface.class, new UniversalJsonAdapter<>(AuthRequest.providers));
builder.registerTypeAdapter(HWIDProvider.class, new UniversalJsonAdapter<>(HWIDProvider.providers));
builder.registerTypeAdapter(OptionalAction.class, new UniversalJsonAdapter<>(OptionalAction.providers));
builder.registerTypeAdapter(SessionStorage.class, new UniversalJsonAdapter<>(SessionStorage.providers));
modulesManager.invokeEvent(new PreGsonPhase(builder));
//ClientWebSocketService.appendTypeAdapters(builder);
}

View file

@ -8,6 +8,7 @@
import pro.gravit.utils.HookSet;
import pro.gravit.utils.helper.LogHelper;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
@ -15,8 +16,6 @@
public class SessionManager implements NeedGarbageCollection {
private final Map<UUID, Entry> clientSet = new ConcurrentHashMap<>(128);
private final Map<UUID, Set<Entry>> uuidIndex = new ConcurrentHashMap<>(32);
private final LaunchServer server;
public HookSet<Client> clientRestoreHook = new HookSet<>();
@ -27,52 +26,35 @@ public SessionManager(LaunchServer server) {
public boolean addClient(Client client) {
if(client == null || client.session == null) return false;
remove(client.session);
Entry e = new Entry(compressClient(client), client.session);
clientSet.put(client.session, e);
if(client.isAuth && client.uuid != null) {
Set<Entry> uuidSet = uuidIndex.computeIfAbsent(client.uuid, k -> ConcurrentHashMap.newKeySet());
uuidSet.add(e);
}
return true;
return server.config.sessions.writeSession(client.uuid, client.session, compressClient(client));
}
public Stream<UUID> findSessionsByUUID(UUID uuid) {
Set<Entry> set = uuidIndex.get(uuid);
if(set != null) return set.stream().map((e) -> e.sessionUuid);
return null;
return server.config.sessions.getSessionsFromUserUUID(uuid);
}
public boolean removeByUUID(UUID uuid) {
Set<Entry> set = uuidIndex.get(uuid);
if(set != null) {
for(Entry e : set) {
clientSet.remove(e.sessionUuid);
}
set.clear();
uuidIndex.remove(uuid);
}
return false;
return server.config.sessions.deleteSessionsByUserUUID(uuid);
}
@Deprecated
public Set<UUID> getSavedUUIDs()
{
return uuidIndex.keySet();
throw new UnsupportedOperationException();
}
public void clear() {
clientSet.clear();
uuidIndex.clear();
server.config.sessions.clear();
}
private String compressClient(Client client) {
return Launcher.gsonManager.gson.toJson(client); //Compress using later
private byte[] compressClient(Client client) {
return Launcher.gsonManager.gson.toJson(client).getBytes(StandardCharsets.UTF_8); //Compress using later
}
private Client decompressClient(String client) {
return Launcher.gsonManager.gson.fromJson(client, Client.class); //Compress using later
private Client decompressClient(byte[] client) {
return Launcher.gsonManager.gson.fromJson(new String(client, StandardCharsets.UTF_8), Client.class); //Compress using later
}
private Client restoreFromString(String data) {
private Client restoreFromString(byte[] data) {
Client result = decompressClient(data);
result.updateAuth(server);
if(result.auth != null && (result.username != null)) {
@ -87,25 +69,11 @@ private Client restoreFromString(String data) {
@Override
public void garbageCollection() {
long time = System.currentTimeMillis();
long session_timeout = server.config.netty.performance.sessionLifetimeMs;
Set<UUID> to_delete = new HashSet<>(32);
clientSet.forEach((uuid, entry) -> {
long timestamp = entry.timestamp;
if(timestamp + session_timeout < time)
to_delete.add(uuid);
});
for(UUID session : to_delete) {
remove(session);
}
to_delete.clear();
}
public Client getClient(UUID session) {
Entry e = clientSet.get(session);
if(e == null) return null;
return restoreFromString(e.data);
return restoreFromString(server.config.sessions.getSessionData(session));
}
@ -115,52 +83,27 @@ public Client getOrNewClient(UUID session) {
}
public boolean remove(UUID session) {
Entry e =clientSet.remove(session);
if(e != null) {
Set<Entry> set = uuidIndex.get(session);
if(set != null) {
removeUuidFromIndexSet(set, e, session);
}
return true;
}
return false;
return server.config.sessions.deleteSession(session);
}
private void removeUuidFromIndexSet(Set<Entry> set, Entry e, UUID session) {
set.remove(e);
if(set.isEmpty()) {
uuidIndex.remove(session);
}
}
@Deprecated
public void removeClient(UUID session) {
remove(session);
}
@Deprecated
public void updateClient(UUID session) {
LogHelper.warning("Using deprecated method: sessionManager.updateClient");
}
@Deprecated
public Set<Client> getSessions() {
// TODO: removeme
LogHelper.warning("Using deprecated method: sessionManager.getSession");
return new HashSet<>();
}
@Deprecated
public void loadSessions(Set<Client> set) {
LogHelper.warning("Using deprecated method: sessionManager.loadSessions");
//clientSet.putAll(set.stream().collect(Collectors.toMap(c -> c.session, Function.identity())));
}
private static class Entry {
public String data;
public UUID sessionUuid;
public long timestamp;
public Entry(String data, UUID sessionUuid) {
this.data = data;
this.sessionUuid = sessionUuid;
this.timestamp = System.currentTimeMillis();
}
}
}

@ -1 +1 @@
Subproject commit e990b35b4001e29793b383dfa7b179aa0b27b307
Subproject commit 5c4f6850bd4feeee0caff5561564b7e54bb94774