diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/HashSessionManager.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/HashSessionManager.java index f6e380a43aa..5ce8491a5e1 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/HashSessionManager.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/HashSessionManager.java @@ -41,7 +41,10 @@ import org.eclipse.jetty.util.log.Logger; /* ------------------------------------------------------------ */ -/** An in-memory implementation of SessionManager. +/** + * HashSessionManager + * + * An in-memory implementation of SessionManager. *

* This manager supports saving sessions to disk, either periodically or at shutdown. * Sessions can also have their content idle saved to disk to reduce the memory overheads of large idle sessions. @@ -78,7 +81,7 @@ public class HashSessionManager extends AbstractSessionManager } /* ------------------------------------------------------------ */ - /* (non-Javadoc) + /** * @see org.eclipse.jetty.servlet.AbstractSessionManager#doStart() */ @Override @@ -111,7 +114,7 @@ public class HashSessionManager extends AbstractSessionManager } /* ------------------------------------------------------------ */ - /* (non-Javadoc) + /** * @see org.eclipse.jetty.servlet.AbstractSessionManager#doStop() */ @Override @@ -253,7 +256,7 @@ public class HashSessionManager extends AbstractSessionManager * @param seconds the period in seconds at which a check is made for sessions to be invalidated. */ public void setScavengePeriod(int seconds) - { + { if (seconds==0) seconds=60; @@ -265,6 +268,7 @@ public class HashSessionManager extends AbstractSessionManager period=1000; _scavengePeriodMs=period; + if (_timer!=null && (period!=old_period || _task==null)) { synchronized (this) @@ -304,25 +308,36 @@ public class HashSessionManager extends AbstractSessionManager // For each session long now=System.currentTimeMillis(); + for (Iterator i=_sessions.values().iterator(); i.hasNext();) { HashedSession session=i.next(); - long idleTime=session.getMaxInactiveInterval()*1000L; + long idleTime=session.getMaxInactiveInterval()*1000L; if (idleTime>0&&session.getAccessed()+idleTime0&&session.getAccessed()+_idleSavePeriodMs 0 && session.getAccessed()+_idleSavePeriodMs < now) { - session.idle(); + try + { + session.idle(); + } + catch (Exception e) + { + __log.warn("Problem idling session "+ session.getId(), e); + } } } - } - catch (Throwable t) - { - __log.warn("Problem scavenging sessions", t); - } + } finally { thread.setContextClassLoader(old_loader); diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/HashedSession.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/HashedSession.java index 33119f4f977..265680b98a9 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/HashedSession.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/HashedSession.java @@ -100,6 +100,7 @@ public class HashedSession extends AbstractSession /* ------------------------------------------------------------ */ synchronized void save(boolean reactivate) + throws Exception { // Only idle the session if not already idled and no previous save/idle has failed if (!isIdled() && !_saveFailed) @@ -128,16 +129,13 @@ public class HashedSession extends AbstractSession catch (Exception e) { saveFailed(); // We won't try again for this session - - LOG.warn("Problem saving session " + super.getId(), e); - if (fos != null) { // Must not leave the file open if the saving failed IO.close(fos); // No point keeping the file if we didn't save the whole session file.delete(); - _idled=false; // assume problem was before _values.clear(); + throw e; } } } @@ -181,7 +179,7 @@ public class HashedSession extends AbstractSession access(System.currentTimeMillis()); if (LOG.isDebugEnabled()) - LOG.debug("Deidling " + super.getId()); + LOG.debug("De-idling " + super.getId()); FileInputStream fis = null; @@ -203,7 +201,7 @@ public class HashedSession extends AbstractSession } catch (Exception e) { - LOG.warn("Problem deidling session " + super.getId(), e); + LOG.warn("Problem de-idling session " + super.getId(), e); IO.close(fis); invalidate(); } @@ -219,8 +217,10 @@ public class HashedSession extends AbstractSession * it to an idled state. */ public synchronized void idle() + throws Exception { save(false); + _idled = true; } /* ------------------------------------------------------------ */ diff --git a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/MultiPartFilter.java b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/MultiPartFilter.java index d466de34644..5ee0dead19e 100644 --- a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/MultiPartFilter.java +++ b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/MultiPartFilter.java @@ -132,7 +132,7 @@ public class MultiPartFilter implements Filter chain.doFilter(request,response); return; } - + InputStream in = new BufferedInputStream(request.getInputStream()); String content_type=srequest.getContentType(); diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/MultipartFilterTest.java b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/MultipartFilterTest.java index 38e2c8bbdca..9af2d126af9 100644 --- a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/MultipartFilterTest.java +++ b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/MultipartFilterTest.java @@ -42,6 +42,7 @@ import org.eclipse.jetty.servlet.FilterMapping; import org.eclipse.jetty.testing.HttpTester; import org.eclipse.jetty.testing.ServletTester; import org.eclipse.jetty.util.IO; +import org.eclipse.jetty.util.QuotedStringTokenizer; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -51,7 +52,22 @@ public class MultipartFilterTest private File _dir; private ServletTester tester; - + + public static class BoundaryServlet extends TestServlet + { + @Override + protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException + { + assertNotNull(req.getParameter("fileName")); + assertEquals("abc", req.getParameter("fileName")); + assertNotNull(req.getParameter("desc")); + assertEquals("123", req.getParameter("desc")); + assertNotNull(req.getParameter("title")); + assertEquals("ttt", req.getParameter("title")); + super.doPost(req, resp); + } + } + public static class TestServlet extends DumpServlet { @@ -258,6 +274,53 @@ public class MultipartFilterTest assertEquals(HttpServletResponse.SC_OK,response.getStatus()); assertTrue(response.getContent().indexOf("brown cow")>=0); } + + + @Test + public void testNoBoundary() throws Exception + { + // generated and parsed test + HttpTester request = new HttpTester(); + HttpTester response = new HttpTester(); + tester.addServlet(BoundaryServlet.class,"/testb"); + request.setMethod("POST"); + request.setVersion("HTTP/1.0"); + request.setHeader("Host","tester"); + request.setURI("/context/testb"); + + request.setHeader("Content-Type","multipart/form-data"); + + String content = "--\r\n"+ + "Content-Disposition: form-data; name=\"fileName\"\r\n"+ + "Content-Type: text/plain; charset=US-ASCII\r\n"+ + "Content-Transfer-Encoding: 8bit\r\n"+ + "\r\n"+ + "abc\r\n"+ + "--\r\n"+ + "Content-Disposition: form-data; name=\"desc\"\r\n"+ + "Content-Type: text/plain; charset=US-ASCII\r\n"+ + "Content-Transfer-Encoding: 8bit\r\n"+ + "\r\n"+ + "123\r\n"+ + "--\r\n"+ + "Content-Disposition: form-data; name=\"title\"\r\n"+ + "Content-Type: text/plain; charset=US-ASCII\r\n"+ + "Content-Transfer-Encoding: 8bit\r\n"+ + "\r\n"+ + "ttt\r\n"+ + "--\r\n"+ + "Content-Disposition: form-data; name=\"fileup\"; filename=\"test.upload\"\r\n"+ + "Content-Type: application/octet-stream\r\n"+ + "Content-Transfer-Encoding: binary\r\n"+ + "\r\n"+ + "000\r\n"+ + "----\r\n"; + request.setContent(content); + + response.parse(tester.getResponses(request.generate())); + assertTrue(response.getMethod()==null); + assertEquals(HttpServletResponse.SC_OK,response.getStatus()); + } /* * see the testParameterMap test diff --git a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/ClasspathPattern.java b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/ClasspathPattern.java index 63ec5b4ca76..f6bd679f1c3 100644 --- a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/ClasspathPattern.java +++ b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/ClasspathPattern.java @@ -189,16 +189,24 @@ public class ClasspathPattern if (_entries != null) { name = name.replace('/','.'); - name = name.replaceFirst("^[.]+",""); - name = name.replaceAll("\\$.*$",""); - + + int startIndex = 0; + + while(startIndex < name.length() && name.charAt(startIndex) == '.') { + startIndex++; + } + + int dollar = name.indexOf("$"); + + int endIndex = dollar != -1 ? dollar : name.length(); + for (Entry entry : _entries) { if (entry != null) { if (entry.partial) { - if (name.startsWith(entry.classpath)) + if (name.regionMatches(startIndex, entry.classpath, 0, entry.classpath.length())) { result = entry.result; break; @@ -206,7 +214,9 @@ public class ClasspathPattern } else { - if (name.equals(entry.classpath)) + int regionLength = endIndex-startIndex; + if (regionLength == entry.classpath.length() + && name.regionMatches(startIndex, entry.classpath, 0, regionLength)) { result = entry.result; break; diff --git a/tests/test-sessions/test-hash-sessions/src/test/java/org/eclipse/jetty/server/session/HashTestServer.java b/tests/test-sessions/test-hash-sessions/src/test/java/org/eclipse/jetty/server/session/HashTestServer.java index 924a51d6184..25c3ba59ff7 100644 --- a/tests/test-sessions/test-hash-sessions/src/test/java/org/eclipse/jetty/server/session/HashTestServer.java +++ b/tests/test-sessions/test-hash-sessions/src/test/java/org/eclipse/jetty/server/session/HashTestServer.java @@ -48,7 +48,7 @@ public class HashTestServer extends AbstractTestServer public SessionManager newSessionManager() { HashSessionManager manager = new HashSessionManager(); - manager.setScavengePeriod((int)TimeUnit.SECONDS.toMillis(_scavengePeriod)); + manager.setScavengePeriod(_scavengePeriod); return manager; } diff --git a/tests/test-sessions/test-hash-sessions/src/test/java/org/eclipse/jetty/server/session/IdleSessionTest.java b/tests/test-sessions/test-hash-sessions/src/test/java/org/eclipse/jetty/server/session/IdleSessionTest.java new file mode 100644 index 00000000000..18dbb430e34 --- /dev/null +++ b/tests/test-sessions/test-hash-sessions/src/test/java/org/eclipse/jetty/server/session/IdleSessionTest.java @@ -0,0 +1,217 @@ +// +// ======================================================================== +// Copyright (c) 1995-2012 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// +package org.eclipse.jetty.server.session; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertNotNull; + + +import java.io.File; +import java.io.IOException; + +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.eclipse.jetty.client.ContentExchange; +import org.eclipse.jetty.client.HttpClient; +import org.eclipse.jetty.http.HttpMethods; +import org.eclipse.jetty.server.SessionManager; +import org.eclipse.jetty.server.session.AbstractSessionExpiryTest.TestServlet; +import org.eclipse.jetty.servlet.ServletContextHandler; +import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.util.IO; +import org.junit.Test; + + +/** + * IdleSessionTest + * + * Checks that a session can be idled and de-idled on the next request if it hasn't expired. + * + */ +public class IdleSessionTest +{ + public class IdleHashTestServer extends HashTestServer + { + private int _idlePeriod; + private File _storeDir; + + /** + * @param port + * @param maxInactivePeriod + * @param scavengePeriod + * @param idlePeriod + */ + public IdleHashTestServer(int port, int maxInactivePeriod, int scavengePeriod, int idlePeriod, File storeDir) + { + super(port, maxInactivePeriod, scavengePeriod); + _idlePeriod = idlePeriod; + _storeDir = storeDir; + } + + @Override + public SessionManager newSessionManager() + { + HashSessionManager manager = (HashSessionManager)super.newSessionManager(); + manager.setStoreDirectory(_storeDir); + manager.setIdleSavePeriod(_idlePeriod); + return manager; + } + + + + } + + public HashTestServer createServer(int port, int max, int scavenge, int idle, File storeDir) + { + HashTestServer server = new IdleHashTestServer(port, max, scavenge, idle, storeDir); + return server; + } + + + + public void pause (int sec) + { + try + { + Thread.sleep(sec * 1000L); + } + catch (InterruptedException e) + { + e.printStackTrace(); + } + } + + @Test + public void testSessionIdle() throws Exception + { + String contextPath = ""; + String servletMapping = "/server"; + int inactivePeriod = 200; + int scavengePeriod = 3; + int idlePeriod = 5; + + File storeDir = new File (System.getProperty("java.io.tmpdir"), "idle-test"); + storeDir.deleteOnExit(); + + HashTestServer server1 = createServer(0, inactivePeriod, scavengePeriod, idlePeriod, storeDir); + TestServlet servlet = new TestServlet(); + ServletHolder holder = new ServletHolder(servlet); + ServletContextHandler contextHandler = server1.addContext(contextPath); + contextHandler.addServlet(holder, servletMapping); + server1.start(); + int port1 = server1.getPort(); + + try + { + HttpClient client = new HttpClient(); + client.setConnectorType(HttpClient.CONNECTOR_SOCKET); + client.start(); + String url = "http://localhost:" + port1 + contextPath + servletMapping; + + //make a request to set up a session on the server + ContentExchange exchange1 = new ContentExchange(true); + exchange1.setMethod(HttpMethods.GET); + exchange1.setURL(url + "?action=init"); + client.send(exchange1); + exchange1.waitForDone(); + assertEquals(HttpServletResponse.SC_OK,exchange1.getResponseStatus()); + String sessionCookie = exchange1.getResponseFields().getStringField("Set-Cookie"); + assertTrue(sessionCookie != null); + // Mangle the cookie, replacing Path with $Path, etc. + sessionCookie = sessionCookie.replaceFirst("(\\W)(P|p)ath=", "$1\\$Path="); + + //and wait until the session should be idled out + pause(scavengePeriod * 2); + + //check that the file exists + checkSessionIdled(storeDir); + + //make another request to de-idle the session + ContentExchange exchange2 = new ContentExchange(true); + exchange2.setMethod(HttpMethods.GET); + exchange2.setURL(url + "?action=test"); + exchange2.getRequestFields().add("Cookie", sessionCookie); + client.send(exchange2); + exchange2.waitForDone(); + assertEquals(HttpServletResponse.SC_OK,exchange2.getResponseStatus()); + + //check session de-idled + checkSessionDeIdled(storeDir); + + } + finally + { + server1.stop(); + IO.delete(storeDir); + } + } + + + public void checkSessionIdled (File sessionDir) + { + assertNotNull(sessionDir); + assertTrue(sessionDir.exists()); + String[] files = sessionDir.list(); + assertNotNull(files); + assertEquals(1, files.length); + } + + + public void checkSessionDeIdled (File sessionDir) + { + assertNotNull(sessionDir); + assertTrue(sessionDir.exists()); + String[] files = sessionDir.list(); + assertNotNull(files); + assertEquals(0, files.length); + } + + + public static class TestServlet extends HttpServlet + { + public String originalId = null; + public String testId = null; + public String checkId = null; + + @Override + protected void doGet(HttpServletRequest request, HttpServletResponse httpServletResponse) throws ServletException, IOException + { + String action = request.getParameter("action"); + if ("init".equals(action)) + { + HttpSession session = request.getSession(true); + session.setAttribute("test", "test"); + originalId = session.getId(); + assertTrue(!((HashedSession)session).isIdled()); + } + else if ("test".equals(action)) + { + HttpSession session = request.getSession(false); + assertTrue(session != null); + assertTrue(originalId.equals(session.getId())); + assertEquals("test", session.getAttribute("test")); + assertTrue(!((HashedSession)session).isIdled()); + } + } + } +}