Merge remote-tracking branch 'origin/jetty-12.0.x' into jetty-12.0.x-ee9-ContextHandlerClassLoading

This commit is contained in:
Lachlan Roberts 2023-06-14 10:31:51 +10:00
commit a67e48d1ee
290 changed files with 2620 additions and 4300 deletions

View File

@ -2,7 +2,7 @@
// See https://github.com/asciidoctor/asciidoctor-intellij-plugin/wiki/Support-project-specific-configurations
:ee-all: ee{8,9,10}
:ee-current: ee10
:ee-current-caps: EE10
:ee-current-caps: EE 10
:experimental:
:imagesdir: images
:jetty-home: ../../../../../../../jetty-home/target/jetty-home

View File

@ -18,9 +18,17 @@ You can deploy two types of web application resources with Jetty:
* *Standard Web Application Archives*, in the form of `+*.war+` files or web application directories, defined by the link:https://www.oracle.com/java/technologies/java-servlet-tec.html[Servlet specification].
Their deployment is described in xref:og-begin-deploy-war[this section].
* *Jetty context XML files*, that allow you to customize the deployment of standard web applications, and also allow you use Jetty components -- and possibly custom components written by you -- to assemble your web applications.
* *Jetty context XML files*, that allow you to customize the deployment of standard web applications, and also allow you to use Jetty components -- and possibly custom components written by you -- to assemble your web applications.
Their deployment is described in xref:og-deploy[this section].
Jetty supports the deployment of both standard web applications and Jetty context XML files in a specific _environment_.
In the following sections you can find simple examples of deployments of Jakarta {ee-current-caps} web applications.
However, Jetty supports simultaneous deployment of web applications each to a possibly different environment, for example an old Java EE 8 web application alongside a new Jakarta {ee-current-caps} web application.
Refer to the section about xref:og-deploy[deployment] for further information about how to deploy to different environments.
[[og-begin-deploy-war]]
===== Deploying +*.war+ Files
@ -48,7 +56,12 @@ mywebapp.war
To deploy a standard web application, you need to enable the xref:og-module-eeN-deploy[`{ee-current}-deploy` module].
NOTE: The following examples assume you're deploying a Jakarta {ee-current-caps} application; for other versions of Jakarta EE, make sure to activate the corresponding `{ee-all}-deploy` module.
[NOTE]
====
The following examples assume you're deploying a Jakarta {ee-current-caps} application; for other versions of Jakarta EE, make sure to activate the corresponding `{ee-all}-deploy` module.
Refer to the section about xref:og-deploy[deployment] for further information about how to deploy to different environments.
====
[subs=attributes]
----

View File

@ -14,12 +14,31 @@
[[og-deploy]]
=== Web Application Deployment
Most of the times you want to be able to customize the deployment of your web applications, for example by changing the `contextPath`, or by adding JNDI entries, or by configuring virtual hosts, etc.
Most of the time you want to be able to customize the deployment of your web applications, for example by changing the `contextPath`, or by adding JNDI entries, or by configuring virtual hosts, etc.
The customization is performed by the xref:og-module-eeN-deploy[`{ee-all}-deploy` module] by processing xref:og-deploy-jetty[Jetty context XML files].
Jetty supports the deployment of each web application to a specific _environment_.
The available environments are:
* Java EE 8 -- Supports Servlet 4.0 (and associated specifications) in the `+javax.*+` packages.
* Jakarta EE 9 -- Supports Servlet 5.0 (and associated specifications) in the `+jakarta.*+` packages.
* Jakarta EE 10 -- Supports Servlet 6.0 (and associated specifications) in the `+jakarta.*+` packages.
* Jetty core -- Supports web applications written against the Jetty `Handler` APIs, without any Servlet dependencies.
This means that you can simultaneously deploy an old Java EE 8 web application, say `old-ee8.war`, alongside a new Jakarta {ee-current-caps} web application, say `new-{ee-current}.war`, alongside a web application that only uses the Jetty `Handler` APIs, say `app-jetty.xml`.
The customization is performed by processing xref:og-deploy-jetty[Jetty context XML files].
The `deploy` module contains the `DeploymentManager` component that scans the `$JETTY_BASE/webapps` directory for changes, following the deployment rules described in xref:og-deploy-rules[this section].
For each specific environment there is a specific deploy module that you must enable:
* For Java EE 8, xref:og-module-eeN-deploy[`ee8-deploy`]
* For Java EE 9, xref:og-module-eeN-deploy[`ee9-deploy`]
* For Java {ee-current-caps}, xref:og-module-eeN-deploy[`{ee-current}-deploy`]
* For Jetty core, xref:og-module-core-deploy[`core-deploy`]
Each of these modules provide the environment specific features, and depend on the `deploy` module that provides the scanning features.
include::deploy-hot-static.adoc[]
include::deploy-rules.adoc[]
include::deploy-jetty.adoc[]

View File

@ -21,12 +21,12 @@ The web application resources are served by Jetty from the files extracted in th
If you do not want Jetty to extract the `+*.war+` files, you can disable this feature, for example:
.mywebapp.xml
[source,xml,highlight=8]
[source,xml,highlight=8,subs="verbatim,attributes"]
----
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "https://www.eclipse.org/jetty/configure_10_0.dtd">
<Configure class="org.eclipse.jetty.ee9.webapp.WebAppContext">
<Configure class="org.eclipse.jetty.{ee-current}.webapp.WebAppContext">
<Set name="contextPath">/mywebapp</Set>
<Set name="war">/opt/webapps/mywebapp.war</Set>
<Set name="extractWAR">false</Set>

View File

@ -23,17 +23,17 @@ To deploy a web application using a Jetty context XML file, simply place the fil
A simple Jetty context XML file, for example named `wiki.xml` is the following:
.wiki.xml
[source,xml,subs=verbatim]
[source,xml,subs="verbatim,attributes"]
----
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "https://www.eclipse.org/jetty/configure_10_0.dtd">
<Configure class="org.eclipse.jetty.ee9.webapp.WebAppContext"> <1>
<Configure class="org.eclipse.jetty.{ee-current}.webapp.WebAppContext"> <1>
<Set name="contextPath">/wiki</Set> <2>
<Set name="war">/opt/myapps/myapp.war</Set> <3>
</Configure>
----
<1> Configures a link:{javadoc-url}/org/eclipse/jetty/webapp/WebAppContext.html[`WebAppContext`], which is the Jetty component that represents a standard Servlet web application.
<1> Configures a link:{javadoc-url}/org/eclipse/jetty/{ee-current}/webapp/WebAppContext.html[`WebAppContext`], which is the Jetty component that represents a standard Servlet web application.
<2> Specifies the web application `contextPath`, which may be different from the `+*.war+` file name.
<3> Specifies the file system path of the `+*.war+` file.
@ -57,12 +57,12 @@ IMPORTANT: If you place both the Jetty context XML file _and_ the `+*.war+` file
You can use the features of xref:og-xml[Jetty XML files] to avoid to hard-code file system paths or other configurations in your Jetty context XML files, for example by using system properties:
.wiki.xml
[source,xml]
[source,xml,subs="verbatim,attributes"]
----
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "https://www.eclipse.org/jetty/configure_10_0.dtd">
<Configure class="org.eclipse.jetty.ee9.webapp.WebAppContext">
<Configure class="org.eclipse.jetty.{ee-current}.webapp.WebAppContext">
<Set name="contextPath">/wiki</Set>
<Set name="war"><SystemProperty name="myapps.dir"/>/myapp.war</Set>
</Configure>

View File

@ -18,12 +18,12 @@ A web application may _reference_ a JNDI entry, such as a JDBC `DataSource` from
The JNDI entry must be _defined_ in a xref:og-jndi-xml[Jetty XML file], for example a context XML like so:
.mywebapp.xml
[source,xml,subs=normal]
[source,xml,subs="verbatim,attributes"]
----
<?xml version="1.0" encoding="UTF-8"?>
<!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">
<Configure id="wac" class="org.eclipse.jetty.{ee-current}.webapp.WebAppContext">
<Set name="contextPath">/mywebapp</Set>
<Set name="war">/opt/webapps/mywebapp.war</Set>
#&nbsp;&nbsp;<New class="org.eclipse.jetty.plus.jndi.Resource">
@ -47,8 +47,8 @@ For more information and examples on how to use JNDI in Jetty, refer to the xref
Class `com.mysql.cj.jdbc.MysqlConnectionPoolDataSource` is present in the MySQL JDBC driver file, `mysql-connector-java-<version>.jar`, which must be available on the server's classpath .
If the class is instead present _within_ the web application, then the JNDI entry must be declared in a `WEB-INF/jetty-env.xml` file - see the xref:og-jndi[JNDI] feature section for more information and examples.
====
// TODO: add a link to reference the section about how
// to add a JDBC driver jar to the server classpath.

View File

@ -19,12 +19,12 @@ This additional `web.xml` is processed _after_ the `+*.war+` file `web.xml`.
This allows you to add host specific configuration or server specific configuration without having to extract the web application `web.xml`, modify it, and repackage it in the `+*.war+` file.
.mywebapp.xml
[source,xml,highlight=8]
[source,xml,highlight=8,subs="verbatim,attributes"]
----
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "https://www.eclipse.org/jetty/configure_10_0.dtd">
<Configure class="org.eclipse.jetty.ee9.webapp.WebAppContext">
<Configure class="org.eclipse.jetty.{ee-current}.webapp.WebAppContext">
<Set name="contextPath">/mywebapp</Set>
<Set name="war">/opt/webapps/mywebapp.war</Set>
<Set name="overrideDescriptor">/opt/webapps/mywebapp-web.xml</Set>
@ -39,8 +39,8 @@ The format of the additional `web.xml` is exactly the same as a standard `web.xm
<?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">
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_6_0.xsd"
version="6.0">
<servlet>
<servlet-name>my-servlet</servlet-name>
<init-param>

View File

@ -20,19 +20,44 @@ _Updating_ a `+*.war+` file or a Jetty context XML file causes the `DeploymentMa
_Removing_ a `+*.war+` file, a `+*.war+` directory, a Jetty context XML file or a normal directory from `$JETTY_BASE/webapps` causes the `DeploymentManager` to undeploy the web application, which means that the Jetty context component representing the web application is stopped and removed from the Jetty server.
[[og-deploy-rules-context-path]]
===== Context Path Resolution
When a file or directory is added to `$JETTY_BASE/webapps`, the `DeploymentManager` derives the web application `contextPath` from the file or directory name, with the following rules:
* If the directory name is, for example, `mywebapp/`, it is deployed as a standard web application if it contains a `WEB-INF/` subdirectory, otherwise it is deployed as a web application of static content.
The `contextPath` would be `/mywebapp` (that is, the web application is reachable at `+http://localhost:8080/mywebapp/+`).
* If the directory name is `ROOT`, case insensitive, the `contextPath` is `/` (that is, the web application is reachable at `+http://localhost:8080/+`).
* If the directory name is `ROOT`, case-insensitive, the `contextPath` is `/` (that is, the web application is reachable at `+http://localhost:8080/+`).
* If the directory name ends with `.d`, for example `config.d/`, it is ignored, although it may be referenced to configure other web applications (for example to store common files).
* If the `+*.war+` file name is, for example, `mywebapp.war`, it is deployed as a standard web application with the context path `/mywebapp` (that is, the web application is reachable at `+http://localhost:8080/mywebapp/+`).
* If the file name is `ROOT.war`, case insensitive, the `contextPath` is `/` (that is, the web application is reachable at `+http://localhost:8080/+`).
* If the file name is `ROOT.war`, case-insensitive, the `contextPath` is `/` (that is, the web application is reachable at `+http://localhost:8080/+`).
* If both the `mywebapp.war` file and the `mywebapp/` directory exist, only the file is deployed.
This allows the directory with the same name to be the `+*.war+` file unpack location and avoid that the web application is deployed twice.
* A xref:og-deploy-jetty[Jetty context XML file] named `mywebapp.xml` is deployed as a web application by processing the directives contained in the XML file itself, which must set the `contextPath`.
* A xref:og-deploy-jetty[Jetty context XML file] named `mywebapp.xml` is deployed as a web application by processing the directives contained in the XML file itself, which must set the `contextPath`, which could be different from the name of the XML file.
* If both `mywebapp.xml` and `mywebapp.war` exist, only the XML file is deployed.
This allows the XML file to reference the `+*.war+` file and avoid that the web application is deployed twice.
[[og-deploy-rules-environment]]
===== Environment Resolution
A web application is always deployed to a specific environment.
If you enabled only one specific deployer module, for example `{ee-current}-deploy`, then the web applications and the Jetty context XML files in `$JETTY_BASE/webapps` will be deployed to the `{ee-current}` environment.
You can enable multiple deployer modules if you need to deploy multiple web applications each to a specific environment.
For example, you have an `old-ee9.war` web application that you want to deploy to the Jakarta EE 9 environment, and a `new-{ee-current}.war` web application that you want to deploy to the Jakarta {ee-current-caps} environment.
First, you must enable both the `ee9-deploy` and the `{ee-current}-deploy` modules.
Then, you add a `+*.properties+` file with the same name of the web application, in the example above `$JETTY_BASE/webapps/old-ee9.properties`, with the following content:
[source,properties]
====
environment=ee9
====
You may also add a `+*.properties+` file for `new-{ee-current}.war`, but it is not necessary because the most recent environment is used by default.
// TODO: verify the statement above. For an ee8 and an ee9 webapp, is it true that ee9 will be used by default (or ee10 will)?
// TODO: add section about the work directory from
// old_docs/contexts/temporary-directories.adoc

View File

