diff --git a/core/src/main/java/io/undertow/server/DefaultByteBufferPool.java b/core/src/main/java/io/undertow/server/DefaultByteBufferPool.java index 040ff23ac8..c28aef4818 100644 --- a/core/src/main/java/io/undertow/server/DefaultByteBufferPool.java +++ b/core/src/main/java/io/undertow/server/DefaultByteBufferPool.java @@ -26,7 +26,9 @@ import java.nio.ByteBuffer; import java.util.ArrayDeque; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; @@ -39,7 +41,7 @@ */ public class DefaultByteBufferPool implements ByteBufferPool { - private final ThreadLocal threadLocalCache = new ThreadLocal<>(); + private final ThreadLocalCache threadLocalCache = new ThreadLocalCache(); // Access requires synchronization on the threadLocalDataList instance private final List> threadLocalDataList = new ArrayList<>(); private final ConcurrentLinkedQueue queue = new ConcurrentLinkedQueue<>(); @@ -229,6 +231,7 @@ public void close() { local.buffers.clear(); } ref.clear(); + threadLocalCache.remove(local); } threadLocalDataList.clear(); } @@ -336,4 +339,29 @@ protected void finalize() throws Throwable { } } + // This is used instead of Java ThreadLocal class. Unlike in the ThreadLocal class, the remove() method in this + // class can be called by a different thread than the one that initialized the data. + private static class ThreadLocalCache { + + Map localsByThread = new HashMap<>(); + + ThreadLocalData get() { + return localsByThread.get(Thread.currentThread()); + } + + void set(ThreadLocalData threadLocalData) { + localsByThread.put(Thread.currentThread(), threadLocalData); + } + + void remove(ThreadLocalData threadLocalData) { + // Find the entry containing given data instance and remove it from the map. + for (Map.Entry entry: localsByThread.entrySet()) { + if (threadLocalData.equals(entry.getValue())) { + localsByThread.remove(entry.getKey(), entry.getValue()); + break; + } + } + } + } + } diff --git a/core/src/test/java/io/undertow/server/handlers/session/InMemorySessionTestCase.java b/core/src/test/java/io/undertow/server/handlers/session/InMemorySessionTestCase.java index eb29688ed8..90f09d12a9 100644 --- a/core/src/test/java/io/undertow/server/handlers/session/InMemorySessionTestCase.java +++ b/core/src/test/java/io/undertow/server/handlers/session/InMemorySessionTestCase.java @@ -20,6 +20,7 @@ import java.io.IOException; +import io.undertow.UndertowMessages; import io.undertow.server.HttpHandler; import io.undertow.server.HttpServerExchange; import io.undertow.server.session.InMemorySessionManager; @@ -249,4 +250,37 @@ public void run() { client.getConnectionManager().shutdown(); } } + + @Test + public void inMemorySessionNoConfigTest() throws IOException { + try (TestHttpClient client = new TestHttpClient()) { + client.setCookieStore(new BasicCookieStore()); + + final SessionAttachmentHandler handler = new SessionAttachmentHandler(new InMemorySessionManager(""), null); + final Throwable[] thrown = {null}; + handler.setNext(new HttpHandler() { + @Override + public void handleRequest(final HttpServerExchange exchange) throws Exception { + final SessionManager manager = exchange.getAttachment(SessionManager.ATTACHMENT_KEY); + try { + manager.getSession(exchange, null); + } catch (Throwable t) { + thrown[0] = t; + } + } + }); + DefaultServer.setRootHandler(handler); + + HttpGet get = new HttpGet(DefaultServer.getDefaultServerURL() + "/notamatchingpath"); + client.execute(get); + + if (thrown[0] != null) { + Assert.assertTrue(thrown[0] instanceof IllegalStateException); + Assert.assertEquals(UndertowMessages.MESSAGES.couldNotFindSessionCookieConfig().getCause(), thrown[0].getCause()); + Assert.assertEquals(UndertowMessages.MESSAGES.couldNotFindSessionCookieConfig().getMessage(), thrown[0].getMessage()); + } else { + Assert.fail("No exception was thrown."); + } + } + } }