This commit is contained in:
2024-11-30 19:03:49 +08:00
commit 1e6763c160
3806 changed files with 737676 additions and 0 deletions

View File

@@ -0,0 +1,361 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.catalina.session;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.security.SecureRandom;
import org.junit.Assert;
import org.junit.Test;
import org.apache.catalina.Context;
import org.apache.catalina.Host;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.Session;
import org.apache.catalina.core.StandardContext;
import org.apache.catalina.core.StandardHost;
/**
* Named Benchmarks so it is not automatically executed as part of the unit
* tests.
*/
public class Benchmarks {
/*
* Results on markt's 4-core Windows dev box
* 1 thread - ~1,400ms
* 2 threads - ~2,100ms
* 4 threads - ~3,100ms
* 16 threads - ~14,700ms
*
* Results on markt's 2-core OSX dev box
* 1 thread - ~1,400ms
* 2 threads - ~1,700ms
* 4 threads - ~3,500ms
* 16 threads - ~14,465ms
*/
@Test
public void testManagerBaseGenerateSessionId() throws Exception {
doTestManagerBaseGenerateSessionId(1, 1000000);
doTestManagerBaseGenerateSessionId(1, 1000000);
doTestManagerBaseGenerateSessionId(1, 1000000);
doTestManagerBaseGenerateSessionId(2, 1000000);
doTestManagerBaseGenerateSessionId(2, 1000000);
doTestManagerBaseGenerateSessionId(2, 1000000);
doTestManagerBaseGenerateSessionId(4, 1000000);
doTestManagerBaseGenerateSessionId(4, 1000000);
doTestManagerBaseGenerateSessionId(4, 1000000);
doTestManagerBaseGenerateSessionId(16, 1000000);
// Reduce iterations as context switching will slow things down
doTestManagerBaseGenerateSessionId(100, 100000);
doTestManagerBaseGenerateSessionId(400, 10000);
}
private void doTestManagerBaseGenerateSessionId(int threadCount,
int iterCount) throws Exception {
// Create a default session manager
StandardManager mgr = new StandardManager();
try {
mgr.startInternal();
} catch (LifecycleException e) {
// Ignore - this is expected
}
mgr.generateSessionId();
while (mgr.sessionCreationTiming.size() <
ManagerBase.TIMING_STATS_CACHE_SIZE) {
mgr.sessionCreationTiming.add(null);
}
while (mgr.sessionExpirationTiming.size() <
ManagerBase.TIMING_STATS_CACHE_SIZE) {
mgr.sessionExpirationTiming.add(null);
}
Thread[] threads = new Thread[threadCount];
for (int i = 0; i < threadCount; i++) {
threads[i] = new Thread(
new TestThreadGenerateSessionId(mgr, iterCount));
}
long start = System.currentTimeMillis();
for (int i = 0; i < threadCount; i++) {
threads[i].start();
}
for (int i = 0; i < threadCount; i++) {
try {
threads[i].join();
} catch (InterruptedException e) {
e.printStackTrace();
Assert.fail(e.getMessage());
}
}
long end = System.currentTimeMillis();
StringBuilder result = new StringBuilder();
result.append("Threads: ");
result.append(threadCount);
result.append(", Time(ms): ");
result.append(end-start);
System.out.println(result.toString());
}
private static final class TestThreadGenerateSessionId implements Runnable {
private ManagerBase mgr;
private int count;
public TestThreadGenerateSessionId(ManagerBase mgr, int count) {
this.mgr = mgr;
this.count = count;
}
@Override
public void run() {
for (int i = 0; i < count; i++) {
mgr.generateSessionId();
}
}
}
/*
* Results on markt's 4-core Windows dev box
* 1 thread - ~3,800ms
* 2 threads - ~6,700ms
* 4 threads - ~11,000ms
* 16 threads - ~43,500ms
*
* Results on markt's 2-core OSX dev box
* 1 thread - ~4,100ms
* 2 threads - ~5,700ms
* 4 threads - ~11,700ms
* 16 threads - ~45,600ms
*/
@Test
public void testManagerBaseCreateSession() throws LifecycleException {
doTestManagerBaseCreateSession(1, 100000);
doTestManagerBaseCreateSession(2, 1000000);
doTestManagerBaseCreateSession(4, 1000000);
doTestManagerBaseCreateSession(16, 1000000);
// Reduce iterations as context switching will slow things down
doTestManagerBaseCreateSession(100, 100000);
doTestManagerBaseCreateSession(400, 10000);
}
private void doTestManagerBaseCreateSession(int threadCount,
int iterCount) throws LifecycleException {
// Create a default session manager
StandardManager mgr = new StandardManager();
mgr.setPathname(null);
Host host = new StandardHost();
host.setName("unittest");
Context context = new StandardContext();
context.setPath("");
context.setParent(host);
mgr.setContext(context);
mgr.start();
mgr.generateSessionId();
while (mgr.sessionCreationTiming.size() <
ManagerBase.TIMING_STATS_CACHE_SIZE) {
mgr.sessionCreationTiming.add(null);
}
while (mgr.sessionExpirationTiming.size() <
ManagerBase.TIMING_STATS_CACHE_SIZE) {
mgr.sessionExpirationTiming.add(null);
}
Thread[] threads = new Thread[threadCount];
for (int i = 0; i < threadCount; i++) {
threads[i] = new Thread(
new TestThreadCreateSession(mgr, iterCount));
}
long start = System.currentTimeMillis();
for (int i = 0; i < threadCount; i++) {
threads[i].start();
}
for (int i = 0; i < threadCount; i++) {
try {
threads[i].join();
} catch (InterruptedException e) {
e.printStackTrace();
Assert.fail(e.getMessage());
}
}
long end = System.currentTimeMillis();
StringBuilder result = new StringBuilder();
result.append("Threads: ");
result.append(threadCount);
result.append(", Time(ms): ");
result.append(end-start);
System.out.println(result.toString());
}
private static final class TestThreadCreateSession implements Runnable {
private ManagerBase mgr;
private int count;
public TestThreadCreateSession(ManagerBase mgr, int count) {
this.mgr = mgr;
this.count = count;
}
@Override
public void run() {
for (int i = 0; i < count; i++) {
Session session = mgr.createSession(mgr.generateSessionId());
session.expire();
}
}
}
/*
* SecureRandom vs. reading /dev/urandom. Very different performance noted
* on some platforms.
*
* Results on markt's 4-core Windows dev box
* SecureRandom /dev/urandom
* 1 thread - ~766ms N/A
* 2 threads - ~843ms N/A
* 4 threads - ~766ms N/A
*
* Results on markt's 2-core OSX dev box
* SecureRandom /dev/urandom
* 1 thread - ~759ms ~3,500ms
* 2 threads - ~725ms ~5,200ms
* 4 threads - ~1,265ms ~10,500ms
*/
@Test
public void testSecureRandomVsDevURandom() throws Exception {
doTestSecureRandomVsDevURandom(1, 1000000);
doTestSecureRandomVsDevURandom(2, 1000000);
doTestSecureRandomVsDevURandom(4, 1000000);
}
private void doTestSecureRandomVsDevURandom(int threadCount, int iterCount)
throws Exception {
doTestSecureRandomVsDevURandomInner(threadCount, iterCount, true);
doTestSecureRandomVsDevURandomInner(threadCount, iterCount, false);
}
private void doTestSecureRandomVsDevURandomInner(int threadCount,
int iterCount, boolean useSecureRandom) throws Exception {
Thread[] threads = new Thread[threadCount];
for (int i = 0; i < threadCount; i++) {
if (useSecureRandom) {
threads[i] = new Thread(new TestThreadSecureRandom(iterCount));
} else {
threads[i] = new Thread(new TestThreadDevUrandom(iterCount));
}
}
long start = System.currentTimeMillis();
for (int i = 0; i < threadCount; i++) {
threads[i].start();
}
for (int i = 0; i < threadCount; i++) {
try {
threads[i].join();
} catch (InterruptedException e) {
e.printStackTrace();
Assert.fail(e.getMessage());
}
}
long end = System.currentTimeMillis();
StringBuilder result = new StringBuilder();
if (useSecureRandom) {
result.append("SecureRandom ");
} else {
result.append("/dev/urandom ");
}
result.append("Threads: ");
result.append(threadCount);
result.append(", Time(ms): ");
result.append(end-start);
System.out.println(result.toString());
}
private static final class TestThreadSecureRandom implements Runnable {
private SecureRandom secureRandom;
private byte[] bytes = new byte[16];
private int count;
TestThreadSecureRandom(int iterCount) throws Exception {
this.count = iterCount;
this.secureRandom = SecureRandom.getInstance("SHA1PRNG");
}
@Override
public void run() {
for (int i = 0; i < count; i++) {
secureRandom.nextBytes(bytes);
}
}
}
private static final class TestThreadDevUrandom implements Runnable {
private InputStream is;
private byte[] bytes = new byte[16];
private int count;
TestThreadDevUrandom(int iterCount) {
try {
is = new FileInputStream("/dev/urandom");
} catch (FileNotFoundException e) {
e.printStackTrace();
}
this.count = iterCount;
}
@Override
public void run() {
try {
int read = 0;
for (int i = 0; i < count; i++) {
read = is.read(bytes);
if (read < bytes.length) {
throw new IOException("Only read " + read + " bytes");
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}

View File

@@ -0,0 +1,101 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.catalina.session;
import java.io.File;
import java.io.IOException;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.apache.catalina.Manager;
import org.apache.tomcat.unittest.TesterContext;
import org.apache.tomcat.unittest.TesterServletContext;
import org.apache.tomcat.util.http.fileupload.FileUtils;
public class FileStoreTest {
private static final String SESS_TEMPPATH = "SESS_TEMP";
private static final File dir = new File(SESS_TEMPPATH);
private static FileStore fileStore;
private static File file1 = new File(SESS_TEMPPATH + "/tmp1.session");
private static File file2 = new File(SESS_TEMPPATH + "/tmp2.session");
private static Manager manager = new StandardManager();
@BeforeClass
public static void setup() {
TesterContext testerContext = new TesterContext();
testerContext.setServletContext(new TesterServletContext());
manager.setContext(testerContext);
fileStore = new FileStore();
fileStore.setManager(manager);
}
@AfterClass
public static void cleanup() throws IOException {
FileUtils.cleanDirectory(dir);
FileUtils.deleteDirectory(dir);
}
@Before
public void beforeEachTest() throws IOException {
fileStore.setDirectory(SESS_TEMPPATH);
if (!dir.mkdir()) {
Assert.fail();
}
if (!file1.createNewFile()) {
Assert.fail();
}
if (!file2.createNewFile()) {
Assert.fail();
}
}
@Test
public void getSize() throws Exception {
Assert.assertEquals(2, fileStore.getSize());
}
@Test
public void clear() throws Exception {
fileStore.clear();
Assert.assertEquals(0, fileStore.getSize());
}
@Test
public void keys() throws Exception {
Assert.assertArrayEquals(new String[]{"tmp1", "tmp2"}, fileStore.keys());
fileStore.clear();
Assert.assertArrayEquals(new String[]{}, fileStore.keys());
}
@Test
public void removeTest() throws Exception {
fileStore.remove("tmp1");
Assert.assertEquals(1, fileStore.getSize());
}
}

View File

@@ -0,0 +1,161 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.catalina.session;
import java.util.concurrent.atomic.AtomicInteger;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
import org.junit.Assert;
import org.junit.Test;
import org.apache.catalina.Context;
import org.apache.catalina.Host;
import org.apache.catalina.Manager;
import org.apache.catalina.Session;
import org.apache.catalina.Store;
import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.RequestFacade;
import org.apache.tomcat.unittest.TesterContext;
import org.apache.tomcat.unittest.TesterHost;
import org.easymock.EasyMock;
import org.easymock.IAnswer;
public class TestPersistentManager {
@Test
public void testMinIdleSwap() throws Exception {
PersistentManager manager = new PersistentManager();
manager.setStore(new TesterStore());
Host host = new TesterHost();
Context context = new TesterContext();
context.setParent(host);
manager.setContext(context);
manager.setMaxActiveSessions(2);
manager.setMinIdleSwap(0);
manager.start();
// Create the maximum number of sessions
manager.createSession(null);
manager.createSession(null);
// Given the minIdleSwap settings, this should swap one out to get below
// the limit
manager.processPersistenceChecks();
Assert.assertEquals(1, manager.getActiveSessions());
Assert.assertEquals(2, manager.getActiveSessionsFull());
manager.createSession(null);
Assert.assertEquals(2, manager.getActiveSessions());
Assert.assertEquals(3, manager.getActiveSessionsFull());
}
@Test
public void testBug62175() throws Exception {
final PersistentManager manager = new PersistentManager();
final AtomicInteger sessionExpireCounter = new AtomicInteger();
Store mockStore = EasyMock.createNiceMock(Store.class);
EasyMock.expect(mockStore.load(EasyMock.anyString())).andAnswer(new IAnswer<Session>() {
@Override
public Session answer() throws Throwable {
return timedOutSession(manager, sessionExpireCounter);
}
}).anyTimes();
EasyMock.replay(mockStore);
manager.setStore(mockStore);
Host host = new TesterHost();
final RequestCachingSessionListener requestCachingSessionListener = new RequestCachingSessionListener();
final Context context = new TesterContext() {
@Override
public Object[] getApplicationLifecycleListeners() {
return new Object[] { requestCachingSessionListener };
}
@Override
public Manager getManager() {
return manager;
}
};
context.setParent(host);
Request req = new Request() {
@Override
public Context getContext() {
return context;
}
};
req.setRequestedSessionId("invalidSession");
HttpServletRequest request = new RequestFacade(req);
requestCachingSessionListener.request = request;
manager.setContext(context);
manager.start();
Assert.assertNull(request.getSession(false));
Assert.assertEquals(1, sessionExpireCounter.get());
}
private static class RequestCachingSessionListener implements HttpSessionListener {
private HttpServletRequest request;
@Override
public void sessionCreated(HttpSessionEvent se) {
// do nothing
}
@Override
public void sessionDestroyed(HttpSessionEvent se) {
request.getSession(false);
}
}
private StandardSession timedOutSession(final PersistentManager manager, final AtomicInteger counter) {
StandardSession timedOutSession = new StandardSession(manager) {
private static final long serialVersionUID = -5910605558747844210L;
@Override
public void expire() {
counter.incrementAndGet();
super.expire();
}
};
timedOutSession.isValid = true;
timedOutSession.expiring = false;
timedOutSession.maxInactiveInterval = 1;
timedOutSession.lastAccessedTime = 0;
timedOutSession.id = "invalidSession";
return timedOutSession;
}
}

View File

@@ -0,0 +1,241 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.catalina.session;
import java.io.IOException;
import java.util.Arrays;
import java.util.concurrent.atomic.AtomicInteger;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.apache.catalina.Context;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.Session;
import org.apache.catalina.core.StandardContext;
import org.apache.catalina.startup.Tomcat;
import org.apache.catalina.startup.TomcatBaseTest;
import org.apache.catalina.valves.PersistentValve;
public class TestPersistentManagerIntegration extends TomcatBaseTest {
private final String ACTIVITY_CHECK = "org.apache.catalina.session.StandardSession.ACTIVITY_CHECK";
private String oldActivityCheck;
/**
* As documented in config/manager.html, the "ACTIVITY_CHECK" property must
* be set to "true" for PersistentManager to function correctly.
*/
@Before
public void setActivityCheck() {
oldActivityCheck = System.setProperty(ACTIVITY_CHECK, "true");
}
@After
public void resetActivityCheck() {
if (oldActivityCheck != null) {
System.setProperty(ACTIVITY_CHECK, oldActivityCheck);
} else {
System.clearProperty(ACTIVITY_CHECK);
}
}
/**
* Wait enough for the system clock to update its value. On some systems
* (e.g. old Windows) the clock granularity is tens of milliseconds.
*/
private void waitForClockUpdate() throws InterruptedException {
long startTime = System.currentTimeMillis();
int waitTime = 1;
do {
Thread.sleep(waitTime);
waitTime *= 10;
} while (System.currentTimeMillis() == startTime);
}
/**
* Wait while session access counter has a positive value.
*/
private void waitWhileSessionIsActive(StandardSession session)
throws InterruptedException {
long maxWaitTime = System.currentTimeMillis() + 60000;
AtomicInteger accessCount = session.accessCount;
while (accessCount.get() > 0) {
// Wait until o.a.c.connector.Request.recycle() completes,
// as it updates lastAccessedTime.
Assert.assertTrue(System.currentTimeMillis() < maxWaitTime);
Thread.sleep(200);
}
}
@Test
public void noSessionCreate_57637() throws IOException, LifecycleException {
// Setup Tomcat instance
Tomcat tomcat = getTomcatInstance();
// No file system docBase required
StandardContext ctx = (StandardContext) tomcat.addContext("", null);
ctx.setDistributable(true);
Tomcat.addServlet(ctx, "DummyServlet", new DummyServlet());
ctx.addServletMappingDecoded("/dummy", "DummyServlet");
PersistentManager manager = new PersistentManager();
TesterStore store = new TesterStore();
manager.setStore(store);
manager.setMaxIdleBackup(0);
ctx.setManager(manager);
ctx.addValve(new PersistentValve());
tomcat.start();
Assert.assertEquals(manager.getActiveSessions(), 0);
Assert.assertTrue("No sessions managed", manager.getSessionIdsFull().isEmpty());
Assert.assertEquals(
"NO_SESSION",
getUrl(
"http://localhost:" + getPort()
+ "/dummy?no_create_session=true").toString());
Assert.assertEquals(manager.getActiveSessions(), 0);
Assert.assertTrue("No sessions where created", manager.getSessionIdsFull().isEmpty());
}
@Test
public void testCreateSessionAndPassivate() throws IOException, LifecycleException, ClassNotFoundException {
// Setup Tomcat instance
Tomcat tomcat = getTomcatInstance();
// No file system docBase required
StandardContext ctx = (StandardContext) tomcat.addContext("", null);
ctx.setDistributable(true);
Tomcat.addServlet(ctx, "DummyServlet", new DummyServlet());
ctx.addServletMappingDecoded("/dummy", "DummyServlet");
PersistentManager manager = new PersistentManager();
TesterStore store = new TesterStore();
manager.setStore(store);
manager.setMaxIdleBackup(0);
ctx.setManager(manager);
ctx.addValve(new PersistentValve());
tomcat.start();
Assert.assertEquals("No active sessions", manager.getActiveSessions(), 0);
Assert.assertTrue("No sessions managed", manager.getSessionIdsFull().isEmpty());
String sessionId = getUrl(
"http://localhost:" + getPort()
+ "/dummy?no_create_session=false").toString();
Assert.assertNotNull("Session is stored", store.load(sessionId));
Assert.assertEquals("All sessions are passivated", manager.getActiveSessions(), 0);
Assert.assertTrue("One session was created", !manager.getSessionIdsFull().isEmpty());
}
@Test
public void backsUpOnce_56698() throws IOException, LifecycleException,
InterruptedException {
// Setup Tomcat instance
Tomcat tomcat = getTomcatInstance();
// No file system docBase required
Context ctx = tomcat.addContext("", null);
ctx.setDistributable(true);
Tomcat.addServlet(ctx, "DummyServlet", new DummyServlet());
ctx.addServletMappingDecoded("/dummy", "DummyServlet");
PersistentManager manager = new PersistentManager();
TesterStore store = new TesterStore();
manager.setStore(store);
manager.setMaxIdleBackup(0);
ctx.setManager(manager);
tomcat.start();
String sessionId = getUrl("http://localhost:" + getPort() + "/dummy")
.toString();
// Note: PersistenceManager.findSession() silently updates
// session.lastAccessedTime, so call it only once before other work.
Session session = manager.findSession(sessionId);
// Wait until request processing ends, as Request.recycle() updates
// session.lastAccessedTime via session.endAccess().
waitWhileSessionIsActive((StandardSession) session);
long lastAccessedTime = session.getLastAccessedTimeInternal();
// Session should be idle at least for 0 second (maxIdleBackup)
// to be eligible for persistence, thus no need to wait.
// Waiting a bit, to catch changes in last accessed time of a session
waitForClockUpdate();
manager.processPersistenceChecks();
Assert.assertEquals(Arrays.asList(sessionId), store.getSavedIds());
Assert.assertEquals(lastAccessedTime, session.getLastAccessedTimeInternal());
// session was not accessed, so no save will be performed
waitForClockUpdate();
manager.processPersistenceChecks();
Assert.assertEquals(Arrays.asList(sessionId), store.getSavedIds());
Assert.assertEquals(lastAccessedTime, session.getLastAccessedTimeInternal());
// access session
session.access();
session.endAccess();
// session was accessed, so it will be saved once again
manager.processPersistenceChecks();
Assert.assertEquals(Arrays.asList(sessionId, sessionId),
store.getSavedIds());
// session was not accessed, so once again no save will happen
manager.processPersistenceChecks();
Assert.assertEquals(Arrays.asList(sessionId, sessionId),
store.getSavedIds());
}
private static class DummyServlet extends HttpServlet {
private static final long serialVersionUID = -3696433049266123995L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
boolean createSession = !Boolean.parseBoolean(req
.getParameter("no_create_session"));
HttpSession session = req.getSession(createSession);
if (session == null) {
resp.getWriter().print("NO_SESSION");
} else {
String id = session.getId();
resp.getWriter().print(id);
}
}
}
}

View File

@@ -0,0 +1,154 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.catalina.session;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import org.junit.Assert;
import org.junit.Test;
import org.apache.catalina.Manager;
import org.apache.catalina.core.StandardContext;
public class TestStandardSession {
private static final Manager TEST_MANAGER;
static {
TEST_MANAGER = new StandardManager();
TEST_MANAGER.setContext(new StandardContext());
}
@Test
public void testSerializationEmpty() throws Exception {
StandardSession s1 = new StandardSession(TEST_MANAGER);
s1.setValid(true);
StandardSession s2 = serializeThenDeserialize(s1);
validateSame(s1, s2, 0);
}
@Test
public void testSerializationSimple01() throws Exception {
StandardSession s1 = new StandardSession(TEST_MANAGER);
s1.setValid(true);
s1.setAttribute("attr01", "value01");
StandardSession s2 = serializeThenDeserialize(s1);
validateSame(s1, s2, 1);
}
@Test
public void testSerializationSimple02() throws Exception {
StandardSession s1 = new StandardSession(TEST_MANAGER);
s1.setValid(true);
s1.setAttribute("attr01", new NonSerializable());
StandardSession s2 = serializeThenDeserialize(s1);
validateSame(s1, s2, 0);
}
@Test
public void testSerializationSimple03() throws Exception {
StandardSession s1 = new StandardSession(TEST_MANAGER);
s1.setValid(true);
s1.setAttribute("attr01", "value01");
s1.setAttribute("attr02", new NonSerializable());
StandardSession s2 = serializeThenDeserialize(s1);
validateSame(s1, s2, 1);
}
/*
* See Bug 58284
*/
@Test
public void serializeSkipsNonSerializableAttributes() throws Exception {
final String nonSerializableKey = "nonSerializable";
final String nestedNonSerializableKey = "nestedNonSerializable";
final String serializableKey = "serializable";
final Object serializableValue = "foo";
StandardSession s1 = new StandardSession(TEST_MANAGER);
s1.setValid(true);
Map<String, NonSerializable> value = new HashMap<>();
value.put("key", new NonSerializable());
s1.setAttribute(nestedNonSerializableKey, value);
s1.setAttribute(serializableKey, serializableValue);
s1.setAttribute(nonSerializableKey, new NonSerializable());
StandardSession s2 = serializeThenDeserialize(s1);
Assert.assertNull(s2.getAttribute(nestedNonSerializableKey));
Assert.assertNull(s2.getAttribute(nonSerializableKey));
Assert.assertEquals(serializableValue, s2.getAttribute(serializableKey));
}
private StandardSession serializeThenDeserialize(StandardSession source)
throws IOException, ClassNotFoundException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
source.writeObjectData(oos);
StandardSession dest = new StandardSession(TEST_MANAGER);
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
dest.readObjectData(ois);
return dest;
}
private void validateSame(StandardSession s1, StandardSession s2, int expectedCount) {
int count = 0;
Enumeration<String> names = s1.getAttributeNames();
while (names.hasMoreElements()) {
count ++;
String name = names.nextElement();
Object v1 = s1.getAttribute(name);
Object v2 = s2.getAttribute(name);
Assert.assertEquals(v1, v2);
}
Assert.assertEquals(expectedCount, count);
}
private static class NonSerializable {
}
}

View File

@@ -0,0 +1,103 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.catalina.session;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.junit.Assert;
import org.junit.Test;
import org.apache.catalina.Context;
import org.apache.catalina.ha.tcp.SimpleTcpCluster;
import org.apache.catalina.startup.Tomcat;
import org.apache.catalina.startup.TomcatBaseTest;
import org.apache.tomcat.util.buf.ByteChunk;
public class TestStandardSessionIntegration extends TomcatBaseTest {
/*
* Test session.invalidate() in a clustered environment.
*/
@Test
public void testBug56578a() throws Exception {
doTestInvalidate(true);
}
@Test
public void testBug56578b() throws Exception {
doTestInvalidate(false);
}
private void doTestInvalidate(boolean useClustering) throws Exception {
// Setup Tomcat instance
Tomcat tomcat = getTomcatInstance();
// No file system docBase required
Context ctx = tomcat.addContext("", null);
Tomcat.addServlet(ctx, "bug56578", new Bug56578Servlet());
ctx.addServletMappingDecoded("/bug56578", "bug56578");
if (useClustering) {
tomcat.getEngine().setCluster(new SimpleTcpCluster());
ctx.setDistributable(true);
ctx.setManager(ctx.getCluster().createManager(""));
}
tomcat.start();
ByteChunk res = getUrl("http://localhost:" + getPort() + "/bug56578");
Assert.assertEquals("PASS", res.toString());
}
private static class Bug56578Servlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.setContentType("text/plain");
resp.setCharacterEncoding("UTF-8");
PrintWriter pw = resp.getWriter();
HttpSession session = req.getSession(true);
session.invalidate();
// Ugly but the easiest way to test of the session is valid or not
boolean result;
try {
session.getCreationTime();
result = false;
} catch (IllegalStateException ise) {
result = true;
}
if (result) {
pw.print("PASS");
} else {
pw.print("FAIL");
}
}
}
}

View File

@@ -0,0 +1,90 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.catalina.session;
import java.beans.PropertyChangeListener;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.catalina.Manager;
import org.apache.catalina.Session;
import org.apache.catalina.Store;
class TesterStore implements Store {
private Manager manager;
private Map<String, Session> sessions = new HashMap<>();
private List<String> savedIds = new ArrayList<>();
List<String> getSavedIds() {
return savedIds;
}
@Override
public Manager getManager() {
return this.manager;
}
@Override
public void setManager(Manager manager) {
this.manager = manager;
}
@Override
public int getSize() throws IOException {
return savedIds.size();
}
@Override
public void addPropertyChangeListener(PropertyChangeListener listener) {
}
@Override
public String[] keys() throws IOException {
return new ArrayList<>(sessions.keySet()).toArray(new String[] {});
}
@Override
public Session load(String id) throws ClassNotFoundException,
IOException {
return sessions.get(id);
}
@Override
public void remove(String id) throws IOException {
sessions.remove(id);
}
@Override
public void clear() throws IOException {
}
@Override
public void removePropertyChangeListener(PropertyChangeListener listener) {
}
@Override
public void save(Session session) throws IOException {
sessions.put(session.getId(), session);
savedIds.add(session.getId());
}
}

View File

@@ -0,0 +1,147 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.catalina.session;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import org.junit.Assert;
import org.junit.Test;
/**
* The design of the session manager depends on the thread-safety - or not - of
* a number of classes. In some cases the Javadoc is unclear on the
* thread-safety of a class. These tests were written to test the thread-safety
* of key classes.
*
* Named Threading so it is not automatically executed as part of the unit
* tests.
*/
public class Threading {
/*
* {@link FileInputStream#read(byte[])} and related methods are all native
* methods so it isn't immediately obvious if they are thread-safe or not.
*
* <pre>
* Windows JDK 1.6.0_22_x64 - Thread safe
* OSX JDK 1.6.0_22_x64 - Not thread safe
* OSX JDK 1.7.0_51_x64 - Not thread safe
* </pre>
*
* Therefore, have to assume that {@link FileInputStream#read(byte[])} is
* not thread safe.
*/
@Test
public void testFileInputStream() throws Exception {
doTestFileInputStream(1);
doTestFileInputStream(2);
doTestFileInputStream(4);
doTestFileInputStream(16);
}
public void doTestFileInputStream(int threadCount) throws Exception {
// Assumes "ant release" has been run
// Will need to be updated as new releases are made
File file = new File(
"./output/release/v8.0.15-dev/bin/apache-tomcat-8.0.15-dev.zip");
FileInputStream fis = new FileInputStream(file);
Thread[] threads = new Thread[threadCount];
FisReaderThread[] runnables = new FisReaderThread[threadCount];
for (int i = 0; i < threadCount; i++) {
runnables[i] = new FisReaderThread(fis);
threads[i] = new Thread(runnables[i]);
}
long start = System.currentTimeMillis();
for (int i = 0; i < threadCount; i++) {
threads[i].start();
}
for (int i = 0; i < threadCount; i++) {
try {
threads[i].join();
if (runnables[i].isfailed()) {
Assert.fail();
}
} catch (InterruptedException e) {
e.printStackTrace();
Assert.fail(e.getMessage());
}
}
long end = System.currentTimeMillis();
long byteCount = 0;
for (int i = 0; i < threadCount; i++) {
byteCount += runnables[i].getByteCount();
}
StringBuilder result = new StringBuilder();
result.append("Threads: ");
result.append(threadCount);
result.append(", Time(ms): ");
result.append(end-start);
result.append(", Bytes: ");
result.append(byteCount);
System.out.println(result.toString());
}
private static final class FisReaderThread implements Runnable {
private FileInputStream fis;
// Small buffer to make the process slow
private byte[] buffer = new byte[4];
private long byteCount = 0;
private boolean fail = false;
public FisReaderThread(FileInputStream fis) {
this.fis = fis;
}
@Override
public void run() {
int read = 0;
while (read > -1) {
byteCount += read;
try {
// Uncomment the sync block to test adding the sync fixes
// issues on platforms where fis is not thread-safe
// synchronized (fis) {
read = fis.read(buffer);
//}
} catch (IOException e) {
e.printStackTrace();
fail = true;
read = -1;
}
}
}
public long getByteCount() {
return byteCount;
}
public boolean isfailed() {
return fail;
}
}
}