@ -53,12 +53,12 @@ For example if the non-ASCII domain name `www.√integral.com` is given to a bro
If you have a web application `mywebapp.war` you can configure its virtual hosts in this way:
[source,xml]
[source,xml,subs="verbatim,attributes"]
----
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "https://www.eclipse.org/jetty/configure_10_0.dtd">
<Configure class="org.eclipse.jetty.ee9.webapp.WebAppContext">
<Configure class="org.eclipse.jetty.{ee-current}.webapp.WebAppContext">
<Set name="contextPath">/mywebapp</Set>
<Set name="war">/opt/webapps/mywebapp.war</Set>
<Set name="virtualHosts">
@ -98,12 +98,12 @@ You have `domain.war` that you want to deploy at `+http://domain.biz/+` and `hob
To achieve this, you simply use the same context path of `/` for each of your webapps, while specifying different virtual hosts for each of your webapps:
.domain.xml
[source,xml]
[source,xml,subs="verbatim,attributes"]
----
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "https://www.eclipse.org/jetty/configure_10_0.dtd">
<Configure class="org.eclipse.jetty.ee9.webapp.WebAppContext">
<Configure class="org.eclipse.jetty.{ee-current}.webapp.WebAppContext">
<Set name="contextPath">/</Set>
<Set name="war">/opt/webapps/domain.war</Set>
<Set name="virtualHosts">
@ -115,12 +115,12 @@ To achieve this, you simply use the same context path of `/` for each of your we
----
.hobby.xml
[source,xml]
[source,xml,subs="verbatim,attributes"]
----
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "https://www.eclipse.org/jetty/configure_10_0.dtd">
<Configure class="org.eclipse.jetty.ee9.webapp.WebAppContext">
<Configure class="org.eclipse.jetty.{ee-current}.webapp.WebAppContext">
<Set name="contextPath">/</Set>
<Set name="war">/opt/webapps/hobby.war</Set>
<Set name="virtualHosts">
@ -143,12 +143,12 @@ This configuration may be useful when Jetty sits behind a load balancer.
In this case, you want to xref:og-protocols[configure multiple connectors], each with a different name, and then reference the connector name in the web application virtual host configuration:
.domain.xml
[source,xml,highlight=10]
[source,xml,highlight=10,subs="verbatim,attributes"]
----
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "https://www.eclipse.org/jetty/configure_10_0.dtd">
<Configure class="org.eclipse.jetty.ee9.webapp.WebAppContext">
<Configure class="org.eclipse.jetty.{ee-current}.webapp.WebAppContext">
<Set name="contextPath">/</Set>
<Set name="war">/opt/webapps/domain.war</Set>
<Set name="virtualHosts">
@ -160,12 +160,12 @@ In this case, you want to xref:og-protocols[configure multiple connectors], each
----
.hobby.xml
[source,xml,highlight=10]
[source,xml,highlight=10,subs="verbatim,attributes"]
----
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "https://www.eclipse.org/jetty/configure_10_0.dtd">
<Configure class="org.eclipse.jetty.ee9.webapp.WebAppContext">
<Configure class="org.eclipse.jetty.{ee-current}.webapp.WebAppContext">
<Set name="contextPath">/</Set>
<Set name="war">/opt/webapps/hobby.war</Set>
<Set name="virtualHosts">

View File

@ -11,16 +11,13 @@
// ========================================================================
//
module org.eclipse.jetty.ee
{
requires org.slf4j;
[[og-module-core-deploy]]
===== Module `core-deploy`
requires transitive org.eclipse.jetty.io;
requires transitive org.eclipse.jetty.security;
include::{jetty-home}/modules/{ee-current}-deploy.mod[tags=description]
// Only required if using JMX.
requires static org.eclipse.jetty.jmx;
Deployment is managed via a `DeploymentManager` component that watches a directory for changes.
See xref:og-deploy[how to deploy web applications] for more information.
exports org.eclipse.jetty.ee;
exports org.eclipse.jetty.ee.security;
}
TODO
// TODO see module-eeN-deploy.adoc

View File

@ -17,6 +17,7 @@
include::module-alpn.adoc[]
include::module-bytebufferpool.adoc[]
include::module-console-capture.adoc[]
include::module-core-deploy.adoc[]
include::module-eeN-deploy.adoc[]
include::module-http.adoc[]
include::module-http2.adoc[]

View File

@ -79,11 +79,6 @@
<artifactId>jetty-deploy</artifactId>
<version>12.0.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-ee</artifactId>
<version>12.0.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-http</artifactId>

View File

@ -315,6 +315,11 @@ public abstract class HttpConnection implements IConnection, Attachable
return String.format("%s@%h", getClass().getSimpleName(), this);
}
/**
* <p>Enforces the total timeout for requests that have been sent.</p>
* <p>The total timeout for exchanges that are in the destination queue
* is enforced in {@link HttpDestination}.</p>
*/
private class RequestTimeouts extends CyclicTimeouts<HttpChannel>
{
private RequestTimeouts(Scheduler scheduler)
@ -332,11 +337,14 @@ public abstract class HttpConnection implements IConnection, Attachable
protected boolean onExpired(HttpChannel channel)
{
HttpExchange exchange = channel.getHttpExchange();
if (exchange != null)
{
HttpRequest request = exchange.getRequest();
request.abort(new TimeoutException("Total timeout " + request.getConversation().getTimeout() + " ms elapsed"));
}
// The expiration lost the race, as the
// exchange may have just been completed.
if (exchange == null)
return false;
HttpRequest request = exchange.getRequest();
request.abort(new TimeoutException("Total timeout " + request.getConversation().getTimeout() + " ms elapsed"));
// The implementation of the Iterator returned above may not support
// removal, but the HttpChannel will be removed by request.abort().
return false;
}
}

View File

@ -550,7 +550,7 @@ public class HttpDestination extends ContainerLifeCycle implements Destination,
}
/**
* <p>Enforces the total timeout for for exchanges that are still in the queue.</p>
* <p>Enforces the total timeout for exchanges that are still in the queue.</p>
* <p>The total timeout for exchanges that are not in the destination queue
* is enforced in {@link HttpConnection}.</p>
*/
@ -572,6 +572,8 @@ public class HttpDestination extends ContainerLifeCycle implements Destination,
{
HttpRequest request = exchange.getRequest();
request.abort(new TimeoutException("Total timeout " + request.getConversation().getTimeout() + " ms elapsed"));
// The implementation of the Iterator returned above does not support
// removal, but the HttpExchange will be removed by request.abort().
return false;
}
}

View File

@ -44,10 +44,6 @@
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-ee</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-jmx</artifactId>

View File

@ -14,7 +14,6 @@
module org.eclipse.jetty.deploy
{
requires java.xml;
requires org.eclipse.jetty.ee;
requires org.eclipse.jetty.xml;
requires org.eclipse.jetty.server;
requires org.slf4j;

View File

@ -20,7 +20,7 @@ import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import org.eclipse.jetty.ee.Deployable;
import org.eclipse.jetty.server.Deployable;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.util.FileID;

View File

@ -32,7 +32,7 @@ import org.eclipse.jetty.deploy.bindings.StandardUndeployer;
import org.eclipse.jetty.deploy.graph.Edge;
import org.eclipse.jetty.deploy.graph.Node;
import org.eclipse.jetty.deploy.graph.Route;
import org.eclipse.jetty.ee.Deployable;
import org.eclipse.jetty.server.Deployable;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.ContextHandlerCollection;
import org.eclipse.jetty.util.ExceptionUtil;

View File

@ -32,7 +32,7 @@ import java.util.function.Supplier;
import java.util.stream.Stream;
import org.eclipse.jetty.deploy.App;
import org.eclipse.jetty.ee.Deployable;
import org.eclipse.jetty.server.Deployable;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.util.FileID;

View File

@ -29,7 +29,7 @@ import java.util.stream.Collectors;
import org.eclipse.jetty.deploy.App;
import org.eclipse.jetty.deploy.AppProvider;
import org.eclipse.jetty.deploy.DeploymentManager;
import org.eclipse.jetty.ee.Deployable;
import org.eclipse.jetty.server.Deployable;
import org.eclipse.jetty.util.FileID;
import org.eclipse.jetty.util.Scanner;
import org.eclipse.jetty.util.StringUtil;

View File

@ -1,56 +0,0 @@
<?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/maven-v4_0_0.xsd">
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-core</artifactId>
<version>12.0.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jetty-ee</artifactId>
<name>Core :: EE Utilities</name>
<properties>
<bundle-symbolic-name>${project.groupId}.ee</bundle-symbolic-name>
<spotbugs.onlyAnalyze>org.eclipse.jetty.ee.*</spotbugs.onlyAnalyze>
</properties>
<build>
<plugins>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<argLine>
@{argLine} ${jetty.surefire.argLine} --add-reads org.eclipse.jetty.io=org.eclipse.jetty.logging
</argLine>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-io</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-security</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-jmx</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-slf4j-impl</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.toolchain</groupId>
<artifactId>jetty-test-helper</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -192,8 +192,11 @@ public class JettyHttpExchangeDelegate extends HttpExchange
@Override
public void setStreams(InputStream i, OutputStream o)
{
_inputStream = i;
_outputStream = o;
assert _inputStream != null;
if (i != null)
_inputStream = i;
if (o != null)
_outputStream = o;
}
@Override

View File

@ -592,6 +592,12 @@ public interface HttpCookie
return this;
}
public Builder sameSite(SameSite sameSite)
{
_attributes = lazyAttributePut(_attributes, SAME_SITE_ATTRIBUTE, sameSite.attributeValue);
return this;
}
/**
* @return an immutable {@link HttpCookie} instance.
*/
@ -878,7 +884,7 @@ public interface HttpCookie
private static Map<String, String> lazyAttributePut(Map<String, String> attributes, String key, String value)
{
if (value == null)
return attributes;
return lazyAttributeRemove(attributes, key);
if (attributes == null)
attributes = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
attributes.put(key, value);

View File

@ -152,7 +152,7 @@ public class HttpField
*/
public boolean contains(String search)
{
return contains(_value, search);
return contains(getValue(), search);
}
/**
@ -337,7 +337,7 @@ public class HttpField
return false;
if (!_name.equalsIgnoreCase(field.getName()))
return false;
return Objects.equals(_value, field.getValue());
return Objects.equals(getValue(), field.getValue());
}
public HttpHeader getHeader()
@ -347,12 +347,12 @@ public class HttpField
public int getIntValue()
{
return Integer.parseInt(_value);
return Integer.parseInt(getValue());
}
public long getLongValue()
{
return Long.parseLong(_value);
return Long.parseLong(getValue());
}
public String getLowerCaseName()
@ -380,16 +380,17 @@ public class HttpField
public List<String> getValueList()
{
if (_value == null)
String value = getValue();
if (value == null)
return null;
QuotedCSV list = new QuotedCSV(false, _value);
QuotedCSV list = new QuotedCSV(false, value);
return list.getValues();
}
@Override
public int hashCode()
{
int vhc = Objects.hashCode(_value);
int vhc = Objects.hashCode(getValue());
if (_header == null)
return vhc ^ nameHashCode();
return vhc ^ _header.hashCode();

View File

@ -2385,6 +2385,8 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements Session
protected boolean onExpired(HTTP2Stream stream)
{
stream.onIdleTimeout(new TimeoutException("Idle timeout " + stream.getIdleTimeout() + " ms elapsed"));
// The implementation of the Iterator returned above does not support
// removal, but the HTTP2Stream will be removed by stream.onIdleTimeout().
return false;
}
}

View File

@ -311,10 +311,9 @@ public class HTTP2Stream implements Stream, Attachable, Closeable, Callback, Dum
notifyIdleTimeout(this, timeout, Promise.from(timedOut ->
{
if (timedOut)
{
// Tell the other peer that we timed out.
reset(new ResetFrame(getId(), ErrorCode.CANCEL_STREAM_ERROR.code), Callback.NOOP);
}
else
notIdle();
}, x -> reset(new ResetFrame(getId(), ErrorCode.INTERNAL_ERROR.code), Callback.NOOP)));
}

View File

@ -78,7 +78,6 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
@Disabled // TODO: bug/race condition in HttpChannelState, where the last write callback is invoked _after_ complete().
public class MaxConcurrentStreamsTest extends AbstractTest
{
private void start(int maxConcurrentStreams, Handler handler) throws Exception

View File

@ -46,7 +46,7 @@ public abstract class HTTP3Stream implements Stream, CyclicTimeouts.Expirable, A
private CloseState closeState = CloseState.NOT_CLOSED;
private FrameState frameState = FrameState.INITIAL;
private long idleTimeout;
private long expireNanoTime;
private long expireNanoTime = Long.MAX_VALUE;
private Object attachment;
private boolean dataDemand;
private boolean dataStalled;
@ -129,6 +129,8 @@ public abstract class HTTP3Stream implements Stream, CyclicTimeouts.Expirable, A
{
if (timedOut)
endPoint.close(HTTP3ErrorCode.REQUEST_CANCELLED_ERROR.code(), timeout);
else
notIdle();
promise.succeeded(timedOut);
}, promise::failed));
}

View File

@ -125,8 +125,14 @@ public abstract class HTTP3StreamConnection extends AbstractConnection
case FRAME ->
{
action.getAndSet(null).run();
// Do not release the buffer here to avoid races with
// user-spawned threads that may call Stream.read().
// Do not release the buffer before the stream started closing
// to avoid races with user-spawned threads that may call Stream.read().
if (remotelyClosed)
{
// The last frame may have caused a write that we need to flush.
getEndPoint().getQuicSession().flush();
tryReleaseBuffer(false);
}
}
}
}
@ -190,7 +196,7 @@ public abstract class HTTP3StreamConnection extends AbstractConnection
}
if (!parser.isDataMode())
throw new IllegalStateException();
continue;
if (stream.hasDemandOrStall())
{
@ -213,9 +219,6 @@ public abstract class HTTP3StreamConnection extends AbstractConnection
// From now on it's the application that drives
// demand, reads, parse+fill and fill interest.
return;
// TODO: do we loop here?
// There might be a trailer, loop around.
}
default -> throw new IllegalStateException("unknown message parser result: " + result);
}

View File

@ -65,6 +65,10 @@ public abstract class CyclicTimeouts<T extends CyclicTimeouts.Expirable> impleme
* <p>This method may be invoked multiple times, and even concurrently,
* for the same expirable entity and therefore the expiration of the
* entity, if any, should be an idempotent action.</p>
* <p>When {@code false} is returned, the implementation should adjust
* the {@link Expirable} expiration, so that a call to
* {@link Expirable#getExpireNanoTime()} after this method has returned
* yields a new expiration nanoTime.</p>
*
* @param expirable the entity that is expired
* @return whether the entity should be removed from the iterator via {@link Iterator#remove()}
@ -78,11 +82,10 @@ public abstract class CyclicTimeouts<T extends CyclicTimeouts.Expirable> impleme
long now = NanoTime.now();
long earliest = Long.MAX_VALUE;
// Reset the earliest timeout so we can expire again.
// A concurrent call to schedule(long) may lose an
// earliest value, but the corresponding entity will
// be seen during the iteration below.
earliestTimeout.set(earliest);
// Move the earliest timeout far in the future, so we can expire again.
// A concurrent call to schedule(long) may lose an earliest value, but
// the corresponding entity will be seen during the iteration below.
earliestTimeout.set(now + Long.MAX_VALUE);
Iterator<T> iterator = iterator();
if (iterator == null)
@ -98,23 +101,26 @@ public abstract class CyclicTimeouts<T extends CyclicTimeouts.Expirable> impleme
if (LOG.isDebugEnabled())
LOG.debug("Entity {} expires in {} ms for {}", expirable, NanoTime.millisElapsed(now, expiresAt), this);
if (expiresAt == -1)
continue;
if (NanoTime.isBeforeOrSame(expiresAt, now))
{
boolean remove = onExpired(expirable);
if (LOG.isDebugEnabled())
LOG.debug("Entity {} expired, remove={} for {}", expirable, remove, this);
if (remove)
{
iterator.remove();
continue;
continue;
}
long newExpiresAt = expirable.getExpireNanoTime();
if (newExpiresAt == expiresAt)
continue;
expiresAt = newExpiresAt;
}
earliest = Math.min(earliest, NanoTime.elapsed(now, expiresAt));
}
if (earliest < Long.MAX_VALUE)
if (earliest != Long.MAX_VALUE)
schedule(now + earliest);
}
@ -126,7 +132,7 @@ public abstract class CyclicTimeouts<T extends CyclicTimeouts.Expirable> impleme
public void schedule(T expirable)
{
long expiresAt = expirable.getExpireNanoTime();
if (expiresAt < Long.MAX_VALUE)
if (expiresAt != Long.MAX_VALUE)
schedule(expiresAt);
}

View File

@ -139,6 +139,23 @@ public abstract class SelectorManager extends ContainerLifeCycle implements Dump
executor.execute(task);
}
/**
* Get total number of keys from each selector.
*
* @return total number of selector keys
*/
@ManagedAttribute(value = "Total number of keys in all selectors", readonly = true)
public int getTotalKeys()
{
int keys = 0;
for (final ManagedSelector selector : _selectors)
{
keys += selector.getTotalKeys();
}
return keys;
}
/**
* @return the number of selectors in use
*/
@ -506,4 +523,10 @@ public abstract class SelectorManager extends ContainerLifeCycle implements Dump
{
}
}
@Override
public String toString()
{
return String.format("%s@%x[keys=%d]", getClass().getSimpleName(), hashCode(), getTotalKeys());
}
}

View File

@ -18,6 +18,7 @@ import java.util.Collection;
import java.util.Objects;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicMarkableReference;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
@ -38,10 +39,15 @@ import org.eclipse.jetty.util.Pool;
*/
public class QueuedPool<P> implements Pool<P>
{
// All code that uses these three fields is fully thread-safe.
private final int maxSize;
private final Queue<Entry<P>> queue = new ConcurrentLinkedQueue<>();
private final AtomicInteger queueSize = new AtomicInteger();
// This lock protects the 'terminated' field.
// Only the 'terminated' field is protected by the RW lock,
// the other fields are totally ignored w.r.t the scope of this lock;
// so when the read lock or the write lock is needed solely depends
// on what is being done to the 'terminated' field.
private final ReadWriteLock rwLock = new ReentrantReadWriteLock();
private boolean terminated;
@ -56,7 +62,7 @@ public class QueuedPool<P> implements Pool<P>
rwLock.readLock().lock();
try
{
if (terminated || queue.size() == maxSize)
if (terminated || queueSize.get() == maxSize)
return null;
return new QueuedEntry<>(this);
}
@ -71,10 +77,16 @@ public class QueuedPool<P> implements Pool<P>
rwLock.readLock().lock();
try
{
if (terminated || queue.size() == maxSize)
return false;
queue.add(entry);
return true;
while (true)
{
int size = queueSize.get();
if (terminated || size == maxSize)
return false;
if (!queueSize.compareAndSet(size, size + 1))
continue;
queue.add(entry);
return true;
}
}
finally
{
@ -92,7 +104,10 @@ public class QueuedPool<P> implements Pool<P>
return null;
QueuedEntry<P> entry = (QueuedEntry<P>)queue.poll();
if (entry != null)
{
queueSize.decrementAndGet();
entry.acquire();
}
return entry;
}
finally
@ -121,9 +136,15 @@ public class QueuedPool<P> implements Pool<P>
rwLock.writeLock().lock();
try
{
// Once 'terminated' has been set to true, no entry can be
// added nor removed from the queue; the setting to true
// as well as the copy and the clearing of the queue MUST be
// atomic otherwise we may not return the exact list of entries
// that remained in the pool when terminate() was called.
terminated = true;
Collection<Entry<P>> copy = new ArrayList<>(queue);
queue.clear();
queueSize.set(0);
return copy;
}
finally
@ -135,7 +156,7 @@ public class QueuedPool<P> implements Pool<P>
@Override
public int size()
{
return queue.size();
return queueSize.get();
}
@Override

View File

@ -41,7 +41,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
public class CyclicTimeoutsTest
{
private Scheduler scheduler;
private CyclicTimeouts<ConstantExpirable> timeouts;
private CyclicTimeouts<CyclicTimeouts.Expirable> timeouts;
@BeforeEach
public void prepare()
@ -65,14 +65,14 @@ public class CyclicTimeoutsTest
timeouts = new CyclicTimeouts<>(scheduler)
{
@Override
protected Iterator<ConstantExpirable> iterator()
protected Iterator<CyclicTimeouts.Expirable> iterator()
{
latch.countDown();
return null;
}
@Override
protected boolean onExpired(ConstantExpirable expirable)
protected boolean onExpired(CyclicTimeouts.Expirable expirable)
{
return false;
}
@ -93,14 +93,14 @@ public class CyclicTimeoutsTest
timeouts = new CyclicTimeouts<>(scheduler)
{
@Override
protected Iterator<ConstantExpirable> iterator()
protected Iterator<CyclicTimeouts.Expirable> iterator()
{
iteratorLatch.countDown();
return Collections.emptyIterator();
}
@Override
protected boolean onExpired(ConstantExpirable expirable)
protected boolean onExpired(CyclicTimeouts.Expirable expirable)
{
expiredLatch.countDown();
return false;
@ -118,22 +118,22 @@ public class CyclicTimeoutsTest
public void testIterateAndExpire(boolean remove) throws Exception
{
ConstantExpirable zero = ConstantExpirable.ofDelay(0, TimeUnit.SECONDS);
ConstantExpirable one = ConstantExpirable.ofDelay(1, TimeUnit.SECONDS);
Collection<ConstantExpirable> collection = new ArrayList<>();
DynamicExpirable one = new DynamicExpirable(NanoTime.now() + TimeUnit.SECONDS.toNanos(1));
Collection<CyclicTimeouts.Expirable> collection = new ArrayList<>();
collection.add(one);
AtomicInteger iterations = new AtomicInteger();
CountDownLatch expiredLatch = new CountDownLatch(1);
timeouts = new CyclicTimeouts<>(scheduler)
{
@Override
protected Iterator<ConstantExpirable> iterator()
protected Iterator<CyclicTimeouts.Expirable> iterator()
{
iterations.incrementAndGet();
return collection.iterator();
}
@Override
protected boolean onExpired(ConstantExpirable expirable)
protected boolean onExpired(CyclicTimeouts.Expirable expirable)
{
assertSame(one, expirable);
expiredLatch.countDown();
@ -169,22 +169,22 @@ public class CyclicTimeoutsTest
long delayMs = 2000;
ConstantExpirable two = ConstantExpirable.ofDelay(delayMs, TimeUnit.MILLISECONDS);
ConstantExpirable overtake = ConstantExpirable.ofDelay(delayMs / 2, TimeUnit.MILLISECONDS);
Collection<ConstantExpirable> collection = new ArrayList<>();
Collection<CyclicTimeouts.Expirable> collection = new ArrayList<>();
collection.add(two);
CountDownLatch expiredLatch = new CountDownLatch(2);
List<ConstantExpirable> expired = new ArrayList<>();
List<CyclicTimeouts.Expirable> expired = new ArrayList<>();
timeouts = new CyclicTimeouts<>(scheduler)
{
private final AtomicBoolean overtakeScheduled = new AtomicBoolean();
@Override
protected Iterator<ConstantExpirable> iterator()
protected Iterator<CyclicTimeouts.Expirable> iterator()
{
return collection.iterator();
}
@Override
protected boolean onExpired(ConstantExpirable expirable)
protected boolean onExpired(CyclicTimeouts.Expirable expirable)
{
expired.add(expirable);
expiredLatch.countDown();
@ -220,6 +220,39 @@ public class CyclicTimeoutsTest
assertSame(two, expired.get(1));
}
@Test
public void testDynamicExpirableEntityIsNotifiedMultipleTimes() throws Exception
{
long delay = 500;
DynamicExpirable entity = new DynamicExpirable(NanoTime.now() + TimeUnit.MILLISECONDS.toNanos(delay));
List<CyclicTimeouts.Expirable> entities = List.of(entity);
CountDownLatch latch = new CountDownLatch(2);
timeouts = new CyclicTimeouts<>(scheduler)
{
@Override
protected Iterator<CyclicTimeouts.Expirable> iterator()
{
return entities.iterator();
}
@Override
protected boolean onExpired(CyclicTimeouts.Expirable expirable)
{
assertSame(entity, expirable);
// Postpone expiration.
entity.expireNanoTime = NanoTime.now() + TimeUnit.MILLISECONDS.toNanos(delay);
latch.countDown();
return false;
}
};
// Trigger the initial call to iterator().
timeouts.schedule(entities.get(0));
assertTrue(latch.await(3 * delay, TimeUnit.MILLISECONDS), latch.toString());
}
private static class ConstantExpirable implements CyclicTimeouts.Expirable
{
private static ConstantExpirable noExpire()
@ -259,4 +292,26 @@ public class CyclicTimeoutsTest
return String.format("%s@%x[%sms]", getClass().getSimpleName(), hashCode(), asString);
}
}
private static class DynamicExpirable implements CyclicTimeouts.Expirable
{
private long expireNanoTime;
public DynamicExpirable(long expireNanoTime)
{
this.expireNanoTime = expireNanoTime;
}
@Override
public long getExpireNanoTime()
{
return expireNanoTime;
}
@Override
public String toString()
{
return String.format("%s@%x[%dms]", getClass().getSimpleName(), hashCode(), NanoTime.millisUntil(expireNanoTime));
}
}
}

View File

@ -46,10 +46,6 @@
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-ee</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-deploy</artifactId>

View File

@ -20,7 +20,7 @@ import java.util.Objects;
import org.eclipse.jetty.deploy.App;
import org.eclipse.jetty.deploy.AppProvider;
import org.eclipse.jetty.deploy.DeploymentManager;
import org.eclipse.jetty.ee.Deployable;
import org.eclipse.jetty.server.Deployable;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.util.StringUtil;

View File

@ -25,8 +25,8 @@ import java.util.Objects;
import org.eclipse.jetty.deploy.App;
import org.eclipse.jetty.deploy.AppProvider;
import org.eclipse.jetty.deploy.DeploymentManager;
import org.eclipse.jetty.ee.Deployable;
import org.eclipse.jetty.osgi.util.BundleFileLocatorHelperFactory;
import org.eclipse.jetty.server.Deployable;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.resource.Resource;

View File

@ -133,6 +133,8 @@ public class ServerQuicConnection extends QuicConnection
protected boolean onExpired(ServerQuicSession session)
{
session.onIdleTimeout();
// The implementation of the Iterator returned above does not support
// removal, but the session will be removed by session.onIdleTimeout().
return false;
}
}

View File

@ -44,7 +44,7 @@ import org.eclipse.jetty.util.thread.Scheduler;
public class ServerQuicSession extends QuicSession implements CyclicTimeouts.Expirable
{
private final Connector connector;
private long expireNanoTime;
private long expireNanoTime = Long.MAX_VALUE;
protected ServerQuicSession(Executor executor, Scheduler scheduler, ByteBufferPool bufferPool, QuicheConnection quicheConnection, QuicConnection connection, SocketAddress remoteAddress, Connector connector)
{
@ -103,6 +103,15 @@ public class ServerQuicSession extends QuicSession implements CyclicTimeouts.Exp
getQuicConnection().schedule(this);
}
@Override
public boolean onIdleTimeout()
{
boolean result = super.onIdleTimeout();
if (!result)
notIdle();
return result;
}
@Override
public Runnable process(SocketAddress remoteAddress, ByteBuffer cipherBufferIn) throws IOException
{

View File

@ -32,7 +32,7 @@ import org.eclipse.jetty.util.Callback;
* cycles. Authentication might not yet be checked or it might be checked
* and failed, checked and deferred or succeeded.
*/
public interface AuthenticationState
public interface AuthenticationState extends Request.AuthenticationState
{
/**
* Get the authentication state of a request
@ -41,8 +41,8 @@ public interface AuthenticationState
*/
static AuthenticationState getAuthenticationState(Request request)
{
Object auth = request.getAttribute(AuthenticationState.class.getName());
return auth instanceof AuthenticationState authenticationState ? authenticationState : null;
Request.AuthenticationState state = Request.getAuthenticationState(request);
return state instanceof AuthenticationState authenticationState ? authenticationState : null;
}
/**
@ -52,7 +52,7 @@ public interface AuthenticationState
*/
static void setAuthenticationState(Request request, AuthenticationState authenticationState)
{
request.setAttribute(AuthenticationState.class.getName(), authenticationState);
Request.setAuthenticationState(request, authenticationState);
}
/**
@ -193,6 +193,15 @@ public interface AuthenticationState
*/
UserIdentity getUserIdentity();
@Override
default Principal getUserPrincipal()
{
UserIdentity user = getUserIdentity();
if (user != null)
return user.getUserPrincipal();
return null;
}
/**
* @param role The role to check.
* @return True if the user is in the passed role

View File

@ -19,7 +19,6 @@ lib/jetty-server-${jetty.version}.jar
lib/jetty-xml-${jetty.version}.jar
lib/jetty-util-${jetty.version}.jar
lib/jetty-io-${jetty.version}.jar
lib/jetty-ee-${jetty.version}.jar
[xml]
etc/jetty.xml

View File

@ -1,26 +0,0 @@
//
// ========================================================================
// 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.server;
/**
* @deprecated {@link org.eclipse.jetty.util.component.ClassLoaderDump}
*/
@Deprecated
public class ClassLoaderDump extends org.eclipse.jetty.util.component.ClassLoaderDump
{
public ClassLoaderDump(ClassLoader loader)
{
super(loader);
}
}

View File

@ -16,6 +16,7 @@ package org.eclipse.jetty.server;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.security.Principal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@ -33,6 +34,7 @@ import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.QuotedCSV;
import org.eclipse.jetty.http.pathmap.PathMappings;
import org.eclipse.jetty.util.DateCache;
import org.eclipse.jetty.util.NanoTime;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.annotation.ManagedAttribute;
import org.eclipse.jetty.util.annotation.ManagedObject;
@ -73,21 +75,18 @@ import static java.lang.invoke.MethodType.methodType;
* <th>Format String</th>
* <th>Description</th>
* </tr>
*
* <tr>
* <td>X</td>
* <td>
* <p>The X character.</p>
* </td>
* </tr>
*
* <tr>
* <td>%%</td>
* <td>
* <p>The percent character.</p>
* </td>
* </tr>
*
* <tr>
* <td>%{format}a</td>
* <td>
@ -99,7 +98,6 @@ import static java.lang.invoke.MethodType.methodType;
* the end-user and the server.</p>
* </td>
* </tr>
*
* <tr>
* <td>%{format}p</td>
* <td>
@ -111,7 +109,6 @@ import static java.lang.invoke.MethodType.methodType;
* the end-user and the server.</p>
* </td>
* </tr>
*
* <tr>
* <td>%{CLF}I</td>
* <td>
@ -121,7 +118,6 @@ import static java.lang.invoke.MethodType.methodType;
* when no bytes are present.</p>
* </td>
* </tr>
*
* <tr>
* <td>%{CLF}O</td>
* <td>
@ -131,7 +127,6 @@ import static java.lang.invoke.MethodType.methodType;
* when no bytes are present.</p>
* </td>
* </tr>
*
* <tr>
* <td>%{CLF}S</td>
* <td>
@ -141,7 +136,6 @@ import static java.lang.invoke.MethodType.methodType;
* when no bytes are present.</p>
* </td>
* </tr>
*
* <tr>
* <td>%{VARNAME}C</td>
* <td>
@ -151,42 +145,36 @@ import static java.lang.invoke.MethodType.methodType;
* When the parameter is missing, all request cookies will be logged.</p>
* </td>
* </tr>
*
* <tr>
* <td>%D</td>
* <td>
* <p>The time taken to serve the request, in microseconds.</p>
* </td>
* </tr>
*
* <tr>
* <td>%{VARNAME}e</td>
* <td>
* <p>The value of the environment variable VARNAME.</p>
* </td>
* </tr>
*
* <tr>
* <td>%f</td>
* <td>
* <p>The file system path of the requested resource.</p>
* </td>
* </tr>
*
* <tr>
* <td>%H</td>
* <td>
* <p>The name and version of the request protocol, such as "HTTP/1.1".</p>
* </td>
* </tr>
*
* <tr>
* <td>%{VARNAME}i</td>
* <td>
* <p>The value of the VARNAME request header.</p>
* </td>
* </tr>
*
* <tr>
* <td>%k</td>
* <td>
@ -195,28 +183,24 @@ import static java.lang.invoke.MethodType.methodType;
* yields the value 1, the second request on the same connection yields the value 2, etc.</p>
* </td>
* </tr>
*
* <tr>
* <td>%m</td>
* <td>
* <p>The HTTP request method.</p>
* </td>
* </tr>
*
* <tr>
* <td>%{VARNAME}o</td>
* <td>
* <p>The value of the VARNAME response header.</p>
* </td>
* </tr>
*
* <tr>
* <td>%q</td>
* <td>
* <p>The query string, prepended with a ? if a query string exists, otherwise an empty string.</p>
* </td>
* </tr>
*
* <!-- TODO ATTRIBUTE LOGGING -->
* <tr>
* <td>%r</td>
@ -224,22 +208,19 @@ import static java.lang.invoke.MethodType.methodType;
* <p>First line of an HTTP/1.1 request (or equivalent information for HTTP/2 or later).</p>
* </td>
* </tr>
*
* <tr>
* <td>%R</td>
* <td>
* <p>The name of the Handler or Servlet generating the response (if any).</p>
* </td>
* </tr>
*
* <tr>
* <tr>
* <td>%s</td>
* <td>
* <p>The HTTP response status code.</p>
* </td>
* </tr>
*
* <tr>
* <tr>
* <td>%{format|timeZone|locale}t</td>
* <td>
* <p>The time at which the request was received.</p>
@ -257,7 +238,6 @@ import static java.lang.invoke.MethodType.methodType;
* </dl>
* </td>
* </tr>
*
* <tr>
* <td>%{UNIT}T</td>
* <td>
@ -267,7 +247,6 @@ import static java.lang.invoke.MethodType.methodType;
* <code>%{us}T</code> is identical to {@code %D}.</p>
* </td>
* </tr>
*
* <tr>
* <td>%{d}u</td>
* <td>
@ -277,15 +256,13 @@ import static java.lang.invoke.MethodType.methodType;
* When the parameter value is "d", deferred authentication will also be checked.</p>
* </td>
* </tr>
*
* <tr>
* <td>%U</td>
* <td>
* <p>The URL path requested, not including any query string.</p>
* </td>
* </tr>
*
* <tr>
* <tr>
* <td>%X</td>
* <td>
* <p>The connection status when response is completed:</p>
@ -299,14 +276,12 @@ import static java.lang.invoke.MethodType.methodType;
* </dl>
* </td>
* </tr>
*
* <tr>
* <td>%{VARNAME}ti</td>
* <td>
* <p>The value of the VARNAME request trailer.</p>
* </td>
* </tr>
*
* <tr>
* <td>%{VARNAME}to</td>
* <td>
@ -319,12 +294,19 @@ import static java.lang.invoke.MethodType.methodType;
@ManagedObject("Custom format request log")
public class CustomRequestLog extends ContainerLifeCycle implements RequestLog
{
/**
* Record holding extra detail for logging
* @param handlerName The name of the entity that handled the request
* @param realPath The real path on the filesystem represented by the request
*/
public record LogDetail(String handlerName, String realPath)
{
}
public static final String DEFAULT_DATE_FORMAT = "dd/MMM/yyyy:HH:mm:ss ZZZ";
public static final String NCSA_FORMAT = "%{client}a - %u %t \"%r\" %s %O";
public static final String EXTENDED_NCSA_FORMAT = NCSA_FORMAT + " \"%{Referer}i\" \"%{User-Agent}i\"";
public static final String HANDLER_NAME = CustomRequestLog.class.getName() + ".handlerName";
public static final String REAL_PATH = CustomRequestLog.class.getName() + ".realPath";
public static final String USER_NAME = CustomRequestLog.class.getName() + ".userPrincipal";
public static final String LOG_DETAIL = CustomRequestLog.class.getName() + ".logDetail";
private static final Logger LOG = LoggerFactory.getLogger(CustomRequestLog.class);
private static final ThreadLocal<StringBuilder> _buffers = ThreadLocal.withInitial(() -> new StringBuilder(256));
@ -620,81 +602,42 @@ public class CustomRequestLog extends ContainerLifeCycle implements RequestLog
MethodType logTypeArg = methodType(void.class, String.class, StringBuilder.class, Request.class, Response.class);
//TODO should we throw IllegalArgumentExceptions when given arguments for codes which do not take them
MethodHandle specificHandle;
switch (code)
MethodHandle specificHandle = switch (code)
{
case "%":
{
specificHandle = dropArguments(dropArguments(append.bindTo("%"), 1, Request.class), 2, Response.class);
break;
}
case "a":
case "%" -> dropArguments(dropArguments(append.bindTo("%"), 1, Request.class), 2, Response.class);
case "a" ->
{
if (StringUtil.isEmpty(arg))
arg = "server";
String method;
switch (arg)
String method = switch (arg)
{
case "server":
method = "logServerHost";
break;
case "server" -> "logServerHost";
case "client" -> "logClientHost";
case "local" -> "logLocalHost";
case "remote" -> "logRemoteHost";
default -> throw new IllegalArgumentException("Invalid arg for %a");
};
case "client":
method = "logClientHost";
break;
case "local":
method = "logLocalHost";
break;
case "remote":
method = "logRemoteHost";
break;
default:
throw new IllegalArgumentException("Invalid arg for %a");
}
specificHandle = lookup.findStatic(CustomRequestLog.class, method, logType);
break;
yield lookup.findStatic(CustomRequestLog.class, method, logType);
}
case "p":
case "p" ->
{
if (StringUtil.isEmpty(arg))
arg = "server";
String method;
switch (arg)
String method = switch (arg)
{
case "server" -> "logServerPort";
case "client" -> "logClientPort";
case "local" -> "logLocalPort";
case "remote" -> "logRemotePort";
default -> throw new IllegalArgumentException("Invalid arg for %p");
};
case "server":
method = "logServerPort";
break;
case "client":
method = "logClientPort";
break;
case "local":
method = "logLocalPort";
break;
case "remote":
method = "logRemotePort";
break;
default:
throw new IllegalArgumentException("Invalid arg for %p");
}
specificHandle = lookup.findStatic(CustomRequestLog.class, method, logType);
break;
yield lookup.findStatic(CustomRequestLog.class, method, logType);
}
case "I":
case "I" ->
{
String method;
if (StringUtil.isEmpty(arg))
@ -704,11 +647,9 @@ public class CustomRequestLog extends ContainerLifeCycle implements RequestLog
else
throw new IllegalArgumentException("Invalid argument for %I");
specificHandle = lookup.findStatic(CustomRequestLog.class, method, logType);
break;
yield lookup.findStatic(CustomRequestLog.class, method, logType);
}
case "O":
case "O" ->
{
String method;
if (StringUtil.isEmpty(arg))
@ -718,11 +659,9 @@ public class CustomRequestLog extends ContainerLifeCycle implements RequestLog
else
throw new IllegalArgumentException("Invalid argument for %O");
specificHandle = lookup.findStatic(CustomRequestLog.class, method, logType);
break;
yield lookup.findStatic(CustomRequestLog.class, method, logType);
}
case "S":
case "S" ->
{
String method;
if (StringUtil.isEmpty(arg))
@ -732,109 +671,49 @@ public class CustomRequestLog extends ContainerLifeCycle implements RequestLog
else
throw new IllegalArgumentException("Invalid argument for %S");
specificHandle = lookup.findStatic(CustomRequestLog.class, method, logType);
break;
yield lookup.findStatic(CustomRequestLog.class, method, logType);
}
case "C":
case "C" ->
{
if (StringUtil.isEmpty(arg))
{
specificHandle = lookup.findStatic(CustomRequestLog.class, "logRequestCookies", logType);
yield lookup.findStatic(CustomRequestLog.class, "logRequestCookies", logType);
}
else
{
specificHandle = lookup.findStatic(CustomRequestLog.class, "logRequestCookie", logTypeArg);
specificHandle = specificHandle.bindTo(arg);
yield lookup.findStatic(CustomRequestLog.class, "logRequestCookie", logTypeArg).bindTo(arg);
}
break;
}
case "D":
{
specificHandle = lookup.findStatic(CustomRequestLog.class, "logLatencyMicroseconds", logType);
break;
}
case "e":
case "D" -> lookup.findStatic(CustomRequestLog.class, "logLatencyMicroseconds", logType);
case "e" ->
{
if (StringUtil.isEmpty(arg))
throw new IllegalArgumentException("No arg for %e");
specificHandle = lookup.findStatic(CustomRequestLog.class, "logEnvironmentVar", logTypeArg);
specificHandle = specificHandle.bindTo(arg);
break;
yield lookup.findStatic(CustomRequestLog.class, "logEnvironmentVar", logTypeArg).bindTo(arg);
}
case "f":
{
specificHandle = lookup.findStatic(CustomRequestLog.class, "logFilename", logType);
break;
}
case "H":
{
specificHandle = lookup.findStatic(CustomRequestLog.class, "logRequestProtocol", logType);
break;
}
case "i":
case "f" -> lookup.findStatic(CustomRequestLog.class, "logFilename", logType);
case "H" -> lookup.findStatic(CustomRequestLog.class, "logRequestProtocol", logType);
case "i" ->
{
if (StringUtil.isEmpty(arg))
throw new IllegalArgumentException("No arg for %i");
specificHandle = lookup.findStatic(CustomRequestLog.class, "logRequestHeader", logTypeArg);
specificHandle = specificHandle.bindTo(arg);
break;
yield lookup.findStatic(CustomRequestLog.class, "logRequestHeader", logTypeArg).bindTo(arg);
}
case "k":
{
specificHandle = lookup.findStatic(CustomRequestLog.class, "logKeepAliveRequests", logType);
break;
}
case "m":
{
specificHandle = lookup.findStatic(CustomRequestLog.class, "logRequestMethod", logType);
break;
}
case "o":
case "k" -> lookup.findStatic(CustomRequestLog.class, "logKeepAliveRequests", logType);
case "m" -> lookup.findStatic(CustomRequestLog.class, "logRequestMethod", logType);
case "o" ->
{
if (StringUtil.isEmpty(arg))
throw new IllegalArgumentException("No arg for %o");
specificHandle = lookup.findStatic(CustomRequestLog.class, "logResponseHeader", logTypeArg);
specificHandle = specificHandle.bindTo(arg);
break;
yield lookup.findStatic(CustomRequestLog.class, "logResponseHeader", logTypeArg).bindTo(arg);
}
case "q":
{
specificHandle = lookup.findStatic(CustomRequestLog.class, "logQueryString", logType);
break;
}
case "r":
{
specificHandle = lookup.findStatic(CustomRequestLog.class, "logRequestFirstLine", logType);
break;
}
case "R":
{
specificHandle = lookup.findStatic(CustomRequestLog.class, "logRequestHandler", logType);
break;
}
case "s":
{
specificHandle = lookup.findStatic(CustomRequestLog.class, "logResponseStatus", logType);
break;
}
case "t":
case "q" -> lookup.findStatic(CustomRequestLog.class, "logQueryString", logType);
case "r" -> lookup.findStatic(CustomRequestLog.class, "logRequestFirstLine", logType);
case "R" -> lookup.findStatic(CustomRequestLog.class, "logRequestHandler", logType);
case "s" -> lookup.findStatic(CustomRequestLog.class, "logResponseStatus", logType);
case "t" ->
{
String format = DEFAULT_DATE_FORMAT;
TimeZone timeZone = TimeZone.getTimeZone("GMT");
@ -845,60 +724,43 @@ public class CustomRequestLog extends ContainerLifeCycle implements RequestLog
String[] args = arg.split("\\|");
switch (args.length)
{
case 1:
format = args[0];
break;
case 2:
case 1 -> format = args[0];
case 2 ->
{
format = args[0];
timeZone = TimeZone.getTimeZone(args[1]);
break;
case 3:
}
case 3 ->
{
format = args[0];
timeZone = TimeZone.getTimeZone(args[1]);
locale = Locale.forLanguageTag(args[2]);
break;
default:
throw new IllegalArgumentException("Too many \"|\" characters in %t");
}
default -> throw new IllegalArgumentException("Too many \"|\" characters in %t");
}
}
DateCache logDateCache = new DateCache(format, locale, timeZone);
MethodType logTypeDateCache = methodType(void.class, DateCache.class, StringBuilder.class, Request.class, Response.class);
specificHandle = lookup.findStatic(CustomRequestLog.class, "logRequestTime", logTypeDateCache);
specificHandle = specificHandle.bindTo(logDateCache);
break;
yield lookup.findStatic(CustomRequestLog.class, "logRequestTime", logTypeDateCache).bindTo(logDateCache);
}
case "T":
case "T" ->
{
if (arg == null)
arg = "s";
String method;
switch (arg)
String method = switch (arg)
{
case "s":
method = "logLatencySeconds";
break;
case "us":
method = "logLatencyMicroseconds";
break;
case "ms":
method = "logLatencyMilliseconds";
break;
default:
throw new IllegalArgumentException("Invalid arg for %T");
}
case "s" -> "logLatencySeconds";
case "us" -> "logLatencyMicroseconds";
case "ms" -> "logLatencyMilliseconds";
default -> throw new IllegalArgumentException("Invalid arg for %T");
};
specificHandle = lookup.findStatic(CustomRequestLog.class, method, logType);
break;
yield lookup.findStatic(CustomRequestLog.class, method, logType);
}
case "u":
case "u" ->
{
String method;
if (StringUtil.isEmpty(arg))
@ -908,45 +770,26 @@ public class CustomRequestLog extends ContainerLifeCycle implements RequestLog
else
throw new IllegalArgumentException("Invalid arg for %u: " + arg);
specificHandle = lookup.findStatic(CustomRequestLog.class, method, logType);
break;
yield lookup.findStatic(CustomRequestLog.class, method, logType);
}
case "U":
{
specificHandle = lookup.findStatic(CustomRequestLog.class, "logUrlRequestPath", logType);
break;
}
case "X":
{
specificHandle = lookup.findStatic(CustomRequestLog.class, "logConnectionStatus", logType);
break;
}
case "ti":
case "U" -> lookup.findStatic(CustomRequestLog.class, "logUrlRequestPath", logType);
case "X" -> lookup.findStatic(CustomRequestLog.class, "logConnectionStatus", logType);
case "ti" ->
{
if (StringUtil.isEmpty(arg))
throw new IllegalArgumentException("No arg for %ti");
specificHandle = lookup.findStatic(CustomRequestLog.class, "logRequestTrailer", logTypeArg);
specificHandle = specificHandle.bindTo(arg);
break;
yield lookup.findStatic(CustomRequestLog.class, "logRequestTrailer", logTypeArg).bindTo(arg);
}
case "to":
case "to" ->
{
if (StringUtil.isEmpty(arg))
throw new IllegalArgumentException("No arg for %to");
specificHandle = lookup.findStatic(CustomRequestLog.class, "logResponseTrailer", logTypeArg);
specificHandle = specificHandle.bindTo(arg);
break;
yield lookup.findStatic(CustomRequestLog.class, "logResponseTrailer", logTypeArg).bindTo(arg);
}
default:
throw new IllegalArgumentException("Unsupported code %" + code);
}
default -> throw new IllegalArgumentException("Unsupported code %" + code);
};
if (modifiers != null && !modifiers.isEmpty())
{
@ -1132,8 +975,8 @@ public class CustomRequestLog extends ContainerLifeCycle implements RequestLog
@SuppressWarnings("unused")
private static void logFilename(StringBuilder b, Request request, Response response)
{
String realPath = (String)request.getAttribute(REAL_PATH);
if (realPath == null)
LogDetail logDetail = (LogDetail)request.getAttribute(LOG_DETAIL);
if (logDetail == null || logDetail.realPath == null)
{
Context context = request.getContext();
Resource baseResource = context.getBaseResource();
@ -1149,7 +992,7 @@ public class CustomRequestLog extends ContainerLifeCycle implements RequestLog
}
else
{
b.append(realPath);
b.append(logDetail.realPath);
}
}
@ -1206,7 +1049,8 @@ public class CustomRequestLog extends ContainerLifeCycle implements RequestLog
@SuppressWarnings("unused")
private static void logRequestHandler(StringBuilder b, Request request, Response response)
{
append(b, (String)request.getAttribute(HANDLER_NAME));
LogDetail logDetail = (LogDetail)request.getAttribute(LOG_DETAIL);
append(b, logDetail == null ? null : logDetail.handlerName);
}
@SuppressWarnings("unused")
@ -1243,14 +1087,15 @@ public class CustomRequestLog extends ContainerLifeCycle implements RequestLog
private static void logLatency(StringBuilder b, Request request, TimeUnit unit)
{
long latency = System.currentTimeMillis() - request.getTimeStamp();
b.append(unit.convert(latency, TimeUnit.MILLISECONDS));
b.append(unit.convert(NanoTime.since(request.getNanoTime()), TimeUnit.NANOSECONDS));
}
@SuppressWarnings("unused")
private static void logRequestAuthentication(StringBuilder b, Request request, Response response)
{
append(b, (String)request.getAttribute(USER_NAME));
Request.AuthenticationState authenticationState = Request.getAuthenticationState(request);
Principal userPrincipal = authenticationState == null ? null : authenticationState.getUserPrincipal();
append(b, userPrincipal == null ? null : userPrincipal.getName());
}
@SuppressWarnings("unused")

View File

@ -11,7 +11,7 @@
// ========================================================================
//
package org.eclipse.jetty.ee;
package org.eclipse.jetty.server;
import java.util.Comparator;
import java.util.Map;
@ -19,6 +19,10 @@ import java.util.function.Predicate;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Interface that can be implemented by ContextHandlers within Environments to allow configuration
* to be passed from the DeploymentManager without dependencies on the Deployment module itself.
*/
public interface Deployable
{
Pattern EE_ENVIRONMENT_NAME_PATTERN = Pattern.compile("ee(\\d*)");

View File

@ -1,279 +0,0 @@
//
// ========================================================================
// 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.server;
/**
* A HttpChannel.Listener that holds a collection of
* other HttpChannel.Listener instances that are efficiently
* invoked without iteration.
* @see AbstractConnector
*/
@Deprecated // TODO update or remove
public class HttpChannelListeners // TODO ??? implements HttpChannel.Listener
{
/* TODO
static final Logger LOG = LoggerFactory.getLogger(HttpChannelListeners.class);
public static HttpChannel.Listener NOOP = new HttpChannel.Listener() {};
private final NotifyRequest onRequestBegin;
private final NotifyRequest onBeforeDispatch;
private final NotifyFailure onDispatchFailure;
private final NotifyRequest onAfterDispatch;
private final NotifyContent onRequestContent;
private final NotifyRequest onRequestContentEnd;
private final NotifyRequest onRequestTrailers;
private final NotifyRequest onRequestEnd;
private final NotifyFailure onRequestFailure;
private final NotifyRequest onResponseBegin;
private final NotifyRequest onResponseCommit;
private final NotifyContent onResponseContent;
private final NotifyRequest onResponseEnd;
private final NotifyFailure onResponseFailure;
private final NotifyRequest onComplete;
public HttpChannelListeners(Collection<HttpChannel.Listener> listeners)
{
try
{
NotifyRequest onRequestBegin = NotifyRequest.NOOP;
NotifyRequest onBeforeDispatch = NotifyRequest.NOOP;
NotifyFailure onDispatchFailure = NotifyFailure.NOOP;
NotifyRequest onAfterDispatch = NotifyRequest.NOOP;
NotifyContent onRequestContent = NotifyContent.NOOP;
NotifyRequest onRequestContentEnd = NotifyRequest.NOOP;
NotifyRequest onRequestTrailers = NotifyRequest.NOOP;
NotifyRequest onRequestEnd = NotifyRequest.NOOP;
NotifyFailure onRequestFailure = NotifyFailure.NOOP;
NotifyRequest onResponseBegin = NotifyRequest.NOOP;
NotifyRequest onResponseCommit = NotifyRequest.NOOP;
NotifyContent onResponseContent = NotifyContent.NOOP;
NotifyRequest onResponseEnd = NotifyRequest.NOOP;
NotifyFailure onResponseFailure = NotifyFailure.NOOP;
NotifyRequest onComplete = NotifyRequest.NOOP;
for (HttpChannel.Listener listener : listeners)
{
if (!listener.getClass().getMethod("onRequestBegin", Request.class).isDefault())
onRequestBegin = combine(onRequestBegin, listener::onRequestBegin);
if (!listener.getClass().getMethod("onBeforeDispatch", Request.class).isDefault())
onBeforeDispatch = combine(onBeforeDispatch, listener::onBeforeDispatch);
if (!listener.getClass().getMethod("onDispatchFailure", Request.class, Throwable.class).isDefault())
onDispatchFailure = combine(onDispatchFailure, listener::onDispatchFailure);
if (!listener.getClass().getMethod("onAfterDispatch", Request.class).isDefault())
onAfterDispatch = combine(onAfterDispatch, listener::onAfterDispatch);
if (!listener.getClass().getMethod("onRequestContent", Request.class, ByteBuffer.class).isDefault())
onRequestContent = combine(onRequestContent, listener::onRequestContent);
if (!listener.getClass().getMethod("onRequestContentEnd", Request.class).isDefault())
onRequestContentEnd = combine(onRequestContentEnd, listener::onRequestContentEnd);
if (!listener.getClass().getMethod("onRequestTrailers", Request.class).isDefault())
onRequestTrailers = combine(onRequestTrailers, listener::onRequestTrailers);
if (!listener.getClass().getMethod("onRequestEnd", Request.class).isDefault())
onRequestEnd = combine(onRequestEnd, listener::onRequestEnd);
if (!listener.getClass().getMethod("onRequestFailure", Request.class, Throwable.class).isDefault())
onRequestFailure = combine(onRequestFailure, listener::onRequestFailure);
if (!listener.getClass().getMethod("onResponseBegin", Request.class).isDefault())
onResponseBegin = combine(onResponseBegin, listener::onResponseBegin);
if (!listener.getClass().getMethod("onResponseCommit", Request.class).isDefault())
onResponseCommit = combine(onResponseCommit, listener::onResponseCommit);
if (!listener.getClass().getMethod("onResponseContent", Request.class, ByteBuffer.class).isDefault())
onResponseContent = combine(onResponseContent, listener::onResponseContent);
if (!listener.getClass().getMethod("onResponseEnd", Request.class).isDefault())
onResponseEnd = combine(onResponseEnd, listener::onResponseEnd);
if (!listener.getClass().getMethod("onResponseFailure", Request.class, Throwable.class).isDefault())
onResponseFailure = combine(onResponseFailure, listener::onResponseFailure);
if (!listener.getClass().getMethod("onComplete", Request.class).isDefault())
onComplete = combine(onComplete, listener::onComplete);
}
this.onRequestBegin = onRequestBegin;
this.onBeforeDispatch = onBeforeDispatch;
this.onDispatchFailure = onDispatchFailure;
this.onAfterDispatch = onAfterDispatch;
this.onRequestContent = onRequestContent;
this.onRequestContentEnd = onRequestContentEnd;
this.onRequestTrailers = onRequestTrailers;
this.onRequestEnd = onRequestEnd;
this.onRequestFailure = onRequestFailure;
this.onResponseBegin = onResponseBegin;
this.onResponseCommit = onResponseCommit;
this.onResponseContent = onResponseContent;
this.onResponseEnd = onResponseEnd;
this.onResponseFailure = onResponseFailure;
this.onComplete = onComplete;
}
catch (Exception e)
{
throw new RuntimeException(e);
}
}
@Override
public void onRequestBegin(Request request)
{
onRequestBegin.onRequest(request);
}
@Override
public void onBeforeDispatch(Request request)
{
onBeforeDispatch.onRequest(request);
}
@Override
public void onDispatchFailure(Request request, Throwable failure)
{
onDispatchFailure.onFailure(request, failure);
}
@Override
public void onAfterDispatch(Request request)
{
onAfterDispatch.onRequest(request);
}
@Override
public void onRequestContent(Request request, ByteBuffer content)
{
onRequestContent.onContent(request, content);
}
@Override
public void onRequestContentEnd(Request request)
{
onRequestContentEnd.onRequest(request);
}
@Override
public void onRequestTrailers(Request request)
{
onRequestTrailers.onRequest(request);
}
@Override
public void onRequestEnd(Request request)
{
onRequestEnd.onRequest(request);
}
@Override
public void onRequestFailure(Request request, Throwable failure)
{
onRequestFailure.onFailure(request, failure);
}
@Override
public void onResponseBegin(Request request)
{
onResponseBegin.onRequest(request);
}
@Override
public void onResponseCommit(Request request)
{
onResponseCommit.onRequest(request);
}
@Override
public void onResponseContent(Request request, ByteBuffer content)
{
onResponseContent.onContent(request, content);
}
@Override
public void onResponseEnd(Request request)
{
onResponseEnd.onRequest(request);
}
@Override
public void onResponseFailure(Request request, Throwable failure)
{
onResponseFailure.onFailure(request, failure);
}
@Override
public void onComplete(Request request)
{
onComplete.onRequest(request);
}
private interface NotifyRequest
{
void onRequest(Request request);
NotifyRequest NOOP = request ->
{
};
}
private interface NotifyFailure
{
void onFailure(Request request, Throwable failure);
NotifyFailure NOOP = (request, failure) ->
{
};
}
private interface NotifyContent
{
void onContent(Request request, ByteBuffer content);
NotifyContent NOOP = (request, content) ->
{
};
}
private static NotifyRequest combine(NotifyRequest first, NotifyRequest second)
{
if (first == NotifyRequest.NOOP)
return second;
if (second == NotifyRequest.NOOP)
return first;
return request ->
{
first.onRequest(request);
second.onRequest(request);
};
}
private static NotifyFailure combine(NotifyFailure first, NotifyFailure second)
{
if (first == NotifyFailure.NOOP)
return second;
if (second == NotifyFailure.NOOP)
return first;
return (request, throwable) ->
{
first.onFailure(request, throwable);
second.onFailure(request, throwable);
};
}
private static NotifyContent combine(NotifyContent first, NotifyContent second)
{
if (first == NotifyContent.NOOP)
return (request, content) -> second.onContent(request, content.slice());
if (second == NotifyContent.NOOP)
return (request, content) -> first.onContent(request, content.slice());
return (request, content) ->
{
content = content.slice();
first.onContent(request, content);
second.onContent(request, content);
};
}
*/
}

View File

@ -19,11 +19,13 @@ import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;
import org.eclipse.jetty.http.CookieCompliance;
import org.eclipse.jetty.http.HttpCookie;
import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.QuotedCSVParser;
import org.eclipse.jetty.http.Syntax;
import org.eclipse.jetty.util.Attributes;
import org.eclipse.jetty.util.Index;
@ -404,6 +406,52 @@ public final class HttpCookieUtils
return oldPath.equals(newPath);
}
/**
* Get a {@link HttpHeader#SET_COOKIE} field as a {@link HttpCookie}, either
* by optimally checking for a {@link SetCookieHttpField} or by parsing
* the value with {@link #parseSetCookie(String)}.
* @param field The field
* @return The field value as a {@link HttpCookie} or null if the field
* is not a {@link HttpHeader#SET_COOKIE} or cannot be parsed.
*/
public static HttpCookie getSetCookie(HttpField field)
{
if (field == null || field.getHeader() != HttpHeader.SET_COOKIE)
return null;
if (field instanceof SetCookieHttpField setCookieHttpField)
return setCookieHttpField.getHttpCookie();
return parseSetCookie(field.getValue());
}
public static HttpCookie parseSetCookie(String value)
{
AtomicReference<HttpCookie.Builder> builder = new AtomicReference<>();
new QuotedCSVParser(false)
{
@Override
protected void parsedParam(StringBuffer buffer, int valueLength, int paramName, int paramValue)
{
String name = buffer.substring(paramName, paramValue - 1);
String value = buffer.substring(paramValue);
HttpCookie.Builder b = builder.get();
if (b == null)
{
b = HttpCookie.build(name, value);
builder.set(b);
}
else
{
b.attribute(name, value);
}
}
}.addValue(value);
HttpCookie.Builder b = builder.get();
if (b == null)
return null;
return b.build();
}
private static void quoteIfNeededAndAppend(String text, StringBuilder builder)
{
if (isQuoteNeeded(text))
@ -416,19 +464,32 @@ public final class HttpCookieUtils
{
}
/**
* A {@link HttpField} that holds an {@link HttpHeader#SET_COOKIE} as a
* {@link HttpCookie} instance, delaying any value generation until
* {@link #getValue()} is called.
*/
public static class SetCookieHttpField extends HttpField
{
private final HttpCookie _cookie;
private final CookieCompliance _compliance;
public SetCookieHttpField(HttpCookie cookie, CookieCompliance compliance)
{
super(HttpHeader.SET_COOKIE, getSetCookie(cookie, compliance));
super(HttpHeader.SET_COOKIE, HttpHeader.SET_COOKIE.asString(), null);
this._cookie = cookie;
_compliance = compliance;
}
public HttpCookie getHttpCookie()
{
return _cookie;
}
@Override
public String getValue()
{
return getSetCookie(_cookie, _compliance);
}
}
}

View File

@ -19,6 +19,7 @@ import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.security.Principal;
import java.util.List;
import java.util.Locale;
import java.util.function.Function;
@ -743,4 +744,39 @@ public interface Request extends Attributes, Content.Source
.path(URIUtil.addPaths(getContextPath(request), newEncodedPathInContext))
.asImmutable();
}
/**
* @param request The request to enquire.
* @return the minimal {@link AuthenticationState} of the request, or null if no authentication in process.
*/
static AuthenticationState getAuthenticationState(Request request)
{
if (request.getAttribute(AuthenticationState.class.getName()) instanceof AuthenticationState authenticationState)
return authenticationState;
return null;
}
/**
* @param request The request to enquire.
* @param state the {@link AuthenticationState} of the request, or null if no authentication in process.
*/
static void setAuthenticationState(Request request, AuthenticationState state)
{
request.setAttribute(AuthenticationState.class.getName(), state);
}
/**
* A minimal Authentication interface, primarily used for logging. It is implemented by the
* {@code jetty-security} module's {@code AuthenticationState} to provide full authentication services.
*/
interface AuthenticationState
{
/**
* @return The authenticated user {@link Principal}, or null if the Authentication is in a non-authenticated state.
*/
default Principal getUserPrincipal()
{
return null;
}
}
}

View File

@ -51,6 +51,8 @@ import org.eclipse.jetty.util.Uptime;
import org.eclipse.jetty.util.annotation.ManagedAttribute;
import org.eclipse.jetty.util.annotation.Name;
import org.eclipse.jetty.util.component.AttributeContainerMap;
import org.eclipse.jetty.util.component.ClassLoaderDump;
import org.eclipse.jetty.util.component.DumpableAttributes;
import org.eclipse.jetty.util.component.DumpableCollection;
import org.eclipse.jetty.util.component.Environment;
import org.eclipse.jetty.util.component.Graceful;
@ -804,8 +806,10 @@ public class Server extends Handler.Wrapper implements Attributes
@Override
public void dump(Appendable out, String indent) throws IOException
{
dumpObjects(out, indent, new org.eclipse.jetty.util.component.ClassLoaderDump(this.getClass().getClassLoader()),
dumpObjects(out, indent,
new ClassLoaderDump(this.getClass().getClassLoader()),
new DumpableCollection("environments", Environment.getAll()),
new DumpableAttributes("attributes", _attributes),
FileSystemPool.INSTANCE);
}

View File

@ -25,7 +25,6 @@ import java.util.EventListener;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
@ -53,8 +52,7 @@ import org.eclipse.jetty.util.TypeUtil;
import org.eclipse.jetty.util.URIUtil;
import org.eclipse.jetty.util.annotation.ManagedAttribute;
import org.eclipse.jetty.util.component.ClassLoaderDump;
import org.eclipse.jetty.util.component.Dumpable;
import org.eclipse.jetty.util.component.Graceful;
import org.eclipse.jetty.util.component.DumpableAttributes;
import org.eclipse.jetty.util.component.LifeCycle;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.util.resource.ResourceFactory;
@ -63,14 +61,8 @@ import org.eclipse.jetty.util.thread.Invocable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ContextHandler extends Handler.Wrapper implements Attributes, Graceful, AliasCheck
public class ContextHandler extends Handler.Wrapper implements Attributes, AliasCheck
{
// TODO where should the alias checking go?
// TODO add protected paths to ServletContextHandler?
// TODO what about ObjectFactory stuff
// TODO what about a Context logger?
// TODO init param stuff to ServletContextHandler
private static final Logger LOG = LoggerFactory.getLogger(ContextHandler.class);
private static final ThreadLocal<Context> __context = new ThreadLocal<>();
@ -147,10 +139,9 @@ public class ContextHandler extends Handler.Wrapper implements Attributes, Grace
public enum Availability
{
STOPPED, // stopped and can't be made unavailable nor shutdown
STARTING, // starting inside of doStart. It may go to any of the next states.
STARTING, // starting inside doStart. It may go to any of the next states.
AVAILABLE, // running normally
UNAVAILABLE, // Either a startup error or explicit call to setAvailable(false)
SHUTDOWN, // graceful shutdown
}
/**
@ -281,8 +272,8 @@ public class ContextHandler extends Handler.Wrapper implements Attributes, Grace
{
dumpObjects(out, indent,
new ClassLoaderDump(getClassLoader()),
Dumpable.named("context " + this, _context),
Dumpable.named("handler attributes " + this, _persistentAttributes));
new DumpableAttributes("handler attributes", _persistentAttributes),
new DumpableAttributes("attributes", _context));
}
@ManagedAttribute(value = "Context")
@ -583,29 +574,6 @@ public class ContextHandler extends Handler.Wrapper implements Attributes, Grace
}
}
/**
* @return true if this context is shutting down
*/
@ManagedAttribute("true for graceful shutdown, which allows existing requests to complete")
public boolean isShutdown()
{
// TODO
return false;
}
/**
* Set shutdown status. This field allows for graceful shutdown of a context. A started context may be put into non accepting state so that existing
* requests can complete, but no new requests are accepted.
*/
@Override
public CompletableFuture<Void> shutdown()
{
// TODO
CompletableFuture<Void> completableFuture = new CompletableFuture<>();
completableFuture.complete(null);
return completableFuture;
}
/**
* @return false if this context is unavailable (sends 503)
*/
@ -651,15 +619,16 @@ public class ContextHandler extends Handler.Wrapper implements Attributes, Grace
Availability availability = _availability.get();
switch (availability)
{
case STARTING:
case AVAILABLE:
if (!_availability.compareAndSet(availability, Availability.UNAVAILABLE))
continue;
break;
default:
break;
case STARTING, AVAILABLE ->
{
if (_availability.compareAndSet(availability, Availability.UNAVAILABLE))
return;
}
default ->
{
return;
}
}
break;
}
}
}
@ -1160,14 +1129,6 @@ public class ContextHandler extends Handler.Wrapper implements Attributes, Grace
return (H)ContextHandler.this;
}
@Override
public Object getAttribute(String name)
{
// TODO the Attributes.Layer is a little different to previous
// behaviour. We need to verify if that is OK
return super.getAttribute(name);
}
@Override
public Request.Handler getErrorHandler()
{

View File

@ -21,6 +21,8 @@ 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.eclipse.jetty.util.CountingCallback;
import org.eclipse.jetty.util.annotation.ManagedAttribute;
import org.eclipse.jetty.util.component.Graceful;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -32,17 +34,17 @@ public class GracefulHandler extends Handler.Wrapper implements Graceful
{
private static final Logger LOG = LoggerFactory.getLogger(GracefulHandler.class);
private final LongAdder dispatchedStats = new LongAdder();
private final Shutdown shutdown;
private final LongAdder _requests = new LongAdder();
private final Shutdown _shutdown;
public GracefulHandler()
{
shutdown = new Shutdown(this)
_shutdown = new Shutdown(this)
{
@Override
public boolean isShutdownDone()
{
long count = dispatchedStats.sum();
long count = getCurrentRequestCount();
if (LOG.isDebugEnabled())
LOG.debug("isShutdownDone: count {}", count);
return count == 0;
@ -50,6 +52,12 @@ public class GracefulHandler extends Handler.Wrapper implements Graceful
};
}
@ManagedAttribute("number of requests being currently handled")
public long getCurrentRequestCount()
{
return _requests.sum();
}
/**
* Flag indicating that Graceful shutdown has been initiated.
*
@ -59,7 +67,7 @@ public class GracefulHandler extends Handler.Wrapper implements Graceful
@Override
public boolean isShutdown()
{
return shutdown.isShutdown();
return _shutdown.isShutdown();
}
@Override
@ -86,18 +94,18 @@ public class GracefulHandler extends Handler.Wrapper implements Graceful
{
boolean handled = super.handle(request, response, shutdownCallback);
if (!handled)
shutdownCallback.decrement();
shutdownCallback.completed();
return handled;
}
catch (Throwable t)
{
shutdownCallback.decrement();
throw t;
Response.writeError(request, response, shutdownCallback, t);
return true;
}
finally
{
if (isShutdown())
shutdown.check();
_shutdown.check();
}
}
@ -106,43 +114,28 @@ public class GracefulHandler extends Handler.Wrapper implements Graceful
{
if (LOG.isDebugEnabled())
LOG.debug("Shutdown requested");
return shutdown.shutdown();
return _shutdown.shutdown();
}
private class ShutdownTrackingCallback extends Callback.Nested
private class ShutdownTrackingCallback extends CountingCallback
{
final Request request;
final Response response;
public ShutdownTrackingCallback(Request request, Response response, Callback callback)
{
super(callback);
super(callback, 1);
this.request = request;
this.response = response;
dispatchedStats.increment();
}
public void decrement()
{
dispatchedStats.decrement();
_requests.increment();
}
@Override
public void failed(Throwable x)
public void completed()
{
decrement();
super.failed(x);
_requests.decrement();
if (isShutdown())
shutdown.check();
}
@Override
public void succeeded()
{
decrement();
super.succeeded();
if (isShutdown())
shutdown.check();
_shutdown.check();
}
}
}

View File

@ -13,22 +13,16 @@
package org.eclipse.jetty.server.internal;
import java.util.EnumSet;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.BiFunction;
import java.util.stream.Stream;
import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpHeaderValue;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
// TODO: review whether it needs to override these many methods, as it may be enough to override iterator().
public class ResponseHttpFields implements HttpFields.Mutable
{
private static final Logger LOG = LoggerFactory.getLogger(ResponseHttpFields.class);
@ -85,52 +79,12 @@ public class ResponseHttpFields implements HttpFields.Mutable
return _fields.asImmutable();
}
@Override
public Mutable add(String name, String value)
{
return _committed.get() ? this : _fields.add(name, value);
}
@Override
public Mutable add(HttpHeader header, HttpHeaderValue value)
{
return _fields.add(header, value);
}
@Override
public Mutable add(HttpHeader header, String value)
{
return _committed.get() ? this : _fields.add(header, value);
}
@Override
public Mutable add(HttpField field)
{
return _committed.get() ? this : _fields.add(field);
}
@Override
public Mutable add(HttpFields fields)
{
return _committed.get() ? this : _fields.add(fields);
}
@Override
public Mutable addCSV(HttpHeader header, String... values)
{
return _committed.get() ? this : _fields.addCSV(header, values);
}
@Override
public Mutable addCSV(String name, String... values)
{
return _committed.get() ? this : _fields.addCSV(name, values);
}
@Override
public Mutable addDateField(String name, long date)
{
return _committed.get() ? this : _fields.addDateField(name, date);
if (field != null && !_committed.get())
_fields.add(field);
return this;
}
@Override
@ -231,109 +185,27 @@ public class ResponseHttpFields implements HttpFields.Mutable
}
@Override
public void set(HttpField httpField)
public void set(HttpField field)
{
if (_committed.get())
throw new UnsupportedOperationException("Read Only");
i.set(httpField);
if (field == null)
i.remove();
else
i.set(field);
}
@Override
public void add(HttpField httpField)
public void add(HttpField field)
{
if (_committed.get())
throw new UnsupportedOperationException("Read Only");
i.add(httpField);
if (field != null)
i.add(field);
}
};
}
@Override
public Mutable put(HttpField field)
{
return _committed.get() ? this : _fields.put(field);
}
@Override
public Mutable put(String name, String value)
{
return _committed.get() ? this : _fields.put(name, value);
}
@Override
public Mutable put(HttpHeader header, HttpHeaderValue value)
{
return _committed.get() ? this : _fields.put(header, value);
}
@Override
public Mutable put(HttpHeader header, String value)
{
return _committed.get() ? this : _fields.put(header, value);
}
@Override
public Mutable put(String name, List<String> list)
{
return _committed.get() ? this : _fields.put(name, list);
}
@Override
public Mutable putDate(HttpHeader name, long date)
{
return _committed.get() ? this : _fields.putDate(name, date);
}
@Override
public Mutable putDate(String name, long date)
{
return _committed.get() ? this : _fields.putDate(name, date);
}
@Override
public Mutable put(HttpHeader header, long value)
{
return _committed.get() ? this : _fields.put(header, value);
}
@Override
public Mutable put(String name, long value)
{
return _committed.get() ? this : _fields.put(name, value);
}
@Override
public void computeField(HttpHeader header, BiFunction<HttpHeader, List<HttpField>, HttpField> computeFn)
{
if (!_committed.get())
_fields.computeField(header, computeFn);
}
@Override
public void computeField(String name, BiFunction<String, List<HttpField>, HttpField> computeFn)
{
if (!_committed.get())
_fields.computeField(name, computeFn);
}
@Override
public Mutable remove(HttpHeader name)
{
return _committed.get() ? this : _fields.remove(name);
}
@Override
public Mutable remove(EnumSet<HttpHeader> fields)
{
return _committed.get() ? this : _fields.remove(fields);
}
@Override
public Mutable remove(String name)
{
return _committed.get() ? this : _fields.remove(name);
}
@Override
public String toString()
{

View File

@ -39,6 +39,7 @@ import org.eclipse.jetty.io.QuietException;
import org.eclipse.jetty.util.BlockingArrayQueue;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.DateCache;
import org.eclipse.jetty.util.NanoTime;
import org.eclipse.jetty.util.component.LifeCycle;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
@ -570,14 +571,14 @@ public class CustomRequestLogTest
@ValueSource(strings = {"us", "ms", "s"})
public void testLogLatency(String unit) throws Exception
{
long delay = 1000;
long delay = 1500;
AtomicLong requestTimeRef = new AtomicLong();
start("%{" + unit + "}T", new SimpleHandler()
{
@Override
public boolean handle(Request request, Response response, Callback callback) throws Exception
{
requestTimeRef.set(request.getTimeStamp());
requestTimeRef.set(request.getNanoTime());
Thread.sleep(delay);
callback.succeeded();
return true;
@ -596,12 +597,13 @@ public class CustomRequestLogTest
assertEquals(HttpStatus.OK_200, response.getStatus());
String log = _logs.poll(5, TimeUnit.SECONDS);
assertNotNull(log);
long lowerBound = requestTimeRef.get();
long upperBound = System.currentTimeMillis();
long lowerBound = delay / 2;
long upperBound = TimeUnit.MICROSECONDS.convert(NanoTime.since(requestTimeRef.get()), TimeUnit.NANOSECONDS);
long measuredDuration = Long.parseLong(log);
long durationLowerBound = timeUnit.convert(delay, TimeUnit.MILLISECONDS);
long durationUpperBound = timeUnit.convert(upperBound - lowerBound, TimeUnit.MILLISECONDS);
long durationLowerBound = timeUnit.convert(lowerBound, TimeUnit.MILLISECONDS);
long durationUpperBound = timeUnit.convert(upperBound, TimeUnit.MILLISECONDS);
assertThat(measuredDuration, greaterThanOrEqualTo(durationLowerBound));
assertThat(measuredDuration, lessThanOrEqualTo(durationUpperBound));

View File

@ -52,12 +52,11 @@ public class GracefulHandlerTest
{
private static final Logger LOG = LoggerFactory.getLogger(GracefulHandlerTest.class);
private Server server;
private ServerConnector connector;
public Server createServer(Handler handler) throws Exception
{
server = new Server();
connector = new ServerConnector(server, 1, 1);
ServerConnector connector = new ServerConnector(server, 1, 1);
connector.setIdleTimeout(10000);
connector.setShutdownIdleTimeout(1000);
connector.setPort(0);

View File

@ -35,6 +35,7 @@ import org.eclipse.jetty.http.MimeTypes;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.NanoTime;
import org.eclipse.jetty.util.component.LifeCycle;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
@ -158,7 +159,6 @@ public class LargeHeaderTest
output.flush();
String rawResponse = readResponse(client, 1, input);
System.err.println(rawResponse);
assertThat(rawResponse, containsString(" 500 "));
}
}
@ -284,7 +284,11 @@ public class LargeHeaderTest
output.write(rawRequest.formatted(count).getBytes(UTF_8));
output.flush();
long start = NanoTime.now();
String rawResponse = readResponse(client, count, input);
if (NanoTime.secondsSince(start) >= 1)
LOG.warn("X-Count: {} - Slow Response", count);
if (rawResponse.isEmpty())
{
LOG.warn("X-Count: {} - Empty Raw Response", count);

View File

@ -13,9 +13,15 @@
package org.eclipse.jetty.server;
import java.util.ListIterator;
import org.eclipse.jetty.http.HttpCookie;
import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.http.HttpTester;
import org.eclipse.jetty.io.Content;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.component.LifeCycle;
import org.junit.jupiter.api.AfterEach;
@ -23,6 +29,7 @@ import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.is;
import static org.junit.jupiter.api.Assertions.assertEquals;
@ -53,7 +60,7 @@ public class ResponseTest
server.setHandler(new Handler.Abstract()
{
@Override
public boolean handle(Request request, Response response, Callback callback) throws Exception
public boolean handle(Request request, Response response, Callback callback)
{
Response.sendRedirect(request, response, callback, "/somewhere/else");
return true;
@ -89,7 +96,7 @@ public class ResponseTest
server.setHandler(new Handler.Abstract()
{
@Override
public boolean handle(Request request, Response response, Callback callback) throws Exception
public boolean handle(Request request, Response response, Callback callback)
{
Response.sendRedirect(request, response, callback, "/somewhere/else");
return true;
@ -123,7 +130,7 @@ public class ResponseTest
server.setHandler(new Handler.Abstract()
{
@Override
public boolean handle(Request request, Response response, Callback callback) throws Exception
public boolean handle(Request request, Response response, Callback callback)
{
Response.sendRedirect(request, response, callback, "/somewhere/else");
return true;
@ -159,7 +166,7 @@ public class ResponseTest
server.setHandler(new Handler.Abstract()
{
@Override
public boolean handle(Request request, Response response, Callback callback) throws Exception
public boolean handle(Request request, Response response, Callback callback)
{
Response.sendRedirect(request, response, callback, "/somewhere/else");
return true;
@ -186,4 +193,58 @@ public class ResponseTest
assertEquals(HttpStatus.SEE_OTHER_303, response.getStatus());
assertThat(response.get(HttpHeader.LOCATION), is("/somewhere/else"));
}
@Test
public void testHttpCookieProcessing() throws Exception
{
server.setHandler(new Handler.Abstract()
{
@Override
public boolean handle(Request request, Response response, Callback callback)
{
request.addHttpStreamWrapper(httpStream -> new HttpStream.Wrapper(httpStream)
{
@Override
public void prepareResponse(HttpFields.Mutable headers)
{
super.prepareResponse(headers);
for (ListIterator<HttpField> i = headers.listIterator(); i.hasNext();)
{
HttpField field = i.next();
if (field.getHeader() != HttpHeader.SET_COOKIE)
continue;
HttpCookie cookie = HttpCookieUtils.getSetCookie(field);
if (cookie == null)
continue;
i.set(new HttpCookieUtils.SetCookieHttpField(
HttpCookie.build(cookie)
.domain("customized")
.sameSite(HttpCookie.SameSite.LAX)
.build(),
request.getConnectionMetaData().getHttpConfiguration().getResponseCookieCompliance()));
}
}
});
response.setStatus(200);
Response.addCookie(response, HttpCookie.from("name", "test1"));
response.getHeaders().add(HttpHeader.SET_COOKIE, "other=test2; Domain=wrong; SameSite=wrong; Attr=x");
Content.Sink.write(response, true, "OK", callback);
return true;
}
});
server.start();
String request = """
POST /path HTTP/1.0\r
Host: hostname\r
\r
""";
HttpTester.Response response = HttpTester.parseResponse(connector.getResponse(request));
assertEquals(HttpStatus.OK_200, response.getStatus());
assertThat(response.getValuesList(HttpHeader.SET_COOKIE), containsInAnyOrder(
"name=test1; Domain=customized; SameSite=Lax",
"other=test2; Domain=customized; SameSite=Lax; Attr=x"));
}
}

View File

@ -19,20 +19,27 @@ import java.io.Writer;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Stream;
import org.awaitility.Awaitility;
import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.http.HttpTester;
import org.eclipse.jetty.http.HttpURI;
import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.http.MetaData;
import org.eclipse.jetty.http.MimeTypes;
import org.eclipse.jetty.io.Content;
import org.eclipse.jetty.io.QuietException;
import org.eclipse.jetty.logging.StacklessLogging;
import org.eclipse.jetty.server.ConnectionMetaData;
import org.eclipse.jetty.server.Connector;
@ -40,6 +47,7 @@ import org.eclipse.jetty.server.Context;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.HttpChannel;
import org.eclipse.jetty.server.HttpStream;
import org.eclipse.jetty.server.LocalConnector;
import org.eclipse.jetty.server.MockConnectionMetaData;
import org.eclipse.jetty.server.MockConnector;
import org.eclipse.jetty.server.MockHttpStream;
@ -52,6 +60,8 @@ import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.component.Dumpable;
import org.eclipse.jetty.util.component.Graceful;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
@ -891,4 +901,215 @@ public class ContextHandlerTest
assertThat(r, sameInstance(request));
}
}
@Test
public void testGraceful() throws Exception
{
// This is really just another test of GracefulHandler, but good to check it works inside of ContextHandler
CountDownLatch latch0 = new CountDownLatch(1);
CountDownLatch latch1 = new CountDownLatch(1);
CountDownLatch requests = new CountDownLatch(7);
Handler handler = new Handler.Abstract()
{
@Override
public boolean handle(Request request, Response response, Callback callback) throws Exception
{
requests.countDown();
switch (request.getContext().getPathInContext(request.getHttpURI().getCanonicalPath()))
{
case "/ignore0" ->
{
try
{
latch0.await();
}
catch (InterruptedException e)
{
throw new RuntimeException(e);
}
return false;
}
case "/ignore1" ->
{
try
{
latch1.await();
}
catch (InterruptedException e)
{
throw new RuntimeException(e);
}
return false;
}
case "/ok0" ->
{
try
{
latch0.await();
}
catch (InterruptedException e)
{
throw new RuntimeException(e);
}
}
case "/ok1" ->
{
try
{
latch1.await();
}
catch (InterruptedException e)
{
throw new RuntimeException(e);
}
}
case "/fail0" ->
{
try
{
latch0.await();
}
catch (InterruptedException e)
{
throw new RuntimeException(e);
}
throw new QuietException.Exception("expected0");
}
case "/fail1" ->
{
try
{
latch1.await();
}
catch (InterruptedException e)
{
throw new RuntimeException(e);
}
callback.failed(new QuietException.Exception("expected1"));
}
default ->
{
}
}
response.setStatus(HttpStatus.OK_200);
callback.succeeded();
return true;
}
};
_contextHandler.setHandler(handler);
GracefulHandler gracefulHandler = new GracefulHandler();
_contextHandler.insertHandler(gracefulHandler);
LocalConnector connector = new LocalConnector(_server);
_server.addConnector(connector);
_server.start();
HttpTester.Response response = HttpTester.parseResponse(connector.getResponse("GET /ctx/ HTTP/1.0\r\n\r\n"));
assertThat(response.getStatus(), is(HttpStatus.OK_200));
List<LocalConnector.LocalEndPoint> endPoints = new ArrayList<>();
for (String target : new String[] {"/ignore", "/ok", "/fail"})
{
for (int batch = 0; batch <= 1; batch++)
{
LocalConnector.LocalEndPoint endPoint = connector.executeRequest("GET /ctx%s%d HTTP/1.0\r\n\r\n".formatted(target, batch));
endPoints.add(endPoint);
}
}
assertTrue(requests.await(10, TimeUnit.SECONDS));
assertThat(gracefulHandler.getCurrentRequestCount(), is(6L));
CompletableFuture<Void> shutdown = Graceful.shutdown(_contextHandler);
assertFalse(shutdown.isDone());
assertThat(gracefulHandler.getCurrentRequestCount(), is(6L));
response = HttpTester.parseResponse(connector.getResponse("GET /ctx/ HTTP/1.0\r\n\r\n"));
assertThat(response.getStatus(), is(HttpStatus.SERVICE_UNAVAILABLE_503));
latch0.countDown();
response = HttpTester.parseResponse(endPoints.get(0).getResponse());
assertThat(response.getStatus(), is(HttpStatus.NOT_FOUND_404));
response = HttpTester.parseResponse(endPoints.get(2).getResponse());
assertThat(response.getStatus(), is(HttpStatus.OK_200));
response = HttpTester.parseResponse(endPoints.get(4).getResponse());
assertThat(response.getStatus(), is(HttpStatus.INTERNAL_SERVER_ERROR_500));
assertFalse(shutdown.isDone());
Awaitility.waitAtMost(10, TimeUnit.SECONDS).until(() -> gracefulHandler.getCurrentRequestCount() == 3L);
assertThat(gracefulHandler.getCurrentRequestCount(), is(3L));
latch1.countDown();
response = HttpTester.parseResponse(endPoints.get(1).getResponse());
assertThat(response.getStatus(), is(HttpStatus.NOT_FOUND_404));
response = HttpTester.parseResponse(endPoints.get(3).getResponse());
assertThat(response.getStatus(), is(HttpStatus.OK_200));
response = HttpTester.parseResponse(endPoints.get(5).getResponse());
assertThat(response.getStatus(), is(HttpStatus.INTERNAL_SERVER_ERROR_500));
shutdown.get(10, TimeUnit.SECONDS);
assertTrue(shutdown.isDone());
assertThat(gracefulHandler.getCurrentRequestCount(), is(0L));
}
@Test
public void testContextDump() throws Exception
{
Server server = new Server();
ContextHandler contextHandler = new ContextHandler("/ctx");
server.setHandler(contextHandler);
contextHandler.setHandler(new Handler.Abstract()
{
@Override
public boolean handle(Request request, Response response, Callback callback) throws Exception
{
callback.succeeded();
return true;
}
@Override
public String toString()
{
return "TestHandler";
}
});
contextHandler.setAttribute("name", "hidden");
contextHandler.setAttribute("persistent1", "value1");
contextHandler.setAttribute("persistent2", Dumpable.named("named", "value2"));
server.start();
contextHandler.getContext().setAttribute("name", "override");
contextHandler.getContext().setAttribute("transient1", "value1");
contextHandler.getContext().setAttribute("transient2", Dumpable.named("named", "value2"));
String dump = contextHandler.dump().replaceAll("\\r?\\n", "\n");
assertThat(dump, containsString("oejsh.ContextHandler@"));
String expected = """
+> No ClassLoader
+> handler attributes size=3
| +> name: hidden
| +> persistent1: value1
| +> persistent2: named: value2
+> attributes size=5
+> name: override
+> persistent1: value1
+> persistent2: named: value2
+> transient1: value1
+> transient2: named: value2
""";
assertThat(dump, containsString(expected));
}
}

View File

@ -109,6 +109,7 @@ public abstract class AbstractSessionDataStoreTest
//create the SessionDataStore
_sessionIdManager = new DefaultSessionIdManager(_server);
_sessionIdManager.setWorkerName("");
_server.addBean(_sessionIdManager, true);
_sessionManager = new TestableSessionManager();

View File

@ -54,7 +54,6 @@ public class JdbcTestHelper
public static final String LAST_SAVE_COL = "lstime";
public static final String MAP_COL = "mo";
public static final String MAX_IDLE_COL = "mi";
public static final String TABLE = "mysessions";
public static final String ID_COL = "mysessionid";
public static final String ACCESS_COL = "atime";
public static final String CONTEXT_COL = "cpath";
@ -94,12 +93,12 @@ public class JdbcTestHelper
}
}
public static void shutdown(String connectionUrl)
public static void shutdown(String sessionTableName)
throws Exception
{
try (Connection connection = getConnection())
{
connection.prepareStatement("truncate table " + TABLE).executeUpdate();
connection.prepareStatement("truncate table " + sessionTableName).executeUpdate();
}
}
@ -130,24 +129,24 @@ public class JdbcTestHelper
/**
* @return a fresh JDBCSessionDataStoreFactory
*/
public static SessionDataStoreFactory newSessionDataStoreFactory()
public static SessionDataStoreFactory newSessionDataStoreFactory(String sessionTableName)
{
return newSessionDataStoreFactory(buildDatabaseAdaptor());
return newSessionDataStoreFactory(buildDatabaseAdaptor(), sessionTableName);
}
public static SessionDataStoreFactory newSessionDataStoreFactory(DatabaseAdaptor da)
public static SessionDataStoreFactory newSessionDataStoreFactory(DatabaseAdaptor da, String sessionTableName)
{
JDBCSessionDataStoreFactory factory = new JDBCSessionDataStoreFactory();
factory.setDatabaseAdaptor(da);
JDBCSessionDataStore.SessionTableSchema sessionTableSchema = newSessionTableSchema();
JDBCSessionDataStore.SessionTableSchema sessionTableSchema = newSessionTableSchema(sessionTableName);
factory.setSessionTableSchema(sessionTableSchema);
return factory;
}
public static JDBCSessionDataStore.SessionTableSchema newSessionTableSchema()
public static JDBCSessionDataStore.SessionTableSchema newSessionTableSchema(String sessionTableName)
{
JDBCSessionDataStore.SessionTableSchema sessionTableSchema = new JDBCSessionDataStore.SessionTableSchema();
sessionTableSchema.setTableName(TABLE);
sessionTableSchema.setTableName(sessionTableName);
sessionTableSchema.setIdColumn(ID_COL);
sessionTableSchema.setAccessTimeColumn(ACCESS_COL);
sessionTableSchema.setContextPathColumn(CONTEXT_COL);
@ -162,10 +161,10 @@ public class JdbcTestHelper
return sessionTableSchema;
}
public static void prepareTables() throws SQLException
public static void prepareTables(String sessionTableName) throws SQLException
{
DatabaseAdaptor da = buildDatabaseAdaptor();
JDBCSessionDataStore.SessionTableSchema sessionTableSchema = newSessionTableSchema();
JDBCSessionDataStore.SessionTableSchema sessionTableSchema = newSessionTableSchema(sessionTableName);
sessionTableSchema.setDatabaseAdaptor(da);
sessionTableSchema.prepareTables();
}
@ -199,13 +198,13 @@ public class JdbcTestHelper
}
}
public static boolean existsInSessionTable(String id, boolean verbose)
public static boolean existsInSessionTable(String id, boolean verbose, String sessionTableName)
throws Exception
{
try (Connection con = getConnection())
{
PreparedStatement statement = con.prepareStatement("select * from " +
TABLE +
sessionTableName +
" where " + ID_COL + " = ?");
statement.setString(1, id);
ResultSet result = statement.executeQuery();
@ -225,7 +224,7 @@ public class JdbcTestHelper
}
@SuppressWarnings("unchecked")
public static boolean checkSessionPersisted(SessionData data)
public static boolean checkSessionPersisted(SessionData data, String sessionTableName)
throws Exception
{
PreparedStatement statement = null;
@ -233,7 +232,7 @@ public class JdbcTestHelper
try (Connection con = getConnection())
{
statement = con.prepareStatement(
"select * from " + TABLE +
"select * from " + sessionTableName +
" where " + ID_COL + " = ? and " + CONTEXT_COL +
" = ? and virtualHost = ?");
statement.setString(1, data.getId());
@ -293,11 +292,11 @@ public class JdbcTestHelper
return true;
}
public static void insertSession(SessionData data) throws Exception
public static void insertSession(SessionData data, String sessionTableName) throws Exception
{
try (Connection con = getConnection())
{
PreparedStatement statement = con.prepareStatement("insert into " + TABLE +
PreparedStatement statement = con.prepareStatement("insert into " + sessionTableName +
" (" + ID_COL + ", " + CONTEXT_COL + ", virtualHost, " + LAST_NODE_COL +
", " + ACCESS_COL + ", " + LAST_ACCESS_COL + ", " + CREATE_COL + ", " + COOKIE_COL +
", " + LAST_SAVE_COL + ", " + EXPIRY_COL + ", " + MAX_IDLE_COL + "," + MAP_COL + " ) " +
@ -336,12 +335,12 @@ public class JdbcTestHelper
public static void insertUnreadableSession(String id, String contextPath, String vhost,
String lastNode, long created, long accessed,
long lastAccessed, long maxIdle, long expiry,
long cookieSet, long lastSaved)
long cookieSet, long lastSaved, String sessionTableName)
throws Exception
{
try (Connection con = getConnection())
{
PreparedStatement statement = con.prepareStatement("insert into " + TABLE +
PreparedStatement statement = con.prepareStatement("insert into " + sessionTableName +
" (" + ID_COL + ", " + CONTEXT_COL + ", virtualHost, " + LAST_NODE_COL +
", " + ACCESS_COL + ", " + LAST_ACCESS_COL + ", " + CREATE_COL + ", " + COOKIE_COL +
", " + LAST_SAVE_COL + ", " + EXPIRY_COL + ", " + MAX_IDLE_COL + "," + MAP_COL + " ) " +
@ -368,13 +367,13 @@ public class JdbcTestHelper
}
}
public static Set<String> getSessionIds()
public static Set<String> getSessionIds(String sessionTableName)
throws Exception
{
HashSet<String> ids = new HashSet<>();
try (Connection con = getConnection())
{
PreparedStatement statement = con.prepareStatement("select " + ID_COL + " from " + TABLE);
PreparedStatement statement = con.prepareStatement("select " + ID_COL + " from " + sessionTableName);
ResultSet result = statement.executeQuery();
while (result.next())
{

View File

@ -45,7 +45,6 @@ import org.eclipse.jetty.util.NanoTime;
import org.eclipse.jetty.util.thread.Scheduler;
import org.hamcrest.Matchers;
import org.junit.jupiter.api.Assumptions;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import org.slf4j.Logger;
@ -181,7 +180,11 @@ public class HttpClientLoadTest extends AbstractTest
{
ThreadLocalRandom random = ThreadLocalRandom.current();
// Choose a random destination
String host = random.nextBoolean() ? "localhost" : "127.0.0.1";
String host;
if (transport == Transport.H3)
host = "localhost";
else
host = random.nextBoolean() ? "localhost" : "127.0.0.1";
// Choose a random method
HttpMethod method = random.nextBoolean() ? HttpMethod.GET : HttpMethod.POST;

View File

@ -673,6 +673,7 @@ public class HttpClientTest extends AbstractTest
{
assumeTrue(Net.isIpv6InterfaceAvailable());
assumeTrue(transport != Transport.UNIX_DOMAIN);
assumeTrue(transport != Transport.H3);
start(transport, new Handler.Abstract()
{

View File

@ -64,63 +64,51 @@ public interface Attributes
// TODO: change to getAttributeNames() once jetty-core is cleaned of servlet-api usages
Set<String> getAttributeNameSet();
// TODO something better than this
default Map<String, Object> asAttributeMap()
{
return new AbstractMap<>()
{
private final Set<String> _attributeNameSet = getAttributeNameSet();
private final AbstractSet<Entry<String, Object>> _entrySet = new AbstractSet<>()
{
@Override
public Iterator<Entry<String, Object>> iterator()
{
Iterator<String> names = _attributeNameSet.iterator();
return new Iterator<>()
{
@Override
public boolean hasNext()
{
return names.hasNext();
}
@Override
public Entry<String, Object> next()
{
String name = names.next();
return new SimpleEntry<>(name, getAttribute(name));
}
};
}
@Override
public int size()
{
return _attributeNameSet.size();
}
};
@Override
public int size()
{
return _attributeNameSet.size();
}
@Override
public Set<Entry<String, Object>> entrySet()
{
return new AbstractSet<>()
{
Iterator<String> names = getAttributeNameSet().iterator();
@Override
public Iterator<Entry<String, Object>> iterator()
{
return new Iterator<>()
{
@Override
public boolean hasNext()
{
return names.hasNext();
}
@Override
public Entry<String, Object> next()
{
String name = names.next();
return new Map.Entry<>()
{
@Override
public String getKey()
{
return name;
}
@Override
public Object getValue()
{
return getAttribute(name);
}
@Override
public Object setValue(Object value)
{
throw new UnsupportedOperationException();
}
};
}
};
}
@Override
public int size()
{
return 0;
}
};
return _entrySet;
}
};
}

View File

@ -28,7 +28,7 @@ import java.util.concurrent.atomic.AtomicReference;
import org.eclipse.jetty.util.component.Dumpable;
/**
* @deprecated use {@link Attributes.Lazy}
* @deprecated use {@link Attributes.Mapped}
*/
@Deprecated
public class AttributesMap implements Attributes, Dumpable

View File

@ -13,6 +13,7 @@
package org.eclipse.jetty.util;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.function.Consumer;
@ -172,6 +173,12 @@ public interface Callback extends Invocable
{
return invocationType;
}
@Override
public String toString()
{
return "Callback@%x{%s, %s,%s}".formatted(hashCode(), invocationType, success, failure);
}
};
}
@ -183,13 +190,7 @@ public interface Callback extends Invocable
*/
static Callback from(Runnable completed)
{
return new Completing()
{
public void completed()
{
completed.run();
}
};
return from(Invocable.getInvocationType(completed), completed);
}
/**
@ -202,13 +203,25 @@ public interface Callback extends Invocable
*/
static Callback from(InvocationType invocationType, Runnable completed)
{
return new Completing(invocationType)
return new Completing()
{
@Override
public void completed()
{
completed.run();
}
@Override
public InvocationType getInvocationType()
{
return invocationType;
}
@Override
public String toString()
{
return "Callback.Completing@%x{%s,%s}".formatted(hashCode(), invocationType, completed);
}
};
}
@ -309,81 +322,40 @@ public interface Callback extends Invocable
*/
static Callback from(Callback callback1, Callback callback2)
{
return new Callback()
{
@Override
public void succeeded()
{
callback1.succeeded();
callback2.succeeded();
}
@Override
public void failed(Throwable x)
{
callback1.failed(x);
callback2.failed(x);
}
};
return combine(callback1, callback2);
}
/**
* <p>A Callback implementation that calls the {@link #completed()} method when it either succeeds or fails.</p>
*/
class Completing implements Callback
interface Completing extends Callback
{
private final InvocationType invocationType;
public Completing()
{
this(InvocationType.BLOCKING);
}
public Completing(InvocationType invocationType)
{
this.invocationType = invocationType;
}
void completed();
@Override
public void succeeded()
default void succeeded()
{
completed();
}
@Override
public void failed(Throwable x)
default void failed(Throwable x)
{
completed();
}
@Override
public InvocationType getInvocationType()
{
return invocationType;
}
public void completed()
{
}
}
/**
* Nested Completing Callback that completes after
* completing the nested callback
*/
class Nested extends Completing
class Nested implements Completing
{
private final Callback callback;
public Nested(Callback callback)
{
super(Invocable.getInvocationType(callback));
this.callback = callback;
}
public Nested(Nested nested)
{
this(nested.callback);
this.callback = Objects.requireNonNull(callback);
}
public Callback getCallback()
@ -391,6 +363,11 @@ public interface Callback extends Invocable
return callback;
}
@Override
public void completed()
{
}
@Override
public void succeeded()
{
@ -461,7 +438,7 @@ public interface Callback extends Invocable
}
catch (Throwable t)
{
if (x != t)
if (ExceptionUtil.areNotAssociated(x, t))
x.addSuppressed(t);
}
finally

View File

@ -0,0 +1,40 @@
//
// ========================================================================
// 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.util.component;
import java.io.IOException;
import java.util.Map;
import org.eclipse.jetty.util.Attributes;
public class DumpableAttributes implements Dumpable
{
private final String _name;
private final Attributes _attributes;
public DumpableAttributes(String name, Attributes attributes)
{
_name = name;
_attributes = attributes;
}
@Override
public void dump(Appendable out, String indent) throws IOException
{
Object[] array = _attributes.asAttributeMap().entrySet().stream()
.sorted(Map.Entry.comparingByKey())
.map(e -> Dumpable.named(e.getKey(), e.getValue())).toArray(Object[]::new);
Dumpable.dumpObjects(out, indent, _name + " size=" + array.length, array);
}
}

View File

@ -44,12 +44,6 @@ public class DumpableCollection implements Dumpable
return new DumpableCollection(name, collection);
}
@Override
public String dump()
{
return Dumpable.dump(this);
}
@Override
public void dump(Appendable out, String indent) throws IOException
{
@ -57,3 +51,4 @@ public class DumpableCollection implements Dumpable
Dumpable.dumpObjects(out, indent, _name + " size=" + (array == null ? 0 : array.length), array);
}
}

View File

@ -127,7 +127,7 @@ public interface Environment extends Attributes
@Override
public String toString()
{
return "%s@%s{%s}".formatted(TypeUtil.toShortName(this.getClass()), hashCode(), _name);
return "%s@%x{%s}".formatted(TypeUtil.toShortName(this.getClass()), hashCode(), _name);
}
}

View File

@ -108,6 +108,11 @@ public interface Graceful
done.complete(null);
}
/**
* This method can be called after {@link #shutdown()} has been called, but before
* {@link #check()} has been called with {@link #isShutdownDone()} having returned
* true to cancel the effects of the {@link #shutdown()} call.
*/
public void cancel()
{
CompletableFuture<Void> done = _done.get();

View File

@ -0,0 +1,88 @@
//
// ========================================================================
// 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.util.component;
import java.util.concurrent.CancellationException;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicBoolean;
import org.junit.jupiter.api.Test;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.nullValue;
import static org.junit.jupiter.api.Assertions.assertThrows;
public class GracefulShutdownTest
{
@Test
public void testGracefulShutdown() throws Exception
{
AtomicBoolean isShutdown = new AtomicBoolean();
Graceful.Shutdown shutdown = new Graceful.Shutdown("testGracefulShutdown")
{
@Override
public boolean isShutdownDone()
{
return isShutdown.get();
}
};
assertThat(shutdown.isShutdown(), is(false));
shutdown.check();
assertThat(shutdown.isShutdown(), is(false));
CompletableFuture<Void> cf = shutdown.shutdown();
assertThat(shutdown.isShutdown(), is(true));
shutdown.check();
assertThat(shutdown.isShutdown(), is(true));
assertThrows(TimeoutException.class, () -> cf.get(10, TimeUnit.MILLISECONDS));
isShutdown.set(true);
shutdown.check();
assertThat(shutdown.isShutdown(), is(true));
assertThat(cf.get(), nullValue());
}
@Test
public void testGracefulShutdownCancel() throws Exception
{
AtomicBoolean isShutdown = new AtomicBoolean();
Graceful.Shutdown shutdown = new Graceful.Shutdown("testGracefulShutdownCancel")
{
@Override
public boolean isShutdownDone()
{
return isShutdown.get();
}
};
CompletableFuture<Void> cf1 = shutdown.shutdown();
shutdown.cancel();
assertThat(shutdown.isShutdown(), is(false));
assertThrows(CancellationException.class, cf1::get);
CompletableFuture<Void> cf2 = shutdown.shutdown();
assertThat(shutdown.isShutdown(), is(true));
isShutdown.set(true);
assertThrows(TimeoutException.class, () -> cf2.get(10, TimeUnit.MILLISECONDS));
shutdown.check();
assertThat(shutdown.isShutdown(), is(true));
assertThat(cf2.get(), nullValue());
}
}

View File

@ -17,7 +17,6 @@
<module>jetty-client</module>
<module>jetty-deploy</module>
<module>jetty-demos</module>
<module>jetty-ee</module>
<module>jetty-fcgi</module>
<module>jetty-http</module>
<module>jetty-http2</module>

View File

@ -15,8 +15,8 @@ package org.eclipse.jetty.ee10.annotations;
import jakarta.annotation.security.DeclareRoles;
import jakarta.servlet.Servlet;
import org.eclipse.jetty.ee.security.ConstraintAware;
import org.eclipse.jetty.ee10.annotations.AnnotationIntrospector.AbstractIntrospectableAnnotationHandler;
import org.eclipse.jetty.ee10.servlet.security.ConstraintAware;
import org.eclipse.jetty.ee10.servlet.security.ConstraintSecurityHandler;
import org.eclipse.jetty.ee10.webapp.WebAppContext;
import org.slf4j.Logger;

View File

@ -18,11 +18,11 @@ import java.util.List;
import jakarta.servlet.ServletSecurityElement;
import jakarta.servlet.annotation.ServletSecurity;
import org.eclipse.jetty.ee.security.ConstraintAware;
import org.eclipse.jetty.ee.security.ConstraintMapping;
import org.eclipse.jetty.ee10.annotations.AnnotationIntrospector.AbstractIntrospectableAnnotationHandler;
import org.eclipse.jetty.ee10.servlet.ServletHolder;
import org.eclipse.jetty.ee10.servlet.ServletMapping;
import org.eclipse.jetty.ee10.servlet.security.ConstraintAware;
import org.eclipse.jetty.ee10.servlet.security.ConstraintMapping;
import org.eclipse.jetty.ee10.servlet.security.ConstraintSecurityHandler;
import org.eclipse.jetty.ee10.webapp.WebAppContext;
import org.slf4j.Logger;

View File

@ -21,10 +21,10 @@ import jakarta.servlet.annotation.ServletSecurity;
import jakarta.servlet.annotation.ServletSecurity.EmptyRoleSemantic;
import jakarta.servlet.annotation.ServletSecurity.TransportGuarantee;
import jakarta.servlet.http.HttpServlet;
import org.eclipse.jetty.ee.security.ConstraintAware;
import org.eclipse.jetty.ee.security.ConstraintMapping;
import org.eclipse.jetty.ee10.servlet.ServletHolder;
import org.eclipse.jetty.ee10.servlet.ServletMapping;
import org.eclipse.jetty.ee10.servlet.security.ConstraintAware;
import org.eclipse.jetty.ee10.servlet.security.ConstraintMapping;
import org.eclipse.jetty.ee10.servlet.security.ConstraintSecurityHandler;
import org.eclipse.jetty.ee10.webapp.WebAppContext;
import org.eclipse.jetty.security.Constraint;

View File

@ -9,7 +9,7 @@
<body>
<div class="topnav">
<a class="menu" href="http://localhost:8080/">Demo Home</a>
<a class="menu" href="https://github.com/eclipse/jetty.project/tree/jetty-11.0.x/demos/demo-async-rest">Source</a>
<a class="menu" href="https://github.com/eclipse/jetty.project/tree/jetty-12.0.x/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-async-rest">Source</a>
<a class="menu" href="https://www.eclipse.org/jetty/">Jetty Project Home</a>
<a class="menu" href="https://www.eclipse.org/jetty/documentation/current/">Documentation</a>
<a class="menu" href="https://webtide.com">Commercial Support</a>

View File

@ -22,7 +22,6 @@ import java.nio.file.Paths;
import org.eclipse.jetty.deploy.DeploymentManager;
import org.eclipse.jetty.deploy.providers.ContextProvider;
import org.eclipse.jetty.ee.Deployable;
import org.eclipse.jetty.ee10.annotations.AnnotationConfiguration;
import org.eclipse.jetty.ee10.plus.webapp.EnvConfiguration;
import org.eclipse.jetty.ee10.plus.webapp.PlusConfiguration;
@ -36,6 +35,7 @@ import org.eclipse.jetty.rewrite.handler.RewriteHandler;
import org.eclipse.jetty.security.HashLoginService;
import org.eclipse.jetty.server.AsyncRequestLogWriter;
import org.eclipse.jetty.server.CustomRequestLog;
import org.eclipse.jetty.server.Deployable;
import org.eclipse.jetty.server.HttpConfiguration;
import org.eclipse.jetty.server.HttpConnectionFactory;
import org.eclipse.jetty.server.LowResourceMonitor;

View File

@ -16,9 +16,9 @@ package org.eclipse.jetty.ee10.demos;
import java.io.FileNotFoundException;
import java.util.Collections;
import org.eclipse.jetty.ee.security.ConstraintMapping;
import org.eclipse.jetty.ee10.servlet.ServletContextHandler;
import org.eclipse.jetty.ee10.servlet.ServletHolder;
import org.eclipse.jetty.ee10.servlet.security.ConstraintMapping;
import org.eclipse.jetty.ee10.servlet.security.ConstraintSecurityHandler;
import org.eclipse.jetty.security.Constraint;
import org.eclipse.jetty.security.HashLoginService;

View File

@ -9,7 +9,7 @@
<body>
<div class="topnav">
<a class="menu" href="http://localhost:8080/">Demo Home</a>
<a class="menu" href="https://github.com/eclipse/jetty.project/tree/jetty-11.0.x/demos/demo-jaas-webapp">Source</a>
<a class="menu" href="https://github.com/eclipse/jetty.project/tree/jetty-12.0.x/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-jaas-webapp">Source</a>
<a class="menu" href="https://www.eclipse.org/jetty/">Jetty Project Home</a>
<a class="menu" href="https://www.eclipse.org/jetty/documentation/current/">Documentation</a>
<a class="menu" href="https://webtide.com">Commercial Support</a>

View File

@ -26,8 +26,8 @@
<Call name="addRule">
<Arg>
<New class="org.eclipse.jetty.rewrite.handler.RewritePatternRule">
<Arg>/test/rewrite/</Arg>
<Arg>/test/rewrite/info.html</Arg>
<Arg>/ee10-test/rewrite/</Arg>
<Arg>/ee10-test/rewrite/info.html</Arg>
</New>
</Arg>
</Call>
@ -36,8 +36,8 @@
<Call name="addRule">
<Arg>
<New class="org.eclipse.jetty.rewrite.handler.RewritePatternRule">
<Arg>/test/some/old/context</Arg>
<Arg>/test/rewritten/newcontext</Arg>
<Arg>/ee10-test/some/old/context</Arg>
<Arg>/ee10-test/rewritten/newcontext</Arg>
</New>
</Arg>
</Call>
@ -46,8 +46,8 @@
<Call name="addRule">
<Arg>
<New class="org.eclipse.jetty.rewrite.handler.RewritePatternRule">
<Arg>/test/rewrite/for/*</Arg>
<Arg>/test/rewritten/</Arg>
<Arg>/ee10-test/rewrite/for/*</Arg>
<Arg>/ee10-test/rewritten/</Arg>
</New>
</Arg>
</Call>
@ -77,8 +77,8 @@
<Call name="addRule">
<Arg>
<New class="org.eclipse.jetty.rewrite.handler.RedirectPatternRule">
<Arg>/test/redirect/*</Arg>
<Arg>/test/redirected</Arg>
<Arg>/ee10-test/redirect/*</Arg>
<Arg>/ee10-test/redirected</Arg>
</New>
</Arg>
</Call>

View File

@ -20,7 +20,8 @@ ext
ee10-servlets
ee10-websocket-jakarta
ee10-websocket-jetty
ee10-demo-realm
demo-realm
ee10-demo-rewrite
[files]
webapps/ee10-demo-jetty.d/

View File

@ -8,7 +8,7 @@
<body>
<div class="topnav">
<a class="menu" href="http://localhost:8080/">Demo Home</a>
<a class="menu" href="https://github.com/eclipse/jetty.project/tree/jetty-11.0.x/demos/demo-jetty-webapp">Source</a>
<a class="menu" href="https://github.com/eclipse/jetty.project/tree/jetty-12.0.x/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-jetty-webapp">Source</a>
<a class="menu" href="https://www.eclipse.org/jetty/">Jetty Project Home</a>
<a class="menu" href="https://www.eclipse.org/jetty/documentation/current/">Documentation</a>
<a class="menu" href="https://webtide.com">Commercial Support</a>

View File

@ -8,7 +8,7 @@
<body>
<div class="topnav">
<a class="menu" href="http://localhost:8080/">Demo Home</a>
<a class="menu" href="https://github.com/eclipse/jetty.project/tree/jetty-11.0.x/demos/demo-jndi-webapp">Source</a>
<a class="menu" href="https://github.com/eclipse/jetty.project/tree/jetty-12.0.x/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-jndi-webapp">Source</a>
<a class="menu" href="https://www.eclipse.org/jetty/">Jetty Project Home</a>
<a class="menu" href="https://www.eclipse.org/jetty/documentation/current/">Documentation</a>
<a class="menu" href="https://webtide.com">Commercial Support</a>

View File

@ -10,7 +10,7 @@
<div class="topnav">
<a class="menu" href="http://localhost:8080/">Demo Home</a>
<a class="menu" href="https://github.com/eclipse/jetty.project/tree/jetty-11.0.x/demos/demo-jsp-webapp">Source</a>
<a class="menu" href="https://github.com/eclipse/jetty.project/tree/jetty-12.0.x/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-jsp-webapp">Source</a>
<a class="menu" href="https://www.eclipse.org/jetty/">Jetty Project Home</a>
<a class="menu" href="https://www.eclipse.org/jetty/documentation/current/">Documentation</a>
<a class="menu" href="https://webtide.com">Commercial Support</a>

View File

@ -16,7 +16,7 @@ jdbc
ee10-jsp
ee10-annotations
ext
ee10-demo-realm
demo-realm
ee10-demo-mock-resources
[files]

View File

@ -8,7 +8,7 @@
<body>
<div class="topnav">
<a class="menu" href="http://localhost:8080/">Demo Home</a>
<a class="menu" href="https://github.com/eclipse/jetty.project/tree/jetty-11.0.x/demos/demo-spec">Source</a>
<a class="menu" href="https://github.com/eclipse/jetty.project/tree/jetty-12.0.x/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-spec">Source</a>
<a class="menu" href="https://www.eclipse.org/jetty/">Jetty Project Home</a>
<a class="menu" href="https://www.eclipse.org/jetty/documentation/current/">Documentation</a>
<a class="menu" href="https://webtide.com">Commercial Support</a>

View File

@ -10,7 +10,7 @@
<div class="topnav">
<a class="menu" href="http://localhost:8080/">Demo Home</a>
<!-- Change source to suit -->
<a class="menu" href="https://github.com/eclipse/jetty.project/tree/jetty-10.0.x/demos/XXX">Source</a>
<a class="menu" href="https://github.com/eclipse/jetty.project/tree/jetty-12.0.x/jetty-eeX/jetty-eeX-demos/jetty-eeX-XXX">Source</a>
<a class="menu" href="https://www.eclipse.org/jetty/">Jetty Project Home</a>
<a class="menu" href="https://www.eclipse.org/jetty/documentation/current/">Documentation</a>
<a class="menu" href="https://webtide.com">Commercial Support</a>

View File

@ -25,8 +25,8 @@ import jakarta.security.auth.message.config.AuthConfigFactory;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.eclipse.jetty.ee.security.ConstraintMapping;
import org.eclipse.jetty.ee10.servlet.ServletContextHandler;
import org.eclipse.jetty.ee10.servlet.security.ConstraintMapping;
import org.eclipse.jetty.ee10.servlet.security.ConstraintSecurityHandler;
import org.eclipse.jetty.security.AbstractLoginService;
import org.eclipse.jetty.security.Constraint;

View File

@ -202,7 +202,6 @@ public class TestOSGiUtil
res.add(mavenBundle().groupId("org.eclipse.jetty").artifactId("jetty-util").versionAsInProject().start());
res.add(mavenBundle().groupId("org.eclipse.jetty").artifactId("jetty-io").versionAsInProject().start());
res.add(mavenBundle().groupId("org.eclipse.jetty").artifactId("jetty-security").versionAsInProject().start());
res.add(mavenBundle().groupId("org.eclipse.jetty").artifactId("jetty-ee").versionAsInProject().start());
res.add(mavenBundle().groupId("org.eclipse.jetty").artifactId("jetty-server").versionAsInProject().start());
res.add(mavenBundle().groupId("org.eclipse.jetty").artifactId("jetty-session").versionAsInProject().start());
res.add(mavenBundle().groupId("org.eclipse.jetty").artifactId("jetty-deploy").versionAsInProject().start());

View File

@ -30,8 +30,6 @@ import jakarta.servlet.SessionCookieConfig;
import jakarta.servlet.SessionTrackingMode;
import jakarta.servlet.descriptor.JspPropertyGroupDescriptor;
import jakarta.servlet.descriptor.TaglibDescriptor;
import org.eclipse.jetty.ee.security.ConstraintAware;
import org.eclipse.jetty.ee.security.ConstraintMapping;
import org.eclipse.jetty.ee10.annotations.AnnotationConfiguration;
import org.eclipse.jetty.ee10.plus.annotation.LifeCycleCallback;
import org.eclipse.jetty.ee10.plus.annotation.LifeCycleCallbackCollection;
@ -45,6 +43,8 @@ import org.eclipse.jetty.ee10.servlet.ServletHandler;
import org.eclipse.jetty.ee10.servlet.ServletHolder;
import org.eclipse.jetty.ee10.servlet.ServletMapping;
import org.eclipse.jetty.ee10.servlet.Source;
import org.eclipse.jetty.ee10.servlet.security.ConstraintAware;
import org.eclipse.jetty.ee10.servlet.security.ConstraintMapping;
import org.eclipse.jetty.ee10.webapp.AbstractConfiguration;
import org.eclipse.jetty.ee10.webapp.MetaData;
import org.eclipse.jetty.ee10.webapp.MetaData.OriginInfo;

View File

@ -28,11 +28,11 @@ import java.util.List;
import java.util.Locale;
import java.util.Objects;
import org.eclipse.jetty.ee.security.ConstraintMapping;
import org.eclipse.jetty.ee10.plus.webapp.EnvConfiguration;
import org.eclipse.jetty.ee10.plus.webapp.PlusConfiguration;
import org.eclipse.jetty.ee10.servlet.ServletContextHandler;
import org.eclipse.jetty.ee10.servlet.SessionHandler;
import org.eclipse.jetty.ee10.servlet.security.ConstraintMapping;
import org.eclipse.jetty.ee10.servlet.security.ConstraintSecurityHandler;
import org.eclipse.jetty.ee10.webapp.MetaInfConfiguration;
import org.eclipse.jetty.ee10.webapp.WebAppContext;

View File

@ -42,10 +42,6 @@
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-ee</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-session</artifactId>

View File

@ -18,7 +18,6 @@ module org.eclipse.jetty.ee10.servlet
requires org.slf4j;
requires transitive jakarta.servlet;
requires transitive org.eclipse.jetty.ee;
requires transitive org.eclipse.jetty.server;
requires transitive org.eclipse.jetty.security;
requires transitive org.eclipse.jetty.session;

View File

@ -567,8 +567,8 @@ public class ServletApiRequest implements HttpServletRequest
@Override
public <T extends HttpUpgradeHandler> T upgrade(Class<T> handlerClass) throws IOException, ServletException
{
// TODO NYI
return null;
// Not implemented. Throw ServletException as per spec.
throw new ServletException("Not implemented");
}
@Override

View File

@ -38,7 +38,6 @@ import org.eclipse.jetty.http.HttpURI;
import org.eclipse.jetty.io.Connection;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.io.QuietException;
import org.eclipse.jetty.security.AuthenticationState;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.CustomRequestLog;
import org.eclipse.jetty.server.HttpConfiguration;
@ -843,17 +842,12 @@ public class ServletChannel
if (idleTO >= 0 && getIdleTimeout() != _oldIdleTimeout)
setIdleTimeout(_oldIdleTimeout);
if (getServer().getRequestLog() != null)
if (getServer().getRequestLog() instanceof CustomRequestLog)
{
AuthenticationState authenticationState = apiRequest.getAuthentication();
if (authenticationState instanceof AuthenticationState.Succeeded succeededAuthentication)
_servletContextRequest.setAttribute(CustomRequestLog.USER_NAME, succeededAuthentication.getUserIdentity().getUserPrincipal().getName());
String realPath = apiRequest.getServletContext().getRealPath(Request.getPathInContext(_servletContextRequest));
_servletContextRequest.setAttribute(CustomRequestLog.REAL_PATH, realPath);
String servletName = _servletContextRequest.getServletName();
_servletContextRequest.setAttribute(CustomRequestLog.HANDLER_NAME, servletName);
CustomRequestLog.LogDetail logDetail = new CustomRequestLog.LogDetail(
_servletContextRequest.getServletName(),
apiRequest.getServletContext().getRealPath(Request.getPathInContext(_servletContextRequest)));
_servletContextRequest.setAttribute(CustomRequestLog.LOG_DETAIL, logDetail);
}
// Callback will either be succeeded here or failed in abort().

View File

@ -66,8 +66,8 @@ import jakarta.servlet.http.HttpSessionAttributeListener;
import jakarta.servlet.http.HttpSessionBindingListener;
import jakarta.servlet.http.HttpSessionIdListener;
import jakarta.servlet.http.HttpSessionListener;
import org.eclipse.jetty.ee.security.ConstraintAware;
import org.eclipse.jetty.ee.security.ConstraintMapping;
import org.eclipse.jetty.ee10.servlet.security.ConstraintAware;
import org.eclipse.jetty.ee10.servlet.security.ConstraintMapping;
import org.eclipse.jetty.ee10.servlet.security.ConstraintSecurityHandler;
import org.eclipse.jetty.http.HttpURI;
import org.eclipse.jetty.http.pathmap.MatchedResource;
@ -95,7 +95,6 @@ import org.eclipse.jetty.util.component.ContainerLifeCycle;
import org.eclipse.jetty.util.component.Dumpable;
import org.eclipse.jetty.util.component.DumpableCollection;
import org.eclipse.jetty.util.component.Environment;
import org.eclipse.jetty.util.component.Graceful;
import org.eclipse.jetty.util.component.LifeCycle;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.util.resource.ResourceFactory;
@ -120,7 +119,7 @@ import static jakarta.servlet.ServletContext.TEMPDIR;
* cause confusion with {@link ServletContext}.
*/
@ManagedObject("Servlet Context Handler")
public class ServletContextHandler extends ContextHandler implements Graceful
public class ServletContextHandler extends ContextHandler
{
private static final Logger LOG = LoggerFactory.getLogger(ServletContextHandler.class);
public static final Environment __environment = Environment.ensure("ee10");

View File

@ -11,7 +11,7 @@
// ========================================================================
//
package org.eclipse.jetty.ee.security;
package org.eclipse.jetty.ee10.servlet.security;
import java.util.List;
import java.util.Set;

View File

@ -11,7 +11,7 @@
// ========================================================================
//
package org.eclipse.jetty.ee.security;
package org.eclipse.jetty.ee10.servlet.security;
import java.util.Arrays;

View File

@ -32,9 +32,9 @@ import jakarta.servlet.HttpMethodConstraintElement;
import jakarta.servlet.ServletSecurityElement;
import jakarta.servlet.annotation.ServletSecurity.EmptyRoleSemantic;
import jakarta.servlet.annotation.ServletSecurity.TransportGuarantee;
import org.eclipse.jetty.ee.security.ConstraintAware;
import org.eclipse.jetty.ee.security.ConstraintMapping;
import org.eclipse.jetty.ee10.servlet.ServletContextHandler;
import org.eclipse.jetty.ee10.servlet.security.ConstraintAware;
import org.eclipse.jetty.ee10.servlet.security.ConstraintMapping;
import org.eclipse.jetty.http.pathmap.MappedResource;
import org.eclipse.jetty.http.pathmap.MatchedResource;
import org.eclipse.jetty.http.pathmap.PathMappings;

View File

@ -24,7 +24,7 @@ import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.eclipse.jetty.ee.security.ConstraintMapping;
import org.eclipse.jetty.ee10.servlet.security.ConstraintMapping;
import org.eclipse.jetty.ee10.servlet.security.ConstraintSecurityHandler;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.security.Constraint;

View File

@ -34,7 +34,7 @@ import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.eclipse.jetty.client.ContentResponse;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.ee.security.ConstraintMapping;
import org.eclipse.jetty.ee10.servlet.security.ConstraintMapping;
import org.eclipse.jetty.ee10.servlet.security.ConstraintSecurityHandler;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.security.Constraint;

View File

@ -38,11 +38,11 @@ import jakarta.servlet.annotation.ServletSecurity.TransportGuarantee;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.eclipse.jetty.ee.security.ConstraintMapping;
import org.eclipse.jetty.ee10.servlet.ServletContextHandler;
import org.eclipse.jetty.ee10.servlet.ServletHandler;
import org.eclipse.jetty.ee10.servlet.ServletHolder;
import org.eclipse.jetty.ee10.servlet.SessionHandler;
import org.eclipse.jetty.ee10.servlet.security.ConstraintMapping;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.http.HttpTester;

View File

@ -23,9 +23,9 @@ import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.eclipse.jetty.ee.security.ConstraintMapping;
import org.eclipse.jetty.ee10.servlet.ServletContextHandler;
import org.eclipse.jetty.ee10.servlet.SessionHandler;
import org.eclipse.jetty.ee10.servlet.security.ConstraintMapping;
import org.eclipse.jetty.security.Constraint;
import org.eclipse.jetty.security.authentication.BasicAuthenticator;
import org.eclipse.jetty.server.LocalConnector;

View File

@ -37,8 +37,8 @@ import org.eclipse.jetty.client.DigestAuthentication;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.Request;
import org.eclipse.jetty.client.StringRequestContent;
import org.eclipse.jetty.ee.security.ConstraintMapping;
import org.eclipse.jetty.ee10.servlet.ServletContextHandler;
import org.eclipse.jetty.ee10.servlet.security.ConstraintMapping;
import org.eclipse.jetty.ee10.servlet.security.ConstraintSecurityHandler;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.security.AbstractLoginService;

Some files were not shown because too many files have changed in this diff Show More