Introduce Cross Context Dispatch testing

This commit is contained in:
Joakim Erdfelt 2024-06-06 14:40:39 -05:00
parent 6ee17f002c
commit 7011827a8a
No known key found for this signature in database
GPG Key ID: 2D0E1FB8FE4B68B4
50 changed files with 3061 additions and 0 deletions

View File

@ -15,6 +15,7 @@
<module>jetty-jmh</module>
<module>jetty-test-multipart</module>
<module>jetty-test-session-common</module>
<module>test-cross-context-dispatch</module>
<module>test-distribution</module>
<module>test-integration</module>
<module>test-jpms</module>

View File

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.eclipse.jetty.tests.ccd</groupId>
<artifactId>test-cross-context-dispatch</artifactId>
<version>12.0.11-SNAPSHOT</version>
</parent>
<artifactId>ccd-common</artifactId>
<packaging>jar</packaging>
<name>Tests :: Cross Context Dispatch :: Common</name>
<dependencies>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-session</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-slf4j-impl</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,215 @@
//
// ========================================================================
// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.tests.ccd.common;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Deque;
import java.util.List;
import java.util.concurrent.LinkedBlockingDeque;
public class DispatchPlan
{
private final Deque<Step> steps = new LinkedBlockingDeque<>();
private final List<String> events = new ArrayList<>();
private final List<String> expectedEvents = new ArrayList<>();
private final List<Property> expectedProperties = new ArrayList<>();
private final List<String> expectedOutput = new ArrayList<>();
private HttpRequest requestStep;
private String id;
private String expectedContentType;
// if true, assert that all Session.id seen in request attributes are the same id.
private boolean expectedSessionIds;
public DispatchPlan()
{
}
public static DispatchPlan read(Path inputText) throws IOException
{
DispatchPlan plan = new DispatchPlan();
plan.id = inputText.getFileName().toString();
for (String line : Files.readAllLines(inputText, StandardCharsets.UTF_8))
{
if (line.startsWith("#"))
continue; // skip
if (line.startsWith("REQUEST|"))
{
plan.setRequestStep(HttpRequest.parse(line));
}
else if (line.startsWith("STEP|"))
{
plan.addStep(Step.parse(line));
}
else if (line.startsWith("EXPECTED_CONTENT_TYPE|"))
{
plan.setExpectedContentType(dropType(line));
}
else if (line.startsWith("EXPECTED_EVENT|"))
{
plan.addExpectedEvent(dropType(line));
}
else if (line.startsWith("EXPECTED_PROP|"))
{
plan.addExpectedProperty(Property.parse(line));
}
else if (line.startsWith("EXPECTED_OUTPUT|"))
{
plan.addExpectedOutput(dropType(line));
}
else if (line.startsWith("EXPECTED_SESSION_IDS|"))
{
plan.setExpectedSessionIds(Boolean.parseBoolean(dropType(line)));
}
}
return plan;
}
private static String dropType(String line)
{
int idx = line.indexOf("|");
return line.substring(idx + 1);
}
public void addEvent(String format, Object... args)
{
events.add(String.format(format, args));
}
public void addExpectedEvent(String event)
{
expectedEvents.add(event);
}
public void addExpectedOutput(String output)
{
expectedOutput.add(output);
}
public void addExpectedProperty(String name, String value)
{
expectedProperties.add(new Property(name, value));
}
public void addExpectedProperty(Property property)
{
expectedProperties.add(property);
}
public void addStep(Step step)
{
steps.add(step);
}
public List<String> getEvents()
{
return events;
}
public String getExpectedContentType()
{
return expectedContentType;
}
public void setExpectedContentType(String expectedContentType)
{
this.expectedContentType = expectedContentType;
}
public List<String> getExpectedEvents()
{
return expectedEvents;
}
public void setExpectedEvents(String[] events)
{
expectedEvents.clear();
expectedEvents.addAll(List.of(events));
}
public List<String> getExpectedOutput()
{
return expectedOutput;
}
public void setExpectedOutput(String[] output)
{
expectedOutput.clear();
expectedOutput.addAll(List.of(output));
}
public List<Property> getExpectedProperties()
{
return expectedProperties;
}
public void setExpectedProperties(Property[] properties)
{
expectedProperties.clear();
expectedProperties.addAll(List.of(properties));
}
public HttpRequest getRequestStep()
{
return requestStep;
}
public void setRequestStep(HttpRequest requestStep)
{
this.requestStep = requestStep;
}
public Deque<Step> getSteps()
{
return steps;
}
public void setSteps(Step[] stepArr)
{
steps.clear();
for (Step step: stepArr)
steps.add(step);
}
public boolean isExpectedSessionIds()
{
return expectedSessionIds;
}
public void setExpectedSessionIds(boolean expectedSessionIds)
{
this.expectedSessionIds = expectedSessionIds;
}
public String id()
{
return id;
}
public Step popStep()
{
return steps.pollFirst();
}
@Override
public String toString()
{
return "DispatchPlan[id=" + id + "]";
}
}

View File

@ -0,0 +1,77 @@
//
// ========================================================================
// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.tests.ccd.common;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Response;
import org.eclipse.jetty.util.Callback;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class DispatchPlanHandler extends Handler.Wrapper
{
private static final Logger LOG = LoggerFactory.getLogger(DispatchPlanHandler.class);
private Path plansDir;
public Path getPlansDir()
{
return plansDir;
}
public void setPlansDir(Path plansDir)
{
this.plansDir = plansDir;
}
public void setPlansDir(String plansDir)
{
this.setPlansDir(Path.of(plansDir));
}
@Override
public boolean handle(Request request, Response response, Callback callback) throws Exception
{
DispatchPlan dispatchPlan = (DispatchPlan)request.getAttribute(DispatchPlan.class.getName());
if (dispatchPlan == null)
{
String planName = request.getHeaders().get("X-DispatchPlan");
if (planName != null)
{
Path planPath = plansDir.resolve(planName);
if (!Files.isRegularFile(planPath))
{
callback.failed(new IOException("Unable to find: " + planPath));
}
dispatchPlan = DispatchPlan.read(planPath);
dispatchPlan.addEvent("Initial plan: %s", planName);
request.setAttribute(DispatchPlan.class.getName(), dispatchPlan);
}
else
{
LOG.info("Missing Request Header [X-DispatchPlan], skipping DispatchPlan behaviors for this request: {}", request.getHttpURI().toURI());
}
}
if (dispatchPlan != null)
dispatchPlan.addEvent("DispatchPlanHandler.handle() method=%s path-query=%s", request.getMethod(), request.getHttpURI().getPathQuery());
return super.handle(request, response, callback);
}
}

View File

@ -0,0 +1,20 @@
//
// ========================================================================
// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.tests.ccd.common;
public enum DispatchType
{
INCLUDE,
FORWARD;
}

View File

@ -0,0 +1,75 @@
//
// ========================================================================
// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.tests.ccd.common;
import java.util.Map;
public class HttpRequest implements Step
{
private String method;
private String requestPath;
private String body;
private Map<String, String> headers;
public static HttpRequest parse(String line)
{
String[] parts = line.split("\\|");
HttpRequest request = new HttpRequest();
request.setMethod(parts[1]);
request.setRequestPath(parts[2]);
if (parts.length > 4)
request.setBody(parts[3]);
return request;
}
public String getMethod()
{
return method;
}
public void setMethod(String method)
{
this.method = method;
}
public String getRequestPath()
{
return requestPath;
}
public void setRequestPath(String requestPath)
{
this.requestPath = requestPath;
}
public String getBody()
{
return body;
}
public void setBody(String body)
{
this.body = body;
}
public Map<String, String> getHeaders()
{
return headers;
}
public void setHeaders(Map<String, String> headers)
{
this.headers = headers;
}
}

View File

@ -0,0 +1,105 @@
//
// ========================================================================
// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.tests.ccd.common;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import org.eclipse.jetty.session.DefaultSessionCache;
import org.eclipse.jetty.session.ManagedSession;
import org.eclipse.jetty.session.NullSessionDataStore;
import org.eclipse.jetty.session.SessionData;
import org.eclipse.jetty.session.SessionManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class PlanSessionCache extends DefaultSessionCache
{
private static final Logger LOG = LoggerFactory.getLogger(PlanSessionCache.class);
private final Path outputFile;
public PlanSessionCache(SessionManager manager)
{
super(manager);
outputFile = Path.of(System.getProperty("jetty.base"), "work/session.log");
setSessionDataStore(new NullSessionDataStore());
}
@Override
public ManagedSession newSession(SessionData data)
{
logEvent("newSession()", data);
return super.newSession(data);
}
@Override
public void commit(ManagedSession session) throws Exception
{
logEvent("commit()", session);
super.commit(session);
}
@Override
public void release(ManagedSession session) throws Exception
{
logEvent("release()", session);
super.release(session);
}
private void logEvent(String eventType, SessionData data)
{
String name = "SessionCache.event." + eventType;
String value = "";
if (data != null)
{
value = String.format("id=%s|contextPath=%s", data.getId(), data.getContextPath());
}
logAttribute(name, value);
}
private void logEvent(String eventType, ManagedSession session)
{
String name = "SessionCache.event." + eventType;
String value = "";
if (session != null)
{
value = String.format("id=%s", session.getId());
SessionData data = session.getSessionData();
if (data != null)
{
value = String.format("id=%s|contextPath=%s", data.getId(), data.getContextPath());
}
}
logAttribute(name, value);
}
private void logAttribute(String name, String value)
{
String line = name + "=" + value;
if (LOG.isInfoEnabled())
LOG.info(line);
try
{
Files.writeString(outputFile, line + "\n", StandardCharsets.UTF_8, StandardOpenOption.CREATE, StandardOpenOption.WRITE, StandardOpenOption.APPEND);
}
catch (IOException e)
{
LOG.warn("Unable to write to " + outputFile, e);
}
}
}

View File

@ -0,0 +1,69 @@
//
// ========================================================================
// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.tests.ccd.common;
public class Property
{
private String name;
private String value;
public Property()
{
}
public Property(String name, String value)
{
this.name = name;
this.value = value;
}
public static Property parse(String line)
{
String[] parts = line.split("\\|");
String name = parts[1];
String value = null;
if (parts.length > 2)
value = parts[2];
return new Property(name, value);
}
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
public String getValue()
{
return value;
}
public void setValue(String value)
{
this.value = value;
}
@Override
public String toString()
{
return "Property{" +
"name='" + name + '\'' +
", value='" + value + '\'' +
'}';
}
}

View File

