Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve documentation for ambiguous URIs #12554

Merged
merged 4 commits into from
Nov 28, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,17 @@ If you want to customize the violations that you want to allow, you can create y
include::code:example$src/main/java/org/eclipse/jetty/docs/programming/server/ServerDocs.java[tags=uriComplianceCustom]
----

[[servleturi]]
=== Servlet URI Compliance Modes

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.

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.
gregw marked this conversation as resolved.
Show resolved Hide resolved
gregw marked this conversation as resolved.
Show resolved Hide resolved

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.
gregw marked this conversation as resolved.
Show resolved Hide resolved

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)].

[[cookie]]
== Cookie Compliance Modes

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,10 @@ Configure Commands:
Options:
--------

--env=<environmentName>
Sets the environment which will apply to all subsequent libraries,
properties and XML files.

--modules=<moduleName>(,<moduleName>)*
Enables a module for this execution.
To enable a module for all future executions, use the
Expand All @@ -145,7 +149,8 @@ Options:

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

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


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

-D<name>=<value>
Specifies a system property, as well as a start property.
Note: this is a program argument that is interpreted and
added to the existing JVM system properties.

<file>.xml
Specifies a Jetty XML file relative to ${jetty.base}.
This file is in addition to the Jetty XML files resolved
from the [xml] sections of the enabled modules.
If there has been a prior --env argument, the XML will be
executed within that environment

<name>=<value>
Specifies a property value that overrides the same
property defined in a ${jetty.base}/start.d/*.ini file,
or in the [ini] section of a *.mod file.

<name>=<value>
Sets the property value unconditionally.
<name>+=<value>
Appends the given value to the existing value.
<name>?=<value>
Sets the property value only if it is not already set.

If there has been a prior --env argument, then the property is
set only for the current environment, otherwise it is set for
the whole server.

<file>.properties
Specifies a file of property assignments that overrides the same
property defined in a ${jetty.base}/start.d/*.ini file,
or in the [ini] section of a *.mod file.
If there has been a prior --env argument, then the property is
set only for the current environment, otherwise it is set for
the whole server.

jetty.home=<directory>
Sets the ${jetty.home} directory.
By default it is resolved from the start.jar file path.
Expand Down Expand Up @@ -272,26 +312,4 @@ Options:

maven.repo.uri=<url>
The base URL to use to download Maven dependencies.
Defaults to: https://repo1.maven.org/maven2/.

<name>=<value>
Specifies a property value that overrides the same
property defined in a ${jetty.base}/start.d/*.ini file,
or in the [ini] section of a *.mod file.

<name>=<value>
Sets the property value unconditionally.
<name>+=<value>
Appends the given value to the existing value.
<name>?=<value>
Sets the property value only if it is not already set.

-D<name>=<value>
Specifies a system property, as well as a start property.
Note: this is a program argument that is interpreted and
added to the existing JVM system properties.

<xml-file>
Specifies a Jetty XML file relative to ${jetty.base}.
This file is in addition to the Jetty XML files resolved
from the [xml] sections of the enabled modules.
Defaults to: https://repo1.maven.org/maven2/.
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.eclipse.jetty.http.UriCompliance;
import org.eclipse.jetty.http.pathmap.MappedResource;
import org.eclipse.jetty.http.pathmap.MatchedPath;
import org.eclipse.jetty.http.pathmap.MatchedResource;
Expand Down Expand Up @@ -132,8 +133,12 @@ public boolean isDecodeAmbiguousURIs()
}

/**
* @param decodeAmbiguousURIs {@code True} if ambiguous URIs are decoded by {@link ServletApiRequest#getServletPath()}
* and {@link ServletApiRequest#getPathInfo()}.
* <p>Allow or disallow ambiguous URIs to be returned by {@link ServletApiRequest#getServletPath()}
* and {@link ServletApiRequest#getPathInfo()}.</p>
* <p>Note that the {@link org.eclipse.jetty.server.HttpConfiguration#setUriCompliance(UriCompliance)}
* must also be set to allow ambiguous URIs to be accepted by the {@link org.eclipse.jetty.server.Connector}.</p>
*
* @param decodeAmbiguousURIs {@code True} if ambiguous URIs are decoded by all servlet API methods.
*/
public void setDecodeAmbiguousURIs(boolean decodeAmbiguousURIs)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,20 @@
import java.net.URI;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Exchanger;
import java.util.concurrent.TimeUnit;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;

