Skip to content

Commit 6ca9bf7

Browse files
authored
Fix error handling from jsp with post form (#13985)
Issue #13613 - introduce testcase to demonstrate new bug introduced by Issue #13613 - fix error refreshing in EE9 content producer - add the current exception as suppressed to the main one when we already are error handling - duplicate ee9 test to ee10 and ee11 Signed-off-by: Ludovic Orban <[email protected]>
1 parent 46219fb commit 6ca9bf7

File tree

8 files changed

+628
-1
lines changed

8 files changed

+628
-1
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
//
2+
// ========================================================================
3+
// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
4+
//
5+
// This program and the accompanying materials are made available under the
6+
// terms of the Eclipse Public License v. 2.0 which is available at
7+
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
8+
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
9+
//
10+
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
11+
// ========================================================================
12+
//
13+
14+
package org.eclipse.jetty.ee10.test;
15+
16+
import java.io.IOException;
17+
import java.net.URI;
18+
import java.nio.charset.StandardCharsets;
19+
import java.nio.file.Files;
20+
import java.nio.file.Path;
21+
22+
import org.eclipse.jetty.client.ContentResponse;
23+
import org.eclipse.jetty.client.FormRequestContent;
24+
import org.eclipse.jetty.client.HttpClient;
25+
import org.eclipse.jetty.ee10.servlet.ServletChannel;
26+
import org.eclipse.jetty.ee10.test.servlets.AlwaysUnsupportedServlet;
27+
import org.eclipse.jetty.ee10.webapp.WebAppContext;
28+
import org.eclipse.jetty.http.HttpMethod;
29+
import org.eclipse.jetty.http.HttpStatus;
30+
import org.eclipse.jetty.logging.StacklessLogging;
31+
import org.eclipse.jetty.server.Server;
32+
import org.eclipse.jetty.server.ServerConnector;
33+
import org.eclipse.jetty.server.handler.ContextHandlerCollection;
34+
import org.eclipse.jetty.toolchain.test.FS;
35+
import org.eclipse.jetty.toolchain.test.jupiter.WorkDir;
36+
import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension;
37+
import org.eclipse.jetty.util.Fields;
38+
import org.eclipse.jetty.util.TypeUtil;
39+
import org.eclipse.jetty.util.component.LifeCycle;
40+
import org.eclipse.jetty.util.resource.Resource;
41+
import org.hamcrest.Matchers;
42+
import org.junit.jupiter.api.AfterEach;
43+
import org.junit.jupiter.api.BeforeEach;
44+
import org.junit.jupiter.api.Test;
45+
import org.junit.jupiter.api.extension.ExtendWith;
46+
47+
import static org.hamcrest.MatcherAssert.assertThat;
48+
import static org.hamcrest.Matchers.containsString;
49+
import static org.junit.jupiter.api.Assertions.assertNotNull;
50+
51+
@ExtendWith(WorkDirExtension.class)
52+
public class ErrorHandlingViaJspTest
53+
{
54+
public WorkDir workDir;
55+
private Server server;
56+
private HttpClient client;
57+
58+
public void startServer(WebAppContext context) throws Exception
59+
{
60+
server = new Server();
61+
ServerConnector connector = new ServerConnector(server);
62+
connector.setPort(0);
63+
server.addConnector(connector);
64+
65+
ContextHandlerCollection contexts = new ContextHandlerCollection();
66+
server.setHandler(contexts);
67+
contexts.addHandler(context);
68+
69+
server.start();
70+
}
71+
72+
@BeforeEach
73+
public void startClient() throws Exception
74+
{
75+
client = new HttpClient();
76+
client.start();
77+
}
78+
79+
@AfterEach
80+
public void stopAll()
81+
{
82+
LifeCycle.stop(server);
83+
LifeCycle.stop(client);
84+
}
85+
86+
@Test
87+
public void testErrorHandlingViaJsp() throws Exception
88+
{
89+
Path webappDir = workDir.getEmptyPathDir().resolve("webapp");
90+
FS.ensureDirExists(webappDir);
91+
92+
Path webinfDir = Files.createDirectory(webappDir.resolve("WEB-INF"));
93+
Path classesDir = Files.createDirectory(webinfDir.resolve("classes"));
94+
95+
copyClass(AlwaysUnsupportedServlet.class, classesDir);
96+
97+
Path webxml = webinfDir.resolve("web.xml");
98+
99+
Files.writeString(webxml, """
100+
<web-app
101+
xmlns="https://jakarta.ee/xml/ns/jakartaee"
102+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
103+
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd"
104+
metadata-complete="false"
105+
version="5.0">
106+
<display-name>Sample WebApp</display-name>
107+
108+
<servlet>
109+
<servlet-name>always-throw-an-exception</servlet-name>
110+
<servlet-class>%s</servlet-class>
111+
</servlet>
112+
113+
<servlet-mapping>
114+
<servlet-name>always-throw-an-exception</servlet-name>
115+
<url-pattern>/toss/*</url-pattern>
116+
</servlet-mapping>
117+
118+
<error-page>
119+
<exception-type>java.lang.UnsupportedOperationException</exception-type>
120+
<location>/error.jsp</location>
121+
</error-page>
122+
</web-app>
123+
""".formatted(AlwaysUnsupportedServlet.class.getName()), StandardCharsets.UTF_8);
124+
125+
Files.writeString(webappDir.resolve("error.jsp"), """
126+
<%@ page language="java" contentType="text/html; charset=utf-8"
127+
pageEncoding="utf-8" %>
128+
(From JSP Error Handler)
129+
Request.Params : <%= request.getParameterMap() %>
130+
ERROR_EXCEPTION : <%= request.getAttribute("jakarta.servlet.error.exception") %>
131+
ERROR_EXCEPTION_TYPE : <%= request.getAttribute("jakarta.servlet.error.exception_type") %>
132+
ERROR_MESSAGE : <%= request.getAttribute("jakarta.servlet.error.message") %>
133+
ERROR_REQUEST_URI : <%= request.getAttribute("jakarta.servlet.error.request_uri") %>
134+
ERROR_SERVLET_NAME : <%= request.getAttribute("jakarta.servlet.error.servlet_name") %>
135+
ERROR_STATUS_CODE : <%= request.getAttribute("jakarta.servlet.error.status_code") %>
136+
<% response.setStatus(418); %>
137+
""", StandardCharsets.UTF_8);
138+
139+
WebAppContext webAppContext = new WebAppContext();
140+
webAppContext.setContextPath("/");
141+
Resource warResource = webAppContext.getResourceFactory().newResource(webappDir);
142+
webAppContext.setWarResource(warResource);
143+
startServer(webAppContext);
144+
145+
URI serverURI = server.getURI();
146+
147+
Fields form = new Fields();
148+
149+
for (int i = 0; i < 200; i++)
150+
{
151+
form.add("n" + i, "helo" + i);
152+
}
153+
154+
try (StacklessLogging ignore = new StacklessLogging(ServletChannel.class))
155+
{
156+
ContentResponse response = client.newRequest(serverURI.resolve("/toss/form"))
157+
.method(HttpMethod.POST)
158+
.body(new FormRequestContent(form))
159+
.send();
160+
161+
// Ensure that handling of UnsupportedOperationException is done by /error.jsp and not the default ErrorHandler.
162+
assertThat(response.getContentAsString(), containsString("(From JSP Error Handler)"));
163+
assertThat(response.getStatus(), Matchers.is(HttpStatus.IM_A_TEAPOT_418));
164+
}
165+
}
166+
167+
private void copyClass(Class<?> clazz, Path classesDir) throws IOException
168+
{
169+
URI srcUri = TypeUtil.getLocationOfClass(clazz);
170+
assertNotNull(srcUri, "Unable to find class: " + clazz.getName());
171+
if ("file".equals(srcUri.getScheme()))
172+
{
173+
String classRef = TypeUtil.toClassReference(clazz);
174+
Path srcFile = Path.of(srcUri).resolve(classRef);
175+
Path destFile = classesDir.resolve(classRef);
176+
FS.ensureDirExists(destFile.getParent());
177+
Files.copy(srcFile, destFile);
178+
}
179+
}
180+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
//
2+
// ========================================================================
3+
// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
4+
//
5+
// This program and the accompanying materials are made available under the
6+
// terms of the Eclipse Public License v. 2.0 which is available at
7+
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
8+
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
9+
//
10+
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
11+
// ========================================================================
12+
//
13+
14+
package org.eclipse.jetty.ee10.test.servlets;
15+
16+
import jakarta.servlet.http.HttpServlet;
17+
import jakarta.servlet.http.HttpServletRequest;
18+
import jakarta.servlet.http.HttpServletResponse;
19+
20+
public class AlwaysUnsupportedServlet extends HttpServlet
21+
{
22+
@Override
23+
protected void service(HttpServletRequest req, HttpServletResponse resp)
24+
{
25+
throw new UnsupportedOperationException("Intentionally Unsupported by testcase");
26+
}
27+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
//
2+
// ========================================================================
3+
// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
4+
//
5+
// This program and the accompanying materials are made available under the
6+
// terms of the Eclipse Public License v. 2.0 which is available at
7+
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
8+
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
9+
//
10+
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
11+
// ========================================================================
12+
//
13+
14+
package org.eclipse.jetty.ee11.test;
15+
16+
import java.io.IOException;
17+
import java.net.URI;
18+
import java.nio.charset.StandardCharsets;
19+
import java.nio.file.Files;
20+
import java.nio.file.Path;
21+
22+
import org.eclipse.jetty.client.ContentResponse;
23+
import org.eclipse.jetty.client.FormRequestContent;
24+
import org.eclipse.jetty.client.HttpClient;
25+
import org.eclipse.jetty.ee11.servlet.ServletChannel;
26+
import org.eclipse.jetty.ee11.test.servlets.AlwaysUnsupportedServlet;
27+
import org.eclipse.jetty.ee11.webapp.WebAppContext;
28+
import org.eclipse.jetty.http.HttpMethod;
29+
import org.eclipse.jetty.http.HttpStatus;
30+
import org.eclipse.jetty.logging.StacklessLogging;
31+
import org.eclipse.jetty.server.Server;
32+
import org.eclipse.jetty.server.ServerConnector;
33+
import org.eclipse.jetty.server.handler.ContextHandlerCollection;
34+
import org.eclipse.jetty.toolchain.test.FS;
35+
import org.eclipse.jetty.toolchain.test.jupiter.WorkDir;
36+
import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension;
37+
import org.eclipse.jetty.util.Fields;
38+
import org.eclipse.jetty.util.TypeUtil;
39+
import org.eclipse.jetty.util.component.LifeCycle;
40+
import org.eclipse.jetty.util.resource.Resource;
41+
import org.hamcrest.Matchers;
42+
import org.junit.jupiter.api.AfterEach;
43+
import org.junit.jupiter.api.BeforeEach;
44+
import org.junit.jupiter.api.Test;
45+
import org.junit.jupiter.api.extension.ExtendWith;
46+
47+
import static org.hamcrest.MatcherAssert.assertThat;
48+
import static org.hamcrest.Matchers.containsString;
49+
import static org.junit.jupiter.api.Assertions.assertNotNull;
50+
51+
@ExtendWith(WorkDirExtension.class)
52+
public class ErrorHandlingViaJspTest
53+
{
54+
public WorkDir workDir;
55+
private Server server;
56+
private HttpClient client;
57+
58+
public void startServer(WebAppContext context) throws Exception
59+
{
60+
server = new Server();
61+
ServerConnector connector = new ServerConnector(server);
62+
connector.setPort(0);
63+
server.addConnector(connector);
64+
65+
ContextHandlerCollection contexts = new ContextHandlerCollection();
66+
server.setHandler(contexts);
67+
contexts.addHandler(context);
68+
69+
server.start();
70+
}
71+
72+
@BeforeEach
73+
public void startClient() throws Exception
74+
{
75+
client = new HttpClient();
76+
client.start();
77+
}
78+
79+
@AfterEach
80+
public void stopAll()
81+
{
82+
LifeCycle.stop(server);
83+
LifeCycle.stop(client);
84+
}
85+
86+
@Test
87+
public void testErrorHandlingViaJsp() throws Exception
88+
{
89+
Path webappDir = workDir.getEmptyPathDir().resolve("webapp");
90+
FS.ensureDirExists(webappDir);
91+
92+
Path webinfDir = Files.createDirectory(webappDir.resolve("WEB-INF"));
93+
Path classesDir = Files.createDirectory(webinfDir.resolve("classes"));
94+
95+
copyClass(AlwaysUnsupportedServlet.class, classesDir);
96+
97+
Path webxml = webinfDir.resolve("web.xml");
98+
99+
Files.writeString(webxml, """
100+
<web-app
101+
xmlns="https://jakarta.ee/xml/ns/jakartaee"
102+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
103+
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd"
104+
metadata-complete="false"
105+
version="5.0">
106+
<display-name>Sample WebApp</display-name>
107+
108+
<servlet>
109+
<servlet-name>always-throw-an-exception</servlet-name>
110+
<servlet-class>%s</servlet-class>
111+
</servlet>
112+
113+
<servlet-mapping>
114+
<servlet-name>always-throw-an-exception</servlet-name>
115+
<url-pattern>/toss/*</url-pattern>
116+
</servlet-mapping>
117+
118+
<error-page>
119+
<exception-type>java.lang.UnsupportedOperationException</exception-type>
120+
<location>/error.jsp</location>
121+
</error-page>
122+
</web-app>
123+
""".formatted(AlwaysUnsupportedServlet.class.getName()), StandardCharsets.UTF_8);
124+
125+
Files.writeString(webappDir.resolve("error.jsp"), """
126+
<%@ page language="java" contentType="text/html; charset=utf-8"
127+
pageEncoding="utf-8" %>
128+
(From JSP Error Handler)
129+
Request.Params : <%= request.getParameterMap() %>
130+
ERROR_EXCEPTION : <%= request.getAttribute("jakarta.servlet.error.exception") %>
131+
ERROR_EXCEPTION_TYPE : <%= request.getAttribute("jakarta.servlet.error.exception_type") %>
132+
ERROR_MESSAGE : <%= request.getAttribute("jakarta.servlet.error.message") %>
133+
ERROR_REQUEST_URI : <%= request.getAttribute("jakarta.servlet.error.request_uri") %>
134+
ERROR_SERVLET_NAME : <%= request.getAttribute("jakarta.servlet.error.servlet_name") %>
135+
ERROR_STATUS_CODE : <%= request.getAttribute("jakarta.servlet.error.status_code") %>
136+
<% response.setStatus(418); %>
137+
""", StandardCharsets.UTF_8);
138+
139+
WebAppContext webAppContext = new WebAppContext();
140+
webAppContext.setContextPath("/");
141+
Resource warResource = webAppContext.getResourceFactory().newResource(webappDir);
142+
webAppContext.setWarResource(warResource);
143+
startServer(webAppContext);
144+
145+
URI serverURI = server.getURI();
146+
147+
Fields form = new Fields();
148+
149+
for (int i = 0; i < 200; i++)
150+
{
151+
form.add("n" + i, "helo" + i);
152+
}
153+
154+
try (StacklessLogging ignore = new StacklessLogging(ServletChannel.class))
155+
{
156+
ContentResponse response = client.newRequest(serverURI.resolve("/toss/form"))
157+
.method(HttpMethod.POST)
158+
.body(new FormRequestContent(form))
159+
.send();
160+
161+
// Ensure that handling of UnsupportedOperationException is done by /error.jsp and not the default ErrorHandler.
162+
assertThat(response.getContentAsString(), containsString("(From JSP Error Handler)"));
163+
assertThat(response.getStatus(), Matchers.is(HttpStatus.IM_A_TEAPOT_418));
164+
}
165+
}
166+
167+
private void copyClass(Class<?> clazz, Path classesDir) throws IOException
168+
{
169+
URI srcUri = TypeUtil.getLocationOfClass(clazz);
170+
assertNotNull(srcUri, "Unable to find class: " + clazz.getName());
171+
if ("file".equals(srcUri.getScheme()))
172+
{
173+
String classRef = TypeUtil.toClassReference(clazz);
174+
Path srcFile = Path.of(srcUri).resolve(classRef);
175+
Path destFile = classesDir.resolve(classRef);
176+
FS.ensureDirExists(destFile.getParent());
177+
Files.copy(srcFile, destFile);
178+
}
179+
}
180+
}

0 commit comments

Comments
 (0)