@ -0,0 +1,183 @@
//
// ========================================================================
// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.tests.ccd.common;
public interface Step
{
static Step parse(String line)
{
String[] parts = line.split("\\|");
switch (parts[1])
{
case "CONTEXT_FORWARD" ->
{
ContextRedispatch step = new ContextRedispatch();
step.setDispatchType(DispatchType.FORWARD);
step.setContextPath(parts[2]);
step.setDispatchPath(parts[3]);
return step;
}
case "CONTEXT_INCLUDE" ->
{
ContextRedispatch step = new ContextRedispatch();
step.setDispatchType(DispatchType.INCLUDE);
step.setContextPath(parts[2]);
step.setDispatchPath(parts[3]);
return step;
}
case "REQUEST_FORWARD" ->
{
RequestDispatch step = new RequestDispatch();
step.setDispatchType(DispatchType.FORWARD);
step.setDispatchPath(parts[2]);
return step;
}
case "REQUEST_INCLUDE" ->
{
RequestDispatch step = new RequestDispatch();
step.setDispatchType(DispatchType.INCLUDE);
step.setDispatchPath(parts[2]);
return step;
}
case "GET_HTTP_SESSION_ATTRIBUTE" ->
{
GetHttpSession step = new GetHttpSession();
step.setName(parts[2]);
return step;
}
case "SET_HTTP_SESSION_ATTRIBUTE" ->
{
String name = parts[2];
String value = parts[3];
Property prop = new Property(name, value);
HttpSessionSetAttribute step = new HttpSessionSetAttribute(prop);
return step;
}
}
throw new RuntimeException("Unknown STEP type [" + parts[1] + "]");
}
/**
* Will cause an Attribute to be set on the HttpSession via {@code HttpSession.setAttribute(String, Object)}
*/
class HttpSessionSetAttribute implements Step
{
private Property property;
public HttpSessionSetAttribute(Property property)
{
this.property = property;
}
public Property getProperty()
{
return property;
}
}
/**
* Will cause the HttpSession to be fetched via {@code HttpServletRequest#getHttpSession(false)}
* and report the state of the HttpSession in the events (even if null).
*/
class GetHttpSession implements Step
{
private String name;
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
}
/**
* Performs a Redispatch with FORWARD or INCLUDE types using the {@code ServletContext}.
* Uses the {@code ServletContext.getContext(contextPath)} to obtain the
* {@code ServletContext} to then use {@code ServletContext.getRequestDispatcher(dispatchPath)}
* against, which then results in a {@code RequestDispatcher.include} or {@code RequestDispatcher.forward}
* call.
*/
class ContextRedispatch implements Step
{
private DispatchType dispatchType;
private String contextPath;
private String dispatchPath;
public DispatchType getDispatchType()
{
return dispatchType;
}
public void setDispatchType(DispatchType dispatchType)
{
this.dispatchType = dispatchType;
}
public String getContextPath()
{
return contextPath;
}
public void setContextPath(String contextPath)
{
this.contextPath = contextPath;
}
public String getDispatchPath()
{
return dispatchPath;
}
public void setDispatchPath(String dispatchPath)
{
this.dispatchPath = dispatchPath;
}
}
/**
* Performs a Redispatch with FORWARD or INCLUDE types using the {@code HttpServletRequest}.
* Uses the {@code HttpServletRequest.getRequestDispatcher(dispatchPath)} which then
* results in a {@code RequestDispatcher.include} or {@code RequestDispatcher.forward}
* call.
*/
class RequestDispatch implements Step
{
private DispatchType dispatchType;
private String dispatchPath;
public DispatchType getDispatchType()
{
return dispatchType;
}
public void setDispatchType(DispatchType dispatchType)
{
this.dispatchType = dispatchType;
}
public String getDispatchPath()
{
return dispatchPath;
}
public void setDispatchPath(String dispatchPath)
{
this.dispatchPath = dispatchPath;
}
}
}

View File

@ -0,0 +1,76 @@
//
// ========================================================================
// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.tests.ccd.common;
import java.io.IOException;
import java.nio.file.Path;
import org.eclipse.jetty.toolchain.test.MavenPaths;
import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.nullValue;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
@ExtendWith(WorkDirExtension.class)
public class DispatchPlanLoadTest
{
@Test
public void testRead() throws IOException
{
Path planPath = MavenPaths.findTestResourceFile("forward-include-dump.txt");
DispatchPlan plan = DispatchPlan.read(planPath);
assertNotNull(plan);
assertThat("plan.id", plan.id(), is(planPath.getFileName().toString()));
HttpRequest httpRequestStep = plan.getRequestStep();
assertThat(httpRequestStep.getMethod(), is("GET"));
assertThat(httpRequestStep.getRequestPath(), is("/ccd-ee10/redispatch/ee10"));
assertThat(httpRequestStep.getBody(), is(nullValue()));
assertEquals(3, plan.getSteps().size());
Step step = plan.popStep();
assertThat(step, instanceOf(Step.ContextRedispatch.class));
Step.ContextRedispatch contextRedispatchStep = (Step.ContextRedispatch)step;
assertThat(contextRedispatchStep.getDispatchType(), is(DispatchType.FORWARD));
assertThat(contextRedispatchStep.getContextPath(), is("/ccd-ee8"));
assertThat(contextRedispatchStep.getDispatchPath(), is("/redispatch/ee8"));
step = plan.popStep();
assertThat(step, instanceOf(Step.ContextRedispatch.class));
contextRedispatchStep = (Step.ContextRedispatch)step;
assertThat(contextRedispatchStep.getDispatchType(), is(DispatchType.FORWARD));
assertThat(contextRedispatchStep.getContextPath(), is("/ccd-ee9"));
assertThat(contextRedispatchStep.getDispatchPath(), is("/redispatch/ee9"));
step = plan.popStep();
assertThat(step, instanceOf(Step.RequestDispatch.class));
Step.RequestDispatch requestRedispatchStep = (Step.RequestDispatch)step;
assertThat(requestRedispatchStep.getDispatchType(), is(DispatchType.INCLUDE));
assertThat(requestRedispatchStep.getDispatchPath(), is("/dump/ee9"));
assertThat(plan.getExpectedContentType(), is("text/x-java-properties; charset=utf-8"));
assertThat("Expected Events", plan.getExpectedEvents().size(), is(6));
assertThat("Expected Output", plan.getExpectedOutput().size(), is(1));
assertThat("Expected Properties", plan.getExpectedProperties().size(), is(18));
}
}

View File

@ -0,0 +1,30 @@
REQUEST|GET|/ccd-ee10/redispatch/ee10
STEP|CONTEXT_FORWARD|/ccd-ee8|/redispatch/ee8
STEP|CONTEXT_FORWARD|/ccd-ee9|/redispatch/ee9
STEP|REQUEST_INCLUDE|/dump/ee9
EXPECTED_CONTENT_TYPE|text/x-java-properties; charset=utf-8
EXPECTED_EVENT|Initial plan: ee10-forward-to-ee8-include-ee9-dump.json
EXPECTED_EVENT|DispatchPlanHandler.handle() method=GET path-query=/ccd-ee10/redispatch/ee10
EXPECTED_EVENT|org.eclipse.jetty.tests.ccd.ee10.CCDServlet.service() dispatcherType=REQUEST method=GET requestUri=/ccd-ee10/redispatch/ee10
EXPECTED_EVENT|org.eclipse.jetty.tests.ccd.ee8.CCDServlet.service() dispatcherType=FORWARD method=GET requestUri=/ccd-ee8/redispatch/ee8
EXPECTED_EVENT|org.eclipse.jetty.tests.ccd.ee9.CCDServlet.service() dispatcherType=FORWARD method=GET requestUri=/ccd-ee9/redispatch/ee9
EXPECTED_EVENT|org.eclipse.jetty.tests.ccd.ee9.DumpServlet.service() dispatcherType=INCLUDE method=GET requestUri=/ccd-ee9/redispatch/ee9
EXPECTED_PROP|request.dispatcherType|INCLUDE
EXPECTED_PROP|request.requestURI|/ccd-ee9/redispatch/ee9
EXPECTED_PROP|attr[jakarta.servlet.forward.context_path]|/ccd-ee8
EXPECTED_PROP|attr[jakarta.servlet.forward.path_info]|/ee8
EXPECTED_PROP|attr[jakarta.servlet.forward.request_uri]|/ccd-ee8/redispatch/ee8
EXPECTED_PROP|attr[jakarta.servlet.forward.servlet_path]|/redispatch
EXPECTED_PROP|attr[jakarta.servlet.include.context_path]/ccd-ee9
EXPECTED_PROP|attr[jakarta.servlet.include.path_info]|/ee9
EXPECTED_PROP|attr[jakarta.servlet.include.request_uri]|/ccd-ee9/dump/ee9
EXPECTED_PROP|attr[jakarta.servlet.include.servlet_path]/dump
EXPECTED_PROP|attr[javax.servlet.include.context_path]|<null>
EXPECTED_PROP|attr[javax.servlet.include.path_info]|<null>
EXPECTED_PROP|attr[javax.servlet.include.request_uri]|<null>
EXPECTED_PROP|attr[javax.servlet.include.servlet_path]|<null>
EXPECTED_PROP|attr[javax.servlet.forward.context_path]|/ccd-ee8
EXPECTED_PROP|attr[javax.servlet.forward.path_info]|/ee8
EXPECTED_PROP|attr[javax.servlet.forward.request_uri]|/ccd-ee8/redispatch/ee8
EXPECTED_PROP|attr[javax.servlet.forward.servlet_path]|/redispatch
EXPECTED_OUTPUT|foo-bar

View File

@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.eclipse.jetty.tests.ccd</groupId>
<artifactId>test-cross-context-dispatch</artifactId>
<version>12.0.11-SNAPSHOT</version>
</parent>
<artifactId>ccd-ee10-webapp</artifactId>
<packaging>war</packaging>
<name>Tests :: Cross Context Dispatch :: ee10 WebApp</name>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.eclipse.jetty.ee10</groupId>
<artifactId>jetty-ee10-bom</artifactId>
<version>${project.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<version>6.0.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.tests.ccd</groupId>
<artifactId>ccd-common</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,108 @@
//
// ========================================================================
// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.tests.ccd.ee10;
import java.io.IOException;
import java.util.Objects;
import jakarta.servlet.RequestDispatcher;
import jakarta.servlet.ServletContext;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import org.eclipse.jetty.tests.ccd.common.DispatchPlan;
import org.eclipse.jetty.tests.ccd.common.Property;
import org.eclipse.jetty.tests.ccd.common.Step;
public class CCDServlet extends HttpServlet
{
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
{
DispatchPlan dispatchPlan = (DispatchPlan)req.getAttribute(DispatchPlan.class.getName());
if (dispatchPlan == null)
throw new ServletException("Unable to find DispatchPlan");
dispatchPlan.addEvent("%s.service() dispatcherType=%s method=%s requestUri=%s",
this.getClass().getName(),
req.getDispatcherType(), req.getMethod(), req.getRequestURI());
Step step;
while ((step = dispatchPlan.popStep()) != null)
{
if (step instanceof Step.ContextRedispatch contextRedispatchStep)
{
ServletContext otherContext = getServletContext().getContext(contextRedispatchStep.getContextPath());
if (otherContext == null)
throw new NullPointerException("ServletContext.getContext(\"" + contextRedispatchStep.getContextPath() + "\") returned null");
RequestDispatcher dispatcher = otherContext.getRequestDispatcher(contextRedispatchStep.getDispatchPath());
if (dispatcher == null)
throw new NullPointerException("ServletContext.getRequestDispatcher(\"" + contextRedispatchStep.getDispatchPath() + "\") returned null");
switch (contextRedispatchStep.getDispatchType())
{
case FORWARD -> dispatcher.forward(req, resp);
case INCLUDE -> dispatcher.include(req, resp);
}
return;
}
else if (step instanceof Step.RequestDispatch requestDispatchStep)
{
RequestDispatcher dispatcher = req.getRequestDispatcher(requestDispatchStep.getDispatchPath());
if (dispatcher == null)
throw new NullPointerException("HttpServletRequest.getRequestDispatcher(\"" + requestDispatchStep.getDispatchPath() + "\") returned null");
switch (requestDispatchStep.getDispatchType())
{
case FORWARD -> dispatcher.forward(req, resp);
case INCLUDE -> dispatcher.include(req, resp);
}
return;
}
else if (step instanceof Step.GetHttpSession getHttpSessionTask)
{
HttpSession session = req.getSession(false);
if (session == null)
{
dispatchPlan.addEvent("%s.service() HttpSession is null",
this.getClass().getName());
}
else
{
String name = getHttpSessionTask.getName();
Object value = session.getAttribute(name);
dispatchPlan.addEvent("%s.service() HttpSession exists: [%s]=[%s]",
this.getClass().getName(),
name,
Objects.toString(value)
);
}
}
else if (step instanceof Step.HttpSessionSetAttribute sessionSetAttribute)
{
HttpSession session = req.getSession(true);
req.setAttribute("session[" + req.getRequestURI() + "].id", session.getId());
Property prop = sessionSetAttribute.getProperty();
session.setAttribute(prop.getName(), prop.getValue());
}
else
{
throw new RuntimeException("Unable to execute task " + step + " in " + this.getClass().getName());
}
}
}
}

View File

@ -0,0 +1,135 @@
//
// ========================================================================
// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.tests.ccd.ee10;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import java.util.Objects;
import java.util.Properties;
import java.util.function.Function;
import java.util.function.Supplier;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import org.eclipse.jetty.tests.ccd.common.DispatchPlan;
public class DumpServlet extends HttpServlet
{
private static final String NULL = "<null>";
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException
{
DispatchPlan dispatchPlan = (DispatchPlan)req.getAttribute(DispatchPlan.class.getName());
if (dispatchPlan != null)
{
dispatchPlan.addEvent("%s.service() dispatcherType=%s method=%s requestUri=%s",
this.getClass().getName(),
req.getDispatcherType(), req.getMethod(), req.getRequestURI());
}
Properties props = new Properties();
props.setProperty("requestType", req.getClass().getName());
props.setProperty("responseType", resp.getClass().getName());
props.setProperty("request.authType", Objects.toString(req.getAuthType(), NULL));
props.setProperty("request.characterEncoding", Objects.toString(req.getCharacterEncoding(), NULL));
props.setProperty("request.contentLength", Long.toString(req.getContentLengthLong()));
props.setProperty("request.contentType", Objects.toString(req.getContentType(), NULL));
props.setProperty("request.contextPath", Objects.toString(req.getContextPath(), NULL));
props.setProperty("request.dispatcherType", Objects.toString(req.getDispatcherType(), NULL));
props.setProperty("request.localAddr", Objects.toString(req.getLocalAddr(), NULL));
props.setProperty("request.localName", Objects.toString(req.getLocalName(), NULL));
props.setProperty("request.localPort", Integer.toString(req.getLocalPort()));
props.setProperty("request.locale", Objects.toString(req.getLocale(), NULL));
props.setProperty("request.method", Objects.toString(req.getMethod(), NULL));
props.setProperty("request.pathInfo", Objects.toString(req.getPathInfo(), NULL));
props.setProperty("request.pathTranslated", Objects.toString(req.getPathTranslated(), NULL));
props.setProperty("request.protocol", Objects.toString(req.getProtocol(), NULL));
props.setProperty("request.queryString", Objects.toString(req.getQueryString(), NULL));
props.setProperty("request.remoteAddr", Objects.toString(req.getRemoteAddr(), NULL));
props.setProperty("request.remoteHost", Objects.toString(req.getRemoteHost(), NULL));
props.setProperty("request.remotePort", Integer.toString(req.getRemotePort()));
props.setProperty("request.remoteUser", Objects.toString(req.getRemoteUser(), NULL));
props.setProperty("request.requestedSessionId", Objects.toString(req.getRequestedSessionId(), NULL));
props.setProperty("request.requestURI", Objects.toString(req.getRequestURI(), NULL));
props.setProperty("request.requestURL", Objects.toString(req.getRequestURL(), NULL));
props.setProperty("request.serverPort", Integer.toString(req.getServerPort()));
props.setProperty("request.servletPath", Objects.toString(req.getServletPath(), NULL));
props.setProperty("request.session.exists", "false");
HttpSession httpSession = req.getSession(false);
if (httpSession != null)
{
props.setProperty("request.session.exists", "true");
List<String> attrNames = Collections.list(httpSession.getAttributeNames());
attrNames
.forEach((name) ->
{
Object attrVal = httpSession.getAttribute(name);
props.setProperty("session[" + name + "]", Objects.toString(attrVal, NULL));
});
}
addAttributes(props, "req", req::getAttributeNames, req::getAttribute);
addAttributes(props, "context",
() -> getServletContext().getAttributeNames(),
(name) -> getServletContext().getAttribute(name));
List<String> headerNames = Collections.list(req.getHeaderNames());
headerNames
.forEach((name) ->
{
String headerVal = req.getHeader(name);
props.setProperty("header[" + name + "]", Objects.toString(headerVal, NULL));
});
if (dispatchPlan != null)
{
int eventCount = dispatchPlan.getEvents().size();
props.setProperty("dispatchPlan.events.count", Integer.toString(dispatchPlan.getEvents().size()));
for (int i = 0; i < eventCount; i++)
{
props.setProperty("dispatchPlan.event[" + i + "]", dispatchPlan.getEvents().get(i));
}
}
resp.setStatus(HttpServletResponse.SC_OK);
resp.setCharacterEncoding("utf-8");
resp.setContentType("text/x-java-properties");
PrintWriter out = resp.getWriter();
props.store(out, "From " + this.getClass().getName());
}
private void addAttributes(Properties props,
String prefix,
Supplier<Enumeration<String>> getNamesSupplier,
Function<String, Object> getAttributeFunction)
{
List<String> attrNames = Collections.list(getNamesSupplier.get());
attrNames
.forEach((name) ->
{
Object attrVal = getAttributeFunction.apply(name);
props.setProperty(prefix + ".attr[" + name + "]", Objects.toString(attrVal, NULL));
});
}
}

View File

@ -0,0 +1,35 @@
//
// ========================================================================
// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.tests.ccd.ee10;
import java.io.IOException;
import java.util.Objects;
import jakarta.servlet.RequestDispatcher;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
public class ForwardServlet extends HttpServlet
{
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
{
String forwardTo = req.getHeader("X-ForwardTo");
Objects.requireNonNull(forwardTo);
RequestDispatcher requestDispatcher = req.getRequestDispatcher(forwardTo);
requestDispatcher.forward(req, resp);
}
}

View File

@ -0,0 +1,56 @@
//
// ========================================================================
// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.tests.ccd.ee10;
import java.io.IOException;
import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletRequestWrapper;
import jakarta.servlet.http.HttpServletResponse;
/**
* A servlet filter that will harshly change the return value of
* {@link HttpServletRequest#getRequestURI()} to something that does
* not satisfy the Servlet spec URI invariant {@code request URI == context path + servlet path + path info}
*/
public class InternalRequestURIFilter implements Filter
{
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
{
HttpServletRequest httpServletRequest = (HttpServletRequest)request;
HttpServletResponse httpServletResponse = (HttpServletResponse)response;
InternalRequestURIWrapper requestURIWrapper = new InternalRequestURIWrapper(httpServletRequest);
chain.doFilter(requestURIWrapper, httpServletResponse);
}
private static class InternalRequestURIWrapper extends HttpServletRequestWrapper
{
public InternalRequestURIWrapper(HttpServletRequest request)
{
super(request);
}
@Override
public String getRequestURI()
{
return "/internal/";
}
}
}

View File

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_6_0.xsd"
version="6.0">
<display-name>ccd-ee10</display-name>
<servlet>
<servlet-name>ccd</servlet-name>
<servlet-class>org.eclipse.jetty.tests.ccd.ee10.CCDServlet</servlet-class>
</servlet>
<servlet>
<servlet-name>dump</servlet-name>
<servlet-class>org.eclipse.jetty.tests.ccd.ee10.DumpServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ccd</servlet-name>
<url-pattern>/redispatch/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>dump</servlet-name>
<url-pattern>/dump/*</url-pattern>
</servlet-mapping>
</web-app>

View File

@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.eclipse.jetty.tests.ccd</groupId>
<artifactId>test-cross-context-dispatch</artifactId>
<version>12.0.11-SNAPSHOT</version>
</parent>
<artifactId>ccd-ee8-webapp</artifactId>
<packaging>war</packaging>
<name>Tests :: Cross Context Dispatch :: ee8 WebApp</name>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.eclipse.jetty.ee8</groupId>
<artifactId>jetty-ee8-bom</artifactId>
<version>${project.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<version>4.0.4</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.tests.ccd</groupId>
<artifactId>ccd-common</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,107 @@
//
// ========================================================================
// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.tests.ccd.ee8;
import java.io.IOException;
import java.util.Objects;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;
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.tests.ccd.common.DispatchPlan;
import org.eclipse.jetty.tests.ccd.common.Property;
import org.eclipse.jetty.tests.ccd.common.Step;
public class CCDServlet extends HttpServlet
{
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
{
DispatchPlan dispatchPlan = (DispatchPlan)req.getAttribute(DispatchPlan.class.getName());
if (dispatchPlan == null)
throw new ServletException("Unable to find DispatchPlan");
dispatchPlan.addEvent("%s.service() dispatcherType=%s method=%s requestUri=%s",
this.getClass().getName(),
req.getDispatcherType(), req.getMethod(), req.getRequestURI());
Step step;
while ((step = dispatchPlan.popStep()) != null)
{
if (step instanceof Step.ContextRedispatch contextRedispatchStep)
{
ServletContext otherContext = getServletContext().getContext(contextRedispatchStep.getContextPath());
if (otherContext == null)
throw new NullPointerException("ServletContext.getContext(\"" + contextRedispatchStep.getContextPath() + "\") returned null");
RequestDispatcher dispatcher = otherContext.getRequestDispatcher(contextRedispatchStep.getDispatchPath());
if (dispatcher == null)
throw new NullPointerException("ServletContext.getRequestDispatcher(\"" + contextRedispatchStep.getDispatchPath() + "\") returned null");
switch (contextRedispatchStep.getDispatchType())
{
case FORWARD -> dispatcher.forward(req, resp);
case INCLUDE -> dispatcher.include(req, resp);
}
return;
}
else if (step instanceof Step.RequestDispatch requestDispatchStep)
{
RequestDispatcher dispatcher = req.getRequestDispatcher(requestDispatchStep.getDispatchPath());
if (dispatcher == null)
throw new NullPointerException("HttpServletRequest.getRequestDispatcher(\"" + requestDispatchStep.getDispatchPath() + "\") returned null");
switch (requestDispatchStep.getDispatchType())
{
case FORWARD -> dispatcher.forward(req, resp);
case INCLUDE -> dispatcher.include(req, resp);
}
return;
}
else if (step instanceof Step.GetHttpSession getHttpSessionTask)
{
HttpSession session = req.getSession(false);
if (session == null)
{
dispatchPlan.addEvent("%s.service() HttpSession is null",
this.getClass().getName());
}
else
{
String name = getHttpSessionTask.getName();
Object value = session.getAttribute(name);
dispatchPlan.addEvent("%s.service() HttpSession exists: [%s]=[%s]",
this.getClass().getName(),
name,
Objects.toString(value)
);
}
}
else if (step instanceof Step.HttpSessionSetAttribute sessionSetAttribute)
{
HttpSession session = req.getSession(true);
req.setAttribute("session[" + req.getRequestURI() + "].id", session.getId());
Property prop = sessionSetAttribute.getProperty();
session.setAttribute(prop.getName(), prop.getValue());
}
else
{
throw new RuntimeException("Unable to execute task " + step + " in " + this.getClass().getName());
}
}
}
}

View File

@ -0,0 +1,135 @@
//
// ========================================================================
// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.tests.ccd.ee8;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import java.util.Objects;
import java.util.Properties;
import java.util.function.Function;
import java.util.function.Supplier;
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.tests.ccd.common.DispatchPlan;
public class DumpServlet extends HttpServlet
{
private static final String NULL = "<null>";
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException
{
DispatchPlan dispatchPlan = (DispatchPlan)req.getAttribute(DispatchPlan.class.getName());
if (dispatchPlan != null)
{
dispatchPlan.addEvent("%s.service() dispatcherType=%s method=%s requestUri=%s",
this.getClass().getName(),
req.getDispatcherType(), req.getMethod(), req.getRequestURI());
}
Properties props = new Properties();
props.setProperty("requestType", req.getClass().getName());
props.setProperty("responseType", resp.getClass().getName());
props.setProperty("request.authType", Objects.toString(req.getAuthType(), NULL));
props.setProperty("request.characterEncoding", Objects.toString(req.getCharacterEncoding(), NULL));
props.setProperty("request.contentLength", Long.toString(req.getContentLengthLong()));
props.setProperty("request.contentType", Objects.toString(req.getContentType(), NULL));
props.setProperty("request.contextPath", Objects.toString(req.getContextPath(), NULL));
props.setProperty("request.dispatcherType", Objects.toString(req.getDispatcherType(), NULL));
props.setProperty("request.localAddr", Objects.toString(req.getLocalAddr(), NULL));
props.setProperty("request.localName", Objects.toString(req.getLocalName(), NULL));
props.setProperty("request.localPort", Integer.toString(req.getLocalPort()));
props.setProperty("request.locale", Objects.toString(req.getLocale(), NULL));
props.setProperty("request.method", Objects.toString(req.getMethod(), NULL));
props.setProperty("request.pathInfo", Objects.toString(req.getPathInfo(), NULL));
props.setProperty("request.pathTranslated", Objects.toString(req.getPathTranslated(), NULL));
props.setProperty("request.protocol", Objects.toString(req.getProtocol(), NULL));
props.setProperty("request.queryString", Objects.toString(req.getQueryString(), NULL));
props.setProperty("request.remoteAddr", Objects.toString(req.getRemoteAddr(), NULL));
props.setProperty("request.remoteHost", Objects.toString(req.getRemoteHost(), NULL));
props.setProperty("request.remotePort", Integer.toString(req.getRemotePort()));
props.setProperty("request.remoteUser", Objects.toString(req.getRemoteUser(), NULL));
props.setProperty("request.requestedSessionId", Objects.toString(req.getRequestedSessionId(), NULL));
props.setProperty("request.requestURI", Objects.toString(req.getRequestURI(), NULL));
props.setProperty("request.requestURL", Objects.toString(req.getRequestURL(), NULL));
props.setProperty("request.serverPort", Integer.toString(req.getServerPort()));
props.setProperty("request.servletPath", Objects.toString(req.getServletPath(), NULL));
props.setProperty("request.session.exists", "false");
HttpSession httpSession = req.getSession(false);
if (httpSession != null)
{
props.setProperty("request.session.exists", "true");
List<String> attrNames = Collections.list(httpSession.getAttributeNames());
attrNames
.forEach((name) ->
{
Object attrVal = httpSession.getAttribute(name);
props.setProperty("session[" + name + "]", Objects.toString(attrVal, NULL));
});
}
addAttributes(props, "req", req::getAttributeNames, req::getAttribute);
addAttributes(props, "context",
() -> getServletContext().getAttributeNames(),
(name) -> getServletContext().getAttribute(name));
List<String> headerNames = Collections.list(req.getHeaderNames());
headerNames
.forEach((name) ->
{
String headerVal = req.getHeader(name);
props.setProperty("header[" + name + "]", Objects.toString(headerVal, NULL));
});
if (dispatchPlan != null)
{
int eventCount = dispatchPlan.getEvents().size();
props.setProperty("dispatchPlan.events.count", Integer.toString(dispatchPlan.getEvents().size()));
for (int i = 0; i < eventCount; i++)
{
props.setProperty("dispatchPlan.event[" + i + "]", dispatchPlan.getEvents().get(i));
}
}
resp.setStatus(HttpServletResponse.SC_OK);
resp.setCharacterEncoding("utf-8");
resp.setContentType("text/x-java-properties");
PrintWriter out = resp.getWriter();
props.store(out, "From " + this.getClass().getName());
}
private void addAttributes(Properties props,
String prefix,
Supplier<Enumeration<String>> getNamesSupplier,
Function<String, Object> getAttributeFunction)
{
List<String> attrNames = Collections.list(getNamesSupplier.get());
attrNames
.forEach((name) ->
{
Object attrVal = getAttributeFunction.apply(name);
props.setProperty(prefix + ".attr[" + name + "]", Objects.toString(attrVal, NULL));
});
}
}

View File

@ -0,0 +1,34 @@
//
// ========================================================================
// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.tests.ccd.ee8;
import java.io.IOException;
import java.util.Objects;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class ForwardServlet extends HttpServlet
{
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
{
String forwardTo = req.getHeader("X-ForwardTo");
Objects.requireNonNull(forwardTo);
RequestDispatcher requestDispatcher = req.getRequestDispatcher(forwardTo);
requestDispatcher.forward(req, resp);
}
}

View File

@ -0,0 +1,55 @@
//
// ========================================================================
// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.tests.ccd.ee8;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
/**
* A servlet filter that will harshly change the return value of
* {@link HttpServletRequest#getRequestURI()} to something that does
* not satisfy the Servlet spec URI invariant {@code request URI == context path + servlet path + path info}
*/
public class InternalRequestURIFilter implements Filter
{
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
{
HttpServletRequest httpServletRequest = (HttpServletRequest)request;
HttpServletResponse httpServletResponse = (HttpServletResponse)response;
InternalRequestURIWrapper requestURIWrapper = new InternalRequestURIWrapper(httpServletRequest);
chain.doFilter(requestURIWrapper, httpServletResponse);
}
private static class InternalRequestURIWrapper extends HttpServletRequestWrapper
{
public InternalRequestURIWrapper(HttpServletRequest request)
{
super(request);
}
@Override
public String getRequestURI()
{
return "/internal/";
}
}
}

View File

@ -0,0 +1,38 @@
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<display-name>ccd-ee8</display-name>
<servlet>
<servlet-name>ccd</servlet-name>
<servlet-class>org.eclipse.jetty.tests.ccd.ee8.CCDServlet</servlet-class>
</servlet>
<servlet>
<servlet-name>forwardto</servlet-name>
<servlet-class>org.eclipse.jetty.tests.ccd.ee8.ForwardServlet</servlet-class>
</servlet>
<servlet>
<servlet-name>dump</servlet-name>
<servlet-class>org.eclipse.jetty.tests.ccd.ee8.DumpServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ccd</servlet-name>
<url-pattern>/redispatch/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>forwardto</servlet-name>
<url-pattern>/forwardto/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>dump</servlet-name>
<url-pattern>/dump/*</url-pattern>
</servlet-mapping>
</web-app>

View File

@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.eclipse.jetty.tests.ccd</groupId>
<artifactId>test-cross-context-dispatch</artifactId>
<version>12.0.11-SNAPSHOT</version>
</parent>
<artifactId>ccd-ee9-webapp</artifactId>
<packaging>war</packaging>
<name>Tests :: Cross Context Dispatch :: ee9 WebApp</name>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.eclipse.jetty.ee9</groupId>
<artifactId>jetty-ee9-bom</artifactId>
<version>${project.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<version>5.0.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.tests.ccd</groupId>
<artifactId>ccd-common</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,108 @@
//
// ========================================================================
// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.tests.ccd.ee9;
import java.io.IOException;
import java.util.Objects;
import jakarta.servlet.RequestDispatcher;
import jakarta.servlet.ServletContext;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import org.eclipse.jetty.tests.ccd.common.DispatchPlan;
import org.eclipse.jetty.tests.ccd.common.Property;
import org.eclipse.jetty.tests.ccd.common.Step;
public class CCDServlet extends HttpServlet
{
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
{
DispatchPlan dispatchPlan = (DispatchPlan)req.getAttribute(DispatchPlan.class.getName());
if (dispatchPlan == null)
throw new ServletException("Unable to find DispatchPlan");
dispatchPlan.addEvent("%s.service() dispatcherType=%s method=%s requestUri=%s",
this.getClass().getName(),
req.getDispatcherType(), req.getMethod(), req.getRequestURI());
Step step;
while ((step = dispatchPlan.popStep()) != null)
{
if (step instanceof Step.ContextRedispatch contextRedispatchStep)
{
ServletContext otherContext = getServletContext().getContext(contextRedispatchStep.getContextPath());
if (otherContext == null)
throw new NullPointerException("ServletContext.getContext(\"" + contextRedispatchStep.getContextPath() + "\") returned null");
RequestDispatcher dispatcher = otherContext.getRequestDispatcher(contextRedispatchStep.getDispatchPath());
if (dispatcher == null)
throw new NullPointerException("ServletContext.getRequestDispatcher(\"" + contextRedispatchStep.getDispatchPath() + "\") returned null");
switch (contextRedispatchStep.getDispatchType())
{
case FORWARD -> dispatcher.forward(req, resp);
case INCLUDE -> dispatcher.include(req, resp);
}
return;
}
else if (step instanceof Step.RequestDispatch requestDispatchStep)
{
RequestDispatcher dispatcher = req.getRequestDispatcher(requestDispatchStep.getDispatchPath());
if (dispatcher == null)
throw new NullPointerException("HttpServletRequest.getRequestDispatcher(\"" + requestDispatchStep.getDispatchPath() + "\") returned null");
switch (requestDispatchStep.getDispatchType())
{
case FORWARD -> dispatcher.forward(req, resp);
case INCLUDE -> dispatcher.include(req, resp);
}
return;
}
else if (step instanceof Step.GetHttpSession getHttpSessionTask)
{
HttpSession session = req.getSession(false);
if (session == null)
{
dispatchPlan.addEvent("%s.service() HttpSession is null",
this.getClass().getName());
}
else
{
String name = getHttpSessionTask.getName();
Object value = session.getAttribute(name);
dispatchPlan.addEvent("%s.service() HttpSession exists: [%s]=[%s]",
this.getClass().getName(),
name,
Objects.toString(value)
);
}
}
else if (step instanceof Step.HttpSessionSetAttribute sessionSetAttribute)
{
HttpSession session = req.getSession(true);
req.setAttribute("session[" + req.getRequestURI() + "].id", session.getId());
Property prop = sessionSetAttribute.getProperty();
session.setAttribute(prop.getName(), prop.getValue());
}
else
{
throw new RuntimeException("Unable to execute task " + step + " in " + this.getClass().getName());
}
}
}
}

View File

@ -0,0 +1,135 @@
//
// ========================================================================
// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.tests.ccd.ee9;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import java.util.Objects;
import java.util.Properties;
import java.util.function.Function;
import java.util.function.Supplier;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import org.eclipse.jetty.tests.ccd.common.DispatchPlan;
public class DumpServlet extends HttpServlet
{
private static final String NULL = "<null>";
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException
{
DispatchPlan dispatchPlan = (DispatchPlan)req.getAttribute(DispatchPlan.class.getName());
if (dispatchPlan != null)
{
dispatchPlan.addEvent("%s.service() dispatcherType=%s method=%s requestUri=%s",
this.getClass().getName(),
req.getDispatcherType(), req.getMethod(), req.getRequestURI());
}
Properties props = new Properties();
props.setProperty("requestType", req.getClass().getName());
props.setProperty("responseType", resp.getClass().getName());
props.setProperty("request.authType", Objects.toString(req.getAuthType(), NULL));
props.setProperty("request.characterEncoding", Objects.toString(req.getCharacterEncoding(), NULL));
props.setProperty("request.contentLength", Long.toString(req.getContentLengthLong()));
props.setProperty("request.contentType", Objects.toString(req.getContentType(), NULL));
props.setProperty("request.contextPath", Objects.toString(req.getContextPath(), NULL));
props.setProperty("request.dispatcherType", Objects.toString(req.getDispatcherType(), NULL));
props.setProperty("request.localAddr", Objects.toString(req.getLocalAddr(), NULL));
props.setProperty("request.localName", Objects.toString(req.getLocalName(), NULL));
props.setProperty("request.localPort", Integer.toString(req.getLocalPort()));
props.setProperty("request.locale", Objects.toString(req.getLocale(), NULL));
props.setProperty("request.method", Objects.toString(req.getMethod(), NULL));
props.setProperty("request.pathInfo", Objects.toString(req.getPathInfo(), NULL));
props.setProperty("request.pathTranslated", Objects.toString(req.getPathTranslated(), NULL));
props.setProperty("request.protocol", Objects.toString(req.getProtocol(), NULL));
props.setProperty("request.queryString", Objects.toString(req.getQueryString(), NULL));
props.setProperty("request.remoteAddr", Objects.toString(req.getRemoteAddr(), NULL));
props.setProperty("request.remoteHost", Objects.toString(req.getRemoteHost(), NULL));
props.setProperty("request.remotePort", Integer.toString(req.getRemotePort()));
props.setProperty("request.remoteUser", Objects.toString(req.getRemoteUser(), NULL));
props.setProperty("request.requestedSessionId", Objects.toString(req.getRequestedSessionId(), NULL));
props.setProperty("request.requestURI", Objects.toString(req.getRequestURI(), NULL));
props.setProperty("request.requestURL", Objects.toString(req.getRequestURL(), NULL));
props.setProperty("request.serverPort", Integer.toString(req.getServerPort()));
props.setProperty("request.servletPath", Objects.toString(req.getServletPath(), NULL));
props.setProperty("request.session.exists", "false");
HttpSession httpSession = req.getSession(false);
if (httpSession != null)
{
props.setProperty("request.session.exists", "true");
List<String> attrNames = Collections.list(httpSession.getAttributeNames());
attrNames
.forEach((name) ->
{
Object attrVal = httpSession.getAttribute(name);
props.setProperty("session[" + name + "]", Objects.toString(attrVal, NULL));
});
}
addAttributes(props, "req", req::getAttributeNames, req::getAttribute);
addAttributes(props, "context",
() -> getServletContext().getAttributeNames(),
(name) -> getServletContext().getAttribute(name));
List<String> headerNames = Collections.list(req.getHeaderNames());
headerNames
.forEach((name) ->
{
String headerVal = req.getHeader(name);
props.setProperty("header[" + name + "]", Objects.toString(headerVal, NULL));
});
if (dispatchPlan != null)
{
int eventCount = dispatchPlan.getEvents().size();
props.setProperty("dispatchPlan.events.count", Integer.toString(dispatchPlan.getEvents().size()));
for (int i = 0; i < eventCount; i++)
{
props.setProperty("dispatchPlan.event[" + i + "]", dispatchPlan.getEvents().get(i));
}
}
resp.setStatus(HttpServletResponse.SC_OK);
resp.setCharacterEncoding("utf-8");
resp.setContentType("text/x-java-properties");
PrintWriter out = resp.getWriter();
props.store(out, "From " + this.getClass().getName());
}
private void addAttributes(Properties props,
String prefix,
Supplier<Enumeration<String>> getNamesSupplier,
Function<String, Object> getAttributeFunction)
{
List<String> attrNames = Collections.list(getNamesSupplier.get());
attrNames
.forEach((name) ->
{
Object attrVal = getAttributeFunction.apply(name);
props.setProperty(prefix + ".attr[" + name + "]", Objects.toString(attrVal, NULL));
});
}
}

View File

@ -0,0 +1,35 @@
//
// ========================================================================
// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.tests.ccd.ee9;
import java.io.IOException;
import java.util.Objects;
import jakarta.servlet.RequestDispatcher;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
public class ForwardServlet extends HttpServlet
{
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
{
String forwardTo = req.getHeader("X-ForwardTo");
Objects.requireNonNull(forwardTo);
RequestDispatcher requestDispatcher = req.getRequestDispatcher(forwardTo);
requestDispatcher.forward(req, resp);
}
}

View File

@ -0,0 +1,56 @@
//
// ========================================================================
// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.tests.ccd.ee9;
import java.io.IOException;
import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletRequestWrapper;
import jakarta.servlet.http.HttpServletResponse;
/**
* A servlet filter that will harshly change the return value of
* {@link HttpServletRequest#getRequestURI()} to something that does
* not satisfy the Servlet spec URI invariant {@code request URI == context path + servlet path + path info}
*/
public class InternalRequestURIFilter implements Filter
{
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
{
HttpServletRequest httpServletRequest = (HttpServletRequest)request;
HttpServletResponse httpServletResponse = (HttpServletResponse)response;
InternalRequestURIWrapper requestURIWrapper = new InternalRequestURIWrapper(httpServletRequest);
chain.doFilter(requestURIWrapper, httpServletResponse);
}
private static class InternalRequestURIWrapper extends HttpServletRequestWrapper
{
public InternalRequestURIWrapper(HttpServletRequest request)
{
super(request);
}
@Override
public String getRequestURI()
{
return "/internal/";
}
}
}

View File

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd"
version="5.0">
<display-name>ccd-ee9</display-name>
<servlet>
<servlet-name>ccd</servlet-name>
<servlet-class>org.eclipse.jetty.tests.ccd.ee9.CCDServlet</servlet-class>
</servlet>
<servlet>
<servlet-name>dump</servlet-name>
<servlet-class>org.eclipse.jetty.tests.ccd.ee9.DumpServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ccd</servlet-name>
<url-pattern>/redispatch/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>dump</servlet-name>
<url-pattern>/dump/*</url-pattern>
</servlet-mapping>
</web-app>

View File

@ -0,0 +1,86 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.eclipse.jetty.tests.ccd</groupId>
<artifactId>test-cross-context-dispatch</artifactId>
<version>12.0.11-SNAPSHOT</version>
</parent>
<artifactId>ccd-tests</artifactId>
<packaging>jar</packaging>
<name>Tests :: Cross Context Dispatch :: Tests</name>
<properties>
<!-- <junit.jupiter.execution.parallel.enabled>false</junit.jupiter.execution.parallel.enabled>-->
<junit.jupiter.execution.parallel.config.fixed.parallelism>1</junit.jupiter.execution.parallel.config.fixed.parallelism>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-bom</artifactId>
<version>${project.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.eclipse.jetty.tests.ccd</groupId>
<artifactId>ccd-common</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-client</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-deploy</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-slf4j-impl</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.tests</groupId>
<artifactId>jetty-testers</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.toolchain</groupId>
<artifactId>jetty-test-helper</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<systemPropertyVariables>
<mavenRepoPath>${session.repositorySession.localRepository.basedir.absolutePath}</mavenRepoPath>
<jettyVersion>${project.version}</jettyVersion>
<distribution.debug.port>$(distribution.debug.port}</distribution.debug.port>
<home.start.timeout>${home.start.timeout}</home.start.timeout>
</systemPropertyVariables>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>

View File

@ -0,0 +1,216 @@
//
// ========================================================================
// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.tests.redispatch;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.TimeUnit;
import org.eclipse.jetty.client.ContentResponse;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.tests.testers.JettyHomeTester;
import org.eclipse.jetty.tests.testers.Tester;
import org.eclipse.jetty.toolchain.test.FS;
import org.eclipse.jetty.toolchain.test.MavenPaths;
import org.eclipse.jetty.util.component.LifeCycle;
import org.hamcrest.Matcher;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.TestInfo;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
public abstract class AbstractRedispatchTest
{
protected static final int START_TIMEOUT = Integer.getInteger("home.start.timeout", 30);
protected static final List<String> ENVIRONMENTS = List.of("ee8", "ee9", "ee10");
static String toResponseDetails(ContentResponse response)
{
return new ResponseDetails(response).get();
}
static class InitializedJettyBase
{
public Path jettyBase;
public JettyHomeTester distribution;
public int httpPort;
public InitializedJettyBase(TestInfo testInfo) throws Exception
{
Path testsDir = MavenPaths.targetTests();
String cleanBaseName = toCleanDirectoryName(testInfo);
jettyBase = testsDir.resolve(cleanBaseName);
FS.ensureEmpty(jettyBase);
String jettyVersion = System.getProperty("jettyVersion");
distribution = JettyHomeTester.Builder.newInstance()
.jettyVersion(jettyVersion)
.jettyBase(jettyBase)
.build();
httpPort = Tester.freePort();
List<String> configList = new ArrayList<>();
configList.add("--add-modules=http,resources");
for (String env : ENVIRONMENTS)
{
configList.add("--add-modules=" + env + "-deploy," + env + "-webapp");
}
try (JettyHomeTester.Run runConfig = distribution.start(configList))
{
assertTrue(runConfig.awaitFor(START_TIMEOUT, TimeUnit.SECONDS));
assertEquals(0, runConfig.getExitValue());
Path libDir = jettyBase.resolve("lib");
FS.ensureDirExists(libDir);
Path etcDir = jettyBase.resolve("etc");
FS.ensureDirExists(etcDir);
Path modulesDir = jettyBase.resolve("modules");
FS.ensureDirExists(modulesDir);
Path startDir = jettyBase.resolve("start.d");
FS.ensureDirExists(startDir);
// Configure the DispatchPlanHandler
Path ccdJar = distribution.resolveArtifact("org.eclipse.jetty.tests.ccd:ccd-common:jar:" + jettyVersion);
Files.copy(ccdJar, libDir.resolve(ccdJar.getFileName()));
Path installDispatchPlanXml = MavenPaths.findTestResourceFile("install-ccd-handler.xml");
Files.copy(installDispatchPlanXml, etcDir.resolve(installDispatchPlanXml.getFileName()));
String module = """
[depend]
server
[lib]
lib/jetty-util-ajax-$J.jar
lib/ccd-common-$J.jar
[xml]
etc/install-ccd-handler.xml
[ini]
jetty.webapp.addProtectedClasses+=,org.eclipse.jetty.tests.ccd.common.
jetty.webapp.addHiddenClasses+=,-org.eclipse.jetty.tests.ccd.common.
""".replace("$J", jettyVersion);
Files.writeString(modulesDir.resolve("ccd.mod"), module, StandardCharsets.UTF_8);
// -- Error Handler
Path errorHandlerXml = MavenPaths.findTestResourceFile("error-handler.xml");
Files.copy(errorHandlerXml, etcDir.resolve("error-handler.xml"));
String errorHandlerIni = """
etc/error-handler.xml
""";
Files.writeString(startDir.resolve("error-handler.ini"), errorHandlerIni);
// -- Plans Dir
Path plansDir = MavenPaths.findTestResourceDir("plans");
Path ccdIni = startDir.resolve("ccd.ini");
String ini = """
--module=ccd
ccd-plans-dir=$D
""".replace("$D", plansDir.toString());
Files.writeString(ccdIni, ini, StandardCharsets.UTF_8);
// -- Add the test wars
for (String env : ENVIRONMENTS)
{
Path war = distribution.resolveArtifact("org.eclipse.jetty.tests.ccd:ccd-" + env + "-webapp:war:" + jettyVersion);
distribution.installWar(war, "ccd-" + env);
Path warProperties = jettyBase.resolve("webapps/ccd-" + env + ".properties");
Files.writeString(warProperties, "environment: " + env, StandardCharsets.UTF_8);
Path webappXmlSrc = MavenPaths.findTestResourceFile("webapp-xmls/ccd-" + env + ".xml");
Path webappXmlDest = jettyBase.resolve("webapps/ccd-" + env + ".xml");
Files.copy(webappXmlSrc, webappXmlDest);
}
}
}
/**
* Create a name that can be used as a Jetty Base home directory in a safe way.
*
* Note: unlike the WorkDir object, this strips out {@code [} and {@code ]} characters
* and also makes any non-alpha-numeric character just {@code _}, which results in
* a happy {@code ${jetty.base}} and {@code start.jar}.
*
* Failure to use this method can result in start.jar behaving in unintended ways
* when it goes through the Java -> Runtime.exec -> OS behaviors.
*
* This change also makes the created directory named {@code target/tests/<method-name>.<display-name>}
* live and suitable for execution via a console without accidental shell interpretation of special
* characters in the directory name (that can result from characters like "[]" used in a directory name)
*
* @param testInfo the TestInfo to use to generate directory name from.
* @return the safe to use directory name.
*/
public static String toCleanDirectoryName(TestInfo testInfo)
{
StringBuilder name = new StringBuilder();
if (testInfo.getTestMethod().isPresent())
{
name.append(testInfo.getTestMethod().get().getName());
name.append(".");
}
for (char c: testInfo.getDisplayName().toCharArray())
{
if (Character.isLetterOrDigit(c) || c == '.' || c == '-')
name.append(c);
else if (c != '[' && c != ']')
name.append("_");
}
return name.toString();
}
}
protected HttpClient client;
@BeforeEach
public void startClient() throws Exception
{
client = new HttpClient();
client.start();
}
@AfterEach
public void stopClient()
{
LifeCycle.stop(client);
}
public static void dumpProperties(Properties props)
{
props.stringPropertyNames().stream()
.sorted()
.forEach((name) ->
System.out.printf(" %s=%s%n", name, props.getProperty(name)));
}
public static void assertProperty(Properties props, String name, Matcher<String> valueMatcher)
{
assertThat("Property [" + name + "]", props.getProperty(name), valueMatcher);
}
public static void assertProperty(String id, Properties props, String name, Matcher<String> valueMatcher)
{
assertThat("id[" + id + "] property[" + name + "]", props.getProperty(name), valueMatcher);
}
}

View File

@ -0,0 +1,179 @@
//
// ========================================================================
// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.tests.redispatch;
import java.io.IOException;
import java.io.StringReader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.TimeUnit;
import java.util.stream.Stream;
import org.eclipse.jetty.client.ContentResponse;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.tests.ccd.common.DispatchPlan;
import org.eclipse.jetty.tests.ccd.common.HttpRequest;
import org.eclipse.jetty.tests.ccd.common.Property;
import org.eclipse.jetty.tests.testers.JettyHomeTester;
import org.eclipse.jetty.toolchain.test.MavenPaths;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.TestInfo;
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.hasItem;
import static org.hamcrest.Matchers.is;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class RedispatchPlansTests extends AbstractRedispatchTest
{
private InitializedJettyBase jettyBase;
private JettyHomeTester.Run runStart;
@BeforeEach
public void startJettyBase(TestInfo testInfo) throws Exception
{
jettyBase = new InitializedJettyBase(testInfo);
String[] argsStart = {
"jetty.http.port=" + jettyBase.httpPort
};
runStart = jettyBase.distribution.start(argsStart);
assertTrue(runStart.awaitConsoleLogsFor("Started oejs.Server@", START_TIMEOUT, TimeUnit.SECONDS));
}
@AfterEach
public void stopJettyBase()
{
if (runStart.getProcess().isAlive())
runStart.close();
}
public static Stream<Arguments> dispatchPlans() throws IOException
{
List<Arguments> plans = new ArrayList<>();
List<String> disabledTests = new ArrayList<>();
disabledTests.add("ee10-session-ee8-ee9-ee8.txt"); // causes an ISE
Path testPlansDir = MavenPaths.findTestResourceDir("plans");
try (Stream<Path> plansStream = Files.list(testPlansDir))
{
List<Path> testPlans = plansStream
.filter(Files::isRegularFile)
.filter((file) -> file.getFileName().toString().endsWith(".txt"))
.filter((file) -> !disabledTests.contains(file.getFileName().toString()))
.toList();
for (Path plansText : testPlans)
{
plans.add(Arguments.of(DispatchPlan.read(plansText)));
}
}
return plans.stream();
}
@ParameterizedTest
@MethodSource("dispatchPlans")
public void testRedispatch(DispatchPlan dispatchPlan) throws Exception
{
HttpRequest requestStep = dispatchPlan.getRequestStep();
assertNotNull(requestStep);
ContentResponse response = client.newRequest("localhost", jettyBase.httpPort)
.method(requestStep.getMethod())
.headers((headers) ->
headers.put("X-DispatchPlan", dispatchPlan.id()))
.path(requestStep.getRequestPath())
.send();
String responseDetails = toResponseDetails(response);
assertThat(responseDetails, response.getStatus(), is(HttpStatus.OK_200));
Properties responseProps = new Properties();
try (StringReader stringReader = new StringReader(response.getContentAsString()))
{
responseProps.load(stringReader);
}
dumpProperties(responseProps);
int expectedEventCount = dispatchPlan.getExpectedEvents().size();
assertThat(responseProps.getProperty("dispatchPlan.events.count"), is(Integer.toString(expectedEventCount)));
for (int i = 0; i < expectedEventCount; i++)
{
assertThat("id[" + dispatchPlan.id() + "] event[" + i + "]", responseProps.getProperty("dispatchPlan.event[" + i + "]"), is(dispatchPlan.getExpectedEvents().get(i)));
}
if (dispatchPlan.getExpectedContentType() != null)
{
assertThat("Expected ContentType", response.getHeaders().get(HttpHeader.CONTENT_TYPE), is(dispatchPlan.getExpectedContentType()));
}
for (Property expectedProperty : dispatchPlan.getExpectedProperties())
{
assertProperty(dispatchPlan.id(), responseProps, expectedProperty.getName(), is(expectedProperty.getValue()));
}
// Ensure that all seen session ids are the same.
if (dispatchPlan.isExpectedSessionIds())
{
// Verify that Request Attributes for Session.id are in agreement
List<String> attrNames = responseProps.keySet().stream()
.map(Object::toString)
.filter((name) -> name.startsWith("req.attr[session["))
.toList();
if (attrNames.size() > 1)
{
String expectedId = responseProps.getProperty(attrNames.get(0));
for (String name : attrNames)
{
assertEquals(expectedId, responseProps.getProperty(name));
}
}
// stop the forked running server.
// we need to verify the session behaviors, and can only do that on a stopped server.
runStart.close();
// Verify that Context Attributes for Session.id are in agreement
// And that all ids have had their .commit() and .release() methods called.
Path sessionLog = jettyBase.jettyBase.resolve("work/session.log");
assertTrue(Files.isRegularFile(sessionLog), "Missing " + sessionLog);
List<String> logEntries = Files.readAllLines(sessionLog);
List<String> newSessions = logEntries.stream()
.filter(line -> line.contains("SessionCache.event.newSession()"))
.map(line -> line.substring(line.indexOf("=") + 1))
.toList();
// we should have the commit() and release() for each new Session.
for (String sessionId : newSessions)
{
assertThat(logEntries, hasItem("SessionCache.event.commit()=" + sessionId));
assertThat(logEntries, hasItem("SessionCache.event.release()=" + sessionId));
}
}
}
}

View File

@ -0,0 +1,108 @@
//
// ========================================================================
// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.tests.redispatch;
import java.io.StringReader;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.Properties;
import java.util.concurrent.TimeUnit;
import org.eclipse.jetty.client.ContentResponse;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.tests.testers.JettyHomeTester;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInfo;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class RedispatchTests extends AbstractRedispatchTest
{
private JettyHomeTester.Run runStart;
@AfterEach
public void stopRun()
{
runStart.close();
}
/**
* Test ee8 behavior if an HttpServletRequestWrapper messes with the
* {@code getRequestURI()} method.
* see {@code org.eclipse.jetty.tests.ccd.ee8.InternalRequestURIFilter}
*/
@Test
public void testEe8FilterWithAwkwardRequestURI(TestInfo testInfo) throws Exception
{
InitializedJettyBase jettyBase = new InitializedJettyBase(testInfo);
// Now add the filter to the webapp xml init
String xml = """
<?xml version="1.0"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "https://www.eclipse.org/jetty/configure_10_0.dtd">
<Configure class="org.eclipse.jetty.ee8.webapp.WebAppContext">
<Set name="contextPath">/ccd-ee8</Set>
<Set name="war"><Property name="jetty.webapps" default="." />/ccd-ee8</Set>
<Set name="crossContextDispatchSupported">true</Set>
<Call name="addFilter">
<Arg type="String">org.eclipse.jetty.tests.ccd.ee8.InternalRequestURIFilter</Arg>
<Arg type="String">/*</Arg>
<Arg>
<Call class="java.util.EnumSet" name="of">
<Arg><Get class="javax.servlet.DispatcherType" name="REQUEST"/></Arg>
<Arg><Get class="javax.servlet.DispatcherType" name="FORWARD"/></Arg>
<Arg><Get class="javax.servlet.DispatcherType" name="INCLUDE"/></Arg>
</Call>
</Arg>
</Call>
</Configure>
""";
// Note: the InternalRequestURIFilter messes with the requestURI
Files.writeString(jettyBase.jettyBase.resolve("webapps/ccd-ee8.xml"), xml, StandardCharsets.UTF_8);
// Start Jetty instance
String[] argsStart = {
"jetty.http.port=" + jettyBase.httpPort
};
runStart = jettyBase.distribution.start(argsStart);
assertTrue(runStart.awaitConsoleLogsFor("Started oejs.Server@", START_TIMEOUT, TimeUnit.SECONDS));
ContentResponse response = client.newRequest("localhost", jettyBase.httpPort)
.method(HttpMethod.GET)
.headers((headers) ->
headers.put("X-ForwardTo", "/dump/ee8")
)
.path("/ccd-ee8/forwardto/ee8")
.send();
String responseDetails = toResponseDetails(response);
assertThat(responseDetails, response.getStatus(), is(HttpStatus.OK_200));
Properties responseProps = new Properties();
try (StringReader stringReader = new StringReader(response.getContentAsString()))
{
responseProps.load(stringReader);
}
dumpProperties(responseProps);
assertProperty(responseProps, "request.dispatcherType", is("FORWARD"));
assertProperty(responseProps, "request.requestURI", is("/internal/")); // the key change to look for
}
}

