|
| 1 | +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 |
| 2 | + |
| 3 | +Date: Wed, 18 Jan 2023 12:02:42 -0700 |
| 4 | +Subject: [PATCH] Use a proper thread pool in ServerLoginPacketListenerImpl and |
| 5 | + MCUtil |
| 6 | + |
| 7 | +This addresses issue #8797 along with a note from @kennytv about a |
| 8 | +similar situation in MCUtil. The cached thread pool in |
| 9 | +ServerLoginPacketListenerImpl is replaced by a fixed thread pool and a |
| 10 | +bounded ArrayBlockingQueue has been added. |
| 11 | + |
| 12 | +diff --git a/src/main/java/io/papermc/paper/util/MCUtil.java b/src/main/java/io/papermc/paper/util/MCUtil.java |
| 13 | +index d1a59c2af0557a816c094983ec60097fb4de060c..74f9e8d400a9f29d4847ce1db5bd9cf579d8579c 100644 |
| 14 | +--- a/src/main/java/io/papermc/paper/util/MCUtil.java |
| 15 | ++++ b/src/main/java/io/papermc/paper/util/MCUtil.java |
| 16 | +@@ -56,7 +56,7 @@ import java.util.function.Supplier; |
| 17 | + |
| 18 | + public final class MCUtil { |
| 19 | + public static final ThreadPoolExecutor asyncExecutor = new ThreadPoolExecutor( |
| 20 | +- 0, 2, 60L, TimeUnit.SECONDS, |
| 21 | ++ 2, 2, 60L, TimeUnit.SECONDS, // This pool is currently only used for GameProfileCache#save |
| 22 | + new LinkedBlockingQueue<>(), |
| 23 | + new ThreadFactoryBuilder() |
| 24 | + .setNameFormat("Paper Async Task Handler Thread - %1$d") |
| 25 | +diff --git a/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java |
| 26 | +index a25306fe8a35bb70a490e6a0c01d0340bbc0d781..35ede90cef6f1b93bb67e45575bb66070e319014 100644 |
| 27 | +--- a/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java |
| 28 | ++++ b/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java |
| 29 | +@@ -124,7 +124,7 @@ public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener, |
| 30 | + |
| 31 | + } |
| 32 | + |
| 33 | +- private static final java.util.concurrent.ExecutorService authenticatorPool = java.util.concurrent.Executors.newCachedThreadPool(new com.google.common.util.concurrent.ThreadFactoryBuilder().setNameFormat("User Authenticator #%d").setUncaughtExceptionHandler(new DefaultUncaughtExceptionHandler(LOGGER)).build()); // Paper - Cache authenticator threads |
| 34 | ++ private static final java.util.concurrent.ExecutorService authenticatorPool = new java.util.concurrent.ThreadPoolExecutor(16, 16, 60L, java.util.concurrent.TimeUnit.SECONDS, new java.util.concurrent.ArrayBlockingQueue<>(1024), new com.google.common.util.concurrent.ThreadFactoryBuilder().setNameFormat("User Authenticator #%d").setUncaughtExceptionHandler(new DefaultUncaughtExceptionHandler(LOGGER)).build()); // Paper - Cache authenticator threads |
| 35 | + |
| 36 | + // Spigot start |
| 37 | + public void initUUID() |
| 38 | +@@ -269,7 +269,8 @@ public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener, |
| 39 | + // Paper end |
| 40 | + // Spigot start |
| 41 | + // Paper start - Cache authenticator threads |
| 42 | +- authenticatorPool.execute(new Runnable() { |
| 43 | ++ try { |
| 44 | ++ authenticatorPool.execute(new Runnable() { |
| 45 | + @Override |
| 46 | + public void run() { |
| 47 | + try { |
| 48 | +@@ -280,7 +281,11 @@ public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener, |
| 49 | + server.server.getLogger().log(java.util.logging.Level.WARNING, "Exception verifying " + ServerLoginPacketListenerImpl.this.gameProfile.getName(), ex); |
| 50 | + } |
| 51 | + } |
| 52 | +- }); |
| 53 | ++ }); |
| 54 | ++ } catch (java.util.concurrent.RejectedExecutionException rejected) { |
| 55 | ++ ServerLoginPacketListenerImpl.this.disconnect("Failed to verify username!"); // Don't give attackers confirmation of a successful attack - don't change the message |
| 56 | ++ server.server.getLogger().log(java.util.logging.Level.WARNING, "Exception verifying " + ServerLoginPacketListenerImpl.this.gameProfile.getName() + " - auth queue full", rejected); |
| 57 | ++ } |
| 58 | + // Paper end |
| 59 | + // Spigot end |
| 60 | + } |
| 61 | +@@ -321,52 +326,57 @@ public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener, |
| 62 | + } |
| 63 | + |
| 64 | + // Paper start - Cache authenticator threads |
| 65 | +- authenticatorPool.execute(new Runnable() { |
| 66 | +- public void run() { |
| 67 | +- GameProfile gameprofile = ServerLoginPacketListenerImpl.this.gameProfile; |
| 68 | +- |
| 69 | +- try { |
| 70 | +- ServerLoginPacketListenerImpl.this.gameProfile = ServerLoginPacketListenerImpl.this.server.getSessionService().hasJoinedServer(new GameProfile((UUID) null, gameprofile.getName()), s, this.getAddress()); |
| 71 | +- if (ServerLoginPacketListenerImpl.this.gameProfile != null) { |
| 72 | +- // CraftBukkit start - fire PlayerPreLoginEvent |
| 73 | +- if (!ServerLoginPacketListenerImpl.this.connection.isConnected()) { |
| 74 | +- return; |
| 75 | +- } |
| 76 | ++ try { |
| 77 | ++ authenticatorPool.execute(new Runnable() { |
| 78 | ++ public void run() { |
| 79 | ++ GameProfile gameprofile = ServerLoginPacketListenerImpl.this.gameProfile; |
| 80 | ++ |
| 81 | ++ try { |
| 82 | ++ ServerLoginPacketListenerImpl.this.gameProfile = ServerLoginPacketListenerImpl.this.server.getSessionService().hasJoinedServer(new GameProfile((UUID) null, gameprofile.getName()), s, this.getAddress()); |
| 83 | ++ if (ServerLoginPacketListenerImpl.this.gameProfile != null) { |
| 84 | ++ // CraftBukkit start - fire PlayerPreLoginEvent |
| 85 | ++ if (!ServerLoginPacketListenerImpl.this.connection.isConnected()) { |
| 86 | ++ return; |
| 87 | ++ } |
| 88 | + |
| 89 | +- new LoginHandler().fireEvents(); |
| 90 | +- } else if (ServerLoginPacketListenerImpl.this.server.isSingleplayer()) { |
| 91 | +- ServerLoginPacketListenerImpl.LOGGER.warn("Failed to verify username but will let them in anyway!"); |
| 92 | +- ServerLoginPacketListenerImpl.this.gameProfile = gameprofile; |
| 93 | +- ServerLoginPacketListenerImpl.this.state = ServerLoginPacketListenerImpl.State.READY_TO_ACCEPT; |
| 94 | +- } else { |
| 95 | +- ServerLoginPacketListenerImpl.this.disconnect(Component.translatable("multiplayer.disconnect.unverified_username")); |
| 96 | +- ServerLoginPacketListenerImpl.LOGGER.error("Username '{}' tried to join with an invalid session", gameprofile.getName()); |
| 97 | +- } |
| 98 | +- } catch (AuthenticationUnavailableException authenticationunavailableexception) { |
| 99 | +- if (ServerLoginPacketListenerImpl.this.server.isSingleplayer()) { |
| 100 | +- ServerLoginPacketListenerImpl.LOGGER.warn("Authentication servers are down but will let them in anyway!"); |
| 101 | +- ServerLoginPacketListenerImpl.this.gameProfile = gameprofile; |
| 102 | +- ServerLoginPacketListenerImpl.this.state = ServerLoginPacketListenerImpl.State.READY_TO_ACCEPT; |
| 103 | +- } else { |
| 104 | +- ServerLoginPacketListenerImpl.this.disconnect(io.papermc.paper.adventure.PaperAdventure.asVanilla(io.papermc.paper.configuration.GlobalConfiguration.get().messages.kick.authenticationServersDown)); // Paper |
| 105 | +- ServerLoginPacketListenerImpl.LOGGER.error("Couldn't verify username because servers are unavailable"); |
| 106 | ++ new LoginHandler().fireEvents(); |
| 107 | ++ } else if (ServerLoginPacketListenerImpl.this.server.isSingleplayer()) { |
| 108 | ++ ServerLoginPacketListenerImpl.LOGGER.warn("Failed to verify username but will let them in anyway!"); |
| 109 | ++ ServerLoginPacketListenerImpl.this.gameProfile = gameprofile; |
| 110 | ++ ServerLoginPacketListenerImpl.this.state = ServerLoginPacketListenerImpl.State.READY_TO_ACCEPT; |
| 111 | ++ } else { |
| 112 | ++ ServerLoginPacketListenerImpl.this.disconnect(Component.translatable("multiplayer.disconnect.unverified_username")); |
| 113 | ++ ServerLoginPacketListenerImpl.LOGGER.error("Username '{}' tried to join with an invalid session", gameprofile.getName()); |
| 114 | ++ } |
| 115 | ++ } catch (AuthenticationUnavailableException authenticationunavailableexception) { |
| 116 | ++ if (ServerLoginPacketListenerImpl.this.server.isSingleplayer()) { |
| 117 | ++ ServerLoginPacketListenerImpl.LOGGER.warn("Authentication servers are down but will let them in anyway!"); |
| 118 | ++ ServerLoginPacketListenerImpl.this.gameProfile = gameprofile; |
| 119 | ++ ServerLoginPacketListenerImpl.this.state = ServerLoginPacketListenerImpl.State.READY_TO_ACCEPT; |
| 120 | ++ } else { |
| 121 | ++ ServerLoginPacketListenerImpl.this.disconnect(io.papermc.paper.adventure.PaperAdventure.asVanilla(io.papermc.paper.configuration.GlobalConfiguration.get().messages.kick.authenticationServersDown)); // Paper |
| 122 | ++ ServerLoginPacketListenerImpl.LOGGER.error("Couldn't verify username because servers are unavailable"); |
| 123 | ++ } |
| 124 | ++ // CraftBukkit start - catch all exceptions |
| 125 | ++ } catch (Exception exception) { |
| 126 | ++ ServerLoginPacketListenerImpl.this.disconnect("Failed to verify username!"); |
| 127 | ++ server.server.getLogger().log(java.util.logging.Level.WARNING, "Exception verifying " + gameprofile.getName(), exception); |
| 128 | ++ // CraftBukkit end |
| 129 | + } |
| 130 | +- // CraftBukkit start - catch all exceptions |
| 131 | +- } catch (Exception exception) { |
| 132 | +- ServerLoginPacketListenerImpl.this.disconnect("Failed to verify username!"); |
| 133 | +- server.server.getLogger().log(java.util.logging.Level.WARNING, "Exception verifying " + gameprofile.getName(), exception); |
| 134 | +- // CraftBukkit end |
| 135 | +- } |
| 136 | + |
| 137 | +- } |
| 138 | ++ } |
| 139 | + |
| 140 | +- @Nullable |
| 141 | +- private InetAddress getAddress() { |
| 142 | +- SocketAddress socketaddress = ServerLoginPacketListenerImpl.this.connection.getRemoteAddress(); |
| 143 | ++ @Nullable |
| 144 | ++ private InetAddress getAddress() { |
| 145 | ++ SocketAddress socketaddress = ServerLoginPacketListenerImpl.this.connection.getRemoteAddress(); |
| 146 | + |
| 147 | +- return ServerLoginPacketListenerImpl.this.server.getPreventProxyConnections() && socketaddress instanceof InetSocketAddress ? ((InetSocketAddress) socketaddress).getAddress() : null; |
| 148 | +- } |
| 149 | +- }); |
| 150 | ++ return ServerLoginPacketListenerImpl.this.server.getPreventProxyConnections() && socketaddress instanceof InetSocketAddress ? ((InetSocketAddress) socketaddress).getAddress() : null; |
| 151 | ++ } |
| 152 | ++ }); |
| 153 | ++ } catch (java.util.concurrent.RejectedExecutionException rejected) { |
| 154 | ++ ServerLoginPacketListenerImpl.this.disconnect("Failed to verify username!"); // Don't give attackers confirmation of a successful attack - don't change the message |
| 155 | ++ server.server.getLogger().log(java.util.logging.Level.WARNING, "Exception verifying " + (ServerLoginPacketListenerImpl.this.gameProfile != null ? ServerLoginPacketListenerImpl.this.gameProfile.getName() : "(address) " + ServerLoginPacketListenerImpl.this.connection.getRemoteAddress()) + " - auth queue full", rejected); |
| 156 | ++ } |
| 157 | + // Paper end |
| 158 | + } |
| 159 | + |
| 160 | +@@ -458,14 +468,19 @@ public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener, |
| 161 | + //TODO Update handling for lazy sessions, might not even have to do anything? |
| 162 | + |
| 163 | + // Proceed with login |
| 164 | +- authenticatorPool.execute(() -> { |
| 165 | +- try { |
| 166 | +- new LoginHandler().fireEvents(); |
| 167 | +- } catch (Exception ex) { |
| 168 | +- disconnect("Failed to verify username!"); |
| 169 | +- server.server.getLogger().log(java.util.logging.Level.WARNING, "Exception verifying " + gameProfile.getName(), ex); |
| 170 | +- } |
| 171 | +- }); |
| 172 | ++ try { |
| 173 | ++ authenticatorPool.execute(() -> { |
| 174 | ++ try { |
| 175 | ++ new LoginHandler().fireEvents(); |
| 176 | ++ } catch (Exception ex) { |
| 177 | ++ disconnect("Failed to verify username!"); |
| 178 | ++ server.server.getLogger().log(java.util.logging.Level.WARNING, "Exception verifying " + gameProfile.getName(), ex); |
| 179 | ++ } |
| 180 | ++ }); |
| 181 | ++ } catch (java.util.concurrent.RejectedExecutionException rejected) { |
| 182 | ++ disconnect("Failed to verify username!"); // Don't give attackers confirmation of a successful attack - don't change the message |
| 183 | ++ server.server.getLogger().log(java.util.logging.Level.WARNING, "Exception verifying " + gameProfile.getName() + " - auth queue full", rejected); |
| 184 | ++ } |
| 185 | + return; |
| 186 | + } |
| 187 | + // Paper end |
0 commit comments