import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.eclipse.jetty.client.AsyncRequestContent;
import org.eclipse.jetty.client.BytesRequestContent;
import org.eclipse.jetty.client.ContentResponse;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.Request;
import org.eclipse.jetty.client.Response;
import org.eclipse.jetty.client.Result;
import org.eclipse.jetty.client.StringRequestContent;
import org.eclipse.jetty.http.HttpHeader;
Expand All @@ -38,6 +41,7 @@
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.server.handler.SizeLimitHandler;
import org.eclipse.jetty.server.handler.gzip.GzipHandler;
import org.eclipse.jetty.util.Blocker;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.IO;
import org.junit.jupiter.api.AfterEach;
Expand Down Expand Up @@ -144,6 +148,55 @@ protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws I
assertThat(response.getStatus(), equalTo(HttpStatus.PAYLOAD_TOO_LARGE_413));
}

@Test
public void testChunkedEcho() throws Exception
{
start(new HttpServlet()
{
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException
{
String requestContent = IO.toString(req.getInputStream());
resp.getWriter().print(requestContent);
}
});

String content = "x".repeat(SIZE_LIMIT);

URI uri = URI.create("http://localhost:" + _connector.getLocalPort());
Exchanger<Response> exchanger = new Exchanger<>();
AsyncRequestContent asyncRequestContent = new AsyncRequestContent();
_client.POST(uri).body(asyncRequestContent).send(result ->
{
try
{
exchanger.exchange(result.getResponse());
}
catch (InterruptedException e)
{
throw new RuntimeException(e);
}
});

try (Blocker.Callback callback = Blocker.callback())
{
asyncRequestContent.write(false, BufferUtil.toBuffer(content), callback);
asyncRequestContent.flush();
callback.block();
}
try (Blocker.Callback callback = Blocker.callback())
{
asyncRequestContent.write(true, BufferUtil.toBuffer(content), callback);
asyncRequestContent.flush();
callback.block();
}
asyncRequestContent.close();

Response response = exchanger.exchange(null);
assertThat(response.getStatus(), equalTo(HttpStatus.PAYLOAD_TOO_LARGE_413));
}


@Test
public void testGzipEchoNoAcceptEncoding() throws Exception
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#org.eclipse.jetty.LEVEL=DEBUG
#org.eclipse.jetty.http.LEVEL=DEBUG
org.eclipse.jetty.http.LEVEL=DEBUG
gregw marked this conversation as resolved.
Show resolved Hide resolved
#org.eclipse.jetty.server.LEVEL=DEBUG
#org.eclipse.jetty.ee10.servlet.LEVEL=DEBUG
#org.eclipse.jetty.server.ResourceService.LEVEL=DEBUG
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import org.eclipse.jetty.server.Context;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.ExceptionUtil;
import org.eclipse.jetty.util.component.Destroyable;
import org.eclipse.jetty.util.thread.AutoLock;
import org.eclipse.jetty.util.thread.Invocable;
Expand Down Expand Up @@ -311,9 +312,7 @@ private int read(ByteBuffer buffer, byte[] b, int off, int len) throws IOExcepti
LOG.debug("read error={} {}", error, this);
if (error != null)
{
if (error instanceof IOException)
throw (IOException)error;
throw new IOException(error);
ExceptionUtil.ifExceptionThrowAs(IOException.class, error);
gregw marked this conversation as resolved.
Show resolved Hide resolved
}

if (content.isEof())
Expand Down
Loading