View File

@ -0,0 +1,49 @@
//
// ========================================================================
// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.tests.redispatch;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.function.Supplier;
import org.eclipse.jetty.client.ContentResponse;
class ResponseDetails implements Supplier<String>
{
private final ContentResponse response;
public ResponseDetails(ContentResponse response)
{
this.response = response;
}
@Override
public String get()
{
try (StringWriter str = new StringWriter();
PrintWriter out = new PrintWriter(str))
{
out.println(response.toString());
out.println(response.getHeaders().toString());
out.println(response.getContentAsString());
out.flush();
return str.toString();
}
catch (IOException e)
{
throw new RuntimeException("Unable to produce Response details", e);
}
}
}

View File

@ -0,0 +1,10 @@
<?xml version="1.0"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "https://www.eclipse.org/jetty/configure_10_0.dtd">
<Configure id="Server" class="org.eclipse.jetty.server.Server">
<Set name="errorHandler">
<New class="org.eclipse.jetty.server.handler.ErrorHandler">
<Set name="showStacks">true</Set>
</New>
</Set>
</Configure>

View File

@ -0,0 +1,11 @@
<?xml version="1.0"?><!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "https://eclipse.dev/jetty/configure_10_0.dtd">
<Configure id="Server" class="org.eclipse.jetty.server.Server">
<Call name="insertHandler">
<Arg>
<New class="org.eclipse.jetty.tests.ccd.common.DispatchPlanHandler">
<Set name="plansDir"><Property name="ccd-plans-dir"/></Set>
</New>
</Arg>
</Call>
</Configure>

View File

