Merge remote-tracking branch 'origin/jetty-12.0.x' into jetty-12.1.x
This commit is contained in:
commit
b1b2adcaf9
|
@ -441,6 +441,70 @@ public class CreationTest
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test creating a session in a request and then invalidating it in another request
|
||||
*/
|
||||
@Test
|
||||
public void testSessionSimpleCreateInvalidate() throws Exception
|
||||
{
|
||||
String contextPath = "";
|
||||
String servletMapping = "/server";
|
||||
int inactivePeriod = -1; //immortal session
|
||||
int scavengePeriod = 3;
|
||||
DefaultSessionCacheFactory cacheFactory = new DefaultSessionCacheFactory();
|
||||
cacheFactory.setEvictionPolicy(SessionCache.NEVER_EVICT);
|
||||
cacheFactory.setSaveOnCreate(true);
|
||||
cacheFactory.setFlushOnResponseCommit(true); //ensure session saved before response returned
|
||||
SessionDataStoreFactory storeFactory = new TestSessionDataStoreFactory();
|
||||
SessionTestSupport server1 = new SessionTestSupport(0, inactivePeriod, scavengePeriod, cacheFactory, storeFactory);
|
||||
TestServlet servlet = new TestServlet();
|
||||
ServletHolder holder = new ServletHolder(servlet);
|
||||
ServletContextHandler contextHandler = server1.addContext(contextPath);
|
||||
contextHandler.addServlet(holder, servletMapping);
|
||||
servlet.setStore(contextHandler.getSessionHandler().getSessionCache().getSessionDataStore());
|
||||
server1.start();
|
||||
int port1 = server1.getPort();
|
||||
|
||||
try (StacklessLogging stackless = new StacklessLogging(CreationTest.class.getPackage()))
|
||||
{
|
||||
HttpClient client = new HttpClient();
|
||||
client.start();
|
||||
String url = "http://localhost:" + port1 + contextPath + servletMapping + "?action=create";
|
||||
|
||||
//make a request to set up a session on the server
|
||||
ContentResponse response = client.GET(url);
|
||||
assertEquals(HttpServletResponse.SC_OK, response.getStatus());
|
||||
//Ensure session handling is finished
|
||||
Awaitility.waitAtMost(5, TimeUnit.SECONDS).until(() -> contextHandler.getSessionHandler().getSessionCache().getSessionDataStore().exists(servlet._id));
|
||||
|
||||
//make a request to re-obtain the session on the server
|
||||
Request request = client.newRequest("http://localhost:" + port1 + contextPath + servletMapping + "?action=test");
|
||||
response = request.send();
|
||||
assertEquals(HttpServletResponse.SC_OK, response.getStatus());
|
||||
Awaitility.waitAtMost(5, TimeUnit.SECONDS).until(() -> contextHandler.getSessionHandler().getSessionCache().getSessionDataStore().exists(servlet._id));
|
||||
//at this point the last accessed time should be the creation time
|
||||
long lastAccessedTime = servlet._lastAccessedTime;
|
||||
assertEquals(lastAccessedTime, servlet._creationTime);
|
||||
|
||||
//make another request to re-obtain the session on the server
|
||||
request = client.newRequest("http://localhost:" + port1 + contextPath + servletMapping + "?action=test");
|
||||
response = request.send();
|
||||
assertEquals(HttpServletResponse.SC_OK, response.getStatus());
|
||||
Awaitility.waitAtMost(5, TimeUnit.SECONDS).until(() -> contextHandler.getSessionHandler().getSessionCache().getSessionDataStore().exists(servlet._id));
|
||||
//check that the lastAccessedTime is being updated
|
||||
assertTrue(lastAccessedTime < servlet._lastAccessedTime);
|
||||
|
||||
//make a request to invalidate it
|
||||
request = client.newRequest("http://localhost:" + port1 + contextPath + servletMapping + "?action=invalidate");
|
||||
response = request.send();
|
||||
assertEquals(HttpServletResponse.SC_OK, response.getStatus());
|
||||
}
|
||||
finally
|
||||
{
|
||||
server1.stop();
|
||||
}
|
||||
}
|
||||
|
||||
public static class MySessionListener implements HttpSessionListener
|
||||
{
|
||||
@Override
|
||||
|
@ -460,6 +524,8 @@ public class CreationTest
|
|||
private static final long serialVersionUID = 1L;
|
||||
public String _id = null;
|
||||
public SessionDataStore _store;
|
||||
public long _lastAccessedTime;
|
||||
public long _creationTime;
|
||||
|
||||
public void setStore(SessionDataStore store)
|
||||
{
|
||||
|
@ -475,6 +541,7 @@ public class CreationTest
|
|||
case "forward" ->
|
||||
{
|
||||
HttpSession session = createAndSaveSessionId(request);
|
||||
assertTrue(session.isNew());
|
||||
ServletContext contextB = getServletContext().getContext("/contextB");
|
||||
RequestDispatcher dispatcherB = contextB.getRequestDispatcher(request.getServletPath());
|
||||
dispatcherB.forward(request, httpServletResponse);
|
||||
|
@ -488,7 +555,7 @@ public class CreationTest
|
|||
case "forwardc" ->
|
||||
{
|
||||
HttpSession session = createAndSaveSessionId(request);
|
||||
|
||||
assertTrue(session.isNew());
|
||||
//forward to contextC
|
||||
ServletContext contextC = getServletContext().getContext("/contextC");
|
||||
RequestDispatcher dispatcherC = contextC.getRequestDispatcher(request.getServletPath());
|
||||
|
@ -498,7 +565,7 @@ public class CreationTest
|
|||
{
|
||||
HttpSession session = createAndSaveSessionId(request);
|
||||
assertNotNull(session);
|
||||
|
||||
assertTrue(session.isNew());
|
||||
ServletContext contextB = getServletContext().getContext("/contextB");
|
||||
RequestDispatcher dispatcherB = contextB.getRequestDispatcher(request.getServletPath());
|
||||
dispatcherB.forward(request, httpServletResponse);
|
||||
|
@ -509,14 +576,27 @@ public class CreationTest
|
|||
assertNotNull(_id);
|
||||
HttpSession session = request.getSession(false);
|
||||
assertNotNull(session);
|
||||
_lastAccessedTime = session.getLastAccessedTime();
|
||||
assertFalse(session.isNew());
|
||||
assertNotNull(session.getAttribute("value")); //check we see our previous session
|
||||
assertNull(session.getAttribute("B")); //check we don't see stuff from other contexts
|
||||
assertNull(session.getAttribute("C"));
|
||||
}
|
||||
case "invalidate" ->
|
||||
{
|
||||
HttpSession session = request.getSession(false);
|
||||
assertNotNull(session);
|
||||
_id = session.getId();
|
||||
assertFalse(session.isNew());
|
||||
session.invalidate();
|
||||
assertNull(request.getSession(false));
|
||||
}
|
||||
case "create", "createinv", "createinvcreate" ->
|
||||
{
|
||||
currentRequest.set(request);
|
||||
HttpSession session = createAndSaveSessionId(request);
|
||||
assertTrue(session.isNew());
|
||||
_creationTime = session.getCreationTime();
|
||||
String check = request.getParameter("check");
|
||||
if (!StringUtil.isBlank(check) && _store != null)
|
||||
{
|
||||
|
|
|
@ -441,6 +441,70 @@ public class CreationTest
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test creating a session in a request and then invalidating it in another request
|
||||
*/
|
||||
@Test
|
||||
public void testSessionSimpleCreateInvalidate() throws Exception
|
||||
{
|
||||
String contextPath = "";
|
||||
String servletMapping = "/server";
|
||||
int inactivePeriod = -1; //immortal session
|
||||
int scavengePeriod = 3;
|
||||
DefaultSessionCacheFactory cacheFactory = new DefaultSessionCacheFactory();
|
||||
cacheFactory.setEvictionPolicy(SessionCache.NEVER_EVICT);
|
||||
cacheFactory.setSaveOnCreate(true);
|
||||
cacheFactory.setFlushOnResponseCommit(true); //ensure session saved before response returned
|
||||
SessionDataStoreFactory storeFactory = new TestSessionDataStoreFactory();
|
||||
SessionTestSupport server1 = new SessionTestSupport(0, inactivePeriod, scavengePeriod, cacheFactory, storeFactory);
|
||||
TestServlet servlet = new TestServlet();
|
||||
ServletHolder holder = new ServletHolder(servlet);
|
||||
ServletContextHandler contextHandler = server1.addContext(contextPath);
|
||||
contextHandler.addServlet(holder, servletMapping);
|
||||
servlet.setStore(contextHandler.getSessionHandler().getSessionCache().getSessionDataStore());
|
||||
server1.start();
|
||||
int port1 = server1.getPort();
|
||||
|
||||
try (StacklessLogging stackless = new StacklessLogging(CreationTest.class.getPackage()))
|
||||
{
|
||||
HttpClient client = new HttpClient();
|
||||
client.start();
|
||||
String url = "http://localhost:" + port1 + contextPath + servletMapping + "?action=create";
|
||||
|
||||
//make a request to set up a session on the server
|
||||
ContentResponse response = client.GET(url);
|
||||
assertEquals(HttpServletResponse.SC_OK, response.getStatus());
|
||||
//Ensure session handling is finished
|
||||
Awaitility.waitAtMost(5, TimeUnit.SECONDS).until(() -> contextHandler.getSessionHandler().getSessionCache().getSessionDataStore().exists(servlet._id));
|
||||
|
||||
//make a request to re-obtain the session on the server
|
||||
Request request = client.newRequest("http://localhost:" + port1 + contextPath + servletMapping + "?action=test");
|
||||
response = request.send();
|
||||
assertEquals(HttpServletResponse.SC_OK, response.getStatus());
|
||||
Awaitility.waitAtMost(5, TimeUnit.SECONDS).until(() -> contextHandler.getSessionHandler().getSessionCache().getSessionDataStore().exists(servlet._id));
|
||||
//at this point the last accessed time should be the creation time
|
||||
long lastAccessedTime = servlet._lastAccessedTime;
|
||||
assertEquals(lastAccessedTime, servlet._creationTime);
|
||||
|
||||
//make another request to re-obtain the session on the server
|
||||
request = client.newRequest("http://localhost:" + port1 + contextPath + servletMapping + "?action=test");
|
||||
response = request.send();
|
||||
assertEquals(HttpServletResponse.SC_OK, response.getStatus());
|
||||
Awaitility.waitAtMost(5, TimeUnit.SECONDS).until(() -> contextHandler.getSessionHandler().getSessionCache().getSessionDataStore().exists(servlet._id));
|
||||
//check that the lastAccessedTime is being updated
|
||||
assertTrue(lastAccessedTime < servlet._lastAccessedTime);
|
||||
|
||||
//make a request to invalidate it
|
||||
request = client.newRequest("http://localhost:" + port1 + contextPath + servletMapping + "?action=invalidate");
|
||||
response = request.send();
|
||||
assertEquals(HttpServletResponse.SC_OK, response.getStatus());
|
||||
}
|
||||
finally
|
||||
{
|
||||
server1.stop();
|
||||
}
|
||||
}
|
||||
|
||||
public static class MySessionListener implements HttpSessionListener
|
||||
{
|
||||
@Override
|
||||
|
@ -460,6 +524,8 @@ public class CreationTest
|
|||
private static final long serialVersionUID = 1L;
|
||||
public String _id = null;
|
||||
public SessionDataStore _store;
|
||||
public long _lastAccessedTime;
|
||||
public long _creationTime;
|
||||
|
||||
public void setStore(SessionDataStore store)
|
||||
{
|
||||
|
@ -475,6 +541,7 @@ public class CreationTest
|
|||
case "forward" ->
|
||||
{
|
||||
HttpSession session = createAndSaveSessionId(request);
|
||||
assertTrue(session.isNew());
|
||||
ServletContext contextB = getServletContext().getContext("/contextB");
|
||||
RequestDispatcher dispatcherB = contextB.getRequestDispatcher(request.getServletPath());
|
||||
dispatcherB.forward(request, httpServletResponse);
|
||||
|
@ -488,7 +555,7 @@ public class CreationTest
|
|||
case "forwardc" ->
|
||||
{
|
||||
HttpSession session = createAndSaveSessionId(request);
|
||||
|
||||
assertTrue(session.isNew());
|
||||
//forward to contextC
|
||||
ServletContext contextC = getServletContext().getContext("/contextC");
|
||||
RequestDispatcher dispatcherC = contextC.getRequestDispatcher(request.getServletPath());
|
||||
|
@ -498,7 +565,7 @@ public class CreationTest
|
|||
{
|
||||
HttpSession session = createAndSaveSessionId(request);
|
||||
assertNotNull(session);
|
||||
|
||||
assertTrue(session.isNew());
|
||||
ServletContext contextB = getServletContext().getContext("/contextB");
|
||||
RequestDispatcher dispatcherB = contextB.getRequestDispatcher(request.getServletPath());
|
||||
dispatcherB.forward(request, httpServletResponse);
|
||||
|
@ -509,14 +576,27 @@ public class CreationTest
|
|||
assertNotNull(_id);
|
||||
HttpSession session = request.getSession(false);
|
||||
assertNotNull(session);
|
||||
_lastAccessedTime = session.getLastAccessedTime();
|
||||
assertFalse(session.isNew());
|
||||
assertNotNull(session.getAttribute("value")); //check we see our previous session
|
||||
assertNull(session.getAttribute("B")); //check we don't see stuff from other contexts
|
||||
assertNull(session.getAttribute("C"));
|
||||
}
|
||||
case "invalidate" ->
|
||||
{
|
||||
HttpSession session = request.getSession(false);
|
||||
assertNotNull(session);
|
||||
_id = session.getId();
|
||||
assertFalse(session.isNew());
|
||||
session.invalidate();
|
||||
assertNull(request.getSession(false));
|
||||
}
|
||||
case "create", "createinv", "createinvcreate" ->
|
||||
{
|
||||
currentRequest.set(request);
|
||||
HttpSession session = createAndSaveSessionId(request);
|
||||
assertTrue(session.isNew());
|
||||
_creationTime = session.getCreationTime();
|
||||
String check = request.getParameter("check");
|
||||
if (!StringUtil.isBlank(check) && _store != null)
|
||||
{
|
||||
|
|
|
@ -2050,7 +2050,12 @@ public class Request implements HttpServletRequest
|
|||
{
|
||||
parts = _multiParts.getParts();
|
||||
}
|
||||
catch (IOException e)
|
||||
catch (BadMessageException e)
|
||||
{
|
||||
throw e;
|
||||
}
|
||||
// Catch RuntimeException to handle IllegalStateException, IllegalArgumentException, CharacterEncodingException, etc .. (long list)
|
||||
catch (RuntimeException | IOException e)
|
||||
{
|
||||
throw new BadMessageException("Unable to parse form content", e);
|
||||
}
|
||||
|
|
|
@ -520,7 +520,10 @@ public class SessionHandler extends ScopedHandler implements SessionConfig.Mutab
|
|||
coreRequest.setSessionManager(_sessionManager);
|
||||
coreRequest.setRequestedSession(currentRequestedSession);
|
||||
if (currentRequestedSession != null)
|
||||
{
|
||||
coreRequest.setManagedSession(currentRequestedSession.session());
|
||||
currentSession = currentRequestedSession.session();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ASYNC:
|
||||
|
|
|
@ -13,9 +13,12 @@
|
|||
|
||||
package org.eclipse.jetty.ee9.servlet;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Function;
|
||||
|
@ -28,6 +31,7 @@ import org.eclipse.jetty.client.AsyncRequestContent;
|
|||
import org.eclipse.jetty.client.ContentResponse;
|
||||
import org.eclipse.jetty.client.FormRequestContent;
|
||||
import org.eclipse.jetty.client.HttpClient;
|
||||
import org.eclipse.jetty.client.StringRequestContent;
|
||||
import org.eclipse.jetty.ee9.nested.ContextHandler;
|
||||
import org.eclipse.jetty.http.HttpHeader;
|
||||
import org.eclipse.jetty.http.HttpMethod;
|
||||
|
@ -42,6 +46,8 @@ import org.junit.jupiter.params.ParameterizedTest;
|
|||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
public class FormTest
|
||||
|
@ -229,4 +235,98 @@ public class FormTest
|
|||
|
||||
assertEquals(HttpStatus.OK_200, response.getStatus());
|
||||
}
|
||||
|
||||
public static Stream<Arguments> invalidForm()
|
||||
{
|
||||
return Stream.of(
|
||||
Arguments.of("%A", "java.lang.IllegalArgumentException: Not valid encoding '%A?'"),
|
||||
Arguments.of("name%", "java.lang.IllegalArgumentException: Not valid encoding '%??'"),
|
||||
Arguments.of("name%A", "java.lang.IllegalArgumentException: Not valid encoding '%A?'"),
|
||||
Arguments.of("name%A&", "java.lang.IllegalArgumentException: Not valid encoding '%A&'"),
|
||||
Arguments.of("name=%", "java.lang.IllegalArgumentException: Not valid encoding '%??'"),
|
||||
Arguments.of("name=A%%A", "java.lang.IllegalArgumentException: Not valid encoding '%%A'"),
|
||||
Arguments.of("name=A%%3D", "java.lang.IllegalArgumentException: Not valid encoding '%%3'"),
|
||||
Arguments.of("%=", "java.lang.IllegalArgumentException: Not valid encoding '%=?'"),
|
||||
Arguments.of("name=%A", "java.lang.IllegalArgumentException: Not valid encoding '%A?'"),
|
||||
Arguments.of("name=value%A", "ava.lang.IllegalArgumentException: Not valid encoding '%A?'"),
|
||||
Arguments.of("n%AH=v", "java.lang.IllegalArgumentException: Not valid encoding '%AH'"),
|
||||
Arguments.of("n=v%AH", "java.lang.IllegalArgumentException: Not valid encoding '%AH'")
|
||||
);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("invalidForm")
|
||||
public void testContentTypeWithoutCharsetDecodeBadUTF8(String rawForm, String expectedCauseMessage) throws Exception
|
||||
{
|
||||
start(handler -> new HttpServlet()
|
||||
{
|
||||
@Override
|
||||
protected void service(HttpServletRequest request, HttpServletResponse response)
|
||||
{
|
||||
// This is expected to throw an exception due to the bad form input
|
||||
request.getParameterMap();
|
||||
}
|
||||
});
|
||||
|
||||
StringRequestContent requestContent = new StringRequestContent("application/x-www-form-urlencoded", rawForm);
|
||||
|
||||
ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
|
||||
.method(HttpMethod.POST)
|
||||
.path(contextPath + servletPath)
|
||||
.body(requestContent)
|
||||
.timeout(5, TimeUnit.SECONDS)
|
||||
.send();
|
||||
|
||||
assertEquals(HttpStatus.BAD_REQUEST_400, response.getStatus(), response::getContentAsString);
|
||||
String responseContent = response.getContentAsString();
|
||||
assertThat(responseContent, containsString("Unable to parse form content"));
|
||||
assertThat(responseContent, containsString(expectedCauseMessage));
|
||||
}
|
||||
|
||||
public static Stream<Arguments> utf8Form()
|
||||
{
|
||||
return Stream.of(
|
||||
Arguments.of("euro=%E2%82%AC", List.of("param[euro] = \"€\"")),
|
||||
Arguments.of("name=%AB%CD", List.of("param[name] = \"<EFBFBD><EFBFBD>\"")),
|
||||
Arguments.of("name=x%AB%CDz", List.of("param[name] = \"x<EFBFBD><EFBFBD>z\"")),
|
||||
Arguments.of("name=%FF%FF%FF%FF", List.of("param[name] = \"<EFBFBD><EFBFBD><EFBFBD><EFBFBD>\""))
|
||||
);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("utf8Form")
|
||||
public void testUtf8Decoding(String rawForm, List<String> expectedParams) throws Exception
|
||||
{
|
||||
start(handler -> new HttpServlet()
|
||||
{
|
||||
@Override
|
||||
protected void service(HttpServletRequest request, HttpServletResponse response) throws IOException
|
||||
{
|
||||
response.setCharacterEncoding("utf-8");
|
||||
response.setContentType("text/plain");
|
||||
PrintWriter out = response.getWriter();
|
||||
|
||||
Map<String, String[]> paramMap = request.getParameterMap();
|
||||
List<String> names = paramMap.keySet().stream().sorted().toList();
|
||||
for (String name: names)
|
||||
{
|
||||
out.printf("param[%s] = \"%s\"%n", name, String.join(",", paramMap.get(name)));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
StringRequestContent requestContent = new StringRequestContent("application/x-www-form-urlencoded", rawForm);
|
||||
|
||||
ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
|
||||
.method(HttpMethod.POST)
|
||||
.path(contextPath + servletPath)
|
||||
.body(requestContent)
|
||||
.timeout(5, TimeUnit.SECONDS)
|
||||
.send();
|
||||
|
||||
assertEquals(HttpStatus.OK_200, response.getStatus(), response::getContentAsString);
|
||||
String responseContent = response.getContentAsString();
|
||||
for (String expectedParam: expectedParams)
|
||||
assertThat(responseContent, containsString(expectedParam));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ package org.eclipse.jetty.ee9.servlet;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
|
@ -104,18 +105,27 @@ public class MultiPartServletTest
|
|||
@Override
|
||||
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
|
||||
{
|
||||
resp.setCharacterEncoding("utf-8");
|
||||
resp.setContentType("text/plain");
|
||||
|
||||
PrintWriter out = resp.getWriter();
|
||||
|
||||
if (!req.getContentType().contains(MimeTypes.Type.MULTIPART_FORM_DATA.asString()))
|
||||
{
|
||||
resp.setContentType("text/plain");
|
||||
resp.getWriter().println("not content type " + MimeTypes.Type.MULTIPART_FORM_DATA);
|
||||
resp.getWriter().println("contentType: " + req.getContentType());
|
||||
out.println("not content type " + MimeTypes.Type.MULTIPART_FORM_DATA);
|
||||
out.println("contentType: " + req.getContentType());
|
||||
return;
|
||||
}
|
||||
|
||||
resp.setContentType("text/plain");
|
||||
for (Part part : req.getParts())
|
||||
{
|
||||
resp.getWriter().println("Part: name=" + part.getName() + ", size=" + part.getSize());
|
||||
out.printf("Part: name=%s, size=%s", part.getName(), part.getSize());
|
||||
if (part.getSize() <= 100)
|
||||
{
|
||||
String content = IO.toString(part.getInputStream());
|
||||
out.printf(", content=%s", content);
|
||||
}
|
||||
out.println();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -302,6 +312,7 @@ public class MultiPartServletTest
|
|||
startServer(multiPartCompliance);
|
||||
|
||||
String contentType = "multipart/form-data; boundary=---------------------------7e25e1e151054";
|
||||
// NOTE: The extra `\r` here are intentional, do not remove.
|
||||
String rawForm = """
|
||||
-----------------------------7e25e1e151054\r
|
||||
Content-Disposition: form-data; name="user"\r
|
||||
|
@ -370,6 +381,131 @@ public class MultiPartServletTest
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* A part with Content-Transfer-Encoding: base64, and the content is valid Base64 encoded.
|
||||
*
|
||||
* MultiPartCompliance mode set to allow MultiPartCompliance.Violation.BASE64_TRANSFER_ENCODING
|
||||
*/
|
||||
@Test
|
||||
public void testLegacyContentTransferEncodingBase64Allowed() throws Exception
|
||||
{
|
||||
MultiPartCompliance legacyBase64 = MultiPartCompliance.from("LEGACY,BASE64_TRANSFER_ENCODING");
|
||||
|
||||
startServer(legacyBase64);
|
||||
|
||||
String contentType = "multipart/form-data; boundary=8GbcZNTauFWYMt7GeM9BxFMdlNBJ6aLJhGdXp";
|
||||
String rawForm = """
|
||||
--8GbcZNTauFWYMt7GeM9BxFMdlNBJ6aLJhGdXp
|
||||
Content-ID: <foo@example.org>
|
||||
Content-Disposition: form-data; name="quote"
|
||||
Content-Transfer-Encoding: base64
|
||||
|
||||
IkJvb2tzIGFyZSB0aGUgbGliZXJhdGVkIHNwaXJpdHMgb2YgbWVuLiIgLS0gTWFyayBUd2Fpbg==
|
||||
--8GbcZNTauFWYMt7GeM9BxFMdlNBJ6aLJhGdXp--
|
||||
|
||||
""";
|
||||
|
||||
StringRequestContent form = new StringRequestContent(
|
||||
contentType,
|
||||
rawForm
|
||||
);
|
||||
|
||||
ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
|
||||
.path("/")
|
||||
.scheme(HttpScheme.HTTP.asString())
|
||||
.method(HttpMethod.POST)
|
||||
.body(form)
|
||||
.send();
|
||||
|
||||
assertEquals(200, response.getStatus());
|
||||
assertThat(response.getContentAsString(), containsString("Part: name=quote, size=55, content=\"Books are the liberated spirits of men.\" -- Mark Twain"));
|
||||
}
|
||||
|
||||
/**
|
||||
* A part with Content-Transfer-Encoding: base64, but the content is not actually encoded in Base 64.
|
||||
*
|
||||
* MultiPartCompliance mode set to allow MultiPartCompliance.Violation.BASE64_TRANSFER_ENCODING
|
||||
*/
|
||||
@Test
|
||||
public void testLegacyContentTransferEncodingBadBase64Allowed() throws Exception
|
||||
{
|
||||
MultiPartCompliance legacyBase64 = MultiPartCompliance.from("LEGACY,BASE64_TRANSFER_ENCODING");
|
||||
|
||||
startServer(legacyBase64);
|
||||
|
||||
String contentType = "multipart/form-data; boundary=8GbcZNTauFWYMt7GeM9BxFMdlNBJ6aLJhGdXp";
|
||||
String rawForm = """
|
||||
--8GbcZNTauFWYMt7GeM9BxFMdlNBJ6aLJhGdXp
|
||||
Content-ID: <foo@example.org>
|
||||
Content-Disposition: form-data; name="quote"
|
||||
Content-Transfer-Encoding: base64
|
||||
|
||||
"Travel is fatal to prejudice." -- Mark Twain
|
||||
--8GbcZNTauFWYMt7GeM9BxFMdlNBJ6aLJhGdXp--
|
||||
|
||||
""";
|
||||
|
||||
StringRequestContent form = new StringRequestContent(
|
||||
contentType,
|
||||
rawForm
|
||||
);
|
||||
|
||||
InputStreamResponseListener listener = new InputStreamResponseListener();
|
||||
client.newRequest("localhost", connector.getLocalPort())
|
||||
.path("/")
|
||||
.scheme(HttpScheme.HTTP.asString())
|
||||
.method(HttpMethod.POST)
|
||||
.body(form)
|
||||
.send(listener);
|
||||
|
||||
assert400orEof(listener, responseContent ->
|
||||
{
|
||||
assertThat(responseContent, containsString("Unable to parse form content"));
|
||||
assertThat(responseContent, containsString("java.lang.IllegalArgumentException: Last unit does not have enough valid bits"));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* A part with Content-Transfer-Encoding: base64, and the content is valid Base64 encoded.
|
||||
*
|
||||
* MultiPartCompliance mode set to allow MultiPartCompliance.LEGACY, which does not perform
|
||||
* base64 decoding.
|
||||
*/
|
||||
@Test
|
||||
public void testLegacyContentTransferEncodingBase64() throws Exception
|
||||
{
|
||||
MultiPartCompliance legacyBase64 = MultiPartCompliance.LEGACY;
|
||||
|
||||
startServer(legacyBase64);
|
||||
|
||||
String contentType = "multipart/form-data; boundary=8GbcZNTauFWYMt7GeM9BxFMdlNBJ6aLJhGdXp";
|
||||
String rawForm = """
|
||||
--8GbcZNTauFWYMt7GeM9BxFMdlNBJ6aLJhGdXp
|
||||
Content-ID: <foo@example.org>
|
||||
Content-Disposition: form-data; name="quote"
|
||||
Content-Transfer-Encoding: base64
|
||||
|
||||
IkJvb2tzIGFyZSB0aGUgbGliZXJhdGVkIHNwaXJpdHMgb2YgbWVuLiIgLS0gTWFyayBUd2Fpbg==
|
||||
--8GbcZNTauFWYMt7GeM9BxFMdlNBJ6aLJhGdXp--
|
||||
|
||||
""";
|
||||
|
||||
StringRequestContent form = new StringRequestContent(
|
||||
contentType,
|
||||
rawForm
|
||||
);
|
||||
|
||||
ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
|
||||
.path("/")
|
||||
.scheme(HttpScheme.HTTP.asString())
|
||||
.method(HttpMethod.POST)
|
||||
.body(form)
|
||||
.send();
|
||||
|
||||
assertEquals(200, response.getStatus());
|
||||
assertThat(response.getContentAsString(), containsString("Part: name=quote, size=76, content=IkJvb2tzIGFyZSB0aGUgbGliZXJhdGVkIHNwaXJpdHMgb2YgbWVuLiIgLS0gTWFyayBUd2Fpbg=="));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("multipartModes")
|
||||
public void testManyParts(MultiPartCompliance multiPartCompliance) throws Exception
|
||||
|
@ -487,7 +623,7 @@ public class MultiPartServletTest
|
|||
.body(multiPart)
|
||||
.send();
|
||||
|
||||
assertEquals(500, response.getStatus());
|
||||
assertEquals(400, response.getStatus());
|
||||
assertThat(response.getContentAsString(),
|
||||
containsString("Multipart Mime part largePart exceeds max filesize"));
|
||||
}
|
||||
|
|
|
@ -435,6 +435,72 @@ public class CreationTest
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test creating a session in a request and then invalidating it in another request
|
||||
* @throws Exception
|
||||
*/
|
||||
@Test
|
||||
public void testSessionSimpleCreateInvalidate() throws Exception
|
||||
{
|
||||
String contextPath = "";
|
||||
String servletMapping = "/server";
|
||||
int inactivePeriod = -1; //immortal session
|
||||
int scavengePeriod = 3;
|
||||
DefaultSessionCacheFactory cacheFactory = new DefaultSessionCacheFactory();
|
||||
cacheFactory.setEvictionPolicy(SessionCache.NEVER_EVICT);
|
||||
cacheFactory.setSaveOnCreate(true);
|
||||
cacheFactory.setFlushOnResponseCommit(true); //ensure session saved before response returned
|
||||
SessionDataStoreFactory storeFactory = new TestSessionDataStoreFactory();
|
||||
SessionTestSupport server1 = new SessionTestSupport(0, inactivePeriod, scavengePeriod, cacheFactory, storeFactory);
|
||||
TestServlet servlet = new TestServlet();
|
||||
ServletHolder holder = new ServletHolder(servlet);
|
||||
ServletContextHandler contextHandler = server1.addContext(contextPath);
|
||||
contextHandler.addServlet(holder, servletMapping);
|
||||
servlet.setStore(contextHandler.getSessionHandler().getSessionManager().getSessionCache().getSessionDataStore());
|
||||
server1.start();
|
||||
int port1 = server1.getPort();
|
||||
|
||||
try (StacklessLogging stackless = new StacklessLogging(CreationTest.class.getPackage()))
|
||||
{
|
||||
HttpClient client = new HttpClient();
|
||||
client.start();
|
||||
String url = "http://localhost:" + port1 + contextPath + servletMapping + "?action=create";
|
||||
|
||||
//make a request to set up a session on the server
|
||||
ContentResponse response = client.GET(url);
|
||||
assertEquals(HttpServletResponse.SC_OK, response.getStatus());
|
||||
//Ensure session handling is finished
|
||||
Awaitility.waitAtMost(5, TimeUnit.SECONDS).until(() -> contextHandler.getSessionHandler().getSessionManager().getSessionCache().getSessionDataStore().exists(servlet._id));
|
||||
|
||||
//make a request to re-obtain the session on the server
|
||||
Request request = client.newRequest("http://localhost:" + port1 + contextPath + servletMapping + "?action=test");
|
||||
response = request.send();
|
||||
assertEquals(HttpServletResponse.SC_OK, response.getStatus());
|
||||
Awaitility.waitAtMost(5, TimeUnit.SECONDS).until(() -> contextHandler.getSessionHandler().getSessionManager().getSessionCache().getSessionDataStore().exists(servlet._id));
|
||||
//at this point the last accessed time should be the creation time
|
||||
long lastAccessedTime = servlet._lastAccessedTime;
|
||||
assertEquals(lastAccessedTime, servlet._creationTime);
|
||||
|
||||
//make another request to re-obtain the session on the server
|
||||
request = client.newRequest("http://localhost:" + port1 + contextPath + servletMapping + "?action=test");
|
||||
response = request.send();
|
||||
assertEquals(HttpServletResponse.SC_OK, response.getStatus());
|
||||
Awaitility.waitAtMost(5, TimeUnit.SECONDS).until(() -> contextHandler.getSessionHandler().getSessionManager().getSessionCache().getSessionDataStore().exists(servlet._id));
|
||||
//check that the lastAccessedTime is being updated
|
||||
assertTrue(lastAccessedTime < servlet._lastAccessedTime);
|
||||
|
||||
//make a request to invalidate it
|
||||
request = client.newRequest("http://localhost:" + port1 + contextPath + servletMapping + "?action=invalidate");
|
||||
response = request.send();
|
||||
assertEquals(HttpServletResponse.SC_OK, response.getStatus());
|
||||
}
|
||||
finally
|
||||
{
|
||||
server1.stop();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class MySessionListener implements HttpSessionListener
|
||||
{
|
||||
@Override
|
||||
|
@ -454,6 +520,8 @@ public class CreationTest
|
|||
private static final long serialVersionUID = 1L;
|
||||
public String _id = null;
|
||||
public SessionDataStore _store;
|
||||
public long _lastAccessedTime;
|
||||
public long _creationTime;
|
||||
|
||||
public void setStore(SessionDataStore store)
|
||||
{
|
||||
|
@ -469,6 +537,7 @@ public class CreationTest
|
|||
case "forward" ->
|
||||
{
|
||||
HttpSession session = createAndSaveSessionId(request);
|
||||
assertTrue(session.isNew());
|
||||
|
||||
ServletContext contextB = getServletContext().getContext("/contextB");
|
||||
RequestDispatcher dispatcherB = contextB.getRequestDispatcher(request.getServletPath());
|
||||
|
@ -482,6 +551,7 @@ public class CreationTest
|
|||
case "forwardinv" ->
|
||||
{
|
||||
HttpSession session = createAndSaveSessionId(request);
|
||||
assertTrue(session.isNew());
|
||||
|
||||
ServletContext contextB = getServletContext().getContext("/contextB");
|
||||
RequestDispatcher dispatcherB = contextB.getRequestDispatcher(request.getServletPath());
|
||||
|
@ -491,6 +561,7 @@ public class CreationTest
|
|||
case "forwardc" ->
|
||||
{
|
||||
HttpSession session = createAndSaveSessionId(request);
|
||||
assertTrue(session.isNew());
|
||||
//forward to contextC
|
||||
ServletContext contextC = getServletContext().getContext("/contextC");
|
||||
RequestDispatcher dispatcherC = contextC.getRequestDispatcher(request.getServletPath());
|
||||
|
@ -500,14 +571,28 @@ public class CreationTest
|
|||
{
|
||||
HttpSession session = request.getSession(false);
|
||||
assertNotNull(session);
|
||||
_id = session.getId();
|
||||
_lastAccessedTime = session.getLastAccessedTime();
|
||||
assertFalse(session.isNew());
|
||||
assertNotNull(session.getAttribute("value")); //check we see the session we created earlier
|
||||
assertNull(session.getAttribute("B")); //check we don't see stuff from other contexts
|
||||
assertNull(session.getAttribute("C"));
|
||||
}
|
||||
case "invalidate" ->
|
||||
{
|
||||
HttpSession session = request.getSession(false);
|
||||
assertNotNull(session);
|
||||
_id = session.getId();
|
||||
assertFalse(session.isNew());
|
||||
session.invalidate();
|
||||
assertNull(request.getSession(false));
|
||||
}
|
||||
case "create", "createinv", "createinvcreate" ->
|
||||
{
|
||||
currentRequest.set(request);
|
||||
HttpSession session = createAndSaveSessionId(request);
|
||||
assertTrue(session.isNew());
|
||||
_creationTime = session.getCreationTime();
|
||||
String check = request.getParameter("check");
|
||||
if (!StringUtil.isBlank(check) && _store != null)
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue