Skip to content

Commit 3eb2d5e

Browse files
committed
Merge remote-tracking branch 'origin/jetty-12.0.x' into jetty-12.1.x
2 parents 4cef69c + 664d267 commit 3eb2d5e

File tree

10 files changed

+179
-41
lines changed

10 files changed

+179
-41
lines changed

.github/workflows/stale-action.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ jobs:
1313
pull-requests: write # for actions/stale to close stale PRs
1414
runs-on: ubuntu-latest
1515
steps:
16-
- uses: actions/stale@v4
16+
- uses: actions/stale@v9
1717
with:
1818
repo-token: ${{ secrets.GITHUB_TOKEN }}
1919
days-before-stale: 365

documentation/jetty/modules/programming-guide/pages/server/compliance.adoc

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,17 @@ If you want to customize the violations that you want to allow, you can create y
9898
include::code:example$src/main/java/org/eclipse/jetty/docs/programming/server/ServerDocs.java[tags=uriComplianceCustom]
9999
----
100100

101+
[[servleturi]]
102+
=== Servlet URI Compliance Modes
103+
104+
Even if the server has been configured (as above) to allow ambiguous URIs to be received, individual Servlet contexts may not allow such ambiguous URIs to be returned via some specific methods.
105+
106+
Specifically the `HttpServletRequest` methods: link:https://jakarta.ee/specifications/servlet/6.0/apidocs/jakarta.servlet/jakarta/servlet/http/httpservletrequest#getServletPath()[getServletPath()] and link:https://jakarta.ee/specifications/servlet/6.0/apidocs/jakarta.servlet/jakarta/servlet/http/httpservletrequest#getPathInfo()[getPathInfo()], may throw `IllegalArgumentException` for such URIs.
107+
108+
The intention is for safer methods, such as link:https://jakarta.ee/specifications/servlet/6.0/apidocs/jakarta.servlet/jakarta/servlet/http/httpservletrequest#getRequestURI()[getRequestURI] to be used instead.
109+
110+
If necessary, the `ServletHandler` can be configured to allow ambiguous URIs from all methods with link:{javadoc-url}/org/eclipse/jetty/ee10/servlet/ServletHandler.html#setDecodeAmbiguousURIs(boolean)[setDecodeAmbiguousURIs(boolean)].
111+
101112
[[cookie]]
102113
== Cookie Compliance Modes
103114

jetty-core/jetty-start/src/main/resources/org/eclipse/jetty/start/usage.txt

Lines changed: 43 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,10 @@ Configure Commands:
135135
Options:
136136
--------
137137

138+
--env=<environmentName>
139+
Sets the environment which will apply to all subsequent libraries,
140+
properties and XML files.
141+
138142
--modules=<moduleName>(,<moduleName>)*
139143
Enables a module for this execution.
140144
To enable a module for all future executions, use the
@@ -145,7 +149,8 @@ Options:
145149

146150
--libs=<classpath>
147151
Adds the specified class-path entries to the the server
148-
class-path (or module-path).
152+
class-path; or module-path; or environment classpath if
153+
there has been a prior --env argument.
149154

150155
--files=<uri>|<location>
151156
--download=<uri>|<location>
@@ -168,7 +173,6 @@ Options:
168173
and any `--include-jetty-dir` and finally
169174
checking the `${jetty.home}`)
170175

171-
172176
--exec
173177
Executes the generated command line in a forked JVM
174178
(see the --dry-run command).
@@ -234,6 +238,42 @@ Options:
234238
configuration to all specific ${jetty.base} directories
235239
without having to modify ${jetty.home}.
236240

241+
-D<name>=<value>
242+
Specifies a system property, as well as a start property.
243+
Note: this is a program argument that is interpreted and
244+
added to the existing JVM system properties.
245+
246+
<file>.xml
247+
Specifies a Jetty XML file relative to ${jetty.base}.
248+
This file is in addition to the Jetty XML files resolved
249+
from the [xml] sections of the enabled modules.
250+
If there has been a prior --env argument, the XML will be
251+
executed within that environment
252+
253+
<name>=<value>
254+
Specifies a property value that overrides the same
255+
property defined in a ${jetty.base}/start.d/*.ini file,
256+
or in the [ini] section of a *.mod file.
257+
258+
<name>=<value>
259+
Sets the property value unconditionally.
260+
<name>+=<value>
261+
Appends the given value to the existing value.
262+
<name>?=<value>
263+
Sets the property value only if it is not already set.
264+
265+
If there has been a prior --env argument, then the property is
266+
set only for the current environment, otherwise it is set for
267+
the whole server.
268+
269+
<file>.properties
270+
Specifies a file of property assignments that overrides the same
271+
property defined in a ${jetty.base}/start.d/*.ini file,
272+
or in the [ini] section of a *.mod file.
273+
If there has been a prior --env argument, then the property is
274+
set only for the current environment, otherwise it is set for
275+
the whole server.
276+
237277
jetty.home=<directory>
238278
Sets the ${jetty.home} directory.
239279
By default it is resolved from the start.jar file path.
@@ -272,26 +312,4 @@ Options:
272312

273313
maven.repo.uri=<url>
274314
The base URL to use to download Maven dependencies.
275-
Defaults to: https://repo1.maven.org/maven2/.
276-
277-
<name>=<value>
278-
Specifies a property value that overrides the same
279-
property defined in a ${jetty.base}/start.d/*.ini file,
280-
or in the [ini] section of a *.mod file.
281-
282-
<name>=<value>
283-
Sets the property value unconditionally.
284-
<name>+=<value>
285-
Appends the given value to the existing value.
286-
<name>?=<value>
287-
Sets the property value only if it is not already set.
288-
289-
-D<name>=<value>
290-
Specifies a system property, as well as a start property.
291-
Note: this is a program argument that is interpreted and
292-
added to the existing JVM system properties.
293-
294-
<xml-file>
295-
Specifies a Jetty XML file relative to ${jetty.base}.
296-
This file is in addition to the Jetty XML files resolved
297-
from the [xml] sections of the enabled modules.
315+
Defaults to: https://repo1.maven.org/maven2/.

jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/HttpInput.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import jakarta.servlet.ServletInputStream;
2323
import org.eclipse.jetty.io.Content;
2424
import org.eclipse.jetty.server.Context;
25+
import org.eclipse.jetty.util.IO;
2526
import org.eclipse.jetty.util.thread.AutoLock;
2627
import org.eclipse.jetty.util.thread.Invocable;
2728
import org.slf4j.Logger;
@@ -276,9 +277,7 @@ private int read(ByteBuffer buffer, byte[] b, int off, int len) throws IOExcepti
276277
Throwable failure = chunk.getFailure();
277278
if (LOG.isDebugEnabled())
278279
LOG.debug("read failure={} {}", failure, this);
279-
if (failure instanceof IOException)
280-
throw (IOException)failure;
281-
throw new IOException(failure);
280+
throw IO.rethrow(failure);
282281
}
283282

284283
// Empty and not a failure; can only be EOF as per ContentProducer.nextChunk() contract.

jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/ServletHandler.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
import jakarta.servlet.http.HttpServlet;
4444
import jakarta.servlet.http.HttpServletRequest;
4545
import jakarta.servlet.http.HttpServletResponse;
46+
import org.eclipse.jetty.http.UriCompliance;
4647
import org.eclipse.jetty.http.pathmap.MappedResource;
4748
import org.eclipse.jetty.http.pathmap.MatchedPath;
4849
import org.eclipse.jetty.http.pathmap.MatchedResource;
@@ -132,8 +133,12 @@ public boolean isDecodeAmbiguousURIs()
132133
}
133134

134135
/**
135-
* @param decodeAmbiguousURIs {@code True} if ambiguous URIs are decoded by {@link ServletApiRequest#getServletPath()}
136-
* and {@link ServletApiRequest#getPathInfo()}.
136+
* <p>Allow or disallow ambiguous URIs to be returned by {@link ServletApiRequest#getServletPath()}
137+
* and {@link ServletApiRequest#getPathInfo()}.</p>
138+
* <p>Note that the {@link org.eclipse.jetty.server.HttpConfiguration#setUriCompliance(UriCompliance)}
139+
* must also be set to allow ambiguous URIs to be accepted by the {@link org.eclipse.jetty.server.Connector}.</p>
140+
*
141+
* @param decodeAmbiguousURIs {@code True} if ambiguous URIs are decoded by all servlet API methods.
137142
*/
138143
public void setDecodeAmbiguousURIs(boolean decodeAmbiguousURIs)
139144
{

jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/SizeLimitHandlerServletTest.java

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,17 +19,20 @@
1919
import java.net.URI;
2020
import java.util.concurrent.CompletableFuture;
2121
import java.util.concurrent.CountDownLatch;
22+
import java.util.concurrent.Exchanger;
2223
import java.util.concurrent.TimeUnit;
2324
import java.util.zip.GZIPInputStream;
2425
import java.util.zip.GZIPOutputStream;
2526

2627
import jakarta.servlet.http.HttpServlet;
2728
import jakarta.servlet.http.HttpServletRequest;
2829
import jakarta.servlet.http.HttpServletResponse;
30+
import org.eclipse.jetty.client.AsyncRequestContent;
2931
import org.eclipse.jetty.client.BytesRequestContent;
3032
import org.eclipse.jetty.client.ContentResponse;
3133
import org.eclipse.jetty.client.HttpClient;
3234
import org.eclipse.jetty.client.Request;
35+
import org.eclipse.jetty.client.Response;
3336
import org.eclipse.jetty.client.Result;
3437
import org.eclipse.jetty.client.StringRequestContent;
3538
import org.eclipse.jetty.http.HttpHeader;
@@ -38,6 +41,7 @@
3841
import org.eclipse.jetty.server.ServerConnector;
3942
import org.eclipse.jetty.server.handler.SizeLimitHandler;
4043
import org.eclipse.jetty.server.handler.gzip.GzipHandler;
44+
import org.eclipse.jetty.util.Blocker;
4145
import org.eclipse.jetty.util.BufferUtil;
4246
import org.eclipse.jetty.util.IO;
4347
import org.junit.jupiter.api.AfterEach;
@@ -144,6 +148,54 @@ protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws I
144148
assertThat(response.getStatus(), equalTo(HttpStatus.PAYLOAD_TOO_LARGE_413));
145149
}
146150

151+
@Test
152+
public void testChunkedEcho() throws Exception
153+
{
154+
start(new HttpServlet()
155+
{
156+
@Override
157+
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException
158+
{
159+
String requestContent = IO.toString(req.getInputStream());
160+
resp.getWriter().print(requestContent);
161+
}
162+
});
163+
164+
String content = "x".repeat(SIZE_LIMIT);
165+
166+
URI uri = URI.create("http://localhost:" + _connector.getLocalPort());
167+
Exchanger<Response> exchanger = new Exchanger<>();
168+
AsyncRequestContent asyncRequestContent = new AsyncRequestContent();
169+
_client.POST(uri).body(asyncRequestContent).send(result ->
170+
{
171+
try
172+
{
173+
exchanger.exchange(result.getResponse());
174+
}
175+
catch (InterruptedException e)
176+
{
177+
throw new RuntimeException(e);
178+
}
179+
});
180+
181+
try (Blocker.Callback callback = Blocker.callback())
182+
{
183+
asyncRequestContent.write(false, BufferUtil.toBuffer(content), callback);
184+
asyncRequestContent.flush();
185+
callback.block();
186+
}
187+
try (Blocker.Callback callback = Blocker.callback())
188+
{
189+
asyncRequestContent.write(true, BufferUtil.toBuffer(content), callback);
190+
asyncRequestContent.flush();
191+
callback.block();
192+
}
193+
asyncRequestContent.close();
194+
195+
Response response = exchanger.exchange(null);
196+
assertThat(response.getStatus(), equalTo(HttpStatus.PAYLOAD_TOO_LARGE_413));
197+
}
198+
147199
@Test
148200
public void testGzipEchoNoAcceptEncoding() throws Exception
149201
{

jetty-ee11/jetty-ee11-servlet/src/main/java/org/eclipse/jetty/ee11/servlet/HttpInput.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import jakarta.servlet.ServletInputStream;
2323
import org.eclipse.jetty.io.Content;
2424
import org.eclipse.jetty.server.Context;
25+
import org.eclipse.jetty.util.IO;
2526
import org.eclipse.jetty.util.thread.AutoLock;
2627
import org.eclipse.jetty.util.thread.Invocable;
2728
import org.slf4j.Logger;
@@ -276,9 +277,7 @@ private int read(ByteBuffer buffer, byte[] b, int off, int len) throws IOExcepti
276277
Throwable failure = chunk.getFailure();
277278
if (LOG.isDebugEnabled())
278279
LOG.debug("read failure={} {}", failure, this);
279-
if (failure instanceof IOException)
280-
throw (IOException)failure;
281-
throw new IOException(failure);
280+
throw IO.rethrow(failure);
282281
}
283282

284283
// Empty and not a failure; can only be EOF as per ContentProducer.nextChunk() contract.

jetty-ee11/jetty-ee11-servlet/src/main/java/org/eclipse/jetty/ee11/servlet/ServletHandler.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
import jakarta.servlet.http.HttpServlet;
4444
import jakarta.servlet.http.HttpServletRequest;
4545
import jakarta.servlet.http.HttpServletResponse;
46+
import org.eclipse.jetty.http.UriCompliance;
4647
import org.eclipse.jetty.http.pathmap.MappedResource;
4748
import org.eclipse.jetty.http.pathmap.MatchedPath;
4849
import org.eclipse.jetty.http.pathmap.MatchedResource;
@@ -132,8 +133,12 @@ public boolean isDecodeAmbiguousURIs()
132133
}
133134

134135
/**
135-
* @param decodeAmbiguousURIs {@code True} if ambiguous URIs are decoded by {@link ServletApiRequest#getServletPath()}
136-
* and {@link ServletApiRequest#getPathInfo()}.
136+
* <p>Allow or disallow ambiguous URIs to be returned by {@link ServletApiRequest#getServletPath()}
137+
* and {@link ServletApiRequest#getPathInfo()}.</p>
138+
* <p>Note that the {@link org.eclipse.jetty.server.HttpConfiguration#setUriCompliance(UriCompliance)}
139+
* must also be set to allow ambiguous URIs to be accepted by the {@link org.eclipse.jetty.server.Connector}.</p>
140+
*
141+
* @param decodeAmbiguousURIs {@code True} if ambiguous URIs are decoded by all servlet API methods.
137142
*/
138143
public void setDecodeAmbiguousURIs(boolean decodeAmbiguousURIs)
139144
{

jetty-ee11/jetty-ee11-servlet/src/test/java/org/eclipse/jetty/ee11/servlet/SizeLimitHandlerServletTest.java

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,17 +19,20 @@
1919
import java.net.URI;
2020
import java.util.concurrent.CompletableFuture;
2121
import java.util.concurrent.CountDownLatch;
22+
import java.util.concurrent.Exchanger;
2223
import java.util.concurrent.TimeUnit;
2324
import java.util.zip.GZIPInputStream;
2425
import java.util.zip.GZIPOutputStream;
2526

2627
import jakarta.servlet.http.HttpServlet;
2728
import jakarta.servlet.http.HttpServletRequest;
2829
import jakarta.servlet.http.HttpServletResponse;
30+
import org.eclipse.jetty.client.AsyncRequestContent;
2931
import org.eclipse.jetty.client.BytesRequestContent;
3032
import org.eclipse.jetty.client.ContentResponse;
3133
import org.eclipse.jetty.client.HttpClient;
3234
import org.eclipse.jetty.client.Request;
35+
import org.eclipse.jetty.client.Response;
3336
import org.eclipse.jetty.client.Result;
3437
import org.eclipse.jetty.client.StringRequestContent;
3538
import org.eclipse.jetty.http.HttpHeader;
@@ -38,6 +41,7 @@
3841
import org.eclipse.jetty.server.ServerConnector;
3942
import org.eclipse.jetty.server.SizeLimitHandler;
4043
import org.eclipse.jetty.server.handler.gzip.GzipHandler;
44+
import org.eclipse.jetty.util.Blocker;
4145
import org.eclipse.jetty.util.BufferUtil;
4246
import org.eclipse.jetty.util.IO;
4347
import org.junit.jupiter.api.AfterEach;
@@ -144,6 +148,54 @@ protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws I
144148
assertThat(response.getStatus(), equalTo(HttpStatus.PAYLOAD_TOO_LARGE_413));
145149
}
146150

151+
@Test
152+
public void testChunkedEcho() throws Exception
153+
{
154+
start(new HttpServlet()
155+
{
156+
@Override
157+
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException
158+
{
159+
String requestContent = IO.toString(req.getInputStream());
160+
resp.getWriter().print(requestContent);
161+
}
162+
});
163+
164+
String content = "x".repeat(SIZE_LIMIT);
165+
166+
URI uri = URI.create("http://localhost:" + _connector.getLocalPort());
167+
Exchanger<Response> exchanger = new Exchanger<>();
168+
AsyncRequestContent asyncRequestContent = new AsyncRequestContent();
169+
_client.POST(uri).body(asyncRequestContent).send(result ->
170+
{
171+
try
172+
{
173+
exchanger.exchange(result.getResponse());
174+
}
175+
catch (InterruptedException e)
176+
{
177+
throw new RuntimeException(e);
178+
}
179+
});
180+
181+
try (Blocker.Callback callback = Blocker.callback())
182+
{
183+
asyncRequestContent.write(false, BufferUtil.toBuffer(content), callback);
184+
asyncRequestContent.flush();
185+
callback.block();
186+
}
187+
try (Blocker.Callback callback = Blocker.callback())
188+
{
189+
asyncRequestContent.write(true, BufferUtil.toBuffer(content), callback);
190+
asyncRequestContent.flush();
191+
callback.block();
192+
}
193+
asyncRequestContent.close();
194+
195+
Response response = exchanger.exchange(null);
196+
assertThat(response.getStatus(), equalTo(HttpStatus.PAYLOAD_TOO_LARGE_413));
197+
}
198+
147199
@Test
148200
public void testGzipEchoNoAcceptEncoding() throws Exception
149201
{

0 commit comments

Comments
 (0)