@ -0,0 +1,12 @@
REQUEST|GET|/ccd-ee10/redispatch/ee10
STEP|CONTEXT_FORWARD|/ccd-ee10|/dump/ee10
EXPECTED_EVENT|Initial plan: context-ee10-forward-dump.txt
EXPECTED_EVENT|DispatchPlanHandler.handle() method=GET path-query=/ccd-ee10/redispatch/ee10
EXPECTED_EVENT|org.eclipse.jetty.tests.ccd.ee10.CCDServlet.service() dispatcherType=REQUEST method=GET requestUri=/ccd-ee10/redispatch/ee10
EXPECTED_EVENT|org.eclipse.jetty.tests.ccd.ee10.DumpServlet.service() dispatcherType=FORWARD method=GET requestUri=/ccd-ee10/dump/ee10
EXPECTED_PROP|request.dispatcherType|FORWARD
EXPECTED_PROP|request.requestURI|/ccd-ee10/dump/ee10
EXPECTED_PROP|req.attr[jakarta.servlet.forward.context_path]|/ccd-ee10
EXPECTED_PROP|req.attr[jakarta.servlet.forward.path_info]|/ee10
EXPECTED_PROP|req.attr[jakarta.servlet.forward.request_uri]|/ccd-ee10/redispatch/ee10
EXPECTED_PROP|req.attr[jakarta.servlet.forward.servlet_path]|/redispatch

View File

@ -0,0 +1,12 @@
REQUEST|GET|/ccd-ee8/redispatch/ee8
STEP|CONTEXT_FORWARD|/ccd-ee8|/dump/ee8
EXPECTED_EVENT|Initial plan: context-ee8-forward-dump.txt
EXPECTED_EVENT|DispatchPlanHandler.handle() method=GET path-query=/ccd-ee8/redispatch/ee8
EXPECTED_EVENT|org.eclipse.jetty.tests.ccd.ee8.CCDServlet.service() dispatcherType=REQUEST method=GET requestUri=/ccd-ee8/redispatch/ee8
EXPECTED_EVENT|org.eclipse.jetty.tests.ccd.ee8.DumpServlet.service() dispatcherType=FORWARD method=GET requestUri=/ccd-ee8/dump/ee8
EXPECTED_PROP|request.dispatcherType|FORWARD
EXPECTED_PROP|request.requestURI|/ccd-ee8/dump/ee8
EXPECTED_PROP|req.attr[javax.servlet.forward.context_path]|/ccd-ee8
EXPECTED_PROP|req.attr[javax.servlet.forward.path_info]|/ee8
EXPECTED_PROP|req.attr[javax.servlet.forward.request_uri]|/ccd-ee8/redispatch/ee8
EXPECTED_PROP|req.attr[javax.servlet.forward.servlet_path]|/redispatch

View File

@ -0,0 +1,14 @@
REQUEST|GET|/ccd-ee8/redispatch/ee8
STEP|CONTEXT_INCLUDE|/ccd-ee8|/dump/ee8
EXPECTED_EVENT|Initial plan: context-ee8-include-dump.txt
EXPECTED_EVENT|DispatchPlanHandler.handle() method=GET path-query=/ccd-ee8/redispatch/ee8
EXPECTED_EVENT|org.eclipse.jetty.tests.ccd.ee8.CCDServlet.service() dispatcherType=REQUEST method=GET requestUri=/ccd-ee8/redispatch/ee8
EXPECTED_EVENT|org.eclipse.jetty.tests.ccd.ee8.DumpServlet.service() dispatcherType=INCLUDE method=GET requestUri=/ccd-ee8/redispatch/ee8
EXPECTED_PROP|request.dispatcherType|INCLUDE
EXPECTED_PROP|request.contextPath|/ccd-ee8
EXPECTED_PROP|request.pathInfo|/ee8
EXPECTED_PROP|request.requestURI|/ccd-ee8/redispatch/ee8
EXPECTED_PROP|req.attr[javax.servlet.include.context_path]|/ccd-ee8
EXPECTED_PROP|req.attr[javax.servlet.include.path_info]|/ee8
EXPECTED_PROP|req.attr[javax.servlet.include.request_uri]|/ccd-ee8/dump/ee8
EXPECTED_PROP|req.attr[javax.servlet.include.servlet_path]|/dump

View File

@ -0,0 +1,30 @@
REQUEST|GET|/ccd-ee10/redispatch/ee10
STEP|SET_HTTP_SESSION_ATTRIBUTE|test-name-10|test-value-ee10
STEP|CONTEXT_FORWARD|/ccd-ee8|/redispatch/ee8
STEP|CONTEXT_FORWARD|/ccd-ee9|/redispatch/ee9
STEP|REQUEST_INCLUDE|/dump/ee9
EXPECTED_EVENT|Initial plan: ee10-forward-to-ee8-include-ee9-dump.txt
EXPECTED_EVENT|DispatchPlanHandler.handle() method=GET path-query=/ccd-ee10/redispatch/ee10
EXPECTED_EVENT|org.eclipse.jetty.tests.ccd.ee10.CCDServlet.service() dispatcherType=REQUEST method=GET requestUri=/ccd-ee10/redispatch/ee10
EXPECTED_EVENT|org.eclipse.jetty.tests.ccd.ee8.CCDServlet.service() dispatcherType=FORWARD method=GET requestUri=/ccd-ee8/redispatch/ee8
EXPECTED_EVENT|org.eclipse.jetty.tests.ccd.ee9.CCDServlet.service() dispatcherType=FORWARD method=GET requestUri=/ccd-ee9/redispatch/ee9
EXPECTED_EVENT|org.eclipse.jetty.tests.ccd.ee9.DumpServlet.service() dispatcherType=INCLUDE method=GET requestUri=/ccd-ee9/redispatch/ee9
EXPECTED_PROP|request.dispatcherType|INCLUDE
EXPECTED_PROP|request.requestURI|/ccd-ee9/redispatch/ee9
EXPECTED_PROP|req.attr[jakarta.servlet.forward.context_path]|/ccd-ee8
EXPECTED_PROP|req.attr[jakarta.servlet.forward.path_info]|/ee8
EXPECTED_PROP|req.attr[jakarta.servlet.forward.request_uri]|/ccd-ee8/redispatch/ee8
EXPECTED_PROP|req.attr[jakarta.servlet.forward.servlet_path]|/redispatch
EXPECTED_PROP|req.attr[jakarta.servlet.include.context_path]/ccd-ee9
EXPECTED_PROP|req.attr[jakarta.servlet.include.path_info]|/ee9
EXPECTED_PROP|req.attr[jakarta.servlet.include.request_uri]|/ccd-ee9/dump/ee9
EXPECTED_PROP|req.attr[jakarta.servlet.include.servlet_path]/dump
EXPECTED_PROP|req.attr[javax.servlet.include.context_path]|<null>
EXPECTED_PROP|req.attr[javax.servlet.include.path_info]|<null>
EXPECTED_PROP|req.attr[javax.servlet.include.request_uri]|<null>
EXPECTED_PROP|req.attr[javax.servlet.include.servlet_path]|<null>
EXPECTED_PROP|req.attr[javax.servlet.forward.context_path]|/ccd-ee8
EXPECTED_PROP|req.attr[javax.servlet.forward.path_info]|/ee8
EXPECTED_PROP|req.attr[javax.servlet.forward.request_uri]|/ccd-ee8/redispatch/ee8
EXPECTED_PROP|req.attr[javax.servlet.forward.servlet_path]|/redispatch
EXPECTED_SESSION_IDS|true

View File

@ -0,0 +1,12 @@
REQUEST|GET|/ccd-ee10/redispatch/ee10
STEP|REQUEST_FORWARD|/dump/ee10
EXPECTED_EVENT|Initial plan: ee10-request-forward-dump.txt
EXPECTED_EVENT|DispatchPlanHandler.handle() method=GET path-query=/ccd-ee10/redispatch/ee10
EXPECTED_EVENT|org.eclipse.jetty.tests.ccd.ee10.CCDServlet.service() dispatcherType=REQUEST method=GET requestUri=/ccd-ee10/redispatch/ee10
EXPECTED_EVENT|org.eclipse.jetty.tests.ccd.ee10.DumpServlet.service() dispatcherType=FORWARD method=GET requestUri=/ccd-ee10/dump/ee10
EXPECTED_PROP|request.dispatcherType|FORWARD
EXPECTED_PROP|request.requestURI|/ccd-ee10/dump/ee10
EXPECTED_PROP|req.attr[jakarta.servlet.forward.context_path]|/ccd-ee10
EXPECTED_PROP|req.attr[jakarta.servlet.forward.path_info]|/ee10
EXPECTED_PROP|req.attr[jakarta.servlet.forward.request_uri]|/ccd-ee10/redispatch/ee10
EXPECTED_PROP|req.attr[jakarta.servlet.forward.servlet_path]|/redispatch

View File

@ -0,0 +1,12 @@
REQUEST|GET|/ccd-ee10/redispatch/ee10
STEP|REQUEST_INCLUDE|/dump/ee10
EXPECTED_EVENT|Initial plan: ee10-request-include-dump.txt
EXPECTED_EVENT|DispatchPlanHandler.handle() method=GET path-query=/ccd-ee10/redispatch/ee10
EXPECTED_EVENT|org.eclipse.jetty.tests.ccd.ee10.CCDServlet.service() dispatcherType=REQUEST method=GET requestUri=/ccd-ee10/redispatch/ee10
EXPECTED_EVENT|org.eclipse.jetty.tests.ccd.ee10.DumpServlet.service() dispatcherType=INCLUDE method=GET requestUri=/ccd-ee10/redispatch/ee10
EXPECTED_PROP|request.dispatcherType|INCLUDE
EXPECTED_PROP|request.requestURI|/ccd-ee10/redispatch/ee10
EXPECTED_PROP|req.attr[jakarta.servlet.include.context_path]|/ccd-ee10
EXPECTED_PROP|req.attr[jakarta.servlet.include.path_info]|/ee10
EXPECTED_PROP|req.attr[jakarta.servlet.include.request_uri]|/ccd-ee10/dump/ee10
EXPECTED_PROP|req.attr[jakarta.servlet.include.servlet_path]|/dump

View File

@ -0,0 +1,36 @@
REQUEST|GET|/ccd-ee10/redispatch/ee10
# we reach ee10
STEP|SET_HTTP_SESSION_ATTRIBUTE|test-name-10|test-value-ee10
STEP|CONTEXT_FORWARD|/ccd-ee8|/redispatch/ee8
# we reach ee8
STEP|GET_HTTP_SESSION_ATTRIBUTE|test-name-10
STEP|SET_HTTP_SESSION_ATTRIBUTE|test-name-8|test-value-ee8
STEP|GET_HTTP_SESSION_ATTRIBUTE|test-name-10
STEP|CONTEXT_FORWARD|/ccd-ee9|/redispatch/ee9
# we reach ee9
STEP|CONTEXT_FORWARD|/ccd-ee8|/redispatch/ee8
# we reach ee8 again (does the HttpSession still exist, and has values?)
STEP|GET_HTTP_SESSION_ATTRIBUTE|test-name-8
STEP|REQUEST_FORWARD|/dump/ee8
EXPECTED_EVENT|Initial plan: ee10-session-ee8-ee9-ee8.txt
EXPECTED_EVENT|DispatchPlanHandler.handle() method=GET path-query=/ccd-ee10/redispatch/ee10
# we reach ee10
EXPECTED_EVENT|org.eclipse.jetty.tests.ccd.ee10.CCDServlet.service() dispatcherType=REQUEST method=GET requestUri=/ccd-ee10/redispatch/ee10
# we reach ee8
EXPECTED_EVENT|org.eclipse.jetty.tests.ccd.ee8.CCDServlet.service() dispatcherType=FORWARD method=GET requestUri=/ccd-ee8/redispatch/ee8
EXPECTED_EVENT|org.eclipse.jetty.tests.ccd.ee8.CCDServlet.service() HttpSession is null
EXPECTED_EVENT|org.eclipse.jetty.tests.ccd.ee8.CCDServlet.service() HttpSession exists: [test-name-10]=[null]
# we reach ee9
EXPECTED_EVENT|org.eclipse.jetty.tests.ccd.ee9.DumpServlet.service() dispatcherType=INCLUDE method=GET requestUri=/ccd-ee9/redispatch/ee9
# we reached ee8 again
EXPECTED_EVENT|org.eclipse.jetty.tests.ccd.ee8.CCDServlet.service() HttpSession exists: [test-name-8]=[test-value-ee8]
EXPECTED_EVENT|org.eclipse.jetty.tests.ccd.ee8.DumpServlet.service() dispatcherType=FORWARD method=GET requestUri=/ccd-ee8/dump/ee8
EXPECTED_PROP|request.dispatcherType|FORWARD
EXPECTED_PROP|request.requestURI|/ccd-ee8/dump/ee8
EXPECTED_PROP|request.session.exists|true
EXPECTED_PROP|session[test-name-8]|test-value-ee8
EXPECTED_PROP|req.attr[javax.servlet.forward.context_path]|/ccd-ee8
EXPECTED_PROP|req.attr[javax.servlet.forward.path_info]|/ee8
EXPECTED_PROP|req.attr[javax.servlet.forward.request_uri]|/ccd-ee8/dump/ee8
EXPECTED_PROP|req.attr[javax.servlet.forward.servlet_path]|/dump
EXPECTED_SESSION_IDS|true

