Merge remote-tracking branch 'origin/jetty-7' into jetty-8

This commit is contained in:
Jan Bartel 2012-11-22 16:26:38 +11:00
commit 90a86d3a6b
7 changed files with 332 additions and 27 deletions

View File

@ -41,7 +41,10 @@ import org.eclipse.jetty.util.log.Logger;
/* ------------------------------------------------------------ */
/** An in-memory implementation of SessionManager.
/**
* HashSessionManager
*
* An in-memory implementation of SessionManager.
* <p>
* 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<HashedSession> 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()+idleTime<now)
{
// Found a stale session, add it to the list
session.timeout();
try
{
session.timeout();
}
catch (Exception e)
{
__log.warn("Problem scavenging sessions", e);
}
}
else if (_idleSavePeriodMs>0&&session.getAccessed()+_idleSavePeriodMs<now)
else if (_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);

View File

@ -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;
}
/* ------------------------------------------------------------ */

View File

@ -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();

View File

@ -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

View File

@ -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;

View File

@ -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;
}

View File

@ -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());
}
}
}
}