View File

@ -0,0 +1,50 @@
REQUEST|GET|/ccd-ee10/redispatch/ee10
# we reach ee10
STEP|SET_HTTP_SESSION_ATTRIBUTE|test-name-10|test-value-ee10
STEP|CONTEXT_FORWARD|/ccd-ee8|/redispatch/ee8
# we reach ee8
STEP|GET_HTTP_SESSION_ATTRIBUTE|test-name-10
STEP|SET_HTTP_SESSION_ATTRIBUTE|test-name-8|test-value-ee8
STEP|GET_HTTP_SESSION_ATTRIBUTE|test-name-10
STEP|CONTEXT_FORWARD|/ccd-ee9|/redispatch/ee9
# we reach ee9
STEP|GET_HTTP_SESSION_ATTRIBUTE|test-name-10
STEP|SET_HTTP_SESSION_ATTRIBUTE|test-name|test-value-ee9
STEP|GET_HTTP_SESSION_ATTRIBUTE|test-name-10
STEP|GET_HTTP_SESSION_ATTRIBUTE|test-name-8
STEP|REQUEST_INCLUDE|/dump/ee9
EXPECTED_EVENT|Initial plan: ee10-session-forward-to-ee8-session-include-ee9-dump.txt
EXPECTED_EVENT|DispatchPlanHandler.handle() method=GET path-query=/ccd-ee10/redispatch/ee10
# we reach ee10
EXPECTED_EVENT|org.eclipse.jetty.tests.ccd.ee10.CCDServlet.service() dispatcherType=REQUEST method=GET requestUri=/ccd-ee10/redispatch/ee10
# we reach ee8
EXPECTED_EVENT|org.eclipse.jetty.tests.ccd.ee8.CCDServlet.service() dispatcherType=FORWARD method=GET requestUri=/ccd-ee8/redispatch/ee8
EXPECTED_EVENT|org.eclipse.jetty.tests.ccd.ee8.CCDServlet.service() HttpSession is null
EXPECTED_EVENT|org.eclipse.jetty.tests.ccd.ee8.CCDServlet.service() HttpSession exists: [test-name-10]=[null]
# we reach ee9
EXPECTED_EVENT|org.eclipse.jetty.tests.ccd.ee9.CCDServlet.service() dispatcherType=FORWARD method=GET requestUri=/ccd-ee9/redispatch/ee9
EXPECTED_EVENT|org.eclipse.jetty.tests.ccd.ee9.CCDServlet.service() HttpSession is null
EXPECTED_EVENT|org.eclipse.jetty.tests.ccd.ee9.CCDServlet.service() HttpSession exists: [test-name-10]=[null]
EXPECTED_EVENT|org.eclipse.jetty.tests.ccd.ee9.CCDServlet.service() HttpSession exists: [test-name-8]=[null]
EXPECTED_EVENT|org.eclipse.jetty.tests.ccd.ee9.DumpServlet.service() dispatcherType=INCLUDE method=GET requestUri=/ccd-ee9/redispatch/ee9
EXPECTED_PROP|request.dispatcherType|INCLUDE
EXPECTED_PROP|request.requestURI|/ccd-ee9/redispatch/ee9
EXPECTED_PROP|request.session.exists|true
EXPECTED_PROP|session[test-name]|test-value-ee9
EXPECTED_PROP|req.attr[jakarta.servlet.forward.context_path]|/ccd-ee8
EXPECTED_PROP|req.attr[jakarta.servlet.forward.path_info]|/ee8
EXPECTED_PROP|req.attr[jakarta.servlet.forward.request_uri]|/ccd-ee8/redispatch/ee8
EXPECTED_PROP|req.attr[jakarta.servlet.forward.servlet_path]|/redispatch
EXPECTED_PROP|req.attr[jakarta.servlet.include.context_path]/ccd-ee9
EXPECTED_PROP|req.attr[jakarta.servlet.include.path_info]|/ee9
EXPECTED_PROP|req.attr[jakarta.servlet.include.request_uri]|/ccd-ee9/dump/ee9
EXPECTED_PROP|req.attr[jakarta.servlet.include.servlet_path]/dump
EXPECTED_PROP|req.attr[javax.servlet.include.context_path]|<null>
EXPECTED_PROP|req.attr[javax.servlet.include.path_info]|<null>
EXPECTED_PROP|req.attr[javax.servlet.include.request_uri]|<null>
EXPECTED_PROP|req.attr[javax.servlet.include.servlet_path]|<null>
EXPECTED_PROP|req.attr[javax.servlet.forward.context_path]|/ccd-ee8
EXPECTED_PROP|req.attr[javax.servlet.forward.path_info]|/ee8
EXPECTED_PROP|req.attr[javax.servlet.forward.request_uri]|/ccd-ee8/redispatch/ee8
EXPECTED_PROP|req.attr[javax.servlet.forward.servlet_path]|/redispatch
EXPECTED_SESSION_IDS|true

View File

@ -0,0 +1,12 @@
REQUEST|GET|/ccd-ee8/redispatch/ee8
STEP|REQUEST_FORWARD|/dump/ee8
EXPECTED_EVENT|Initial plan: ee8-request-forward-dump.txt
EXPECTED_EVENT|DispatchPlanHandler.handle() method=GET path-query=/ccd-ee8/redispatch/ee8
EXPECTED_EVENT|org.eclipse.jetty.tests.ccd.ee8.CCDServlet.service() dispatcherType=REQUEST method=GET requestUri=/ccd-ee8/redispatch/ee8
EXPECTED_EVENT|org.eclipse.jetty.tests.ccd.ee8.DumpServlet.service() dispatcherType=FORWARD method=GET requestUri=/ccd-ee8/dump/ee8
EXPECTED_PROP|request.dispatcherType|FORWARD
EXPECTED_PROP|request.requestURI|/ccd-ee8/dump/ee8
EXPECTED_PROP|req.attr[javax.servlet.forward.context_path]|/ccd-ee8
EXPECTED_PROP|req.attr[javax.servlet.forward.path_info]|/ee8
EXPECTED_PROP|req.attr[javax.servlet.forward.request_uri]|/ccd-ee8/redispatch/ee8
EXPECTED_PROP|req.attr[javax.servlet.forward.servlet_path]|/redispatch

View File

@ -0,0 +1,12 @@
REQUEST|GET|/ccd-ee8/redispatch/ee8
STEP|REQUEST_INCLUDE|/dump/ee8
EXPECTED_EVENT|Initial plan: ee8-request-include-dump.txt
EXPECTED_EVENT|DispatchPlanHandler.handle() method=GET path-query=/ccd-ee8/redispatch/ee8
EXPECTED_EVENT|org.eclipse.jetty.tests.ccd.ee8.CCDServlet.service() dispatcherType=REQUEST method=GET requestUri=/ccd-ee8/redispatch/ee8
EXPECTED_EVENT|org.eclipse.jetty.tests.ccd.ee8.DumpServlet.service() dispatcherType=INCLUDE method=GET requestUri=/ccd-ee8/redispatch/ee8
EXPECTED_PROP|request.dispatcherType|INCLUDE
EXPECTED_PROP|request.requestURI|/ccd-ee8/redispatch/ee8
EXPECTED_PROP|req.attr[javax.servlet.include.context_path]|/ccd-ee8
EXPECTED_PROP|req.attr[javax.servlet.include.path_info]|/ee8
EXPECTED_PROP|req.attr[javax.servlet.include.request_uri]|/ccd-ee8/dump/ee8
EXPECTED_PROP|req.attr[javax.servlet.include.servlet_path]|/dump

View File

@ -0,0 +1,19 @@
<?xml version="1.0"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "https://www.eclipse.org/jetty/configure_10_0.dtd">
<Configure id="wac" class="org.eclipse.jetty.ee10.webapp.WebAppContext">
<Set name="contextPath">/ccd-ee10</Set>
<Set name="war"><Property name="jetty.webapps" default="." />/ccd-ee10</Set>
<Set name="crossContextDispatchSupported">true</Set>
<Get id="sessMan" name="sessionHandler">
<Set name="sessionCache">
<New class="org.eclipse.jetty.tests.ccd.common.PlanSessionCache">
<Arg>
<Ref refid="sessMan"/>
</Arg>
</New>
</Set>
</Get>
</Configure>

View File

@ -0,0 +1,21 @@
<?xml version="1.0"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "https://www.eclipse.org/jetty/configure_10_0.dtd">
<Configure id="wac" class="org.eclipse.jetty.ee8.webapp.WebAppContext">
<Set name="contextPath">/ccd-ee8</Set>
<Set name="war"><Property name="jetty.webapps" default="." />/ccd-ee8</Set>
<Set name="crossContextDispatchSupported">true</Set>
<Get name="sessionHandler">
<Get id="sessMan" name="sessionManager">
<Set name="sessionCache">
<New class="org.eclipse.jetty.tests.ccd.common.PlanSessionCache">
<Arg>
<Ref refid="sessMan"/>
</Arg>
</New>
</Set>
</Get>
</Get>
</Configure>

View File

@ -0,0 +1,21 @@
<?xml version="1.0"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "https://www.eclipse.org/jetty/configure_10_0.dtd">
<Configure id="wac" class="org.eclipse.jetty.ee9.webapp.WebAppContext">
<Set name="contextPath">/ccd-ee9</Set>
<Set name="war"><Property name="jetty.webapps" default="." />/ccd-ee9</Set>
<Set name="crossContextDispatchSupported">true</Set>
<Get name="sessionHandler">
<Get id="sessMan" name="sessionManager">
<Set name="sessionCache">
<New class="org.eclipse.jetty.tests.ccd.common.PlanSessionCache">
<Arg>
<Ref refid="sessMan"/>
</Arg>
</New>
</Set>
</Get>
</Get>
</Configure>

View File

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.eclipse.jetty.tests</groupId>
<artifactId>tests</artifactId>
<version>12.0.11-SNAPSHOT</version>
</parent>
<groupId>org.eclipse.jetty.tests.ccd</groupId>
<artifactId>test-cross-context-dispatch</artifactId>
<packaging>pom</packaging>
<name>Tests :: Cross Context Dispatch :: Parent</name>
<modules>
<module>ccd-common</module>
<module>ccd-ee10-webapp</module>
<module>ccd-ee9-webapp</module>
<module>ccd-ee8-webapp</module>
<module>ccd-tests</module>
</modules>
<properties>
<distribution.debug.port>-1</distribution.debug.port>
</properties>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<systemPropertyVariables>
<mavenRepoPath>${session.repositoryCrossContext.localRepository.basedir.absolutePath}</mavenRepoPath>
<jettyVersion>${project.version}</jettyVersion>
<distribution.debug.port>$(distribution.debug.port}</distribution.debug.port>
<home.start.timeout>${home.start.timeout}</home.start.timeout>
</systemPropertyVariables>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>