Drop Undertow support
Spring Framework 7.0 requires a Servlet 6.1 baseline for Servlet containers. Partial Servlet 6.1 support is available on the `main` Undertow branch, but there isn't any milestone version available for the undertow 2.4 generation. At this stage of our 4.0 schedule, we think it's safer to drop Undertow support now. Closes: gh-46917
This commit is contained in:
parent
f79c7d90b2
commit
8c0051a02e
|
@ -116,7 +116,7 @@ The main library providing features that support the other parts of Spring Boot.
|
|||
|
||||
* The `SpringApplication` class, providing static convenience methods that can be used to write a stand-alone Spring Application.
|
||||
Its sole job is to create and refresh an appropriate Spring `ApplicationContext`.
|
||||
* Embedded web applications with a choice of container (Tomcat, Jetty, or Undertow).
|
||||
* Embedded web applications with a choice of container (Tomcat, Jetty).
|
||||
* First-class externalized configuration support.
|
||||
* Convenience `ApplicationContext` initializers, including support for sensible logging defaults.
|
||||
|
||||
|
|
|
@ -128,7 +128,6 @@ def dependenciesOf(String version) {
|
|||
"spring-boot-tomcat",
|
||||
"spring-boot-tracing",
|
||||
"spring-boot-tx",
|
||||
"spring-boot-undertow",
|
||||
"spring-boot-validation",
|
||||
"spring-boot-web-server",
|
||||
"spring-boot-webclient",
|
||||
|
|
|
@ -111,7 +111,6 @@ dependencies {
|
|||
implementation(project(path: ":module:spring-boot-security"))
|
||||
implementation(project(path: ":module:spring-boot-test-autoconfigure"))
|
||||
implementation(project(path: ":module:spring-boot-tomcat"))
|
||||
implementation(project(path: ":module:spring-boot-undertow"))
|
||||
implementation(project(path: ":module:spring-boot-web-server-test"))
|
||||
implementation(project(path: ":module:spring-boot-webclient"))
|
||||
implementation(project(path: ":module:spring-boot-webflux"))
|
||||
|
@ -126,7 +125,6 @@ dependencies {
|
|||
implementation("io.micrometer:micrometer-registry-jmx")
|
||||
implementation("io.opentelemetry.instrumentation:opentelemetry-logback-appender-1.0")
|
||||
implementation("io.projectreactor.netty:reactor-netty-http")
|
||||
implementation("io.undertow:undertow-core")
|
||||
implementation("jakarta.annotation:jakarta.annotation-api")
|
||||
implementation("jakarta.jms:jakarta.jms-api")
|
||||
implementation("jakarta.persistence:jakarta.persistence-api")
|
||||
|
|
|
@ -684,8 +684,6 @@
|
|||
* xref:how-to:webserver.adoc#howto.webserver.configure-http2.netty[#howto.webserver.configure-http2.netty]
|
||||
* xref:how-to:webserver.adoc#howto.webserver.configure-http2.tomcat[#howto-configure-http2-tomcat]
|
||||
* xref:how-to:webserver.adoc#howto.webserver.configure-http2.tomcat[#howto.webserver.configure-http2.tomcat]
|
||||
* xref:how-to:webserver.adoc#howto.webserver.configure-http2.undertow[#howto-configure-http2-undertow]
|
||||
* xref:how-to:webserver.adoc#howto.webserver.configure-http2.undertow[#howto.webserver.configure-http2.undertow]
|
||||
* xref:how-to:webserver.adoc#howto.webserver.configure-http2[#howto-configure-http2]
|
||||
* xref:how-to:webserver.adoc#howto.webserver.configure-http2[#howto.webserver.configure-http2]
|
||||
* xref:how-to:webserver.adoc#howto.webserver.configure-ssl.pem-files[#howto.webserver.configure-ssl.pem-files]
|
||||
|
@ -701,8 +699,6 @@
|
|||
* xref:how-to:webserver.adoc#howto.webserver.discover-port[#howto.webserver.discover-port]
|
||||
* xref:how-to:webserver.adoc#howto.webserver.enable-multiple-connectors-in-tomcat[#howto-enable-multiple-connectors-in-tomcat]
|
||||
* xref:how-to:webserver.adoc#howto.webserver.enable-multiple-connectors-in-tomcat[#howto.webserver.enable-multiple-connectors-in-tomcat]
|
||||
* xref:how-to:webserver.adoc#howto.webserver.enable-multiple-listeners-in-undertow[#howto-enable-multiple-listeners-in-undertow]
|
||||
* xref:how-to:webserver.adoc#howto.webserver.enable-multiple-listeners-in-undertow[#howto.webserver.enable-multiple-listeners-in-undertow]
|
||||
* xref:how-to:webserver.adoc#howto.webserver.enable-response-compression[#how-to-enable-http-response-compression]
|
||||
* xref:how-to:webserver.adoc#howto.webserver.enable-response-compression[#howto.webserver.enable-response-compression]
|
||||
* xref:how-to:webserver.adoc#howto.webserver.enable-tomcat-mbean-registry[#howto-enable-tomcat-mbean-registry]
|
||||
|
|
|
@ -32,8 +32,6 @@ Spring Boot supports the following embedded servlet containers:
|
|||
| Jetty 12.0
|
||||
| 6.0
|
||||
|
||||
| Undertow 2.3
|
||||
| 6.0
|
||||
|===
|
||||
|
||||
You can also deploy Spring Boot applications to any servlet 5.0+ compatible container.
|
||||
|
|
|
@ -179,7 +179,7 @@ web: java -Dserver.port=$PORT -jar target/demo-0.0.1-SNAPSHOT.jar
|
|||
----
|
||||
|
||||
Spring Boot makes `-D` arguments available as properties accessible from a Spring javadoc:org.springframework.core.env.Environment[] instance.
|
||||
The `server.port` configuration property is fed to the embedded Tomcat, Jetty, or Undertow instance, which then uses the port when it starts up.
|
||||
The `server.port` configuration property is fed to the embedded Tomcat or Jetty instance, which then uses the port when it starts up.
|
||||
The `$PORT` environment variable is assigned to us by the Heroku PaaS.
|
||||
|
||||
This should be everything you need.
|
||||
|
|
|
@ -12,8 +12,8 @@ This section answers those questions.
|
|||
|
||||
Many Spring Boot starters include default embedded containers.
|
||||
|
||||
* For servlet stack applications, the `spring-boot-starter-web` includes Tomcat by including `spring-boot-starter-tomcat`, but you can use `spring-boot-starter-jetty` or `spring-boot-starter-undertow` instead.
|
||||
* For reactive stack applications, the `spring-boot-starter-webflux` includes Reactor Netty by including `spring-boot-starter-reactor-netty`, but you can use `spring-boot-starter-tomcat`, `spring-boot-starter-jetty`, or `spring-boot-starter-undertow` instead.
|
||||
* For servlet stack applications, the `spring-boot-starter-web` includes Tomcat by including `spring-boot-starter-tomcat`, but you can use `spring-boot-starter-jetty` instead.
|
||||
* For reactive stack applications, the `spring-boot-starter-webflux` includes Reactor Netty by including `spring-boot-starter-reactor-netty`, but you can use `spring-boot-starter-tomcat` or `spring-boot-starter-jetty` instead.
|
||||
|
||||
When switching to a different HTTP server, you need to swap the default dependencies for those that you need instead.
|
||||
To help with this process, Spring Boot provides a separate starter for each of the supported HTTP servers.
|
||||
|
@ -40,16 +40,16 @@ The following Maven example shows how to exclude Tomcat and include Jetty for Sp
|
|||
</dependency>
|
||||
----
|
||||
|
||||
The following Gradle example configures the necessary dependencies and a {url-gradle-docs}/resolution_rules.html#sec:module_replacement[module replacement] to use Undertow in place of Reactor Netty for Spring WebFlux:
|
||||
The following Gradle example configures the necessary dependencies and a {url-gradle-docs}/resolution_rules.html#sec:module_replacement[module replacement] to use Tomcat in place of Reactor Netty for Spring WebFlux:
|
||||
|
||||
[source,gradle]
|
||||
----
|
||||
dependencies {
|
||||
implementation "org.springframework.boot:spring-boot-starter-undertow"
|
||||
implementation "org.springframework.boot:spring-boot-starter-tomcat"
|
||||
implementation "org.springframework.boot:spring-boot-starter-webflux"
|
||||
modules {
|
||||
module("org.springframework.boot:spring-boot-starter-reactor-netty") {
|
||||
replacedBy("org.springframework.boot:spring-boot-starter-undertow", "Use Undertow instead of Reactor Netty")
|
||||
replacedBy("org.springframework.boot:spring-boot-starter-tomcat", "Use Tomcat instead of Reactor Netty")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -116,7 +116,7 @@ Contrary to a test, application code callbacks are processed early (before the v
|
|||
[[howto.webserver.enable-response-compression]]
|
||||
== Enable HTTP Response Compression
|
||||
|
||||
HTTP response compression is supported by Jetty, Tomcat, Reactor Netty, and Undertow.
|
||||
HTTP response compression is supported by Jetty, Tomcat and Reactor Netty.
|
||||
It can be enabled in `application.properties`, as follows:
|
||||
|
||||
[configprops,yaml]
|
||||
|
@ -221,7 +221,7 @@ These properties should instead be defined using `spring.ssl.bundle.<type>.<name
|
|||
[[howto.webserver.configure-ssl.sni]]
|
||||
=== Configure Server Name Indication
|
||||
|
||||
Tomcat, Netty, and Undertow can be configured to use unique SSL trust material for individual host names to support Server Name Indication (SNI).
|
||||
Tomcat, and Netty can be configured to use unique SSL trust material for individual host names to support Server Name Indication (SNI).
|
||||
SNI configuration is not supported with Jetty, but Jetty can https://eclipse.dev/jetty/documentation/jetty-12/operations-guide/index.html#og-protocols-ssl-sni[automatically set up SNI] if multiple certificates are provided to it.
|
||||
|
||||
Assuming xref:reference:features/ssl.adoc[SSL bundles] named `web`, `web-alt1`, and `web-alt2` have been configured, the following configuration can be used to assign each bundle to a host name served by the embedded web server:
|
||||
|
@ -292,13 +292,6 @@ Developers can choose to import only the required dependencies using a classifie
|
|||
|
||||
|
||||
|
||||
[[howto.webserver.configure-http2.undertow]]
|
||||
=== HTTP/2 With Undertow
|
||||
|
||||
Undertow supports `h2c` and `h2` out of the box.
|
||||
|
||||
|
||||
|
||||
[[howto.webserver.configure]]
|
||||
== Configure the Web Server
|
||||
|
||||
|
@ -309,7 +302,7 @@ See the list of xref:appendix:application-properties/index.adoc[].
|
|||
|
||||
The previous sections covered already many common use cases, such as compression, SSL or HTTP/2.
|
||||
However, if a configuration key does not exist for your use case, you should then look at javadoc:org.springframework.boot.web.server.WebServerFactoryCustomizer[].
|
||||
You can declare such a component and get access to the server factory relevant to your choice: you should select the variant for the chosen Server (Tomcat, Jetty, Reactor Netty, Undertow) and the chosen web stack (servlet or reactive).
|
||||
You can declare such a component and get access to the server factory relevant to your choice: you should select the variant for the chosen Server (Tomcat, Jetty, Reactor Netty) and the chosen web stack (servlet or reactive).
|
||||
|
||||
The example below is for Tomcat with the `spring-boot-starter-web` (servlet stack):
|
||||
|
||||
|
@ -335,10 +328,6 @@ In addition Spring Boot provides:
|
|||
| javadoc:org.springframework.boot.web.embedded.jetty.JettyServletWebServerFactory[]
|
||||
| javadoc:org.springframework.boot.web.embedded.jetty.JettyReactiveWebServerFactory[]
|
||||
|
||||
| Undertow
|
||||
| javadoc:org.springframework.boot.web.embedded.undertow.UndertowServletWebServerFactory[]
|
||||
| javadoc:org.springframework.boot.web.embedded.undertow.UndertowReactiveWebServerFactory[]
|
||||
|
||||
| Reactor
|
||||
| N/A
|
||||
| javadoc:org.springframework.boot.web.embedded.netty.NettyReactiveWebServerFactory[]
|
||||
|
@ -402,7 +391,7 @@ By default, javadoc:org.springframework.boot.web.servlet.ServletComponentScan[fo
|
|||
[[howto.webserver.configure-access-logs]]
|
||||
== Configure Access Logging
|
||||
|
||||
Access logs can be configured for Tomcat, Undertow, and Jetty through their respective namespaces.
|
||||
Access logs can be configured for Tomcat and Jetty through their respective namespaces.
|
||||
|
||||
For instance, the following settings log access on Tomcat with a {url-tomcat-docs}/config/valve.html#Access_Logging[custom pattern].
|
||||
|
||||
|
@ -420,24 +409,6 @@ NOTE: The default location for logs is a `logs` directory relative to the Tomcat
|
|||
By default, the `logs` directory is a temporary directory, so you may want to fix Tomcat's base directory or use an absolute path for the logs.
|
||||
In the preceding example, the logs are available in `my-tomcat/logs` relative to the working directory of the application.
|
||||
|
||||
Access logging for Undertow can be configured in a similar fashion, as shown in the following example:
|
||||
|
||||
[configprops,yaml]
|
||||
----
|
||||
server:
|
||||
undertow:
|
||||
accesslog:
|
||||
enabled: true
|
||||
pattern: "%t %a %r %s (%D milliseconds)"
|
||||
options:
|
||||
server:
|
||||
record-request-start-time: true
|
||||
----
|
||||
|
||||
Note that, in addition to enabling access logging and configuring its pattern, recording request start times has also been enabled.
|
||||
This is required when including the response time (`%D`) in the access log pattern.
|
||||
Logs are stored in a `logs` directory relative to the working directory of the application.
|
||||
You can customize this location by setting the configprop:server.undertow.accesslog.dir[] property.
|
||||
|
||||
Finally, access logging for Jetty can also be configured as follows:
|
||||
|
||||
|
@ -537,14 +508,6 @@ server:
|
|||
|
||||
|
||||
|
||||
[[howto.webserver.enable-multiple-listeners-in-undertow]]
|
||||
== Enable Multiple Listeners with Undertow
|
||||
|
||||
Add an javadoc:org.springframework.boot.web.embedded.undertow.UndertowBuilderCustomizer[] to the javadoc:org.springframework.boot.web.embedded.undertow.UndertowServletWebServerFactory[] and add a listener to the `io.undertow.Undertow.Builder`, as shown in the following example:
|
||||
|
||||
include-code::MyUndertowConfiguration[]
|
||||
|
||||
|
||||
|
||||
[[howto.webserver.create-websocket-endpoints-using-serverendpoint]]
|
||||
== Create WebSocket Endpoints Using @ServerEndpoint
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
[[messaging.websockets]]
|
||||
= WebSockets
|
||||
|
||||
Spring Boot provides WebSockets auto-configuration for embedded Tomcat, Jetty, and Undertow.
|
||||
Spring Boot provides WebSockets auto-configuration for embedded Tomcat and Jetty.
|
||||
If you deploy a war file to a standalone container, Spring Boot assumes that the container is responsible for the configuration of its WebSocket support.
|
||||
|
||||
Spring Framework provides {url-spring-framework-docs}/web/websocket.html[rich WebSocket support] for MVC web applications that can be easily accessed through the `spring-boot-starter-websocket` module.
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
[[web.graceful-shutdown]]
|
||||
= Graceful Shutdown
|
||||
|
||||
Graceful shutdown is enabled by default with all four embedded web servers (Jetty, Reactor Netty, Tomcat, and Undertow) and with both reactive and servlet-based web applications.
|
||||
Graceful shutdown is enabled by default with all four embedded web servers (Jetty, Reactor Netty, and Tomcat) and with both reactive and servlet-based web applications.
|
||||
It occurs as part of closing the application context and is performed in the earliest phase of stopping javadoc:org.springframework.context.SmartLifecycle[] beans.
|
||||
This stop processing uses a timeout which provides a grace period during which existing requests will be allowed to complete but no new requests will be permitted.
|
||||
|
||||
|
@ -26,10 +26,9 @@ The exact way in which new requests are not permitted varies depending on the we
|
|||
Implementations may stop accepting requests at the network layer, or they may return a response with a specific HTTP status code or HTTP header.
|
||||
The use of persistent connections can also change the way that requests stop being accepted.
|
||||
|
||||
TIP: To learn more about the specific method used with your web server, see the `shutDownGracefully` API documentation for javadoc:org.springframework.boot.web.embedded.tomcat.TomcatWebServer#shutDownGracefully(org.springframework.boot.web.server.GracefulShutdownCallback)[], javadoc:org.springframework.boot.web.embedded.netty.NettyWebServer#shutDownGracefully(org.springframework.boot.web.server.GracefulShutdownCallback)[], javadoc:org.springframework.boot.web.embedded.jetty.JettyWebServer#shutDownGracefully(org.springframework.boot.web.server.GracefulShutdownCallback)[] or javadoc:org.springframework.boot.web.embedded.undertow.UndertowWebServer#shutDownGracefully(org.springframework.boot.web.server.GracefulShutdownCallback)[].
|
||||
TIP: To learn more about the specific method used with your web server, see the `shutDownGracefully` API documentation for javadoc:org.springframework.boot.web.embedded.tomcat.TomcatWebServer#shutDownGracefully(org.springframework.boot.web.server.GracefulShutdownCallback)[], javadoc:org.springframework.boot.web.embedded.netty.NettyWebServer#shutDownGracefully(org.springframework.boot.web.server.GracefulShutdownCallback)[], or javadoc:org.springframework.boot.web.embedded.jetty.JettyWebServer#shutDownGracefully(org.springframework.boot.web.server.GracefulShutdownCallback)[].
|
||||
|
||||
Jetty, Reactor Netty, and Tomcat will stop accepting new requests at the network layer.
|
||||
Undertow will accept new connections but respond immediately with a service unavailable (503) response.
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
= Web
|
||||
|
||||
Spring Boot is well suited for web application development.
|
||||
You can create a self-contained HTTP server by using embedded Tomcat, Jetty, Undertow, or Netty.
|
||||
You can create a self-contained HTTP server by using embedded Tomcat, Jetty, or Netty.
|
||||
Most web applications use the `spring-boot-starter-web` module to get up and running quickly.
|
||||
You can also choose to build reactive web applications by using the `spring-boot-starter-webflux` module.
|
||||
|
||||
|
|
|
@ -310,7 +310,7 @@ See xref:io/rest-client.adoc#io.rest-client.apiversioning[] for details.
|
|||
[[web.reactive.reactive-server]]
|
||||
== Embedded Reactive Server Support
|
||||
|
||||
Spring Boot includes support for the following embedded reactive web servers: Reactor Netty, Tomcat, Jetty, and Undertow.
|
||||
Spring Boot includes support for the following embedded reactive web servers: Reactor Netty, Tomcat, and Jetty.
|
||||
Most developers use the appropriate starter to obtain a fully configured instance.
|
||||
By default, the embedded server listens for HTTP requests on port 8080.
|
||||
|
||||
|
@ -345,7 +345,7 @@ The following example shows programmatically setting the port:
|
|||
|
||||
include-code::MyWebServerFactoryCustomizer[]
|
||||
|
||||
javadoc:org.springframework.boot.web.embedded.jetty.JettyReactiveWebServerFactory[], javadoc:org.springframework.boot.web.embedded.netty.NettyReactiveWebServerFactory[], javadoc:org.springframework.boot.web.embedded.tomcat.TomcatReactiveWebServerFactory[], and javadoc:org.springframework.boot.web.embedded.undertow.UndertowReactiveWebServerFactory[] are dedicated variants of javadoc:org.springframework.boot.web.reactive.server.ConfigurableReactiveWebServerFactory[] that have additional customization setter methods for Jetty, Reactor Netty, Tomcat, and Undertow respectively.
|
||||
javadoc:org.springframework.boot.web.embedded.jetty.JettyReactiveWebServerFactory[], javadoc:org.springframework.boot.web.embedded.netty.NettyReactiveWebServerFactory[], and javadoc:org.springframework.boot.web.embedded.tomcat.TomcatReactiveWebServerFactory[] are dedicated variants of javadoc:org.springframework.boot.web.reactive.server.ConfigurableReactiveWebServerFactory[] that have additional customization setter methods for Jetty, Reactor Netty, and Tomcat respectively.
|
||||
The following example shows how to customize javadoc:org.springframework.boot.web.embedded.netty.NettyReactiveWebServerFactory[] that provides access to Reactor Netty-specific configuration options:
|
||||
|
||||
include-code::MyNettyWebServerFactoryCustomizer[]
|
||||
|
|
|
@ -537,7 +537,7 @@ Both the servlet and the filter registrations can be given init parameters by us
|
|||
[[web.servlet.embedded-container]]
|
||||
== Embedded Servlet Container Support
|
||||
|
||||
For servlet application, Spring Boot includes support for embedded https://tomcat.apache.org/[Tomcat], https://www.eclipse.org/jetty/[Jetty], and https://github.com/undertow-io/undertow[Undertow] servers.
|
||||
For servlet application, Spring Boot includes support for embedded https://tomcat.apache.org/[Tomcat] and https://www.eclipse.org/jetty/[Jetty] servers.
|
||||
Most developers use the appropriate starter to obtain a fully configured instance.
|
||||
By default, the embedded server listens for HTTP requests on port `8080`.
|
||||
|
||||
|
@ -605,7 +605,7 @@ TIP: javadoc:org.springframework.boot.web.servlet.ServletComponentScan[format=an
|
|||
|
||||
Under the hood, Spring Boot uses a different type of javadoc:org.springframework.context.ApplicationContext[] for embedded servlet container support.
|
||||
The javadoc:org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext[] is a special type of javadoc:org.springframework.web.context.WebApplicationContext[] that bootstraps itself by searching for a single javadoc:org.springframework.boot.web.servlet.server.ServletWebServerFactory[] bean.
|
||||
Usually a javadoc:org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory[], javadoc:org.springframework.boot.web.embedded.jetty.JettyServletWebServerFactory[], or javadoc:org.springframework.boot.web.embedded.undertow.UndertowServletWebServerFactory[] has been auto-configured.
|
||||
Usually a javadoc:org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory[], or javadoc:org.springframework.boot.web.embedded.jetty.JettyServletWebServerFactory[] has been auto-configured.
|
||||
|
||||
NOTE: You usually do not need to be aware of these implementation classes.
|
||||
Most applications are auto-configured, and the appropriate javadoc:org.springframework.context.ApplicationContext[] and javadoc:org.springframework.boot.web.servlet.server.ServletWebServerFactory[] are created on your behalf.
|
||||
|
@ -635,7 +635,7 @@ Common server settings include:
|
|||
* xref:how-to:webserver.adoc#howto.webserver.enable-response-compression[HTTP compression]
|
||||
|
||||
Spring Boot tries as much as possible to expose common settings, but this is not always possible.
|
||||
For those cases, dedicated namespaces offer server-specific customizations (see `server.tomcat` and `server.undertow`).
|
||||
For those cases, dedicated namespaces offer server-specific customizations (see `server.tomcat`).
|
||||
For instance, xref:how-to:webserver.adoc#howto.webserver.configure-access-logs[access logs] can be configured with specific features of the embedded servlet container.
|
||||
|
||||
TIP: See the javadoc:org.springframework.boot.autoconfigure.web.ServerProperties[] class for a complete list.
|
||||
|
@ -649,7 +649,7 @@ The `SameSite` cookie attribute can be used by web browsers to control if and ho
|
|||
The attribute is particularly relevant for modern web browsers which have started to change the default value that is used when the attribute is missing.
|
||||
|
||||
If you want to change the `SameSite` attribute of your session cookie, you can use the configprop:server.servlet.session.cookie.same-site[] property.
|
||||
This property is supported by auto-configured Tomcat, Jetty and Undertow servers.
|
||||
This property is supported by auto-configured Tomcat and Jetty servers.
|
||||
It is also used to configure Spring Session servlet based javadoc:org.springframework.session.SessionRepository[] beans.
|
||||
|
||||
For example, if you want your session cookie to have a `SameSite` attribute of `None`, you can add the following to your `application.properties` or `application.yaml` file:
|
||||
|
@ -705,7 +705,7 @@ The following example shows programmatically setting the port:
|
|||
|
||||
include-code::MyWebServerFactoryCustomizer[]
|
||||
|
||||
javadoc:org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory[], javadoc:org.springframework.boot.web.embedded.jetty.JettyServletWebServerFactory[] and javadoc:org.springframework.boot.web.embedded.undertow.UndertowServletWebServerFactory[] are dedicated variants of javadoc:org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory[] that have additional customization setter methods for Tomcat, Jetty and Undertow respectively.
|
||||
javadoc:org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory[], and javadoc:org.springframework.boot.web.embedded.jetty.JettyServletWebServerFactory[] are dedicated variants of javadoc:org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory[] that have additional customization setter methods for Tomcat, and Jetty respectively.
|
||||
The following example shows how to customize javadoc:org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory[] that provides access to Tomcat-specific configuration options:
|
||||
|
||||
include-code::MyTomcatWebServerFactoryCustomizer[]
|
||||
|
@ -734,7 +734,5 @@ When running a Spring Boot application that uses an embedded servlet container (
|
|||
An executable war will work when launched with `java -jar`, and will also be deployable to any standard container.
|
||||
JSPs are not supported when using an executable jar.
|
||||
|
||||
* Undertow does not support JSPs.
|
||||
|
||||
* Creating a custom `error.jsp` page does not override the default view for xref:web/servlet.adoc#web.servlet.spring-mvc.error-handling[error handling].
|
||||
xref:web/servlet.adoc#web.servlet.spring-mvc.error-handling.error-pages[Custom error pages] should be used instead.
|
||||
|
|
|
@ -1,38 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.docs.howto.webserver.enablemultiplelistenersinundertow;
|
||||
|
||||
import io.undertow.Undertow.Builder;
|
||||
|
||||
import org.springframework.boot.undertow.servlet.UndertowServletWebServerFactory;
|
||||
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
public class MyUndertowConfiguration {
|
||||
|
||||
@Bean
|
||||
public WebServerFactoryCustomizer<UndertowServletWebServerFactory> undertowListenerCustomizer() {
|
||||
return (factory) -> factory.addBuilderCustomizers(this::addHttpListener);
|
||||
}
|
||||
|
||||
private Builder addHttpListener(Builder builder) {
|
||||
return builder.addHttpListener(8080, "0.0.0.0");
|
||||
}
|
||||
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.docs.howto.webserver.enablemultiplelistenersinundertow
|
||||
|
||||
import io.undertow.Undertow
|
||||
import org.springframework.boot.web.server.WebServerFactoryCustomizer
|
||||
import org.springframework.boot.undertow.UndertowBuilderCustomizer
|
||||
import org.springframework.boot.undertow.servlet.UndertowServletWebServerFactory
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
class MyUndertowConfiguration {
|
||||
|
||||
@Bean
|
||||
fun undertowListenerCustomizer(): WebServerFactoryCustomizer<UndertowServletWebServerFactory> {
|
||||
return WebServerFactoryCustomizer { factory: UndertowServletWebServerFactory ->
|
||||
factory.addBuilderCustomizers(
|
||||
UndertowBuilderCustomizer { builder: Undertow.Builder -> addHttpListener(builder) })
|
||||
}
|
||||
}
|
||||
|
||||
private fun addHttpListener(builder: Undertow.Builder): Undertow.Builder {
|
||||
return builder.addHttpListener(8080, "0.0.0.0")
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -26,7 +26,6 @@ dependencies {
|
|||
testImplementation(project(":module:spring-boot-jetty"))
|
||||
testImplementation(project(":module:spring-boot-tomcat"))
|
||||
testImplementation(project(":test-support:spring-boot-test-support"))
|
||||
testImplementation(project(":module:spring-boot-undertow"))
|
||||
testImplementation("io.projectreactor.netty:reactor-netty-http")
|
||||
testImplementation("org.apache.httpcomponents.client5:httpclient5")
|
||||
testImplementation("org.apache.httpcomponents.core5:httpcore5-reactive")
|
||||
|
@ -36,7 +35,6 @@ dependencies {
|
|||
testImplementation("org.springframework:spring-webmvc")
|
||||
|
||||
testRuntimeOnly("ch.qos.logback:logback-classic")
|
||||
testRuntimeOnly("io.undertow:undertow-servlet")
|
||||
testRuntimeOnly("org.eclipse.jetty.ee11:jetty-ee11-servlets")
|
||||
testRuntimeOnly("org.eclipse.jetty.ee11:jetty-ee11-webapp")
|
||||
}
|
||||
|
|
|
@ -38,7 +38,6 @@ dependencies {
|
|||
testRepository(project(path: ":starter:spring-boot-starter-jackson", configuration: "mavenRepository"))
|
||||
testRepository(project(path: ":starter:spring-boot-starter-parent", configuration: "mavenRepository"))
|
||||
testRepository(project(path: ":starter:spring-boot-starter-tomcat", configuration: "mavenRepository"))
|
||||
testRepository(project(path: ":starter:spring-boot-starter-undertow", configuration: "mavenRepository"))
|
||||
|
||||
testRuntimeOnly(project(":starter:spring-boot-starter-logging"))
|
||||
}
|
||||
|
@ -63,9 +62,7 @@ tasks.register("buildApps", GradleBuild) {
|
|||
"jettyBootJar",
|
||||
"jettyBootWar",
|
||||
"tomcatBootJar",
|
||||
"tomcatBootWar",
|
||||
"undertowBootJar",
|
||||
"undertowBootWar"
|
||||
"tomcatBootWar"
|
||||
]
|
||||
}
|
||||
|
||||
|
@ -75,9 +72,7 @@ intTest {
|
|||
layout.buildDirectory.file("spring-boot-server-tests-app/build/libs/spring-boot-server-tests-app-jetty.war"),
|
||||
layout.buildDirectory.file("spring-boot-server-tests-app/build/libs/spring-boot-server-tests-app-resources.jar"),
|
||||
layout.buildDirectory.file("spring-boot-server-tests-app/build/libs/spring-boot-server-tests-app-tomcat.jar"),
|
||||
layout.buildDirectory.file("spring-boot-server-tests-app/build/libs/spring-boot-server-tests-app-tomcat.war"),
|
||||
layout.buildDirectory.file("spring-boot-server-tests-app/build/libs/spring-boot-server-tests-app-undertow.jar"),
|
||||
layout.buildDirectory.file("spring-boot-server-tests-app/build/libs/spring-boot-server-tests-app-undertow.war")
|
||||
layout.buildDirectory.file("spring-boot-server-tests-app/build/libs/spring-boot-server-tests-app-tomcat.war")
|
||||
)
|
||||
.withPropertyName("applicationArchives")
|
||||
.withPathSensitivity(PathSensitivity.RELATIVE)
|
||||
|
|
|
@ -44,9 +44,6 @@ configurations {
|
|||
tomcat {
|
||||
extendsFrom(app)
|
||||
}
|
||||
undertow {
|
||||
extendsFrom(app)
|
||||
}
|
||||
}
|
||||
|
||||
tasks.register("resourcesJar", Jar) { jar ->
|
||||
|
@ -77,14 +74,13 @@ dependencies {
|
|||
app("org.springframework:spring-web")
|
||||
jetty("org.springframework.boot:spring-boot-starter-jetty")
|
||||
tomcat("org.springframework.boot:spring-boot-starter-tomcat")
|
||||
undertow("org.springframework.boot:spring-boot-starter-undertow")
|
||||
}
|
||||
|
||||
static boolean isWindows() {
|
||||
return File.separatorChar == '\\'
|
||||
}
|
||||
|
||||
["jetty", "tomcat", "undertow"].each { webServer ->
|
||||
["jetty", "tomcat"].each { webServer ->
|
||||
def configurer = { task ->
|
||||
task.dependsOn resourcesJar
|
||||
task.mainClass = "com.example.ResourceHandlingApplication"
|
||||
|
|
|
@ -62,7 +62,7 @@ import org.springframework.web.util.UriTemplateHandler;
|
|||
class EmbeddedServerContainerInvocationContextProvider
|
||||
implements TestTemplateInvocationContextProvider, AfterAllCallback {
|
||||
|
||||
private static final Set<String> CONTAINERS = new HashSet<>(Arrays.asList("jetty", "tomcat", "undertow"));
|
||||
private static final Set<String> CONTAINERS = new HashSet<>(Arrays.asList("jetty", "tomcat"));
|
||||
|
||||
private static final BuildOutput buildOutput = new BuildOutput(
|
||||
EmbeddedServerContainerInvocationContextProvider.class);
|
||||
|
|
|
@ -31,7 +31,6 @@ dependencies {
|
|||
app project(path: ":starter:spring-boot-starter", configuration: "mavenRepository")
|
||||
app project(path: ":starter:spring-boot-starter-actuator", configuration: "mavenRepository")
|
||||
app project(path: ":starter:spring-boot-starter-tomcat", configuration: "mavenRepository")
|
||||
app project(path: ":starter:spring-boot-starter-undertow", configuration: "mavenRepository")
|
||||
app project(path: ":starter:spring-boot-starter-web", configuration: "mavenRepository")
|
||||
app project(path: ":starter:spring-boot-starter-webflux", configuration: "mavenRepository")
|
||||
app project(path: ":build-plugin:spring-boot-gradle-plugin", configuration: "mavenRepository")
|
||||
|
@ -61,8 +60,7 @@ tasks.register("buildReactiveServerApps", GradleBuild) {
|
|||
startParameter.buildCacheEnabled = false
|
||||
tasks = [
|
||||
"nettyServerApp",
|
||||
"tomcatServerApp",
|
||||
"undertowServerApp"
|
||||
"tomcatServerApp"
|
||||
]
|
||||
}
|
||||
|
||||
|
@ -76,8 +74,7 @@ tasks.register("buildServletServerApps", GradleBuild) {
|
|||
dir = layout.buildDirectory.dir("spring-boot-sni-servlet-app")
|
||||
startParameter.buildCacheEnabled = false
|
||||
tasks = [
|
||||
"tomcatServerApp",
|
||||
"undertowServerApp"
|
||||
"tomcatServerApp"
|
||||
]
|
||||
}
|
||||
|
||||
|
@ -97,9 +94,7 @@ intTest {
|
|||
inputs.files(
|
||||
layout.buildDirectory.file("spring-boot-server-tests-app/build/libs/spring-boot-server-tests-app-netty-reactive.jar"),
|
||||
layout.buildDirectory.file("spring-boot-server-tests-app/build/libs/spring-boot-server-tests-app-tomcat-reactive.jar"),
|
||||
layout.buildDirectory.file("spring-boot-server-tests-app/build/libs/spring-boot-server-tests-app-tomcat-servlet.jar"),
|
||||
layout.buildDirectory.file("spring-boot-server-tests-app/build/libs/spring-boot-server-tests-app-undertow-reactive.jar"),
|
||||
layout.buildDirectory.file("spring-boot-server-tests-app/build/libs/spring-boot-server-tests-app-undertow-servlet.jar")
|
||||
layout.buildDirectory.file("spring-boot-server-tests-app/build/libs/spring-boot-server-tests-app-tomcat-servlet.jar")
|
||||
)
|
||||
.withPropertyName("applicationArchives")
|
||||
.withPathSensitivity(PathSensitivity.RELATIVE)
|
||||
|
|
|
@ -42,9 +42,6 @@ configurations {
|
|||
tomcat {
|
||||
extendsFrom(app)
|
||||
}
|
||||
undertow {
|
||||
extendsFrom(app)
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
@ -58,10 +55,9 @@ dependencies {
|
|||
app("org.springframework.boot:spring-boot-webflux")
|
||||
netty("org.springframework.boot:spring-boot-starter-webflux")
|
||||
tomcat("org.springframework.boot:spring-boot-starter-tomcat")
|
||||
undertow("org.springframework.boot:spring-boot-starter-undertow")
|
||||
}
|
||||
|
||||
["netty", "tomcat", "undertow"].each { webServer ->
|
||||
["netty", "tomcat"].each { webServer ->
|
||||
def configurer = { task ->
|
||||
task.mainClass = "org.springframework.boot.sni.server.SniServerApplication"
|
||||
task.classpath = configurations.getByName(webServer)
|
||||
|
|
|
@ -39,9 +39,6 @@ configurations {
|
|||
tomcat {
|
||||
extendsFrom(app)
|
||||
}
|
||||
undertow {
|
||||
extendsFrom(app)
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
@ -58,10 +55,9 @@ dependencies {
|
|||
exclude group: 'org.springframework.boot', module: 'spring-boot-starter-tomcat'
|
||||
}
|
||||
tomcat("org.springframework.boot:spring-boot-starter-tomcat")
|
||||
undertow("org.springframework.boot:spring-boot-starter-undertow")
|
||||
}
|
||||
|
||||
["tomcat", "undertow"].each { webServer ->
|
||||
["tomcat"].each { webServer ->
|
||||
def configurer = { task ->
|
||||
task.mainClass = "org.springframework.boot.sni.server.SniServerApplication"
|
||||
task.classpath = configurations.getByName(webServer)
|
||||
|
|
|
@ -42,7 +42,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
|||
class SniIntegrationTests {
|
||||
|
||||
private static final Map<String, String> SERVER_START_MESSAGES = Map.ofEntries(Map.entry("netty", "Netty started"),
|
||||
Map.entry("tomcat", "Tomcat initialized"), Map.entry("undertow", "starting server: Undertow"));
|
||||
Map.entry("tomcat", "Tomcat initialized"));
|
||||
|
||||
public static final String PRIMARY_SERVER_NAME = "hello.example.com";
|
||||
|
||||
|
@ -53,7 +53,7 @@ class SniIntegrationTests {
|
|||
private static final Network SHARED_NETWORK = Network.newNetwork();
|
||||
|
||||
@ParameterizedTest
|
||||
@CsvSource({ "reactive,netty", "reactive,tomcat", "servlet,tomcat", "reactive,undertow", "servlet,undertow" })
|
||||
@CsvSource({ "reactive,netty", "reactive,tomcat", "servlet,tomcat" })
|
||||
void home(String webStack, String server) {
|
||||
try (ApplicationContainer serverContainer = new ServerApplicationContainer(webStack, server)) {
|
||||
serverContainer.start();
|
||||
|
|
|
@ -259,9 +259,6 @@ dependencies {
|
|||
api(project(":module:spring-boot-tx")) {
|
||||
transitive = false
|
||||
}
|
||||
api(project(":module:spring-boot-undertow")) {
|
||||
transitive = false
|
||||
}
|
||||
api(project(":module:spring-boot-validation")) {
|
||||
transitive = false
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ import org.eclipse.jetty.http.HttpMethod;
|
|||
* HttpMethods} rather than just {@code GET}, {@code POST} and {@code HEAD}. By default
|
||||
* Jetty <a href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=446039">intentionally only
|
||||
* supports a limited set of HTTP methods</a> for error pages, however, Spring Boot
|
||||
* prefers Tomcat, Jetty and Undertow to all behave in the same way.
|
||||
* prefers Tomcat and Jetty to all behave in the same way.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Christoph Dreis
|
||||
|
|
|
@ -38,7 +38,6 @@ dependencies {
|
|||
testImplementation(project(":module:spring-boot-jetty"))
|
||||
testImplementation(project(":module:spring-boot-tomcat"))
|
||||
testImplementation(project(":test-support:spring-boot-test-support"))
|
||||
testImplementation(project(":module:spring-boot-undertow"))
|
||||
testImplementation("org.springframework:spring-webmvc")
|
||||
|
||||
testRuntimeOnly("ch.qos.logback:logback-classic")
|
||||
|
|
|
@ -33,7 +33,6 @@ import org.springframework.boot.testsupport.classpath.ForkedClassPath;
|
|||
import org.springframework.boot.testsupport.web.servlet.DirtiesUrlFactories;
|
||||
import org.springframework.boot.tomcat.autoconfigure.servlet.TomcatServletWebServerAutoConfiguration;
|
||||
import org.springframework.boot.tomcat.servlet.TomcatServletWebServerFactory;
|
||||
import org.springframework.boot.undertow.servlet.UndertowServletWebServerFactory;
|
||||
import org.springframework.boot.web.server.autoconfigure.ServerProperties;
|
||||
import org.springframework.boot.web.server.servlet.context.AnnotationConfigServletWebServerApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
@ -104,8 +103,7 @@ class MultipartAutoConfigurationTests {
|
|||
|
||||
static Stream<Arguments> webServerWithNoMultipartConfigurationArguments() {
|
||||
return Stream.of(Arguments.of("Jetty", WebServerWithNoMultipartJetty.class),
|
||||
Arguments.of("Tomcat", WebServerWithNoMultipartTomcat.class),
|
||||
Arguments.of("Undertow", WebServerWithNoMultipartUndertow.class));
|
||||
Arguments.of("Tomcat", WebServerWithNoMultipartTomcat.class));
|
||||
}
|
||||
|
||||
@ParameterizedTest(name = "{0}")
|
||||
|
@ -121,18 +119,7 @@ class MultipartAutoConfigurationTests {
|
|||
|
||||
static Stream<Arguments> webServerWithAutomatedMultipartConfigurationArguments() {
|
||||
return Stream.of(Arguments.of("Jetty", WebServerWithEverythingJetty.class),
|
||||
Arguments.of("Tomcat", WebServerWithEverythingTomcat.class),
|
||||
Arguments.of("Undertow", WebServerWithEverythingUndertow.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void webServerWithNonAbsoluteMultipartLocationUndertowConfiguration() {
|
||||
this.context = new AnnotationConfigServletWebServerApplicationContext(
|
||||
WebServerWithNonAbsolutePathUndertow.class, BaseConfiguration.class);
|
||||
this.context.getBean(MultipartConfigElement.class);
|
||||
verifyServletWorks();
|
||||
assertThat(this.context.getBean(StandardServletMultipartResolver.class))
|
||||
.isSameAs(this.context.getBean(DispatcherServlet.class).getMultipartResolver());
|
||||
Arguments.of("Tomcat", WebServerWithEverythingTomcat.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -248,21 +235,6 @@ class MultipartAutoConfigurationTests {
|
|||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class WebServerWithNoMultipartUndertow {
|
||||
|
||||
@Bean
|
||||
UndertowServletWebServerFactory webServerFactory() {
|
||||
return new UndertowServletWebServerFactory();
|
||||
}
|
||||
|
||||
@Bean
|
||||
WebController controller() {
|
||||
return new WebController();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@Import({ TomcatServletWebServerAutoConfiguration.class, MultipartAutoConfiguration.class })
|
||||
@EnableConfigurationProperties(MultipartProperties.class)
|
||||
|
@ -338,48 +310,6 @@ class MultipartAutoConfigurationTests {
|
|||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@EnableWebMvc
|
||||
static class WebServerWithEverythingUndertow {
|
||||
|
||||
@Bean
|
||||
MultipartConfigElement multipartConfigElement() {
|
||||
return new MultipartConfigElement("");
|
||||
}
|
||||
|
||||
@Bean
|
||||
UndertowServletWebServerFactory webServerFactory() {
|
||||
return new UndertowServletWebServerFactory();
|
||||
}
|
||||
|
||||
@Bean
|
||||
WebController webController() {
|
||||
return new WebController();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@EnableWebMvc
|
||||
static class WebServerWithNonAbsolutePathUndertow {
|
||||
|
||||
@Bean
|
||||
MultipartConfigElement multipartConfigElement() {
|
||||
return new MultipartConfigElement("test/not-absolute");
|
||||
}
|
||||
|
||||
@Bean
|
||||
UndertowServletWebServerFactory webServerFactory() {
|
||||
return new UndertowServletWebServerFactory();
|
||||
}
|
||||
|
||||
@Bean
|
||||
WebController webController() {
|
||||
return new WebController();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class WebServerWithCustomMultipartResolver {
|
||||
|
||||
|
|
|
@ -1,54 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the License);
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
plugins {
|
||||
id "java-library"
|
||||
id "org.springframework.boot.auto-configuration"
|
||||
id "org.springframework.boot.configuration-properties"
|
||||
id "org.springframework.boot.deployed"
|
||||
id "org.springframework.boot.optional-dependencies"
|
||||
}
|
||||
|
||||
description = "Spring Boot Undertow"
|
||||
|
||||
dependencies {
|
||||
api(project(":module:spring-boot-web-server"))
|
||||
api("io.undertow:undertow-servlet")
|
||||
|
||||
optional(project(":core:spring-boot-autoconfigure"))
|
||||
optional(project(":module:spring-boot-actuator-autoconfigure"))
|
||||
optional("io.undertow:undertow-websockets-jsr")
|
||||
optional("org.springframework:spring-webflux")
|
||||
|
||||
testImplementation(project(":core:spring-boot-test"))
|
||||
testImplementation(project(":test-support:spring-boot-test-support"))
|
||||
testImplementation(testFixtures(project(":core:spring-boot-autoconfigure")))
|
||||
testImplementation(testFixtures(project(":module:spring-boot-web-server")))
|
||||
testImplementation("org.apache.httpcomponents.client5:httpclient5")
|
||||
testImplementation("org.apache.tomcat.embed:tomcat-embed-jasper")
|
||||
|
||||
testRuntimeOnly("ch.qos.logback:logback-classic")
|
||||
testRuntimeOnly("io.projectreactor:reactor-test")
|
||||
testRuntimeOnly("io.projectreactor.netty:reactor-netty-http")
|
||||
testRuntimeOnly("org.eclipse.jetty:jetty-client")
|
||||
testRuntimeOnly("org.eclipse.jetty.http2:jetty-http2-client")
|
||||
testRuntimeOnly("org.eclipse.jetty.http2:jetty-http2-client-transport")
|
||||
testRuntimeOnly("org.springframework:spring-webmvc")
|
||||
}
|
||||
|
||||
test {
|
||||
jvmArgs += "--add-opens=java.base/java.net=ALL-UNNAMED"
|
||||
}
|
|
@ -1,120 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.undertow;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import io.undertow.Undertow;
|
||||
import io.undertow.server.HttpHandler;
|
||||
import io.undertow.server.handlers.accesslog.AccessLogHandler;
|
||||
import io.undertow.server.handlers.accesslog.DefaultAccessLogReceiver;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import org.xnio.OptionMap;
|
||||
import org.xnio.Options;
|
||||
import org.xnio.Xnio;
|
||||
import org.xnio.XnioWorker;
|
||||
|
||||
/**
|
||||
* An {@link HttpHandlerFactory} for an {@link AccessLogHandler}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
class AccessLogHttpHandlerFactory implements HttpHandlerFactory {
|
||||
|
||||
private final File directory;
|
||||
|
||||
private final @Nullable String pattern;
|
||||
|
||||
private final @Nullable String prefix;
|
||||
|
||||
private final @Nullable String suffix;
|
||||
|
||||
private final boolean rotate;
|
||||
|
||||
AccessLogHttpHandlerFactory(File directory, @Nullable String pattern, @Nullable String prefix,
|
||||
@Nullable String suffix, boolean rotate) {
|
||||
this.directory = directory;
|
||||
this.pattern = pattern;
|
||||
this.prefix = prefix;
|
||||
this.suffix = suffix;
|
||||
this.rotate = rotate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpHandler getHandler(@Nullable HttpHandler next) {
|
||||
try {
|
||||
createAccessLogDirectoryIfNecessary();
|
||||
XnioWorker worker = createWorker();
|
||||
String baseName = (this.prefix != null) ? this.prefix : "access_log.";
|
||||
String formatString = (this.pattern != null) ? this.pattern : "common";
|
||||
return new ClosableAccessLogHandler(next, worker,
|
||||
new DefaultAccessLogReceiver(worker, this.directory, baseName, this.suffix, this.rotate),
|
||||
formatString);
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new IllegalStateException("Failed to create AccessLogHandler", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void createAccessLogDirectoryIfNecessary() {
|
||||
if (!this.directory.isDirectory() && !this.directory.mkdirs()) {
|
||||
throw new IllegalStateException("Failed to create access log directory '" + this.directory + "'");
|
||||
}
|
||||
}
|
||||
|
||||
private XnioWorker createWorker() throws IOException {
|
||||
Xnio xnio = Xnio.getInstance(Undertow.class.getClassLoader());
|
||||
return xnio.createWorker(OptionMap.builder().set(Options.THREAD_DAEMON, true).getMap());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Closeable} variant of {@link AccessLogHandler}.
|
||||
*/
|
||||
private static class ClosableAccessLogHandler extends AccessLogHandler implements Closeable {
|
||||
|
||||
private final DefaultAccessLogReceiver accessLogReceiver;
|
||||
|
||||
private final XnioWorker worker;
|
||||
|
||||
ClosableAccessLogHandler(@Nullable HttpHandler next, XnioWorker worker,
|
||||
DefaultAccessLogReceiver accessLogReceiver, String formatString) {
|
||||
super(next, accessLogReceiver, formatString, Undertow.class.getClassLoader());
|
||||
this.worker = worker;
|
||||
this.accessLogReceiver = accessLogReceiver;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
try {
|
||||
this.accessLogReceiver.close();
|
||||
this.worker.shutdown();
|
||||
this.worker.awaitTermination(30, TimeUnit.SECONDS);
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
catch (InterruptedException ex) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,135 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.undertow;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import io.undertow.attribute.RequestHeaderAttribute;
|
||||
import io.undertow.predicate.Predicate;
|
||||
import io.undertow.predicate.Predicates;
|
||||
import io.undertow.server.HttpHandler;
|
||||
import io.undertow.server.HttpServerExchange;
|
||||
import io.undertow.server.handlers.encoding.ContentEncodingRepository;
|
||||
import io.undertow.server.handlers.encoding.EncodingHandler;
|
||||
import io.undertow.server.handlers.encoding.GzipEncodingProvider;
|
||||
import io.undertow.util.Headers;
|
||||
import io.undertow.util.HttpString;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.boot.web.server.Compression;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.util.InvalidMimeTypeException;
|
||||
import org.springframework.util.MimeType;
|
||||
import org.springframework.util.MimeTypeUtils;
|
||||
|
||||
/**
|
||||
* {@link HttpHandlerFactory} that adds a compression handler.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class CompressionHttpHandlerFactory implements HttpHandlerFactory {
|
||||
|
||||
private final Compression compression;
|
||||
|
||||
CompressionHttpHandlerFactory(Compression compression) {
|
||||
this.compression = compression;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable HttpHandler getHandler(@Nullable HttpHandler next) {
|
||||
if (!this.compression.getEnabled()) {
|
||||
return next;
|
||||
}
|
||||
ContentEncodingRepository repository = new ContentEncodingRepository();
|
||||
repository.addEncodingHandler("gzip", new GzipEncodingProvider(), 50,
|
||||
Predicates.and(getCompressionPredicates(this.compression)));
|
||||
return new EncodingHandler(repository).setNext(next);
|
||||
}
|
||||
|
||||
private static Predicate[] getCompressionPredicates(Compression compression) {
|
||||
List<Predicate> predicates = new ArrayList<>();
|
||||
predicates.add(new MaxSizePredicate((int) compression.getMinResponseSize().toBytes()));
|
||||
predicates.add(new CompressibleMimeTypePredicate(compression.getMimeTypes()));
|
||||
if (compression.getExcludedUserAgents() != null) {
|
||||
for (String agent : compression.getExcludedUserAgents()) {
|
||||
RequestHeaderAttribute agentHeader = new RequestHeaderAttribute(new HttpString(HttpHeaders.USER_AGENT));
|
||||
predicates.add(Predicates.not(Predicates.regex(agentHeader, agent)));
|
||||
}
|
||||
}
|
||||
return predicates.toArray(new Predicate[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Predicate used to match specific mime types.
|
||||
*/
|
||||
private static class CompressibleMimeTypePredicate implements Predicate {
|
||||
|
||||
private final List<MimeType> mimeTypes;
|
||||
|
||||
CompressibleMimeTypePredicate(String[] mimeTypes) {
|
||||
this.mimeTypes = new ArrayList<>(mimeTypes.length);
|
||||
for (String mimeTypeString : mimeTypes) {
|
||||
this.mimeTypes.add(MimeTypeUtils.parseMimeType(mimeTypeString));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean resolve(HttpServerExchange value) {
|
||||
String contentType = value.getResponseHeaders().getFirst(HttpHeaders.CONTENT_TYPE);
|
||||
if (contentType != null) {
|
||||
try {
|
||||
MimeType parsed = MimeTypeUtils.parseMimeType(contentType);
|
||||
for (MimeType mimeType : this.mimeTypes) {
|
||||
if (mimeType.isCompatibleWith(parsed)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (InvalidMimeTypeException ex) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Predicate that returns true if the Content-Size of a request is above a given value
|
||||
* or is missing.
|
||||
*/
|
||||
private static class MaxSizePredicate implements Predicate {
|
||||
|
||||
private final Predicate maxContentSize;
|
||||
|
||||
MaxSizePredicate(int size) {
|
||||
this.maxContentSize = Predicates.requestLargerThan(size);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean resolve(HttpServerExchange value) {
|
||||
if (value.getResponseHeaders().contains(Headers.CONTENT_LENGTH)) {
|
||||
return this.maxContentSize.resolve(value);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,116 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.undertow;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Collection;
|
||||
|
||||
import io.undertow.Undertow.Builder;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.boot.web.server.ConfigurableWebServerFactory;
|
||||
|
||||
/**
|
||||
* {@link ConfigurableWebServerFactory} for Undertow-specific features.
|
||||
*
|
||||
* @author Brian Clozel
|
||||
* @since 4.0.0
|
||||
*/
|
||||
public interface ConfigurableUndertowWebServerFactory extends ConfigurableWebServerFactory {
|
||||
|
||||
/**
|
||||
* Set {@link UndertowBuilderCustomizer}s that should be applied to the Undertow
|
||||
* {@link Builder}. Calling this method will replace any existing customizers.
|
||||
* @param customizers the customizers to set
|
||||
* @since 4.0.0
|
||||
*/
|
||||
void setBuilderCustomizers(Collection<? extends UndertowBuilderCustomizer> customizers);
|
||||
|
||||
/**
|
||||
* Add {@link UndertowBuilderCustomizer}s that should be used to customize the
|
||||
* Undertow {@link Builder}.
|
||||
* @param customizers the customizers to add
|
||||
*/
|
||||
void addBuilderCustomizers(UndertowBuilderCustomizer... customizers);
|
||||
|
||||
/**
|
||||
* Set the buffer size.
|
||||
* @param bufferSize buffer size
|
||||
*/
|
||||
void setBufferSize(@Nullable Integer bufferSize);
|
||||
|
||||
/**
|
||||
* Set the number of IO Threads.
|
||||
* @param ioThreads number of IO Threads
|
||||
*/
|
||||
void setIoThreads(@Nullable Integer ioThreads);
|
||||
|
||||
/**
|
||||
* Set the number of Worker Threads.
|
||||
* @param workerThreads number of Worker Threads
|
||||
*/
|
||||
void setWorkerThreads(@Nullable Integer workerThreads);
|
||||
|
||||
/**
|
||||
* Set whether direct buffers should be used.
|
||||
* @param useDirectBuffers whether direct buffers should be used
|
||||
*/
|
||||
void setUseDirectBuffers(@Nullable Boolean useDirectBuffers);
|
||||
|
||||
/**
|
||||
* Set the access log directory.
|
||||
* @param accessLogDirectory access log directory
|
||||
*/
|
||||
void setAccessLogDirectory(@Nullable File accessLogDirectory);
|
||||
|
||||
/**
|
||||
* Set the access log pattern.
|
||||
* @param accessLogPattern access log pattern
|
||||
*/
|
||||
void setAccessLogPattern(@Nullable String accessLogPattern);
|
||||
|
||||
/**
|
||||
* Set the access log prefix.
|
||||
* @param accessLogPrefix log prefix
|
||||
*/
|
||||
void setAccessLogPrefix(@Nullable String accessLogPrefix);
|
||||
|
||||
/**
|
||||
* Set the access log suffix.
|
||||
* @param accessLogSuffix access log suffix
|
||||
*/
|
||||
void setAccessLogSuffix(@Nullable String accessLogSuffix);
|
||||
|
||||
/**
|
||||
* Set whether access logs are enabled.
|
||||
* @param accessLogEnabled whether access logs are enabled
|
||||
*/
|
||||
void setAccessLogEnabled(boolean accessLogEnabled);
|
||||
|
||||
/**
|
||||
* Set whether access logs rotation is enabled.
|
||||
* @param accessLogRotate whether access logs rotation is enabled
|
||||
*/
|
||||
void setAccessLogRotate(boolean accessLogRotate);
|
||||
|
||||
/**
|
||||
* Set if x-forward-* headers should be processed.
|
||||
* @param useForwardHeaders if x-forward headers should be used
|
||||
*/
|
||||
void setUseForwardHeaders(boolean useForwardHeaders);
|
||||
|
||||
}
|
|
@ -1,49 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.undertow;
|
||||
|
||||
import java.io.Closeable;
|
||||
|
||||
import io.undertow.server.HttpHandler;
|
||||
import io.undertow.server.handlers.GracefulShutdownHandler;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.boot.undertow.servlet.UndertowServletWebServer;
|
||||
|
||||
/**
|
||||
* Factory used by {@link UndertowServletWebServer} to add {@link HttpHandler
|
||||
* HttpHandlers}. Instances returned from this factory may optionally implement the
|
||||
* following interfaces:
|
||||
* <ul>
|
||||
* <li>{@link Closeable} - if they wish to be closed just before server stops.</li>
|
||||
* <li>{@link GracefulShutdownHandler} - if they wish to manage graceful shutdown.</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 4.0.0
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface HttpHandlerFactory {
|
||||
|
||||
/**
|
||||
* Create the {@link HttpHandler} instance that should be added.
|
||||
* @param next the next handler in the chain
|
||||
* @return the new HTTP handler instance
|
||||
*/
|
||||
@Nullable HttpHandler getHandler(@Nullable HttpHandler next);
|
||||
|
||||
}
|
|
@ -1,94 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.undertow;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
|
||||
import io.undertow.Undertow;
|
||||
import io.undertow.protocols.ssl.SNIContextMatcher;
|
||||
import io.undertow.protocols.ssl.SNISSLContext;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import org.xnio.Options;
|
||||
import org.xnio.Sequence;
|
||||
import org.xnio.SslClientAuthMode;
|
||||
|
||||
import org.springframework.boot.ssl.SslBundle;
|
||||
import org.springframework.boot.ssl.SslOptions;
|
||||
import org.springframework.boot.web.server.Ssl.ClientAuth;
|
||||
|
||||
/**
|
||||
* {@link UndertowBuilderCustomizer} that configures SSL on the given builder instance.
|
||||
*
|
||||
* @author Brian Clozel
|
||||
* @author Raheela Aslam
|
||||
* @author Cyril Dangerville
|
||||
* @author Scott Frederick
|
||||
*/
|
||||
class SslBuilderCustomizer implements UndertowBuilderCustomizer {
|
||||
|
||||
private final int port;
|
||||
|
||||
private final @Nullable InetAddress address;
|
||||
|
||||
private final @Nullable ClientAuth clientAuth;
|
||||
|
||||
private final SslBundle sslBundle;
|
||||
|
||||
private final Map<String, SslBundle> serverNameSslBundles;
|
||||
|
||||
SslBuilderCustomizer(int port, @Nullable InetAddress address, @Nullable ClientAuth clientAuth, SslBundle sslBundle,
|
||||
Map<String, SslBundle> serverNameSslBundles) {
|
||||
this.port = port;
|
||||
this.address = address;
|
||||
this.clientAuth = clientAuth;
|
||||
this.sslBundle = sslBundle;
|
||||
this.serverNameSslBundles = serverNameSslBundles;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void customize(Undertow.Builder builder) {
|
||||
SslOptions options = this.sslBundle.getOptions();
|
||||
builder.addHttpsListener(this.port, getListenAddress(), createSslContext());
|
||||
builder.setSocketOption(Options.SSL_CLIENT_AUTH_MODE, ClientAuth.map(this.clientAuth,
|
||||
SslClientAuthMode.NOT_REQUESTED, SslClientAuthMode.REQUESTED, SslClientAuthMode.REQUIRED));
|
||||
if (options.getEnabledProtocols() != null) {
|
||||
builder.setSocketOption(Options.SSL_ENABLED_PROTOCOLS, Sequence.of(options.getEnabledProtocols()));
|
||||
}
|
||||
if (options.getCiphers() != null) {
|
||||
builder.setSocketOption(Options.SSL_ENABLED_CIPHER_SUITES, Sequence.of(options.getCiphers()));
|
||||
}
|
||||
}
|
||||
|
||||
private SSLContext createSslContext() {
|
||||
SNIContextMatcher.Builder builder = new SNIContextMatcher.Builder();
|
||||
builder.setDefaultContext(this.sslBundle.createSslContext());
|
||||
this.serverNameSslBundles
|
||||
.forEach((serverName, sslBundle) -> builder.addMatch(serverName, sslBundle.createSslContext()));
|
||||
return new SNISSLContext(builder.build());
|
||||
}
|
||||
|
||||
private String getListenAddress() {
|
||||
if (this.address == null) {
|
||||
return "0.0.0.0";
|
||||
}
|
||||
return this.address.getHostAddress();
|
||||
}
|
||||
|
||||
}
|
|
@ -1,39 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.undertow;
|
||||
|
||||
import io.undertow.Undertow.Builder;
|
||||
|
||||
import org.springframework.boot.undertow.servlet.UndertowServletWebServerFactory;
|
||||
|
||||
/**
|
||||
* Callback interface that can be used to customize an Undertow {@link Builder}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @since 4.0.0
|
||||
* @see UndertowServletWebServerFactory
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface UndertowBuilderCustomizer {
|
||||
|
||||
/**
|
||||
* Customize the builder.
|
||||
* @param builder the {@code Builder} to customize
|
||||
*/
|
||||
void customize(Builder builder);
|
||||
|
||||
}
|
|
@ -1,484 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.undertow;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.SocketAddress;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import io.undertow.Undertow;
|
||||
import io.undertow.server.HttpHandler;
|
||||
import io.undertow.server.HttpServerExchange;
|
||||
import io.undertow.server.handlers.GracefulShutdownHandler;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import org.xnio.channels.BoundChannel;
|
||||
|
||||
import org.springframework.aot.hint.RuntimeHints;
|
||||
import org.springframework.aot.hint.RuntimeHintsRegistrar;
|
||||
import org.springframework.boot.web.server.GracefulShutdownCallback;
|
||||
import org.springframework.boot.web.server.GracefulShutdownResult;
|
||||
import org.springframework.boot.web.server.PortInUseException;
|
||||
import org.springframework.boot.web.server.WebServer;
|
||||
import org.springframework.boot.web.server.WebServerException;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* {@link WebServer} that can be used to control an Undertow web server. Usually this
|
||||
* class should be created using a {@link ConfigurableUndertowWebServerFactory} and not
|
||||
* directly.
|
||||
*
|
||||
* @author Ivan Sopov
|
||||
* @author Andy Wilkinson
|
||||
* @author Eddú Meléndez
|
||||
* @author Christoph Dreis
|
||||
* @author Brian Clozel
|
||||
* @since 4.0.0
|
||||
*/
|
||||
public class UndertowWebServer implements WebServer {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(UndertowWebServer.class);
|
||||
|
||||
private final AtomicReference<@Nullable GracefulShutdownCallback> gracefulShutdownCallback = new AtomicReference<>();
|
||||
|
||||
private final Object monitor = new Object();
|
||||
|
||||
private final Undertow.Builder builder;
|
||||
|
||||
private final Iterable<HttpHandlerFactory> httpHandlerFactories;
|
||||
|
||||
private final boolean autoStart;
|
||||
|
||||
private @Nullable Undertow undertow;
|
||||
|
||||
private volatile boolean started = false;
|
||||
|
||||
private volatile @Nullable GracefulShutdownHandler gracefulShutdown;
|
||||
|
||||
private volatile @Nullable List<Closeable> closeables;
|
||||
|
||||
/**
|
||||
* Create a new {@link UndertowWebServer} instance.
|
||||
* @param builder the builder
|
||||
* @param autoStart if the server should be started
|
||||
*/
|
||||
public UndertowWebServer(Undertow.Builder builder, boolean autoStart) {
|
||||
this(builder, Collections.singleton(new CloseableHttpHandlerFactory(null)), autoStart);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link UndertowWebServer} instance.
|
||||
* @param builder the builder
|
||||
* @param httpHandlerFactories the handler factories
|
||||
* @param autoStart if the server should be started
|
||||
* @since 4.0.0
|
||||
*/
|
||||
public UndertowWebServer(Undertow.Builder builder, Iterable<HttpHandlerFactory> httpHandlerFactories,
|
||||
boolean autoStart) {
|
||||
this.builder = builder;
|
||||
this.httpHandlerFactories = httpHandlerFactories;
|
||||
this.autoStart = autoStart;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() throws WebServerException {
|
||||
synchronized (this.monitor) {
|
||||
if (this.started) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if (!this.autoStart) {
|
||||
return;
|
||||
}
|
||||
if (this.undertow == null) {
|
||||
this.undertow = createUndertowServer();
|
||||
}
|
||||
this.undertow.start();
|
||||
this.started = true;
|
||||
String message = getStartedLogMessage();
|
||||
logger.info(message);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
try {
|
||||
PortInUseException.ifPortBindingException(ex, (bindException) -> {
|
||||
List<Port> failedPorts = getConfiguredPorts();
|
||||
failedPorts.removeAll(getActualPorts());
|
||||
if (failedPorts.size() == 1) {
|
||||
throw new PortInUseException(failedPorts.get(0).getNumber());
|
||||
}
|
||||
});
|
||||
throw new WebServerException("Unable to start embedded Undertow", ex);
|
||||
}
|
||||
finally {
|
||||
destroySilently();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void destroySilently() {
|
||||
try {
|
||||
if (this.undertow != null) {
|
||||
this.undertow.stop();
|
||||
Assert.state(this.closeables != null, "'closeables' must not be null");
|
||||
this.closeables.forEach(this::closeSilently);
|
||||
}
|
||||
}
|
||||
catch (Exception ex) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
|
||||
private void closeSilently(Closeable closeable) {
|
||||
try {
|
||||
closeable.close();
|
||||
}
|
||||
catch (Exception ex) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
|
||||
private Undertow createUndertowServer() {
|
||||
this.closeables = new ArrayList<>();
|
||||
this.gracefulShutdown = null;
|
||||
HttpHandler handler = createHttpHandler();
|
||||
this.builder.setHandler(handler);
|
||||
return this.builder.build();
|
||||
}
|
||||
|
||||
protected @Nullable HttpHandler createHttpHandler() {
|
||||
HttpHandler handler = null;
|
||||
for (HttpHandlerFactory factory : this.httpHandlerFactories) {
|
||||
handler = factory.getHandler(handler);
|
||||
if (handler instanceof Closeable closeable) {
|
||||
Assert.state(this.closeables != null, "'closeables' must not be null");
|
||||
this.closeables.add(closeable);
|
||||
}
|
||||
if (handler instanceof GracefulShutdownHandler shutdownHandler) {
|
||||
Assert.state(this.gracefulShutdown == null, "Only a single GracefulShutdownHandler can be defined");
|
||||
this.gracefulShutdown = shutdownHandler;
|
||||
}
|
||||
}
|
||||
return handler;
|
||||
}
|
||||
|
||||
private String getPortsDescription() {
|
||||
StringBuilder description = new StringBuilder();
|
||||
List<UndertowWebServer.Port> ports = getActualPorts();
|
||||
description.append("port");
|
||||
if (ports.size() != 1) {
|
||||
description.append("s");
|
||||
}
|
||||
description.append(" ");
|
||||
if (!ports.isEmpty()) {
|
||||
description.append(StringUtils.collectionToDelimitedString(ports, ", "));
|
||||
}
|
||||
else {
|
||||
description.append("unknown");
|
||||
}
|
||||
return description.toString();
|
||||
}
|
||||
|
||||
private List<Port> getActualPorts() {
|
||||
List<Port> ports = new ArrayList<>();
|
||||
try {
|
||||
if (!this.autoStart) {
|
||||
ports.add(new Port(-1, "unknown"));
|
||||
}
|
||||
else {
|
||||
for (BoundChannel channel : extractChannels()) {
|
||||
ports.add(getPortFromChannel(channel));
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex) {
|
||||
// Continue
|
||||
}
|
||||
return ports;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private List<BoundChannel> extractChannels() {
|
||||
Field channelsField = ReflectionUtils.findField(Undertow.class, "channels");
|
||||
Assert.state(channelsField != null, "'channelsField' must not be null");
|
||||
ReflectionUtils.makeAccessible(channelsField);
|
||||
List<BoundChannel> channels = (List<BoundChannel>) ReflectionUtils.getField(channelsField, this.undertow);
|
||||
Assert.state(channels != null, "'channels' must not be null");
|
||||
return channels;
|
||||
}
|
||||
|
||||
private UndertowWebServer.@Nullable Port getPortFromChannel(BoundChannel channel) {
|
||||
SocketAddress socketAddress = channel.getLocalAddress();
|
||||
if (socketAddress instanceof InetSocketAddress inetSocketAddress) {
|
||||
Field sslField = ReflectionUtils.findField(channel.getClass(), "ssl");
|
||||
String protocol = (sslField != null) ? "https" : "http";
|
||||
return new UndertowWebServer.Port(inetSocketAddress.getPort(), protocol);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private List<UndertowWebServer.Port> getConfiguredPorts() {
|
||||
List<Port> ports = new ArrayList<>();
|
||||
for (Object listener : extractListeners()) {
|
||||
try {
|
||||
Port port = getPortFromListener(listener);
|
||||
if (port.getNumber() != 0) {
|
||||
ports.add(port);
|
||||
}
|
||||
}
|
||||
catch (Exception ex) {
|
||||
// Continue
|
||||
}
|
||||
}
|
||||
return ports;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private List<Object> extractListeners() {
|
||||
Field listenersField = ReflectionUtils.findField(Undertow.class, "listeners");
|
||||
Assert.state(listenersField != null, "'listenersField' must not be null");
|
||||
ReflectionUtils.makeAccessible(listenersField);
|
||||
List<Object> listeners = (List<Object>) ReflectionUtils.getField(listenersField, this.undertow);
|
||||
Assert.state(listeners != null, "'listeners' must not be null");
|
||||
return listeners;
|
||||
}
|
||||
|
||||
private UndertowWebServer.Port getPortFromListener(Object listener) {
|
||||
Field typeField = ReflectionUtils.findField(listener.getClass(), "type");
|
||||
Assert.state(typeField != null, "'typeField' must not be null");
|
||||
String protocol = getProtocol(listener, typeField);
|
||||
Field portField = ReflectionUtils.findField(listener.getClass(), "port");
|
||||
Assert.state(portField != null, "'portField' must not be null");
|
||||
int port = getPort(listener, portField);
|
||||
return new UndertowWebServer.Port(port, protocol);
|
||||
}
|
||||
|
||||
private static Integer getPort(Object listener, Field portField) {
|
||||
ReflectionUtils.makeAccessible(portField);
|
||||
Integer value = (Integer) ReflectionUtils.getField(portField, listener);
|
||||
Assert.state(value != null, "'value' must not be null");
|
||||
return value;
|
||||
}
|
||||
|
||||
private String getProtocol(Object listener, Field typeField) {
|
||||
ReflectionUtils.makeAccessible(typeField);
|
||||
Object value = ReflectionUtils.getField(typeField, listener);
|
||||
Assert.state(value != null, "'value' must not be null");
|
||||
return value.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() throws WebServerException {
|
||||
synchronized (this.monitor) {
|
||||
if (!this.started) {
|
||||
return;
|
||||
}
|
||||
this.started = false;
|
||||
if (this.gracefulShutdown != null) {
|
||||
notifyGracefulCallback(false);
|
||||
}
|
||||
try {
|
||||
if (this.undertow != null) {
|
||||
this.undertow.stop();
|
||||
Assert.state(this.closeables != null, "'closeables' must not be null");
|
||||
for (Closeable closeable : this.closeables) {
|
||||
closeable.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new WebServerException("Unable to stop Undertow", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPort() {
|
||||
List<Port> ports = getActualPorts();
|
||||
if (ports.isEmpty()) {
|
||||
return -1;
|
||||
}
|
||||
return ports.get(0).getNumber();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link Undertow Undertow server}. Returns {@code null} until the server
|
||||
* has been started.
|
||||
* @return the Undertow server or {@code null} if the server hasn't been started yet
|
||||
* @since 4.0.0
|
||||
*/
|
||||
public @Nullable Undertow getUndertow() {
|
||||
return this.undertow;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiates a graceful shutdown of the Undertow web server. Handling of new requests
|
||||
* is prevented and the given {@code callback} is invoked at the end of the attempt.
|
||||
* The attempt can be explicitly ended by invoking {@link #stop}.
|
||||
* <p>
|
||||
* Once shutdown has been initiated Undertow will return an {@code HTTP 503} response
|
||||
* for any new or existing connections.
|
||||
*/
|
||||
@Override
|
||||
public void shutDownGracefully(GracefulShutdownCallback callback) {
|
||||
if (this.gracefulShutdown == null) {
|
||||
callback.shutdownComplete(GracefulShutdownResult.IMMEDIATE);
|
||||
return;
|
||||
}
|
||||
logger.info("Commencing graceful shutdown. Waiting for active requests to complete");
|
||||
this.gracefulShutdownCallback.set(callback);
|
||||
this.gracefulShutdown.shutdown();
|
||||
this.gracefulShutdown.addShutdownListener(this::notifyGracefulCallback);
|
||||
}
|
||||
|
||||
private void notifyGracefulCallback(boolean success) {
|
||||
GracefulShutdownCallback callback = this.gracefulShutdownCallback.getAndSet(null);
|
||||
if (callback != null) {
|
||||
if (success) {
|
||||
logger.info("Graceful shutdown complete");
|
||||
callback.shutdownComplete(GracefulShutdownResult.IDLE);
|
||||
}
|
||||
else {
|
||||
logger.info("Graceful shutdown aborted with one or more requests still active");
|
||||
callback.shutdownComplete(GracefulShutdownResult.REQUESTS_ACTIVE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected String getStartedLogMessage() {
|
||||
return "Undertow started on " + getPortsDescription();
|
||||
}
|
||||
|
||||
/**
|
||||
* An active Undertow port.
|
||||
*/
|
||||
private static final class Port {
|
||||
|
||||
private final int number;
|
||||
|
||||
private final String protocol;
|
||||
|
||||
private Port(int number, String protocol) {
|
||||
this.number = number;
|
||||
this.protocol = protocol;
|
||||
}
|
||||
|
||||
int getNumber() {
|
||||
return this.number;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
UndertowWebServer.Port other = (UndertowWebServer.Port) obj;
|
||||
return this.number == other.number;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return this.number;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.number + " (" + this.protocol + ")";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link HttpHandlerFactory} to wrap a closable.
|
||||
*/
|
||||
private static final class CloseableHttpHandlerFactory implements HttpHandlerFactory {
|
||||
|
||||
private final @Nullable Closeable closeable;
|
||||
|
||||
private CloseableHttpHandlerFactory(@Nullable Closeable closeable) {
|
||||
this.closeable = closeable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable HttpHandler getHandler(@Nullable HttpHandler next) {
|
||||
Closeable closeable = this.closeable;
|
||||
if (closeable == null) {
|
||||
return next;
|
||||
}
|
||||
return new CloseableHttpHandler() {
|
||||
|
||||
@Override
|
||||
public void handleRequest(HttpServerExchange exchange) throws Exception {
|
||||
if (next != null) {
|
||||
next.handleRequest(exchange);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
closeable.close();
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Closeable} {@link HttpHandler}.
|
||||
*/
|
||||
private interface CloseableHttpHandler extends HttpHandler, Closeable {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link RuntimeHintsRegistrar} that allows Undertow's configured and actual ports to
|
||||
* be retrieved at runtime in a native image.
|
||||
*/
|
||||
static class UndertowWebServerRuntimeHints implements RuntimeHintsRegistrar {
|
||||
|
||||
@Override
|
||||
public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) {
|
||||
hints.reflection()
|
||||
.registerTypeIfPresent(classLoader, "io.undertow.Undertow",
|
||||
(hint) -> hint.withField("listeners").withField("channels"));
|
||||
hints.reflection()
|
||||
.registerTypeIfPresent(classLoader, "io.undertow.Undertow$ListenerConfig",
|
||||
(hint) -> hint.withField("type").withField("port"));
|
||||
hints.reflection()
|
||||
.registerTypeIfPresent(classLoader, "io.undertow.protocols.ssl.UndertowAcceptingSslChannel",
|
||||
(hint) -> hint.withField("ssl"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,237 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.undertow;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.InetAddress;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import io.undertow.Handlers;
|
||||
import io.undertow.Undertow;
|
||||
import io.undertow.Undertow.Builder;
|
||||
import io.undertow.UndertowOptions;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.boot.ssl.SslBundle;
|
||||
import org.springframework.boot.web.server.AbstractConfigurableWebServerFactory;
|
||||
import org.springframework.boot.web.server.Compression;
|
||||
import org.springframework.boot.web.server.Http2;
|
||||
import org.springframework.boot.web.server.Shutdown;
|
||||
import org.springframework.boot.web.server.Ssl;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Base class for factories that produce an {@link UndertowWebServer}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @since 4.0.0
|
||||
*/
|
||||
public abstract class UndertowWebServerFactory extends AbstractConfigurableWebServerFactory
|
||||
implements ConfigurableUndertowWebServerFactory {
|
||||
|
||||
private Set<UndertowBuilderCustomizer> builderCustomizers = new LinkedHashSet<>();
|
||||
|
||||
private @Nullable Integer bufferSize;
|
||||
|
||||
private @Nullable Integer ioThreads;
|
||||
|
||||
private @Nullable Integer workerThreads;
|
||||
|
||||
private @Nullable Boolean directBuffers;
|
||||
|
||||
private @Nullable File accessLogDirectory;
|
||||
|
||||
private @Nullable String accessLogPattern;
|
||||
|
||||
private @Nullable String accessLogPrefix;
|
||||
|
||||
private @Nullable String accessLogSuffix;
|
||||
|
||||
private boolean accessLogEnabled;
|
||||
|
||||
private boolean accessLogRotate = true;
|
||||
|
||||
private boolean useForwardHeaders;
|
||||
|
||||
protected UndertowWebServerFactory() {
|
||||
}
|
||||
|
||||
protected UndertowWebServerFactory(int port) {
|
||||
super(port);
|
||||
}
|
||||
|
||||
public Collection<UndertowBuilderCustomizer> getBuilderCustomizers() {
|
||||
return this.builderCustomizers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBuilderCustomizers(Collection<? extends UndertowBuilderCustomizer> customizers) {
|
||||
Assert.notNull(customizers, "'customizers' must not be null");
|
||||
this.builderCustomizers = new LinkedHashSet<>(customizers);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addBuilderCustomizers(UndertowBuilderCustomizer... customizers) {
|
||||
Assert.notNull(customizers, "'customizers' must not be null");
|
||||
this.builderCustomizers.addAll(Arrays.asList(customizers));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBufferSize(@Nullable Integer bufferSize) {
|
||||
this.bufferSize = bufferSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setIoThreads(@Nullable Integer ioThreads) {
|
||||
this.ioThreads = ioThreads;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setWorkerThreads(@Nullable Integer workerThreads) {
|
||||
this.workerThreads = workerThreads;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUseDirectBuffers(@Nullable Boolean directBuffers) {
|
||||
this.directBuffers = directBuffers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAccessLogDirectory(@Nullable File accessLogDirectory) {
|
||||
this.accessLogDirectory = accessLogDirectory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAccessLogPattern(@Nullable String accessLogPattern) {
|
||||
this.accessLogPattern = accessLogPattern;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAccessLogPrefix(@Nullable String accessLogPrefix) {
|
||||
this.accessLogPrefix = accessLogPrefix;
|
||||
}
|
||||
|
||||
public @Nullable String getAccessLogPrefix() {
|
||||
return this.accessLogPrefix;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAccessLogSuffix(@Nullable String accessLogSuffix) {
|
||||
this.accessLogSuffix = accessLogSuffix;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAccessLogEnabled(boolean accessLogEnabled) {
|
||||
this.accessLogEnabled = accessLogEnabled;
|
||||
}
|
||||
|
||||
public boolean isAccessLogEnabled() {
|
||||
return this.accessLogEnabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAccessLogRotate(boolean accessLogRotate) {
|
||||
this.accessLogRotate = accessLogRotate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUseForwardHeaders(boolean useForwardHeaders) {
|
||||
this.useForwardHeaders = useForwardHeaders;
|
||||
}
|
||||
|
||||
public boolean isUseForwardHeaders() {
|
||||
return this.useForwardHeaders;
|
||||
}
|
||||
|
||||
public Builder createBuilder(AbstractConfigurableWebServerFactory factory, Supplier<SslBundle> sslBundleSupplier,
|
||||
Supplier<Map<String, SslBundle>> serverNameSslBundlesSupplier) {
|
||||
InetAddress address = factory.getAddress();
|
||||
int port = factory.getPort();
|
||||
Builder builder = Undertow.builder();
|
||||
if (this.bufferSize != null) {
|
||||
builder.setBufferSize(this.bufferSize);
|
||||
}
|
||||
if (this.ioThreads != null) {
|
||||
builder.setIoThreads(this.ioThreads);
|
||||
}
|
||||
if (this.workerThreads != null) {
|
||||
builder.setWorkerThreads(this.workerThreads);
|
||||
}
|
||||
if (this.directBuffers != null) {
|
||||
builder.setDirectBuffers(this.directBuffers);
|
||||
}
|
||||
Http2 http2 = factory.getHttp2();
|
||||
if (http2 != null) {
|
||||
builder.setServerOption(UndertowOptions.ENABLE_HTTP2, http2.isEnabled());
|
||||
}
|
||||
Ssl ssl = factory.getSsl();
|
||||
if (Ssl.isEnabled(ssl)) {
|
||||
new SslBuilderCustomizer(factory.getPort(), address, ssl.getClientAuth(), sslBundleSupplier.get(),
|
||||
serverNameSslBundlesSupplier.get())
|
||||
.customize(builder);
|
||||
}
|
||||
else {
|
||||
builder.addHttpListener(port, (address != null) ? address.getHostAddress() : "0.0.0.0");
|
||||
}
|
||||
builder.setServerOption(UndertowOptions.SHUTDOWN_TIMEOUT, 0);
|
||||
for (UndertowBuilderCustomizer customizer : this.builderCustomizers) {
|
||||
customizer.customize(builder);
|
||||
}
|
||||
return builder;
|
||||
}
|
||||
|
||||
public List<HttpHandlerFactory> createHttpHandlerFactories(AbstractConfigurableWebServerFactory webServerFactory,
|
||||
HttpHandlerFactory... initialHttpHandlerFactories) {
|
||||
List<HttpHandlerFactory> factories = createHttpHandlerFactories(webServerFactory.getCompression(),
|
||||
this.useForwardHeaders, webServerFactory.getServerHeader(), webServerFactory.getShutdown(),
|
||||
initialHttpHandlerFactories);
|
||||
if (isAccessLogEnabled()) {
|
||||
Assert.state(this.accessLogDirectory != null, "Access log directory is not set");
|
||||
factories.add(new AccessLogHttpHandlerFactory(this.accessLogDirectory, this.accessLogPattern,
|
||||
this.accessLogPrefix, this.accessLogSuffix, this.accessLogRotate));
|
||||
}
|
||||
return factories;
|
||||
}
|
||||
|
||||
static List<HttpHandlerFactory> createHttpHandlerFactories(@Nullable Compression compression,
|
||||
boolean useForwardHeaders, @Nullable String serverHeader, Shutdown shutdown,
|
||||
HttpHandlerFactory... initialHttpHandlerFactories) {
|
||||
List<HttpHandlerFactory> factories = new ArrayList<>(Arrays.asList(initialHttpHandlerFactories));
|
||||
if (compression != null && compression.getEnabled()) {
|
||||
factories.add(new CompressionHttpHandlerFactory(compression));
|
||||
}
|
||||
if (useForwardHeaders) {
|
||||
factories.add(Handlers::proxyPeerAddress);
|
||||
}
|
||||
if (StringUtils.hasText(serverHeader)) {
|
||||
factories.add((next) -> Handlers.header(next, "Server", serverHeader));
|
||||
}
|
||||
if (shutdown == Shutdown.GRACEFUL) {
|
||||
factories.add(Handlers::gracefulShutdown);
|
||||
}
|
||||
return factories;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,406 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.undertow.autoconfigure;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.Duration;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import io.undertow.UndertowOptions;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.util.unit.DataSize;
|
||||
|
||||
/**
|
||||
* Undertow server properties.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @author Stephane Nicoll
|
||||
* @author Andy Wilkinson
|
||||
* @author Ivan Sopov
|
||||
* @author Marcos Barbero
|
||||
* @author Eddú Meléndez
|
||||
* @author Quinten De Swaef
|
||||
* @author Venil Noronha
|
||||
* @author Aurélien Leboulanger
|
||||
* @author Brian Clozel
|
||||
* @author Olivier Lamy
|
||||
* @author Chentao Qu
|
||||
* @author Artsiom Yudovin
|
||||
* @author Andrew McGhie
|
||||
* @author Rafiullah Hamedy
|
||||
* @author Dirk Deyne
|
||||
* @author HaiTao Zhang
|
||||
* @author Victor Mandujano
|
||||
* @author Chris Bono
|
||||
* @author Parviz Rozikov
|
||||
* @author Florian Storz
|
||||
* @author Michael Weidmann
|
||||
* @author Lasse Wulff
|
||||
* @since 4.0.0
|
||||
*/
|
||||
@ConfigurationProperties("server.undertow")
|
||||
public class UndertowServerProperties {
|
||||
|
||||
/**
|
||||
* Maximum size of the HTTP post content. When the value is -1, the default, the size
|
||||
* is unlimited.
|
||||
*/
|
||||
private DataSize maxHttpPostSize = DataSize.ofBytes(-1);
|
||||
|
||||
/**
|
||||
* Size of each buffer. The default is derived from the maximum amount of memory that
|
||||
* is available to the JVM.
|
||||
*/
|
||||
private @Nullable DataSize bufferSize;
|
||||
|
||||
/**
|
||||
* Whether to allocate buffers outside the Java heap. The default is derived from the
|
||||
* maximum amount of memory that is available to the JVM.
|
||||
*/
|
||||
private @Nullable Boolean directBuffers;
|
||||
|
||||
/**
|
||||
* Whether servlet filters should be initialized on startup.
|
||||
*/
|
||||
private boolean eagerFilterInit = true;
|
||||
|
||||
/**
|
||||
* Maximum number of query or path parameters that are allowed. This limit exists to
|
||||
* prevent hash collision based DOS attacks.
|
||||
*/
|
||||
private int maxParameters = UndertowOptions.DEFAULT_MAX_PARAMETERS;
|
||||
|
||||
/**
|
||||
* Maximum number of headers that are allowed. This limit exists to prevent hash
|
||||
* collision based DOS attacks.
|
||||
*/
|
||||
private int maxHeaders = UndertowOptions.DEFAULT_MAX_HEADERS;
|
||||
|
||||
/**
|
||||
* Maximum number of cookies that are allowed. This limit exists to prevent hash
|
||||
* collision based DOS attacks.
|
||||
*/
|
||||
private int maxCookies = 200;
|
||||
|
||||
/**
|
||||
* Whether encoded slash characters (%2F) should be decoded. Decoding can cause
|
||||
* security problems if a front-end proxy does not perform the same decoding. Only
|
||||
* enable this if you have a legacy application that requires it. When set,
|
||||
* server.undertow.allow-encoded-slash has no effect.
|
||||
*/
|
||||
private @Nullable Boolean decodeSlash;
|
||||
|
||||
/**
|
||||
* Whether the URL should be decoded. When disabled, percent-encoded characters in the
|
||||
* URL will be left as-is.
|
||||
*/
|
||||
private boolean decodeUrl = true;
|
||||
|
||||
/**
|
||||
* Charset used to decode URLs.
|
||||
*/
|
||||
private Charset urlCharset = StandardCharsets.UTF_8;
|
||||
|
||||
/**
|
||||
* Whether the 'Connection: keep-alive' header should be added to all responses, even
|
||||
* if not required by the HTTP specification.
|
||||
*/
|
||||
private boolean alwaysSetKeepAlive = true;
|
||||
|
||||
/**
|
||||
* Amount of time a connection can sit idle without processing a request, before it is
|
||||
* closed by the server.
|
||||
*/
|
||||
private @Nullable Duration noRequestTimeout;
|
||||
|
||||
/**
|
||||
* Whether to preserve the path of a request when it is forwarded.
|
||||
*/
|
||||
private boolean preservePathOnForward = false;
|
||||
|
||||
private final Accesslog accesslog = new Accesslog();
|
||||
|
||||
/**
|
||||
* Thread related configuration.
|
||||
*/
|
||||
private final Threads threads = new Threads();
|
||||
|
||||
private final Options options = new Options();
|
||||
|
||||
public DataSize getMaxHttpPostSize() {
|
||||
return this.maxHttpPostSize;
|
||||
}
|
||||
|
||||
public void setMaxHttpPostSize(DataSize maxHttpPostSize) {
|
||||
this.maxHttpPostSize = maxHttpPostSize;
|
||||
}
|
||||
|
||||
public @Nullable DataSize getBufferSize() {
|
||||
return this.bufferSize;
|
||||
}
|
||||
|
||||
public void setBufferSize(@Nullable DataSize bufferSize) {
|
||||
this.bufferSize = bufferSize;
|
||||
}
|
||||
|
||||
public @Nullable Boolean getDirectBuffers() {
|
||||
return this.directBuffers;
|
||||
}
|
||||
|
||||
public void setDirectBuffers(@Nullable Boolean directBuffers) {
|
||||
this.directBuffers = directBuffers;
|
||||
}
|
||||
|
||||
public boolean isEagerFilterInit() {
|
||||
return this.eagerFilterInit;
|
||||
}
|
||||
|
||||
public void setEagerFilterInit(boolean eagerFilterInit) {
|
||||
this.eagerFilterInit = eagerFilterInit;
|
||||
}
|
||||
|
||||
public int getMaxParameters() {
|
||||
return this.maxParameters;
|
||||
}
|
||||
|
||||
public void setMaxParameters(Integer maxParameters) {
|
||||
this.maxParameters = maxParameters;
|
||||
}
|
||||
|
||||
public int getMaxHeaders() {
|
||||
return this.maxHeaders;
|
||||
}
|
||||
|
||||
public void setMaxHeaders(int maxHeaders) {
|
||||
this.maxHeaders = maxHeaders;
|
||||
}
|
||||
|
||||
public Integer getMaxCookies() {
|
||||
return this.maxCookies;
|
||||
}
|
||||
|
||||
public void setMaxCookies(Integer maxCookies) {
|
||||
this.maxCookies = maxCookies;
|
||||
}
|
||||
|
||||
public @Nullable Boolean getDecodeSlash() {
|
||||
return this.decodeSlash;
|
||||
}
|
||||
|
||||
public void setDecodeSlash(@Nullable Boolean decodeSlash) {
|
||||
this.decodeSlash = decodeSlash;
|
||||
}
|
||||
|
||||
public boolean isDecodeUrl() {
|
||||
return this.decodeUrl;
|
||||
}
|
||||
|
||||
public void setDecodeUrl(Boolean decodeUrl) {
|
||||
this.decodeUrl = decodeUrl;
|
||||
}
|
||||
|
||||
public Charset getUrlCharset() {
|
||||
return this.urlCharset;
|
||||
}
|
||||
|
||||
public void setUrlCharset(Charset urlCharset) {
|
||||
this.urlCharset = urlCharset;
|
||||
}
|
||||
|
||||
public boolean isAlwaysSetKeepAlive() {
|
||||
return this.alwaysSetKeepAlive;
|
||||
}
|
||||
|
||||
public void setAlwaysSetKeepAlive(boolean alwaysSetKeepAlive) {
|
||||
this.alwaysSetKeepAlive = alwaysSetKeepAlive;
|
||||
}
|
||||
|
||||
public @Nullable Duration getNoRequestTimeout() {
|
||||
return this.noRequestTimeout;
|
||||
}
|
||||
|
||||
public void setNoRequestTimeout(@Nullable Duration noRequestTimeout) {
|
||||
this.noRequestTimeout = noRequestTimeout;
|
||||
}
|
||||
|
||||
public boolean isPreservePathOnForward() {
|
||||
return this.preservePathOnForward;
|
||||
}
|
||||
|
||||
public void setPreservePathOnForward(boolean preservePathOnForward) {
|
||||
this.preservePathOnForward = preservePathOnForward;
|
||||
}
|
||||
|
||||
public UndertowServerProperties.Accesslog getAccesslog() {
|
||||
return this.accesslog;
|
||||
}
|
||||
|
||||
public UndertowServerProperties.Threads getThreads() {
|
||||
return this.threads;
|
||||
}
|
||||
|
||||
public UndertowServerProperties.Options getOptions() {
|
||||
return this.options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Undertow access log properties.
|
||||
*/
|
||||
public static class Accesslog {
|
||||
|
||||
/**
|
||||
* Whether to enable the access log.
|
||||
*/
|
||||
private boolean enabled = false;
|
||||
|
||||
/**
|
||||
* Format pattern for access logs.
|
||||
*/
|
||||
private String pattern = "common";
|
||||
|
||||
/**
|
||||
* Log file name prefix.
|
||||
*/
|
||||
protected String prefix = "access_log.";
|
||||
|
||||
/**
|
||||
* Log file name suffix.
|
||||
*/
|
||||
private String suffix = "log";
|
||||
|
||||
/**
|
||||
* Undertow access log directory.
|
||||
*/
|
||||
private File dir = new File("logs");
|
||||
|
||||
/**
|
||||
* Whether to enable access log rotation.
|
||||
*/
|
||||
private boolean rotate = true;
|
||||
|
||||
public boolean isEnabled() {
|
||||
return this.enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public String getPattern() {
|
||||
return this.pattern;
|
||||
}
|
||||
|
||||
public void setPattern(String pattern) {
|
||||
this.pattern = pattern;
|
||||
}
|
||||
|
||||
public String getPrefix() {
|
||||
return this.prefix;
|
||||
}
|
||||
|
||||
public void setPrefix(String prefix) {
|
||||
this.prefix = prefix;
|
||||
}
|
||||
|
||||
public String getSuffix() {
|
||||
return this.suffix;
|
||||
}
|
||||
|
||||
public void setSuffix(String suffix) {
|
||||
this.suffix = suffix;
|
||||
}
|
||||
|
||||
public File getDir() {
|
||||
return this.dir;
|
||||
}
|
||||
|
||||
public void setDir(File dir) {
|
||||
this.dir = dir;
|
||||
}
|
||||
|
||||
public boolean isRotate() {
|
||||
return this.rotate;
|
||||
}
|
||||
|
||||
public void setRotate(boolean rotate) {
|
||||
this.rotate = rotate;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Undertow thread properties.
|
||||
*/
|
||||
public static class Threads {
|
||||
|
||||
/**
|
||||
* Number of I/O threads to create for the worker. The default is derived from the
|
||||
* number of available processors.
|
||||
*/
|
||||
private @Nullable Integer io;
|
||||
|
||||
/**
|
||||
* Number of worker threads. The default is 8 times the number of I/O threads.
|
||||
*/
|
||||
private @Nullable Integer worker;
|
||||
|
||||
public @Nullable Integer getIo() {
|
||||
return this.io;
|
||||
}
|
||||
|
||||
public void setIo(@Nullable Integer io) {
|
||||
this.io = io;
|
||||
}
|
||||
|
||||
public @Nullable Integer getWorker() {
|
||||
return this.worker;
|
||||
}
|
||||
|
||||
public void setWorker(@Nullable Integer worker) {
|
||||
this.worker = worker;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class Options {
|
||||
|
||||
/**
|
||||
* Socket options as defined in org.xnio.Options.
|
||||
*/
|
||||
private final Map<String, String> socket = new LinkedHashMap<>();
|
||||
|
||||
/**
|
||||
* Server options as defined in io.undertow.UndertowOptions.
|
||||
*/
|
||||
private final Map<String, String> server = new LinkedHashMap<>();
|
||||
|
||||
public Map<String, String> getServer() {
|
||||
return this.server;
|
||||
}
|
||||
|
||||
public Map<String, String> getSocket() {
|
||||
return this.socket;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.undertow.autoconfigure;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnNotWarDeployment;
|
||||
import org.springframework.boot.web.server.autoconfigure.ServerProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.env.Environment;
|
||||
|
||||
/**
|
||||
* {@link Configuration Configuration} for an Undertow-based reactive or servlet web
|
||||
* server.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @since 4.0.0
|
||||
*/
|
||||
@ConditionalOnNotWarDeployment
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
public class UndertowWebServerConfiguration {
|
||||
|
||||
@Bean
|
||||
UndertowWebServerFactoryCustomizer undertowWebServerFactoryCustomizer(Environment environment,
|
||||
ServerProperties serverProperties, UndertowServerProperties undertowProperties) {
|
||||
return new UndertowWebServerFactoryCustomizer(environment, serverProperties, undertowProperties);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,239 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.undertow.autoconfigure;
|
||||
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.nio.charset.Charset;
|
||||
import java.time.Duration;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
import io.undertow.UndertowOptions;
|
||||
import org.xnio.Option;
|
||||
import org.xnio.Options;
|
||||
|
||||
import org.springframework.boot.cloud.CloudPlatform;
|
||||
import org.springframework.boot.context.properties.PropertyMapper;
|
||||
import org.springframework.boot.undertow.ConfigurableUndertowWebServerFactory;
|
||||
import org.springframework.boot.undertow.autoconfigure.UndertowServerProperties.Accesslog;
|
||||
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
|
||||
import org.springframework.boot.web.server.autoconfigure.ServerProperties;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.util.unit.DataSize;
|
||||
|
||||
/**
|
||||
* Customization for Undertow-specific features common for both Servlet and Reactive
|
||||
* servers.
|
||||
*
|
||||
* @author Brian Clozel
|
||||
* @author Yulin Qin
|
||||
* @author Stephane Nicoll
|
||||
* @author Phillip Webb
|
||||
* @author Arstiom Yudovin
|
||||
* @author Rafiullah Hamedy
|
||||
* @author HaiTao Zhang
|
||||
* @since 4.0.0
|
||||
*/
|
||||
public class UndertowWebServerFactoryCustomizer
|
||||
implements WebServerFactoryCustomizer<ConfigurableUndertowWebServerFactory>, Ordered {
|
||||
|
||||
private final Environment environment;
|
||||
|
||||
private final ServerProperties serverProperties;
|
||||
|
||||
private final UndertowServerProperties undertowProperties;
|
||||
|
||||
public UndertowWebServerFactoryCustomizer(Environment environment, ServerProperties serverProperties,
|
||||
UndertowServerProperties undertowProperties) {
|
||||
this.environment = environment;
|
||||
this.serverProperties = serverProperties;
|
||||
this.undertowProperties = undertowProperties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void customize(ConfigurableUndertowWebServerFactory factory) {
|
||||
PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
|
||||
ServerOptions options = new ServerOptions(factory);
|
||||
map.from(this.serverProperties::getMaxHttpRequestHeaderSize)
|
||||
.asInt(DataSize::toBytes)
|
||||
.when(this::isPositive)
|
||||
.to(options.option(UndertowOptions.MAX_HEADER_SIZE));
|
||||
mapUndertowProperties(factory, options);
|
||||
mapAccessLogProperties(factory);
|
||||
map.from(this::getOrDeduceUseForwardHeaders).to(factory::setUseForwardHeaders);
|
||||
}
|
||||
|
||||
private void mapUndertowProperties(ConfigurableUndertowWebServerFactory factory, ServerOptions serverOptions) {
|
||||
PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
|
||||
map.from(this.undertowProperties::getBufferSize)
|
||||
.whenNonNull()
|
||||
.asInt(DataSize::toBytes)
|
||||
.to(factory::setBufferSize);
|
||||
UndertowServerProperties.Threads threadProperties = this.undertowProperties.getThreads();
|
||||
map.from(threadProperties::getIo).to(factory::setIoThreads);
|
||||
map.from(threadProperties::getWorker).to(factory::setWorkerThreads);
|
||||
map.from(this.undertowProperties::getDirectBuffers).to(factory::setUseDirectBuffers);
|
||||
map.from(this.undertowProperties::getMaxHttpPostSize)
|
||||
.as(DataSize::toBytes)
|
||||
.when(this::isPositive)
|
||||
.to(serverOptions.option(UndertowOptions.MAX_ENTITY_SIZE));
|
||||
map.from(this.undertowProperties::getMaxParameters).to(serverOptions.option(UndertowOptions.MAX_PARAMETERS));
|
||||
map.from(this.undertowProperties::getMaxHeaders).to(serverOptions.option(UndertowOptions.MAX_HEADERS));
|
||||
map.from(this.undertowProperties::getMaxCookies).to(serverOptions.option(UndertowOptions.MAX_COOKIES));
|
||||
mapSlashProperty(this.undertowProperties, serverOptions);
|
||||
map.from(this.undertowProperties::isDecodeUrl).to(serverOptions.option(UndertowOptions.DECODE_URL));
|
||||
map.from(this.undertowProperties::getUrlCharset)
|
||||
.as(Charset::name)
|
||||
.to(serverOptions.option(UndertowOptions.URL_CHARSET));
|
||||
map.from(this.undertowProperties::isAlwaysSetKeepAlive)
|
||||
.to(serverOptions.option(UndertowOptions.ALWAYS_SET_KEEP_ALIVE));
|
||||
map.from(this.undertowProperties::getNoRequestTimeout)
|
||||
.asInt(Duration::toMillis)
|
||||
.to(serverOptions.option(UndertowOptions.NO_REQUEST_TIMEOUT));
|
||||
map.from(this.undertowProperties.getOptions()::getServer).to(serverOptions.forEach(serverOptions::option));
|
||||
SocketOptions socketOptions = new SocketOptions(factory);
|
||||
map.from(this.undertowProperties.getOptions()::getSocket).to(socketOptions.forEach(socketOptions::option));
|
||||
}
|
||||
|
||||
private void mapSlashProperty(UndertowServerProperties properties, ServerOptions serverOptions) {
|
||||
PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
|
||||
map.from(properties::getDecodeSlash).to(serverOptions.option(UndertowOptions.DECODE_SLASH));
|
||||
|
||||
}
|
||||
|
||||
private boolean isPositive(Number value) {
|
||||
return value.longValue() > 0;
|
||||
}
|
||||
|
||||
private void mapAccessLogProperties(ConfigurableUndertowWebServerFactory factory) {
|
||||
Accesslog properties = this.undertowProperties.getAccesslog();
|
||||
PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
|
||||
map.from(properties::isEnabled).to(factory::setAccessLogEnabled);
|
||||
map.from(properties::getDir).to(factory::setAccessLogDirectory);
|
||||
map.from(properties::getPattern).to(factory::setAccessLogPattern);
|
||||
map.from(properties::getPrefix).to(factory::setAccessLogPrefix);
|
||||
map.from(properties::getSuffix).to(factory::setAccessLogSuffix);
|
||||
map.from(properties::isRotate).to(factory::setAccessLogRotate);
|
||||
}
|
||||
|
||||
private boolean getOrDeduceUseForwardHeaders() {
|
||||
if (this.serverProperties.getForwardHeadersStrategy() == null) {
|
||||
CloudPlatform platform = CloudPlatform.getActive(this.environment);
|
||||
return platform != null && platform.isUsingForwardHeaders();
|
||||
}
|
||||
return this.serverProperties.getForwardHeadersStrategy().equals(ServerProperties.ForwardHeadersStrategy.NATIVE);
|
||||
}
|
||||
|
||||
private abstract static class AbstractOptions {
|
||||
|
||||
private final Class<?> source;
|
||||
|
||||
private final Map<String, Option<?>> nameLookup;
|
||||
|
||||
private final ConfigurableUndertowWebServerFactory factory;
|
||||
|
||||
AbstractOptions(Class<?> source, ConfigurableUndertowWebServerFactory factory) {
|
||||
Map<String, Option<?>> lookup = new HashMap<>();
|
||||
ReflectionUtils.doWithLocalFields(source, (field) -> {
|
||||
int modifiers = field.getModifiers();
|
||||
if (Modifier.isPublic(modifiers) && Modifier.isStatic(modifiers)
|
||||
&& Option.class.isAssignableFrom(field.getType())) {
|
||||
try {
|
||||
Option<?> option = (Option<?>) field.get(null);
|
||||
lookup.put(getCanonicalName(field.getName()), option);
|
||||
}
|
||||
catch (IllegalAccessException ex) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
});
|
||||
this.source = source;
|
||||
this.nameLookup = Collections.unmodifiableMap(lookup);
|
||||
this.factory = factory;
|
||||
}
|
||||
|
||||
protected ConfigurableUndertowWebServerFactory getFactory() {
|
||||
return this.factory;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
<T> Consumer<Map<String, String>> forEach(Function<Option<T>, Consumer<T>> function) {
|
||||
return (map) -> map.forEach((key, value) -> {
|
||||
Option<T> option = (Option<T>) this.nameLookup.get(getCanonicalName(key));
|
||||
Assert.state(option != null,
|
||||
() -> "Unable to find '" + key + "' in " + ClassUtils.getShortName(this.source));
|
||||
T parsed = option.parseValue(value, getClass().getClassLoader());
|
||||
function.apply(option).accept(parsed);
|
||||
});
|
||||
}
|
||||
|
||||
private static String getCanonicalName(String name) {
|
||||
StringBuilder canonicalName = new StringBuilder(name.length());
|
||||
name.chars()
|
||||
.filter(Character::isLetterOrDigit)
|
||||
.map(Character::toLowerCase)
|
||||
.forEach((c) -> canonicalName.append((char) c));
|
||||
return canonicalName.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link ConfigurableUndertowWebServerFactory} wrapper that makes it easier to apply
|
||||
* {@link UndertowOptions server options}.
|
||||
*/
|
||||
private static class ServerOptions extends AbstractOptions {
|
||||
|
||||
ServerOptions(ConfigurableUndertowWebServerFactory factory) {
|
||||
super(UndertowOptions.class, factory);
|
||||
}
|
||||
|
||||
<T> Consumer<T> option(Option<T> option) {
|
||||
return (value) -> getFactory().addBuilderCustomizers((builder) -> builder.setServerOption(option, value));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link ConfigurableUndertowWebServerFactory} wrapper that makes it easier to apply
|
||||
* {@link Options socket options}.
|
||||
*/
|
||||
private static class SocketOptions extends AbstractOptions {
|
||||
|
||||
SocketOptions(ConfigurableUndertowWebServerFactory factory) {
|
||||
super(Options.class, factory);
|
||||
}
|
||||
|
||||
<T> Consumer<T> option(Option<T> option) {
|
||||
return (value) -> getFactory().addBuilderCustomizers((builder) -> builder.setSocketOption(option, value));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,47 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.undertow.autoconfigure.actuate.web.server;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.boot.actuate.autoconfigure.web.server.AccessLogCustomizer;
|
||||
import org.springframework.boot.undertow.ConfigurableUndertowWebServerFactory;
|
||||
|
||||
/**
|
||||
* {@link AccessLogCustomizer} for Undertow.
|
||||
*
|
||||
* @param <T> the type of factory that can be customized
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
class UndertowAccessLogCustomizer<T extends ConfigurableUndertowWebServerFactory> extends AccessLogCustomizer<T> {
|
||||
|
||||
private final Function<T, @Nullable String> accessLogPrefixExtractor;
|
||||
|
||||
UndertowAccessLogCustomizer(UndertowManagementServerProperties properties,
|
||||
Function<T, @Nullable String> accessLogPrefixExtractor) {
|
||||
super(properties.getAccesslog().getPrefix());
|
||||
this.accessLogPrefixExtractor = accessLogPrefixExtractor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void customize(T factory) {
|
||||
factory.setAccessLogPrefix(customizePrefix(this.accessLogPrefixExtractor.apply(factory)));
|
||||
}
|
||||
|
||||
}
|
|
@ -1,53 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.undertow.autoconfigure.actuate.web.server;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
/**
|
||||
* Properties for an Undertow-based management server.
|
||||
*
|
||||
* @author Moritz Halbritter
|
||||
* @since 4.0.0
|
||||
*/
|
||||
@ConfigurationProperties("management.server.undertow")
|
||||
public class UndertowManagementServerProperties {
|
||||
|
||||
private final Accesslog accesslog = new Accesslog();
|
||||
|
||||
public Accesslog getAccesslog() {
|
||||
return this.accesslog;
|
||||
}
|
||||
|
||||
public static class Accesslog {
|
||||
|
||||
/**
|
||||
* Management log file name prefix.
|
||||
*/
|
||||
private String prefix = "management_";
|
||||
|
||||
public String getPrefix() {
|
||||
return this.prefix;
|
||||
}
|
||||
|
||||
public void setPrefix(String prefix) {
|
||||
this.prefix = prefix;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,59 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.undertow.autoconfigure.actuate.web.server;
|
||||
|
||||
import io.undertow.Undertow;
|
||||
|
||||
import org.springframework.boot.WebApplicationType;
|
||||
import org.springframework.boot.actuate.autoconfigure.web.ManagementContextConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.web.ManagementContextType;
|
||||
import org.springframework.boot.actuate.autoconfigure.web.server.ManagementContextFactory;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.undertow.autoconfigure.reactive.UndertowReactiveWebServerAutoConfiguration;
|
||||
import org.springframework.boot.undertow.reactive.UndertowReactiveWebServerFactory;
|
||||
import org.springframework.boot.web.server.reactive.ReactiveWebServerFactory;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
/**
|
||||
* {@link ManagementContextConfiguration @ManagementContextConfiguration} for
|
||||
* Undertow-based reactive web endpoint infrastructure when a separate management context
|
||||
* running on a different port is required.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
@ConditionalOnClass(Undertow.class)
|
||||
@ConditionalOnWebApplication(type = Type.REACTIVE)
|
||||
@EnableConfigurationProperties(UndertowManagementServerProperties.class)
|
||||
@ManagementContextConfiguration(value = ManagementContextType.CHILD, proxyBeanMethods = false)
|
||||
class UndertowReactiveManagementChildContextConfiguration {
|
||||
|
||||
@Bean
|
||||
static ManagementContextFactory reactiveWebChildContextFactory() {
|
||||
return new ManagementContextFactory(WebApplicationType.REACTIVE, ReactiveWebServerFactory.class,
|
||||
UndertowReactiveWebServerAutoConfiguration.class);
|
||||
}
|
||||
|
||||
@Bean
|
||||
UndertowAccessLogCustomizer<UndertowReactiveWebServerFactory> undertowManagementAccessLogCustomizer(
|
||||
UndertowManagementServerProperties properties) {
|
||||
return new UndertowAccessLogCustomizer<>(properties, UndertowReactiveWebServerFactory::getAccessLogPrefix);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,51 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.undertow.autoconfigure.actuate.web.server;
|
||||
|
||||
import io.undertow.Undertow;
|
||||
|
||||
import org.springframework.boot.WebApplicationType;
|
||||
import org.springframework.boot.actuate.autoconfigure.web.server.ConditionalOnManagementPort;
|
||||
import org.springframework.boot.actuate.autoconfigure.web.server.ManagementContextFactory;
|
||||
import org.springframework.boot.actuate.autoconfigure.web.server.ManagementPortType;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;
|
||||
import org.springframework.boot.undertow.autoconfigure.reactive.UndertowReactiveWebServerAutoConfiguration;
|
||||
import org.springframework.boot.web.server.reactive.ReactiveWebServerFactory;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
/**
|
||||
* Auto-configuration for an Undertow-based reactive management context.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @since 4.0.0
|
||||
*/
|
||||
@AutoConfiguration
|
||||
@ConditionalOnClass({ Undertow.class, ManagementContextFactory.class })
|
||||
@ConditionalOnWebApplication(type = Type.REACTIVE)
|
||||
@ConditionalOnManagementPort(ManagementPortType.DIFFERENT)
|
||||
public final class UndertowReactiveManagementContextAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
static ManagementContextFactory reactiveWebChildContextFactory() {
|
||||
return new ManagementContextFactory(WebApplicationType.REACTIVE, ReactiveWebServerFactory.class,
|
||||
UndertowReactiveWebServerAutoConfiguration.class);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,49 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.undertow.autoconfigure.actuate.web.server;
|
||||
|
||||
import io.undertow.Undertow;
|
||||
|
||||
import org.springframework.boot.actuate.autoconfigure.web.ManagementContextConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.web.ManagementContextType;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.undertow.servlet.UndertowServletWebServerFactory;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
/**
|
||||
* {@link ManagementContextConfiguration @ManagementContextConfiguration} for
|
||||
* Undertow-based servlet web endpoint infrastructure when a separate management context
|
||||
* running on a different port is required.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
@ConditionalOnClass(Undertow.class)
|
||||
@ConditionalOnWebApplication(type = Type.SERVLET)
|
||||
@EnableConfigurationProperties(UndertowManagementServerProperties.class)
|
||||
@ManagementContextConfiguration(value = ManagementContextType.CHILD, proxyBeanMethods = false)
|
||||
class UndertowServletManagementChildContextConfiguration {
|
||||
|
||||
@Bean
|
||||
UndertowAccessLogCustomizer<UndertowServletWebServerFactory> undertowManagementAccessLogCustomizer(
|
||||
UndertowManagementServerProperties properties) {
|
||||
return new UndertowAccessLogCustomizer<>(properties, UndertowServletWebServerFactory::getAccessLogPrefix);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,51 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.undertow.autoconfigure.actuate.web.server;
|
||||
|
||||
import io.undertow.Undertow;
|
||||
|
||||
import org.springframework.boot.WebApplicationType;
|
||||
import org.springframework.boot.actuate.autoconfigure.web.server.ConditionalOnManagementPort;
|
||||
import org.springframework.boot.actuate.autoconfigure.web.server.ManagementContextFactory;
|
||||
import org.springframework.boot.actuate.autoconfigure.web.server.ManagementPortType;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;
|
||||
import org.springframework.boot.undertow.autoconfigure.servlet.UndertowServletWebServerAutoConfiguration;
|
||||
import org.springframework.boot.web.server.servlet.ServletWebServerFactory;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
/**
|
||||
* Auto-configuration for an Undertow-based servlet management context.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @since 4.0.0
|
||||
*/
|
||||
@AutoConfiguration
|
||||
@ConditionalOnClass({ Undertow.class, ManagementContextFactory.class })
|
||||
@ConditionalOnWebApplication(type = Type.SERVLET)
|
||||
@ConditionalOnManagementPort(ManagementPortType.DIFFERENT)
|
||||
public final class UndertowServletManagementContextAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
static ManagementContextFactory servletWebChildContextFactory() {
|
||||
return new ManagementContextFactory(WebApplicationType.SERVLET, ServletWebServerFactory.class,
|
||||
UndertowServletWebServerAutoConfiguration.class);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Actuator Undertow actuator web concerns.
|
||||
*/
|
||||
@NullMarked
|
||||
package org.springframework.boot.undertow.autoconfigure.actuate.web.server;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
|
@ -1,24 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Classes related to the auto-configuration of a servlet or reactive web server using
|
||||
* Undertow.
|
||||
*/
|
||||
@NullMarked
|
||||
package org.springframework.boot.undertow.autoconfigure;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
|
@ -1,62 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.undertow.autoconfigure.reactive;
|
||||
|
||||
import io.undertow.Undertow;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.undertow.UndertowBuilderCustomizer;
|
||||
import org.springframework.boot.undertow.autoconfigure.UndertowServerProperties;
|
||||
import org.springframework.boot.undertow.autoconfigure.UndertowWebServerConfiguration;
|
||||
import org.springframework.boot.undertow.reactive.UndertowReactiveWebServerFactory;
|
||||
import org.springframework.boot.web.server.autoconfigure.reactive.ReactiveWebServerConfiguration;
|
||||
import org.springframework.boot.web.server.reactive.ReactiveWebServerFactory;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.http.ReactiveHttpInputMessage;
|
||||
|
||||
/**
|
||||
* {@link EnableAutoConfiguration Auto-configuration} for an Undertow-based reactive web
|
||||
* server.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @since 4.0.0
|
||||
*/
|
||||
@AutoConfiguration
|
||||
@ConditionalOnClass({ ReactiveHttpInputMessage.class, Undertow.class })
|
||||
@ConditionalOnWebApplication(type = Type.REACTIVE)
|
||||
@EnableConfigurationProperties(UndertowServerProperties.class)
|
||||
@Import({ UndertowWebServerConfiguration.class, ReactiveWebServerConfiguration.class })
|
||||
public final class UndertowReactiveWebServerAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(ReactiveWebServerFactory.class)
|
||||
UndertowReactiveWebServerFactory undertowReactiveWebServerFactory(
|
||||
ObjectProvider<UndertowBuilderCustomizer> builderCustomizers) {
|
||||
UndertowReactiveWebServerFactory factory = new UndertowReactiveWebServerFactory();
|
||||
factory.getBuilderCustomizers().addAll(builderCustomizers.orderedStream().toList());
|
||||
return factory;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Classes related to the auto-configuration of a reactive web server using Undertow.
|
||||
*/
|
||||
@NullMarked
|
||||
package org.springframework.boot.undertow.autoconfigure.reactive;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
|
@ -1,97 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.undertow.autoconfigure.servlet;
|
||||
|
||||
import io.undertow.Undertow;
|
||||
import io.undertow.servlet.api.DeploymentInfo;
|
||||
import io.undertow.websockets.jsr.Bootstrap;
|
||||
import jakarta.servlet.ServletRequest;
|
||||
import org.xnio.SslClientAuthMode;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnThreading;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;
|
||||
import org.springframework.boot.autoconfigure.condition.SearchStrategy;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.thread.Threading;
|
||||
import org.springframework.boot.undertow.UndertowBuilderCustomizer;
|
||||
import org.springframework.boot.undertow.autoconfigure.UndertowServerProperties;
|
||||
import org.springframework.boot.undertow.autoconfigure.UndertowWebServerConfiguration;
|
||||
import org.springframework.boot.undertow.servlet.UndertowDeploymentInfoCustomizer;
|
||||
import org.springframework.boot.undertow.servlet.UndertowServletWebServerFactory;
|
||||
import org.springframework.boot.web.server.autoconfigure.servlet.ServletWebServerConfiguration;
|
||||
import org.springframework.boot.web.server.servlet.ServletWebServerFactory;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.core.task.VirtualThreadTaskExecutor;
|
||||
|
||||
/**
|
||||
* {@link EnableAutoConfiguration Auto-configuration} for an Undertow-based servlet web
|
||||
* server.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @since 4.0.0
|
||||
*/
|
||||
@AutoConfiguration
|
||||
@ConditionalOnClass({ ServletRequest.class, Undertow.class, SslClientAuthMode.class, DeploymentInfo.class })
|
||||
@ConditionalOnWebApplication(type = Type.SERVLET)
|
||||
@EnableConfigurationProperties(UndertowServerProperties.class)
|
||||
@Import({ UndertowWebServerConfiguration.class, ServletWebServerConfiguration.class })
|
||||
public final class UndertowServletWebServerAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
|
||||
UndertowServletWebServerFactory undertowServletWebServerFactory(
|
||||
ObjectProvider<UndertowDeploymentInfoCustomizer> deploymentInfoCustomizers,
|
||||
ObjectProvider<UndertowBuilderCustomizer> builderCustomizers) {
|
||||
UndertowServletWebServerFactory factory = new UndertowServletWebServerFactory();
|
||||
factory.getDeploymentInfoCustomizers().addAll(deploymentInfoCustomizers.orderedStream().toList());
|
||||
factory.getBuilderCustomizers().addAll(builderCustomizers.orderedStream().toList());
|
||||
return factory;
|
||||
}
|
||||
|
||||
@Bean
|
||||
UndertowServletWebServerFactoryCustomizer undertowServletWebServerFactoryCustomizer(
|
||||
UndertowServerProperties undertowProperties) {
|
||||
return new UndertowServletWebServerFactoryCustomizer(undertowProperties);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnThreading(Threading.VIRTUAL)
|
||||
UndertowDeploymentInfoCustomizer virtualThreadsUndertowDeploymentInfoCustomizer() {
|
||||
return (deploymentInfo) -> deploymentInfo.setExecutor(new VirtualThreadTaskExecutor("undertow-"));
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnClass(Bootstrap.class)
|
||||
static class UndertowWebSocketConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(name = "websocketServletWebServerCustomizer")
|
||||
WebSocketUndertowServletWebServerFactoryCustomizer websocketServletWebServerCustomizer() {
|
||||
return new WebSocketUndertowServletWebServerFactoryCustomizer();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,44 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.undertow.autoconfigure.servlet;
|
||||
|
||||
import org.springframework.boot.undertow.autoconfigure.UndertowServerProperties;
|
||||
import org.springframework.boot.undertow.servlet.UndertowServletWebServerFactory;
|
||||
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
|
||||
import org.springframework.boot.web.server.autoconfigure.ServerProperties;
|
||||
|
||||
/**
|
||||
* {@link WebServerFactoryCustomizer} to apply {@link ServerProperties} to Undertow
|
||||
* Servlet web servers.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
class UndertowServletWebServerFactoryCustomizer implements WebServerFactoryCustomizer<UndertowServletWebServerFactory> {
|
||||
|
||||
private final UndertowServerProperties undertowProperties;
|
||||
|
||||
UndertowServletWebServerFactoryCustomizer(UndertowServerProperties undertowProperties) {
|
||||
this.undertowProperties = undertowProperties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void customize(UndertowServletWebServerFactory factory) {
|
||||
factory.setEagerFilterInit(this.undertowProperties.isEagerFilterInit());
|
||||
factory.setPreservePathOnForward(this.undertowProperties.isPreservePathOnForward());
|
||||
}
|
||||
|
||||
}
|
|
@ -1,57 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.undertow.autoconfigure.servlet;
|
||||
|
||||
import io.undertow.servlet.api.DeploymentInfo;
|
||||
import io.undertow.websockets.jsr.WebSocketDeploymentInfo;
|
||||
|
||||
import org.springframework.boot.undertow.servlet.UndertowDeploymentInfoCustomizer;
|
||||
import org.springframework.boot.undertow.servlet.UndertowServletWebServerFactory;
|
||||
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
|
||||
import org.springframework.core.Ordered;
|
||||
|
||||
/**
|
||||
* WebSocket customizer for {@link UndertowServletWebServerFactory}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 4.0.0
|
||||
*/
|
||||
public class WebSocketUndertowServletWebServerFactoryCustomizer
|
||||
implements WebServerFactoryCustomizer<UndertowServletWebServerFactory>, Ordered {
|
||||
|
||||
@Override
|
||||
public void customize(UndertowServletWebServerFactory factory) {
|
||||
WebsocketDeploymentInfoCustomizer customizer = new WebsocketDeploymentInfoCustomizer();
|
||||
factory.addDeploymentInfoCustomizers(customizer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static final class WebsocketDeploymentInfoCustomizer implements UndertowDeploymentInfoCustomizer {
|
||||
|
||||
@Override
|
||||
public void customize(DeploymentInfo deploymentInfo) {
|
||||
WebSocketDeploymentInfo info = new WebSocketDeploymentInfo();
|
||||
deploymentInfo.addServletContextAttribute(WebSocketDeploymentInfo.ATTRIBUTE_NAME, info);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Classes related to the auto-configuration of a servlet web server using Undertow.
|
||||
*/
|
||||
@NullMarked
|
||||
package org.springframework.boot.undertow.autoconfigure.servlet;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
|
@ -1,26 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Reactive and servlet web server implementations backed by Undertow.
|
||||
*
|
||||
* @see org.springframework.boot.undertow.servlet.UndertowServletWebServerFactory
|
||||
* @see org.springframework.boot.undertow.reactive.UndertowReactiveWebServerFactory
|
||||
*/
|
||||
@NullMarked
|
||||
package org.springframework.boot.undertow;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
|
@ -1,65 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.undertow.reactive;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import io.undertow.Undertow;
|
||||
|
||||
import org.springframework.boot.undertow.ConfigurableUndertowWebServerFactory;
|
||||
import org.springframework.boot.undertow.HttpHandlerFactory;
|
||||
import org.springframework.boot.undertow.UndertowWebServer;
|
||||
import org.springframework.boot.undertow.UndertowWebServerFactory;
|
||||
import org.springframework.boot.web.server.WebServer;
|
||||
import org.springframework.boot.web.server.reactive.ConfigurableReactiveWebServerFactory;
|
||||
import org.springframework.boot.web.server.reactive.ReactiveWebServerFactory;
|
||||
import org.springframework.http.server.reactive.UndertowHttpHandlerAdapter;
|
||||
|
||||
/**
|
||||
* {@link ReactiveWebServerFactory} that can be used to create {@link UndertowWebServer}s.
|
||||
*
|
||||
* @author Brian Clozel
|
||||
* @author Scott Frederick
|
||||
* @since 4.0.0
|
||||
*/
|
||||
public class UndertowReactiveWebServerFactory extends UndertowWebServerFactory
|
||||
implements ConfigurableUndertowWebServerFactory, ConfigurableReactiveWebServerFactory {
|
||||
|
||||
/**
|
||||
* Create a new {@link UndertowReactiveWebServerFactory} instance.
|
||||
*/
|
||||
public UndertowReactiveWebServerFactory() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link UndertowReactiveWebServerFactory} that listens for requests
|
||||
* using the specified port.
|
||||
* @param port the port to listen on
|
||||
*/
|
||||
public UndertowReactiveWebServerFactory(int port) {
|
||||
super(port);
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebServer getWebServer(org.springframework.http.server.reactive.HttpHandler httpHandler) {
|
||||
Undertow.Builder builder = createBuilder(this, this::getSslBundle, this::getServerNameSslBundles);
|
||||
List<HttpHandlerFactory> httpHandlerFactories = createHttpHandlerFactories(this,
|
||||
(next) -> new UndertowHttpHandlerAdapter(httpHandler));
|
||||
return new UndertowWebServer(builder, httpHandlerFactories, getPort() >= 0);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Reactive web server implementation backed by Undertow.
|
||||
*/
|
||||
@NullMarked
|
||||
package org.springframework.boot.undertow.reactive;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
|
@ -1,75 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.undertow.servlet;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import io.undertow.UndertowMessages;
|
||||
import io.undertow.server.handlers.resource.Resource;
|
||||
import io.undertow.server.handlers.resource.ResourceChangeListener;
|
||||
import io.undertow.server.handlers.resource.ResourceManager;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* A {@link ResourceManager} that delegates to multiple {@code ResourceManager} instances.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
class CompositeResourceManager implements ResourceManager {
|
||||
|
||||
private final List<ResourceManager> resourceManagers;
|
||||
|
||||
CompositeResourceManager(ResourceManager... resourceManagers) {
|
||||
this.resourceManagers = Arrays.asList(resourceManagers);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
for (ResourceManager resourceManager : this.resourceManagers) {
|
||||
resourceManager.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Resource getResource(String path) throws IOException {
|
||||
for (ResourceManager resourceManager : this.resourceManagers) {
|
||||
Resource resource = resourceManager.getResource(path);
|
||||
if (resource != null) {
|
||||
return resource;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isResourceChangeListenerSupported() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerResourceChangeListener(ResourceChangeListener listener) {
|
||||
throw UndertowMessages.MESSAGES.resourceChangeListenerNotSupported();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeResourceChangeListener(ResourceChangeListener listener) {
|
||||
throw UndertowMessages.MESSAGES.resourceChangeListenerNotSupported();
|
||||
}
|
||||
|
||||
}
|
|
@ -1,96 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.undertow.servlet;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
|
||||
import io.undertow.server.HttpHandler;
|
||||
import io.undertow.server.HttpServerExchange;
|
||||
import io.undertow.servlet.api.DeploymentManager;
|
||||
import jakarta.servlet.ServletException;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.boot.undertow.HttpHandlerFactory;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* {@link HttpHandlerFactory} that for a {@link DeploymentManager}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class DeploymentManagerHttpHandlerFactory implements HttpHandlerFactory {
|
||||
|
||||
private final DeploymentManager deploymentManager;
|
||||
|
||||
DeploymentManagerHttpHandlerFactory(DeploymentManager deploymentManager) {
|
||||
this.deploymentManager = deploymentManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable HttpHandler getHandler(@Nullable HttpHandler next) {
|
||||
Assert.state(next == null, "DeploymentManagerHttpHandlerFactory must be first");
|
||||
return new DeploymentManagerHandler(this.deploymentManager);
|
||||
}
|
||||
|
||||
DeploymentManager getDeploymentManager() {
|
||||
return this.deploymentManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link HttpHandler} that delegates to a {@link DeploymentManager}.
|
||||
*/
|
||||
static class DeploymentManagerHandler implements HttpHandler, Closeable {
|
||||
|
||||
private final DeploymentManager deploymentManager;
|
||||
|
||||
private final HttpHandler handler;
|
||||
|
||||
DeploymentManagerHandler(DeploymentManager deploymentManager) {
|
||||
this.deploymentManager = deploymentManager;
|
||||
try {
|
||||
this.handler = deploymentManager.start();
|
||||
}
|
||||
catch (ServletException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleRequest(HttpServerExchange exchange) throws Exception {
|
||||
this.handler.handleRequest(exchange);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
try {
|
||||
this.deploymentManager.stop();
|
||||
this.deploymentManager.undeploy();
|
||||
}
|
||||
catch (ServletException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
DeploymentManager getDeploymentManager() {
|
||||
return this.deploymentManager;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,148 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.undertow.servlet;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import io.undertow.servlet.UndertowServletLogger;
|
||||
import io.undertow.servlet.api.SessionPersistenceManager;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.core.ConfigurableObjectInputStream;
|
||||
|
||||
/**
|
||||
* {@link SessionPersistenceManager} that stores session information in a file.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Peter Leibiger
|
||||
* @author Raja Kolli
|
||||
*/
|
||||
class FileSessionPersistence implements SessionPersistenceManager {
|
||||
|
||||
private final File dir;
|
||||
|
||||
FileSessionPersistence(File dir) {
|
||||
this.dir = dir;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void persistSessions(String deploymentName, Map<String, PersistentSession> sessionData) {
|
||||
try {
|
||||
save(sessionData, getSessionFile(deploymentName));
|
||||
}
|
||||
catch (Exception ex) {
|
||||
UndertowServletLogger.ROOT_LOGGER.failedToPersistSessions(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void save(Map<String, PersistentSession> sessionData, File file) throws IOException {
|
||||
try (ObjectOutputStream stream = new ObjectOutputStream(new FileOutputStream(file))) {
|
||||
save(sessionData, stream);
|
||||
}
|
||||
}
|
||||
|
||||
private void save(Map<String, PersistentSession> sessionData, ObjectOutputStream stream) throws IOException {
|
||||
Map<String, Serializable> session = new LinkedHashMap<>();
|
||||
sessionData.forEach((key, value) -> session.put(key, new SerializablePersistentSession(value)));
|
||||
stream.writeObject(session);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Map<String, PersistentSession> loadSessionAttributes(String deploymentName,
|
||||
final ClassLoader classLoader) {
|
||||
try {
|
||||
File file = getSessionFile(deploymentName);
|
||||
if (file.exists()) {
|
||||
return load(file, classLoader);
|
||||
}
|
||||
}
|
||||
catch (Exception ex) {
|
||||
UndertowServletLogger.ROOT_LOGGER.failedtoLoadPersistentSessions(ex);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private Map<String, PersistentSession> load(File file, ClassLoader classLoader)
|
||||
throws IOException, ClassNotFoundException {
|
||||
try (ObjectInputStream stream = new ConfigurableObjectInputStream(new FileInputStream(file), classLoader)) {
|
||||
return load(stream);
|
||||
}
|
||||
}
|
||||
|
||||
private Map<String, PersistentSession> load(ObjectInputStream stream) throws ClassNotFoundException, IOException {
|
||||
Map<String, SerializablePersistentSession> session = readSession(stream);
|
||||
long time = System.currentTimeMillis();
|
||||
Map<String, PersistentSession> result = new LinkedHashMap<>();
|
||||
session.forEach((key, value) -> {
|
||||
PersistentSession entrySession = value.getPersistentSession();
|
||||
if (entrySession.getExpiration().getTime() > time) {
|
||||
result.put(key, entrySession);
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Map<String, SerializablePersistentSession> readSession(ObjectInputStream stream)
|
||||
throws ClassNotFoundException, IOException {
|
||||
return ((Map<String, SerializablePersistentSession>) stream.readObject());
|
||||
}
|
||||
|
||||
private File getSessionFile(String deploymentName) {
|
||||
if (!this.dir.exists()) {
|
||||
this.dir.mkdirs();
|
||||
}
|
||||
return new File(this.dir, deploymentName + ".session");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear(String deploymentName) {
|
||||
getSessionFile(deploymentName).delete();
|
||||
}
|
||||
|
||||
/**
|
||||
* Session data in a serializable form.
|
||||
*/
|
||||
static class SerializablePersistentSession implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 0L;
|
||||
|
||||
private final Date expiration;
|
||||
|
||||
private final Map<String, Object> sessionData;
|
||||
|
||||
SerializablePersistentSession(PersistentSession session) {
|
||||
this.expiration = session.getExpiration();
|
||||
this.sessionData = new LinkedHashMap<>(session.getSessionData());
|
||||
}
|
||||
|
||||
PersistentSession getPersistentSession() {
|
||||
return new PersistentSession(this.expiration, this.sessionData);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,83 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.undertow.servlet;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
|
||||
import io.undertow.UndertowMessages;
|
||||
import io.undertow.server.handlers.resource.Resource;
|
||||
import io.undertow.server.handlers.resource.ResourceChangeListener;
|
||||
import io.undertow.server.handlers.resource.ResourceManager;
|
||||
import io.undertow.server.handlers.resource.URLResource;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* {@link ResourceManager} for JAR resources.
|
||||
*
|
||||
* @author Ivan Sopov
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
class JarResourceManager implements ResourceManager {
|
||||
|
||||
private final String jarPath;
|
||||
|
||||
JarResourceManager(File jarFile) {
|
||||
try {
|
||||
this.jarPath = jarFile.getAbsoluteFile().toURI().toURL().toString();
|
||||
}
|
||||
catch (MalformedURLException ex) {
|
||||
throw new IllegalArgumentException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Resource getResource(String path) throws IOException {
|
||||
URL url = new URL("jar:" + this.jarPath + "!" + (path.startsWith("/") ? path : "/" + path));
|
||||
URLResource resource = new URLResource(url, path);
|
||||
if (StringUtils.hasText(path) && !"/".equals(path) && resource.getContentLength() < 0) {
|
||||
return null;
|
||||
}
|
||||
return resource;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isResourceChangeListenerSupported() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerResourceChangeListener(ResourceChangeListener listener) {
|
||||
throw UndertowMessages.MESSAGES.resourceChangeListenerNotSupported();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeResourceChangeListener(ResourceChangeListener listener) {
|
||||
throw UndertowMessages.MESSAGES.resourceChangeListenerNotSupported();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,37 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.undertow.servlet;
|
||||
|
||||
import io.undertow.servlet.api.DeploymentInfo;
|
||||
|
||||
/**
|
||||
* Callback interface that can be used to customize an Undertow {@link DeploymentInfo}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 4.0.0
|
||||
* @see UndertowServletWebServerFactory
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface UndertowDeploymentInfoCustomizer {
|
||||
|
||||
/**
|
||||
* Customize the deployment info.
|
||||
* @param deploymentInfo the {@code DeploymentInfo} to customize
|
||||
*/
|
||||
void customize(DeploymentInfo deploymentInfo);
|
||||
|
||||
}
|
|
@ -1,96 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.undertow.servlet;
|
||||
|
||||
import io.undertow.Handlers;
|
||||
import io.undertow.Undertow.Builder;
|
||||
import io.undertow.server.HttpHandler;
|
||||
import io.undertow.servlet.api.DeploymentManager;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.boot.undertow.HttpHandlerFactory;
|
||||
import org.springframework.boot.undertow.UndertowWebServer;
|
||||
import org.springframework.boot.web.server.WebServer;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* {@link WebServer} that can be used to control an embedded Undertow server. Typically
|
||||
* this class should be created using {@link UndertowServletWebServerFactory} and not
|
||||
* directly.
|
||||
*
|
||||
* @author Ivan Sopov
|
||||
* @author Andy Wilkinson
|
||||
* @author Eddú Meléndez
|
||||
* @author Christoph Dreis
|
||||
* @author Kristine Jetzke
|
||||
* @since 4.0.0
|
||||
* @see UndertowServletWebServerFactory
|
||||
*/
|
||||
public class UndertowServletWebServer extends UndertowWebServer {
|
||||
|
||||
private final String contextPath;
|
||||
|
||||
private final @Nullable DeploymentManager manager;
|
||||
|
||||
/**
|
||||
* Create a new {@link UndertowServletWebServer} instance.
|
||||
* @param builder the builder
|
||||
* @param httpHandlerFactories the handler factories
|
||||
* @param contextPath the root context path
|
||||
* @param autoStart if the server should be started
|
||||
* @since 4.0.0
|
||||
*/
|
||||
public UndertowServletWebServer(Builder builder, Iterable<HttpHandlerFactory> httpHandlerFactories,
|
||||
String contextPath, boolean autoStart) {
|
||||
super(builder, httpHandlerFactories, autoStart);
|
||||
this.contextPath = contextPath;
|
||||
this.manager = findManager(httpHandlerFactories);
|
||||
}
|
||||
|
||||
private @Nullable DeploymentManager findManager(Iterable<HttpHandlerFactory> httpHandlerFactories) {
|
||||
for (HttpHandlerFactory httpHandlerFactory : httpHandlerFactories) {
|
||||
if (httpHandlerFactory instanceof DeploymentManagerHttpHandlerFactory deploymentManagerFactory) {
|
||||
return deploymentManagerFactory.getDeploymentManager();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @Nullable HttpHandler createHttpHandler() {
|
||||
HttpHandler handler = super.createHttpHandler();
|
||||
if (StringUtils.hasLength(this.contextPath)) {
|
||||
handler = Handlers.path().addPrefixPath(this.contextPath, handler);
|
||||
}
|
||||
return handler;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getStartedLogMessage() {
|
||||
String contextPath = StringUtils.hasText(this.contextPath) ? this.contextPath : "/";
|
||||
StringBuilder message = new StringBuilder(super.getStartedLogMessage());
|
||||
message.append(" with context path '");
|
||||
message.append(contextPath);
|
||||
message.append("'");
|
||||
return message.toString();
|
||||
}
|
||||
|
||||
public @Nullable DeploymentManager getDeploymentManager() {
|
||||
return this.manager;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,608 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.undertow.servlet;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.EventListener;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import io.undertow.Undertow.Builder;
|
||||
import io.undertow.server.HttpHandler;
|
||||
import io.undertow.server.HttpServerExchange;
|
||||
import io.undertow.server.handlers.Cookie;
|
||||
import io.undertow.server.handlers.resource.FileResourceManager;
|
||||
import io.undertow.server.handlers.resource.Resource;
|
||||
import io.undertow.server.handlers.resource.ResourceChangeListener;
|
||||
import io.undertow.server.handlers.resource.ResourceManager;
|
||||
import io.undertow.server.handlers.resource.URLResource;
|
||||
import io.undertow.server.session.SessionManager;
|
||||
import io.undertow.servlet.Servlets;
|
||||
import io.undertow.servlet.api.Deployment;
|
||||
import io.undertow.servlet.api.DeploymentInfo;
|
||||
import io.undertow.servlet.api.DeploymentManager;
|
||||
import io.undertow.servlet.api.ListenerInfo;
|
||||
import io.undertow.servlet.api.MimeMapping;
|
||||
import io.undertow.servlet.api.ServletContainerInitializerInfo;
|
||||
import io.undertow.servlet.api.ServletStackTraces;
|
||||
import io.undertow.servlet.core.DeploymentImpl;
|
||||
import io.undertow.servlet.handlers.DefaultServlet;
|
||||
import io.undertow.servlet.util.ImmediateInstanceFactory;
|
||||
import jakarta.servlet.ServletContainerInitializer;
|
||||
import jakarta.servlet.ServletContext;
|
||||
import jakarta.servlet.ServletException;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.boot.context.properties.PropertyMapper;
|
||||
import org.springframework.boot.undertow.HttpHandlerFactory;
|
||||
import org.springframework.boot.undertow.UndertowWebServerFactory;
|
||||
import org.springframework.boot.web.error.ErrorPage;
|
||||
import org.springframework.boot.web.server.Cookie.SameSite;
|
||||
import org.springframework.boot.web.server.MimeMappings.Mapping;
|
||||
import org.springframework.boot.web.server.WebServer;
|
||||
import org.springframework.boot.web.server.servlet.ConfigurableServletWebServerFactory;
|
||||
import org.springframework.boot.web.server.servlet.ContextPath;
|
||||
import org.springframework.boot.web.server.servlet.CookieSameSiteSupplier;
|
||||
import org.springframework.boot.web.server.servlet.DocumentRoot;
|
||||
import org.springframework.boot.web.server.servlet.ServletContextInitializers;
|
||||
import org.springframework.boot.web.server.servlet.ServletWebServerFactory;
|
||||
import org.springframework.boot.web.server.servlet.ServletWebServerSettings;
|
||||
import org.springframework.boot.web.servlet.ServletContextInitializer;
|
||||
import org.springframework.context.ResourceLoaderAware;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
/**
|
||||
* {@link ServletWebServerFactory} that can be used to create
|
||||
* {@link UndertowServletWebServer}s.
|
||||
* <p>
|
||||
* Unless explicitly configured otherwise, the factory will create servers that listen for
|
||||
* HTTP requests on port 8080.
|
||||
*
|
||||
* @author Ivan Sopov
|
||||
* @author Andy Wilkinson
|
||||
* @author Marcos Barbero
|
||||
* @author Eddú Meléndez
|
||||
* @author Scott Frederick
|
||||
* @since 4.0.0
|
||||
* @see UndertowServletWebServer
|
||||
*/
|
||||
public class UndertowServletWebServerFactory extends UndertowWebServerFactory
|
||||
implements ConfigurableServletWebServerFactory, ResourceLoaderAware {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(UndertowServletWebServerFactory.class);
|
||||
|
||||
private static final Pattern ENCODED_SLASH = Pattern.compile("%2F", Pattern.LITERAL);
|
||||
|
||||
private static final Set<Class<?>> NO_CLASSES = Collections.emptySet();
|
||||
|
||||
private final ServletWebServerSettings settings = new ServletWebServerSettings();
|
||||
|
||||
private Set<UndertowDeploymentInfoCustomizer> deploymentInfoCustomizers = new LinkedHashSet<>();
|
||||
|
||||
@SuppressWarnings("NullAway.Init")
|
||||
private ResourceLoader resourceLoader;
|
||||
|
||||
private boolean eagerFilterInit = true;
|
||||
|
||||
private boolean preservePathOnForward;
|
||||
|
||||
/**
|
||||
* Create a new {@link UndertowServletWebServerFactory} instance.
|
||||
*/
|
||||
public UndertowServletWebServerFactory() {
|
||||
getSettings().getJsp().setRegistered(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link UndertowServletWebServerFactory} that listens for requests
|
||||
* using the specified port.
|
||||
* @param port the port to listen on
|
||||
*/
|
||||
public UndertowServletWebServerFactory(int port) {
|
||||
super(port);
|
||||
getSettings().getJsp().setRegistered(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link UndertowServletWebServerFactory} with the specified context
|
||||
* path and port.
|
||||
* @param contextPath the root context path
|
||||
* @param port the port to listen on
|
||||
*/
|
||||
public UndertowServletWebServerFactory(String contextPath, int port) {
|
||||
super(port);
|
||||
getSettings().setContextPath(ContextPath.of(contextPath));
|
||||
getSettings().getJsp().setRegistered(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a mutable collection of the {@link UndertowDeploymentInfoCustomizer}s that
|
||||
* will be applied to the Undertow {@link DeploymentInfo}.
|
||||
* @return the customizers that will be applied
|
||||
*/
|
||||
public Collection<UndertowDeploymentInfoCustomizer> getDeploymentInfoCustomizers() {
|
||||
return this.deploymentInfoCustomizers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set {@link UndertowDeploymentInfoCustomizer}s that should be applied to the
|
||||
* Undertow {@link DeploymentInfo}. Calling this method will replace any existing
|
||||
* customizers.
|
||||
* @param customizers the customizers to set
|
||||
*/
|
||||
public void setDeploymentInfoCustomizers(Collection<? extends UndertowDeploymentInfoCustomizer> customizers) {
|
||||
Assert.notNull(customizers, "'customizers' must not be null");
|
||||
this.deploymentInfoCustomizers = new LinkedHashSet<>(customizers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add {@link UndertowDeploymentInfoCustomizer}s that should be used to customize the
|
||||
* Undertow {@link DeploymentInfo}.
|
||||
* @param customizers the customizers to add
|
||||
*/
|
||||
public void addDeploymentInfoCustomizers(UndertowDeploymentInfoCustomizer... customizers) {
|
||||
Assert.notNull(customizers, "'customizers' must not be null");
|
||||
this.deploymentInfoCustomizers.addAll(Arrays.asList(customizers));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setResourceLoader(ResourceLoader resourceLoader) {
|
||||
this.resourceLoader = resourceLoader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return if filters should be eagerly initialized.
|
||||
* @return {@code true} if filters are eagerly initialized, otherwise {@code false}.
|
||||
*/
|
||||
public boolean isEagerFilterInit() {
|
||||
return this.eagerFilterInit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether filters should be eagerly initialized.
|
||||
* @param eagerFilterInit {@code true} if filters are eagerly initialized, otherwise
|
||||
* {@code false}.
|
||||
*/
|
||||
public void setEagerFilterInit(boolean eagerFilterInit) {
|
||||
this.eagerFilterInit = eagerFilterInit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether the request path should be preserved on forward.
|
||||
* @return {@code true} if the path should be preserved when a request is forwarded,
|
||||
* otherwise {@code false}.
|
||||
*/
|
||||
public boolean isPreservePathOnForward() {
|
||||
return this.preservePathOnForward;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether the request path should be preserved on forward.
|
||||
* @param preservePathOnForward {@code true} if the path should be preserved when a
|
||||
* request is forwarded, otherwise {@code false}.
|
||||
*/
|
||||
public void setPreservePathOnForward(boolean preservePathOnForward) {
|
||||
this.preservePathOnForward = preservePathOnForward;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebServer getWebServer(ServletContextInitializer... initializers) {
|
||||
Builder builder = createBuilder(this, this::getSslBundle, this::getServerNameSslBundles);
|
||||
DeploymentManager manager = createManager(initializers);
|
||||
return getUndertowWebServer(builder, manager, getPort());
|
||||
}
|
||||
|
||||
private DeploymentManager createManager(ServletContextInitializer... initializers) {
|
||||
DeploymentInfo deployment = Servlets.deployment();
|
||||
registerServletContainerInitializerToDriveServletContextInitializers(deployment, initializers);
|
||||
deployment.setClassLoader(getServletClassLoader());
|
||||
deployment.setContextPath(getSettings().getContextPath().toString());
|
||||
deployment.setDisplayName(getSettings().getDisplayName());
|
||||
deployment.setDeploymentName("spring-boot");
|
||||
if (getSettings().isRegisterDefaultServlet()) {
|
||||
deployment.addServlet(Servlets.servlet("default", DefaultServlet.class));
|
||||
}
|
||||
configureErrorPages(deployment);
|
||||
deployment.setServletStackTraces(ServletStackTraces.NONE);
|
||||
deployment.setResourceManager(getDocumentRootResourceManager());
|
||||
deployment.setTempDir(createTempDir("undertow"));
|
||||
deployment.setEagerFilterInit(this.eagerFilterInit);
|
||||
deployment.setPreservePathOnForward(this.preservePathOnForward);
|
||||
configureMimeMappings(deployment);
|
||||
configureWebListeners(deployment);
|
||||
for (UndertowDeploymentInfoCustomizer customizer : this.deploymentInfoCustomizers) {
|
||||
customizer.customize(deployment);
|
||||
}
|
||||
if (getSettings().getSession().isPersistent()) {
|
||||
File dir = getSettings().getSession().getSessionStoreDirectory().getValidDirectory(true);
|
||||
deployment.setSessionPersistenceManager(new FileSessionPersistence(dir));
|
||||
}
|
||||
addLocaleMappings(deployment);
|
||||
DeploymentManager manager = Servlets.newContainer().addDeployment(deployment);
|
||||
manager.deploy();
|
||||
if (manager.getDeployment() instanceof DeploymentImpl managerDeployment) {
|
||||
removeSuperfluousMimeMappings(managerDeployment, deployment);
|
||||
}
|
||||
SessionManager sessionManager = manager.getDeployment().getSessionManager();
|
||||
Duration timeoutDuration = getSettings().getSession().getTimeout();
|
||||
int sessionTimeout = (isZeroOrLess(timeoutDuration) ? -1 : (int) timeoutDuration.getSeconds());
|
||||
sessionManager.setDefaultSessionTimeout(sessionTimeout);
|
||||
return manager;
|
||||
}
|
||||
|
||||
private void configureWebListeners(DeploymentInfo deployment) {
|
||||
for (String className : getSettings().getWebListenerClassNames()) {
|
||||
try {
|
||||
deployment.addListener(new ListenerInfo(loadWebListenerClass(className)));
|
||||
}
|
||||
catch (ClassNotFoundException ex) {
|
||||
throw new IllegalStateException("Failed to load web listener class '" + className + "'", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Class<? extends EventListener> loadWebListenerClass(String className) throws ClassNotFoundException {
|
||||
ClassLoader classLoader = getServletClassLoader();
|
||||
Assert.state(classLoader != null, "'classLoader' must not be null");
|
||||
return (Class<? extends EventListener>) classLoader.loadClass(className);
|
||||
}
|
||||
|
||||
private boolean isZeroOrLess(@Nullable Duration timeoutDuration) {
|
||||
return timeoutDuration == null || timeoutDuration.isZero() || timeoutDuration.isNegative();
|
||||
}
|
||||
|
||||
private void addLocaleMappings(DeploymentInfo deployment) {
|
||||
getSettings().getLocaleCharsetMappings()
|
||||
.forEach((locale, charset) -> deployment.addLocaleCharsetMapping(locale.toString(), charset.toString()));
|
||||
}
|
||||
|
||||
private void registerServletContainerInitializerToDriveServletContextInitializers(DeploymentInfo deployment,
|
||||
ServletContextInitializer... initializers) {
|
||||
ServletContextInitializers mergedInitializers = ServletContextInitializers.from(this.settings, initializers);
|
||||
Initializer initializer = new Initializer(mergedInitializers);
|
||||
deployment.addServletContainerInitializer(new ServletContainerInitializerInfo(Initializer.class,
|
||||
new ImmediateInstanceFactory<>(initializer), NO_CLASSES));
|
||||
}
|
||||
|
||||
private @Nullable ClassLoader getServletClassLoader() {
|
||||
if (this.resourceLoader != null) {
|
||||
return this.resourceLoader.getClassLoader();
|
||||
}
|
||||
return getClass().getClassLoader();
|
||||
}
|
||||
|
||||
private ResourceManager getDocumentRootResourceManager() {
|
||||
DocumentRoot documentRoot = new DocumentRoot(logger);
|
||||
documentRoot.setDirectory(this.settings.getDocumentRoot());
|
||||
File root = documentRoot.getValidDirectory();
|
||||
File docBase = getCanonicalDocumentRoot(root);
|
||||
List<URL> metaInfResourceUrls = getSettings().getStaticResourceUrls();
|
||||
List<URL> resourceJarUrls = new ArrayList<>();
|
||||
List<ResourceManager> managers = new ArrayList<>();
|
||||
ResourceManager rootManager = (docBase.isDirectory() ? new FileResourceManager(docBase, 0)
|
||||
: new JarResourceManager(docBase));
|
||||
if (root != null) {
|
||||
rootManager = new LoaderHidingResourceManager(rootManager);
|
||||
}
|
||||
managers.add(rootManager);
|
||||
for (URL url : metaInfResourceUrls) {
|
||||
if ("file".equals(url.getProtocol())) {
|
||||
try {
|
||||
File file = new File(url.toURI());
|
||||
if (file.isFile()) {
|
||||
resourceJarUrls.add(new URL("jar:" + url + "!/"));
|
||||
}
|
||||
else {
|
||||
managers.add(new FileResourceManager(new File(file, "META-INF/resources"), 0));
|
||||
}
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
else {
|
||||
resourceJarUrls.add(url);
|
||||
}
|
||||
}
|
||||
managers.add(new MetaInfResourcesResourceManager(resourceJarUrls));
|
||||
return new CompositeResourceManager(managers.toArray(new ResourceManager[0]));
|
||||
}
|
||||
|
||||
private File getCanonicalDocumentRoot(@Nullable File docBase) {
|
||||
try {
|
||||
File root = (docBase != null) ? docBase : createTempDir("undertow-docbase");
|
||||
return root.getCanonicalFile();
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new IllegalStateException("Cannot get canonical document root", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void configureErrorPages(DeploymentInfo deployment) {
|
||||
for (ErrorPage errorPage : getErrorPages()) {
|
||||
deployment.addErrorPage(getUndertowErrorPage(errorPage));
|
||||
}
|
||||
}
|
||||
|
||||
private io.undertow.servlet.api.ErrorPage getUndertowErrorPage(ErrorPage errorPage) {
|
||||
if (errorPage.getStatus() != null) {
|
||||
return new io.undertow.servlet.api.ErrorPage(errorPage.getPath(), errorPage.getStatusCode());
|
||||
}
|
||||
if (errorPage.getException() != null) {
|
||||
return new io.undertow.servlet.api.ErrorPage(errorPage.getPath(), errorPage.getException());
|
||||
}
|
||||
return new io.undertow.servlet.api.ErrorPage(errorPage.getPath());
|
||||
}
|
||||
|
||||
private void configureMimeMappings(DeploymentInfo deployment) {
|
||||
for (Mapping mimeMapping : getSettings().getMimeMappings()) {
|
||||
deployment.addMimeMapping(new MimeMapping(mimeMapping.getExtension(), mimeMapping.getMimeType()));
|
||||
}
|
||||
}
|
||||
|
||||
private void removeSuperfluousMimeMappings(DeploymentImpl deployment, DeploymentInfo deploymentInfo) {
|
||||
// DeploymentManagerImpl will always add MimeMappings.DEFAULT_MIME_MAPPINGS
|
||||
// but we only want ours
|
||||
Map<String, String> mappings = new HashMap<>();
|
||||
for (MimeMapping mapping : deploymentInfo.getMimeMappings()) {
|
||||
mappings.put(mapping.getExtension().toLowerCase(Locale.ENGLISH), mapping.getMimeType());
|
||||
}
|
||||
deployment.setMimeExtensionMappings(mappings);
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method called to create the {@link UndertowServletWebServer}. Subclasses
|
||||
* can override this method to return a different {@link UndertowServletWebServer} or
|
||||
* apply additional processing to the {@link Builder} and {@link DeploymentManager}
|
||||
* used to bootstrap Undertow
|
||||
* @param builder the builder
|
||||
* @param manager the deployment manager
|
||||
* @param port the port that Undertow should listen on
|
||||
* @return a new {@link UndertowServletWebServer} instance
|
||||
*/
|
||||
protected UndertowServletWebServer getUndertowWebServer(Builder builder, DeploymentManager manager, int port) {
|
||||
List<HttpHandlerFactory> initialHandlerFactories = new ArrayList<>();
|
||||
initialHandlerFactories.add(new DeploymentManagerHttpHandlerFactory(manager));
|
||||
HttpHandlerFactory cooHandlerFactory = getCookieHandlerFactory(manager.getDeployment());
|
||||
if (cooHandlerFactory != null) {
|
||||
initialHandlerFactories.add(cooHandlerFactory);
|
||||
}
|
||||
List<HttpHandlerFactory> httpHandlerFactories = createHttpHandlerFactories(this,
|
||||
initialHandlerFactories.toArray(new HttpHandlerFactory[0]));
|
||||
return new UndertowServletWebServer(builder, httpHandlerFactories, getSettings().getContextPath().toString(),
|
||||
port >= 0);
|
||||
}
|
||||
|
||||
private @Nullable HttpHandlerFactory getCookieHandlerFactory(Deployment deployment) {
|
||||
SameSite sessionSameSite = getSettings().getSession().getCookie().getSameSite();
|
||||
List<CookieSameSiteSupplier> suppliers = new ArrayList<>();
|
||||
if (sessionSameSite != null) {
|
||||
String sessionCookieName = deployment.getServletContext().getSessionCookieConfig().getName();
|
||||
suppliers.add(CookieSameSiteSupplier.of(sessionSameSite).whenHasName(sessionCookieName));
|
||||
}
|
||||
if (!CollectionUtils.isEmpty(getSettings().getCookieSameSiteSuppliers())) {
|
||||
suppliers.addAll(getSettings().getCookieSameSiteSuppliers());
|
||||
}
|
||||
return (!suppliers.isEmpty()) ? (next) -> new SuppliedSameSiteCookieHandler(next, suppliers) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServletWebServerSettings getSettings() {
|
||||
return this.settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link ServletContainerInitializer} to initialize {@link ServletContextInitializer
|
||||
* ServletContextInitializers}.
|
||||
*/
|
||||
private static class Initializer implements ServletContainerInitializer {
|
||||
|
||||
private final ServletContextInitializers initializers;
|
||||
|
||||
Initializer(ServletContextInitializers initializers) {
|
||||
this.initializers = initializers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartup(Set<Class<?>> classes, ServletContext servletContext) throws ServletException {
|
||||
for (ServletContextInitializer initializer : this.initializers) {
|
||||
initializer.onStartup(servletContext);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link ResourceManager} that exposes resource in {@code META-INF/resources}
|
||||
* directory of nested (in {@code BOOT-INF/lib} or {@code WEB-INF/lib}) jars.
|
||||
*/
|
||||
private static final class MetaInfResourcesResourceManager implements ResourceManager {
|
||||
|
||||
private final List<URL> metaInfResourceJarUrls;
|
||||
|
||||
private MetaInfResourcesResourceManager(List<URL> metaInfResourceJarUrls) {
|
||||
this.metaInfResourceJarUrls = metaInfResourceJarUrls;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Resource getResource(String path) {
|
||||
for (URL url : this.metaInfResourceJarUrls) {
|
||||
URLResource resource = getMetaInfResource(url, path);
|
||||
if (resource != null) {
|
||||
return resource;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isResourceChangeListenerSupported() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerResourceChangeListener(ResourceChangeListener listener) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeResourceChangeListener(ResourceChangeListener listener) {
|
||||
|
||||
}
|
||||
|
||||
private @Nullable URLResource getMetaInfResource(URL resourceJar, String path) {
|
||||
try {
|
||||
String urlPath = URLEncoder.encode(ENCODED_SLASH.matcher(path).replaceAll("/"), StandardCharsets.UTF_8);
|
||||
URL resourceUrl = new URL(resourceJar + "META-INF/resources" + urlPath);
|
||||
URLResource resource = new URLResource(resourceUrl, path);
|
||||
if (resource.getContentLength() < 0) {
|
||||
return null;
|
||||
}
|
||||
return resource;
|
||||
}
|
||||
catch (Exception ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link ResourceManager} to hide Spring Boot loader classes.
|
||||
*/
|
||||
private static final class LoaderHidingResourceManager implements ResourceManager {
|
||||
|
||||
private final ResourceManager delegate;
|
||||
|
||||
private LoaderHidingResourceManager(ResourceManager delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Resource getResource(String path) throws IOException {
|
||||
if (path.startsWith("/org/springframework/boot")) {
|
||||
return null;
|
||||
}
|
||||
return this.delegate.getResource(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isResourceChangeListenerSupported() {
|
||||
return this.delegate.isResourceChangeListenerSupported();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerResourceChangeListener(ResourceChangeListener listener) {
|
||||
this.delegate.registerResourceChangeListener(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeResourceChangeListener(ResourceChangeListener listener) {
|
||||
this.delegate.removeResourceChangeListener(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
this.delegate.close();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link HttpHandler} to apply {@link CookieSameSiteSupplier supplied}
|
||||
* {@link SameSite} cookie values.
|
||||
*/
|
||||
private static class SuppliedSameSiteCookieHandler implements HttpHandler {
|
||||
|
||||
private final @Nullable HttpHandler next;
|
||||
|
||||
private final List<CookieSameSiteSupplier> suppliers;
|
||||
|
||||
SuppliedSameSiteCookieHandler(@Nullable HttpHandler next, List<CookieSameSiteSupplier> suppliers) {
|
||||
this.next = next;
|
||||
this.suppliers = suppliers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleRequest(HttpServerExchange exchange) throws Exception {
|
||||
exchange.addResponseCommitListener(this::beforeCommit);
|
||||
if (this.next != null) {
|
||||
this.next.handleRequest(exchange);
|
||||
}
|
||||
}
|
||||
|
||||
private void beforeCommit(HttpServerExchange exchange) {
|
||||
for (Cookie cookie : exchange.responseCookies()) {
|
||||
SameSite sameSite = getSameSite(asServletCookie(cookie));
|
||||
if (sameSite == SameSite.OMITTED) {
|
||||
cookie.setSameSite(false);
|
||||
}
|
||||
else if (sameSite != null) {
|
||||
cookie.setSameSiteMode(sameSite.attributeValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("removal")
|
||||
private jakarta.servlet.http.Cookie asServletCookie(Cookie cookie) {
|
||||
PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
|
||||
jakarta.servlet.http.Cookie result = new jakarta.servlet.http.Cookie(cookie.getName(), cookie.getValue());
|
||||
map.from(cookie::getComment).to(result::setComment);
|
||||
map.from(cookie::getDomain).to(result::setDomain);
|
||||
map.from(cookie::getMaxAge).to(result::setMaxAge);
|
||||
map.from(cookie::getPath).to(result::setPath);
|
||||
result.setSecure(cookie.isSecure());
|
||||
result.setVersion(cookie.getVersion());
|
||||
result.setHttpOnly(cookie.isHttpOnly());
|
||||
return result;
|
||||
}
|
||||
|
||||
private @Nullable SameSite getSameSite(jakarta.servlet.http.Cookie cookie) {
|
||||
for (CookieSameSiteSupplier supplier : this.suppliers) {
|
||||
SameSite sameSite = supplier.getSameSite(cookie);
|
||||
if (sameSite != null) {
|
||||
return sameSite;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Servlet web server implementation backed by Undertow.
|
||||
*/
|
||||
@NullMarked
|
||||
package org.springframework.boot.undertow.servlet;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
|
@ -1,13 +0,0 @@
|
|||
{
|
||||
"groups": [],
|
||||
"properties": [
|
||||
{
|
||||
"name": "server.undertow.buffers-per-region",
|
||||
"type": "java.lang.Integer",
|
||||
"description": "Number of buffer per region.",
|
||||
"deprecation": {
|
||||
"level": "error"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,2 +0,0 @@
|
|||
org.springframework.aot.hint.RuntimeHintsRegistrar=\
|
||||
org.springframework.boot.undertow.UndertowWebServer$UndertowWebServerRuntimeHints
|
|
@ -1,2 +0,0 @@
|
|||
org.springframework.boot.undertow.autoconfigure.actuate.web.server.UndertowReactiveManagementChildContextConfiguration
|
||||
org.springframework.boot.undertow.autoconfigure.actuate.web.server.UndertowServletManagementChildContextConfiguration
|
|
@ -1,4 +0,0 @@
|
|||
org.springframework.boot.undertow.autoconfigure.actuate.web.server.UndertowReactiveManagementContextAutoConfiguration
|
||||
org.springframework.boot.undertow.autoconfigure.actuate.web.server.UndertowServletManagementContextAutoConfiguration
|
||||
org.springframework.boot.undertow.autoconfigure.reactive.UndertowReactiveWebServerAutoConfiguration
|
||||
org.springframework.boot.undertow.autoconfigure.servlet.UndertowServletWebServerAutoConfiguration
|
|
@ -1,34 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.undertow;
|
||||
|
||||
/**
|
||||
* Helper class to provide public access to package-private methods for testing purposes.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public final class UndertowAccess {
|
||||
|
||||
private UndertowAccess() {
|
||||
|
||||
}
|
||||
|
||||
public static String getStartedLogMessage(UndertowWebServer undertowWebServer) {
|
||||
return undertowWebServer.getStartedLogMessage();
|
||||
}
|
||||
|
||||
}
|
|
@ -1,57 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.undertow;
|
||||
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import io.undertow.Undertow;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.aot.hint.RuntimeHints;
|
||||
import org.springframework.aot.hint.predicate.RuntimeHintsPredicates;
|
||||
import org.springframework.boot.undertow.UndertowWebServer.UndertowWebServerRuntimeHints;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link UndertowWebServerRuntimeHints}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
class UndertowWebServerRuntimeHintsTests {
|
||||
|
||||
@Test
|
||||
void registersHints() throws ClassNotFoundException {
|
||||
RuntimeHints runtimeHints = new RuntimeHints();
|
||||
new UndertowWebServerRuntimeHints().registerHints(runtimeHints, getClass().getClassLoader());
|
||||
assertThat(RuntimeHintsPredicates.reflection().onFieldAccess(Undertow.class, "listeners"))
|
||||
.accepts(runtimeHints);
|
||||
assertThat(RuntimeHintsPredicates.reflection().onFieldAccess(Undertow.class, "channels")).accepts(runtimeHints);
|
||||
assertThat(reflectionOnField("io.undertow.Undertow$ListenerConfig", "type")).accepts(runtimeHints);
|
||||
assertThat(reflectionOnField("io.undertow.Undertow$ListenerConfig", "port")).accepts(runtimeHints);
|
||||
assertThat(reflectionOnField("io.undertow.protocols.ssl.UndertowAcceptingSslChannel", "ssl"))
|
||||
.accepts(runtimeHints);
|
||||
}
|
||||
|
||||
private Predicate<RuntimeHints> reflectionOnField(String className, String fieldName)
|
||||
throws ClassNotFoundException {
|
||||
return RuntimeHintsPredicates.reflection()
|
||||
.onFieldAccess(ReflectionUtils.findField(Class.forName(className), fieldName));
|
||||
}
|
||||
|
||||
}
|
|
@ -1,79 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.undertow.autoconfigure;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import io.undertow.UndertowOptions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.context.properties.bind.Bindable;
|
||||
import org.springframework.boot.context.properties.bind.Binder;
|
||||
import org.springframework.boot.context.properties.source.ConfigurationPropertySource;
|
||||
import org.springframework.boot.context.properties.source.MapConfigurationPropertySource;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link UndertowServerProperties}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
class UndertowServerPropertiesTests {
|
||||
|
||||
private final UndertowServerProperties properties = new UndertowServerProperties();
|
||||
|
||||
@Test
|
||||
void testCustomizeUndertowServerOption() {
|
||||
bind("server.undertow.options.server.ALWAYS_SET_KEEP_ALIVE", "true");
|
||||
assertThat(this.properties.getOptions().getServer()).containsEntry("ALWAYS_SET_KEEP_ALIVE", "true");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCustomizeUndertowSocketOption() {
|
||||
bind("server.undertow.options.socket.ALWAYS_SET_KEEP_ALIVE", "true");
|
||||
assertThat(this.properties.getOptions().getSocket()).containsEntry("ALWAYS_SET_KEEP_ALIVE", "true");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCustomizeUndertowIoThreads() {
|
||||
bind("server.undertow.threads.io", "4");
|
||||
assertThat(this.properties.getThreads().getIo()).isEqualTo(4);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCustomizeUndertowWorkerThreads() {
|
||||
bind("server.undertow.threads.worker", "10");
|
||||
assertThat(this.properties.getThreads().getWorker()).isEqualTo(10);
|
||||
}
|
||||
|
||||
@Test
|
||||
void undertowMaxHttpPostSizeMatchesDefault() {
|
||||
assertThat(this.properties.getMaxHttpPostSize().toBytes()).isEqualTo(UndertowOptions.DEFAULT_MAX_ENTITY_SIZE);
|
||||
}
|
||||
|
||||
private void bind(String name, String value) {
|
||||
bind(Collections.singletonMap(name, value));
|
||||
}
|
||||
|
||||
private void bind(Map<String, String> map) {
|
||||
ConfigurationPropertySource source = new MapConfigurationPropertySource(map);
|
||||
new Binder(source).bind("server.undertow", Bindable.ofInstance(this.properties));
|
||||
}
|
||||
|
||||
}
|
|
@ -1,268 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.undertow.autoconfigure;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
|
||||
import io.undertow.Undertow;
|
||||
import io.undertow.Undertow.Builder;
|
||||
import io.undertow.UndertowOptions;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.xnio.Option;
|
||||
import org.xnio.OptionMap;
|
||||
import org.xnio.Options;
|
||||
|
||||
import org.springframework.boot.context.properties.bind.Bindable;
|
||||
import org.springframework.boot.context.properties.bind.Binder;
|
||||
import org.springframework.boot.context.properties.source.ConfigurationPropertySources;
|
||||
import org.springframework.boot.undertow.ConfigurableUndertowWebServerFactory;
|
||||
import org.springframework.boot.undertow.UndertowBuilderCustomizer;
|
||||
import org.springframework.boot.web.server.autoconfigure.ServerProperties;
|
||||
import org.springframework.mock.env.MockEnvironment;
|
||||
import org.springframework.test.context.support.TestPropertySourceUtils;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.BDDMockito.then;
|
||||
import static org.mockito.BDDMockito.willAnswer;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Tests for {@link UndertowWebServerFactoryCustomizer}.
|
||||
*
|
||||
* @author Brian Clozel
|
||||
* @author Phillip Webb
|
||||
* @author Artsiom Yudovin
|
||||
* @author Rafiullah Hamedy
|
||||
* @author HaiTao Zhang
|
||||
*/
|
||||
class UndertowWebServerFactoryCustomizerTests {
|
||||
|
||||
private final MockEnvironment environment = new MockEnvironment();
|
||||
|
||||
private final ServerProperties serverProperties = new ServerProperties();
|
||||
|
||||
private final UndertowServerProperties undertowProperties = new UndertowServerProperties();
|
||||
|
||||
private UndertowWebServerFactoryCustomizer customizer;
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
ConfigurationPropertySources.attach(this.environment);
|
||||
this.customizer = new UndertowWebServerFactoryCustomizer(this.environment, this.serverProperties,
|
||||
this.undertowProperties);
|
||||
}
|
||||
|
||||
@Test
|
||||
void customizeUndertowAccessLog() {
|
||||
bind("server.undertow.accesslog.enabled=true", "server.undertow.accesslog.pattern=foo",
|
||||
"server.undertow.accesslog.prefix=test_log", "server.undertow.accesslog.suffix=txt",
|
||||
"server.undertow.accesslog.dir=test-logs", "server.undertow.accesslog.rotate=false");
|
||||
ConfigurableUndertowWebServerFactory factory = mock(ConfigurableUndertowWebServerFactory.class);
|
||||
this.customizer.customize(factory);
|
||||
then(factory).should().setAccessLogEnabled(true);
|
||||
then(factory).should().setAccessLogPattern("foo");
|
||||
then(factory).should().setAccessLogPrefix("test_log");
|
||||
then(factory).should().setAccessLogSuffix("txt");
|
||||
then(factory).should().setAccessLogDirectory(new File("test-logs"));
|
||||
then(factory).should().setAccessLogRotate(false);
|
||||
}
|
||||
|
||||
@Test
|
||||
void customMaxHttpRequestHeaderSize() {
|
||||
bind("server.max-http-request-header-size=2048");
|
||||
assertThat(boundServerOption(UndertowOptions.MAX_HEADER_SIZE)).isEqualTo(2048);
|
||||
}
|
||||
|
||||
@Test
|
||||
void customMaxHttpRequestHeaderSizeIgnoredIfNegative() {
|
||||
bind("server.max-http-request-header-size=-1");
|
||||
assertThat(boundServerOption(UndertowOptions.MAX_HEADER_SIZE)).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void customMaxHttpRequestHeaderSizeIgnoredIfZero() {
|
||||
bind("server.max-http-request-header-size=0");
|
||||
assertThat(boundServerOption(UndertowOptions.MAX_HEADER_SIZE)).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void customMaxHttpPostSize() {
|
||||
bind("server.undertow.max-http-post-size=256");
|
||||
assertThat(boundServerOption(UndertowOptions.MAX_ENTITY_SIZE)).isEqualTo(256);
|
||||
}
|
||||
|
||||
@Test
|
||||
void customConnectionTimeout() {
|
||||
bind("server.undertow.no-request-timeout=1m");
|
||||
assertThat(boundServerOption(UndertowOptions.NO_REQUEST_TIMEOUT)).isEqualTo(60000);
|
||||
}
|
||||
|
||||
@Test
|
||||
void customMaxParameters() {
|
||||
bind("server.undertow.max-parameters=4");
|
||||
assertThat(boundServerOption(UndertowOptions.MAX_PARAMETERS)).isEqualTo(4);
|
||||
}
|
||||
|
||||
@Test
|
||||
void customMaxHeaders() {
|
||||
bind("server.undertow.max-headers=4");
|
||||
assertThat(boundServerOption(UndertowOptions.MAX_HEADERS)).isEqualTo(4);
|
||||
}
|
||||
|
||||
@Test
|
||||
void customMaxCookies() {
|
||||
bind("server.undertow.max-cookies=4");
|
||||
assertThat(boundServerOption(UndertowOptions.MAX_COOKIES)).isEqualTo(4);
|
||||
}
|
||||
|
||||
@Test
|
||||
void customizeIoThreads() {
|
||||
bind("server.undertow.threads.io=4");
|
||||
ConfigurableUndertowWebServerFactory factory = mock(ConfigurableUndertowWebServerFactory.class);
|
||||
this.customizer.customize(factory);
|
||||
then(factory).should().setIoThreads(4);
|
||||
}
|
||||
|
||||
@Test
|
||||
void customizeWorkerThreads() {
|
||||
bind("server.undertow.threads.worker=10");
|
||||
ConfigurableUndertowWebServerFactory factory = mock(ConfigurableUndertowWebServerFactory.class);
|
||||
this.customizer.customize(factory);
|
||||
then(factory).should().setWorkerThreads(10);
|
||||
}
|
||||
|
||||
@Test
|
||||
void enableSlashDecoding() {
|
||||
bind("server.undertow.decode-slash=true");
|
||||
assertThat(boundServerOption(UndertowOptions.DECODE_SLASH)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void disableUrlDecoding() {
|
||||
bind("server.undertow.decode-url=false");
|
||||
assertThat(boundServerOption(UndertowOptions.DECODE_URL)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void customUrlCharset() {
|
||||
bind("server.undertow.url-charset=UTF-16");
|
||||
assertThat(boundServerOption(UndertowOptions.URL_CHARSET)).isEqualTo(StandardCharsets.UTF_16.name());
|
||||
}
|
||||
|
||||
@Test
|
||||
void disableAlwaysSetKeepAlive() {
|
||||
bind("server.undertow.always-set-keep-alive=false");
|
||||
assertThat(boundServerOption(UndertowOptions.ALWAYS_SET_KEEP_ALIVE)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void customServerOption() {
|
||||
bind("server.undertow.options.server.ALWAYS_SET_KEEP_ALIVE=false");
|
||||
assertThat(boundServerOption(UndertowOptions.ALWAYS_SET_KEEP_ALIVE)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void customServerOptionShouldBeRelaxed() {
|
||||
bind("server.undertow.options.server.always-set-keep-alive=false");
|
||||
assertThat(boundServerOption(UndertowOptions.ALWAYS_SET_KEEP_ALIVE)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void customSocketOption() {
|
||||
bind("server.undertow.options.socket.CONNECTION_LOW_WATER=8");
|
||||
assertThat(boundSocketOption(Options.CONNECTION_LOW_WATER)).isEqualTo(8);
|
||||
}
|
||||
|
||||
@Test
|
||||
void customSocketOptionShouldBeRelaxed() {
|
||||
bind("server.undertow.options.socket.connection-low-water=8");
|
||||
assertThat(boundSocketOption(Options.CONNECTION_LOW_WATER)).isEqualTo(8);
|
||||
}
|
||||
|
||||
@Test
|
||||
void deduceUseForwardHeaders() {
|
||||
this.environment.setProperty("DYNO", "-");
|
||||
ConfigurableUndertowWebServerFactory factory = mock(ConfigurableUndertowWebServerFactory.class);
|
||||
this.customizer.customize(factory);
|
||||
then(factory).should().setUseForwardHeaders(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
void defaultUseForwardHeaders() {
|
||||
ConfigurableUndertowWebServerFactory factory = mock(ConfigurableUndertowWebServerFactory.class);
|
||||
this.customizer.customize(factory);
|
||||
then(factory).should().setUseForwardHeaders(false);
|
||||
}
|
||||
|
||||
@Test
|
||||
void forwardHeadersWhenStrategyIsNativeShouldConfigureValve() {
|
||||
this.serverProperties.setForwardHeadersStrategy(ServerProperties.ForwardHeadersStrategy.NATIVE);
|
||||
ConfigurableUndertowWebServerFactory factory = mock(ConfigurableUndertowWebServerFactory.class);
|
||||
this.customizer.customize(factory);
|
||||
then(factory).should().setUseForwardHeaders(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
void forwardHeadersWhenStrategyIsNoneShouldNotConfigureValve() {
|
||||
this.environment.setProperty("DYNO", "-");
|
||||
this.serverProperties.setForwardHeadersStrategy(ServerProperties.ForwardHeadersStrategy.NONE);
|
||||
ConfigurableUndertowWebServerFactory factory = mock(ConfigurableUndertowWebServerFactory.class);
|
||||
this.customizer.customize(factory);
|
||||
then(factory).should().setUseForwardHeaders(false);
|
||||
}
|
||||
|
||||
private <T> T boundServerOption(Option<T> option) {
|
||||
Builder builder = Undertow.builder();
|
||||
ConfigurableUndertowWebServerFactory factory = mockFactory(builder);
|
||||
this.customizer.customize(factory);
|
||||
OptionMap map = ((OptionMap.Builder) ReflectionTestUtils.getField(builder, "serverOptions")).getMap();
|
||||
return map.get(option);
|
||||
}
|
||||
|
||||
private <T> T boundSocketOption(Option<T> option) {
|
||||
Builder builder = Undertow.builder();
|
||||
ConfigurableUndertowWebServerFactory factory = mockFactory(builder);
|
||||
this.customizer.customize(factory);
|
||||
OptionMap map = ((OptionMap.Builder) ReflectionTestUtils.getField(builder, "socketOptions")).getMap();
|
||||
return map.get(option);
|
||||
}
|
||||
|
||||
private ConfigurableUndertowWebServerFactory mockFactory(Builder builder) {
|
||||
ConfigurableUndertowWebServerFactory factory = mock(ConfigurableUndertowWebServerFactory.class);
|
||||
willAnswer((invocation) -> {
|
||||
Object argument = invocation.getArgument(0);
|
||||
Arrays.stream((argument instanceof UndertowBuilderCustomizer undertowCustomizer)
|
||||
? new UndertowBuilderCustomizer[] { undertowCustomizer } : (UndertowBuilderCustomizer[]) argument)
|
||||
.forEach((customizer) -> customizer.customize(builder));
|
||||
return null;
|
||||
}).given(factory).addBuilderCustomizers(any());
|
||||
return factory;
|
||||
}
|
||||
|
||||
private void bind(String... inlinedProperties) {
|
||||
TestPropertySourceUtils.addInlinedPropertiesToEnvironment(this.environment, inlinedProperties);
|
||||
Binder binder = new Binder(ConfigurationPropertySources.get(this.environment));
|
||||
binder.bind("server", Bindable.ofInstance(this.serverProperties));
|
||||
binder.bind("server.undertow", Bindable.ofInstance(this.undertowProperties));
|
||||
}
|
||||
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.undertow.autoconfigure.actuate.web;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.undertow.autoconfigure.actuate.web.server.UndertowManagementServerProperties;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link UndertowManagementServerProperties}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
class UndertowManagementServerPropertiesTests {
|
||||
|
||||
@Test
|
||||
void accessLogsArePrefixedByDefault() {
|
||||
UndertowManagementServerProperties properties = new UndertowManagementServerProperties();
|
||||
assertThat(properties.getAccesslog().getPrefix()).isEqualTo("management_");
|
||||
}
|
||||
|
||||
}
|
|
@ -1,109 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.undertow.autoconfigure.reactive;
|
||||
|
||||
import io.undertow.Undertow.Builder;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.undertow.UndertowBuilderCustomizer;
|
||||
import org.springframework.boot.undertow.reactive.UndertowReactiveWebServerFactory;
|
||||
import org.springframework.boot.undertow.servlet.UndertowDeploymentInfoCustomizer;
|
||||
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
|
||||
import org.springframework.boot.web.server.autoconfigure.reactive.AbstractReactiveWebServerAutoConfigurationTests;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.BDDMockito.then;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Tests for {@link UndertowReactiveWebServerAutoConfiguration}.
|
||||
*
|
||||
* @author Brian Clozel
|
||||
* @author Raheela Aslam
|
||||
* @author Madhura Bhave
|
||||
* @author Scott Frederick
|
||||
*/
|
||||
class UndertowReactiveWebServerAutoConfigurationTests extends AbstractReactiveWebServerAutoConfigurationTests {
|
||||
|
||||
UndertowReactiveWebServerAutoConfigurationTests() {
|
||||
super(UndertowReactiveWebServerAutoConfiguration.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void undertowBuilderCustomizerBeanIsAddedToFactory() {
|
||||
this.serverRunner.withUserConfiguration(UndertowBuilderCustomizerConfiguration.class).run((context) -> {
|
||||
UndertowReactiveWebServerFactory factory = context.getBean(UndertowReactiveWebServerFactory.class);
|
||||
assertThat(factory.getBuilderCustomizers())
|
||||
.contains(context.getBean("builderCustomizer", UndertowBuilderCustomizer.class));
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void undertowBuilderCustomizerRegisteredAsBeanAndViaFactoryIsOnlyCalledOnce() {
|
||||
this.serverRunner.withUserConfiguration(DoubleRegistrationUndertowBuilderCustomizerConfiguration.class)
|
||||
.run((context) -> {
|
||||
UndertowReactiveWebServerFactory factory = context.getBean(UndertowReactiveWebServerFactory.class);
|
||||
UndertowBuilderCustomizer customizer = context.getBean("builderCustomizer",
|
||||
UndertowBuilderCustomizer.class);
|
||||
assertThat(factory.getBuilderCustomizers()).contains(customizer);
|
||||
then(customizer).should().customize(any(Builder.class));
|
||||
});
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class UndertowBuilderCustomizerConfiguration {
|
||||
|
||||
@Bean
|
||||
UndertowBuilderCustomizer builderCustomizer() {
|
||||
return (builder) -> {
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class DoubleRegistrationUndertowBuilderCustomizerConfiguration {
|
||||
|
||||
private final UndertowBuilderCustomizer customizer = mock(UndertowBuilderCustomizer.class);
|
||||
|
||||
@Bean
|
||||
UndertowBuilderCustomizer builderCustomizer() {
|
||||
return this.customizer;
|
||||
}
|
||||
|
||||
@Bean
|
||||
WebServerFactoryCustomizer<UndertowReactiveWebServerFactory> undertowCustomizer() {
|
||||
return (undertow) -> undertow.addBuilderCustomizers(this.customizer);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class UndertowDeploymentInfoCustomizerConfiguration {
|
||||
|
||||
@Bean
|
||||
UndertowDeploymentInfoCustomizer deploymentInfoCustomizer() {
|
||||
return (deploymentInfo) -> {
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,158 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.undertow.autoconfigure.servlet;
|
||||
|
||||
import io.undertow.Undertow.Builder;
|
||||
import io.undertow.servlet.api.DeploymentInfo;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.undertow.UndertowBuilderCustomizer;
|
||||
import org.springframework.boot.undertow.servlet.UndertowDeploymentInfoCustomizer;
|
||||
import org.springframework.boot.undertow.servlet.UndertowServletWebServerFactory;
|
||||
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
|
||||
import org.springframework.boot.web.server.autoconfigure.servlet.AbstractServletWebServerAutoConfigurationTests;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.BDDMockito.then;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Tests for {@link UndertowServletWebServerAutoConfiguration}.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @author Phillip Webb
|
||||
* @author Stephane Nicoll
|
||||
* @author Raheela Aslam
|
||||
* @author Madhura Bhave
|
||||
*/
|
||||
class UndertowServletWebServerAutoConfigurationTests extends AbstractServletWebServerAutoConfigurationTests {
|
||||
|
||||
UndertowServletWebServerAutoConfigurationTests() {
|
||||
super(UndertowServletWebServerAutoConfiguration.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void undertowDeploymentInfoCustomizerBeanIsAddedToFactory() {
|
||||
this.serverRunner.withUserConfiguration(UndertowDeploymentInfoCustomizerConfiguration.class).run((context) -> {
|
||||
UndertowServletWebServerFactory factory = context.getBean(UndertowServletWebServerFactory.class);
|
||||
UndertowDeploymentInfoCustomizer customizer = context.getBean("deploymentInfoCustomizer",
|
||||
UndertowDeploymentInfoCustomizer.class);
|
||||
assertThat(factory.getDeploymentInfoCustomizers()).contains(customizer);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void undertowDeploymentInfoCustomizerRegisteredAsBeanAndViaFactoryIsOnlyCalledOnce() {
|
||||
this.serverRunner.withUserConfiguration(DoubleRegistrationUndertowDeploymentInfoCustomizerConfiguration.class)
|
||||
.run((context) -> {
|
||||
UndertowServletWebServerFactory factory = context.getBean(UndertowServletWebServerFactory.class);
|
||||
UndertowDeploymentInfoCustomizer customizer = context.getBean("deploymentInfoCustomizer",
|
||||
UndertowDeploymentInfoCustomizer.class);
|
||||
assertThat(factory.getDeploymentInfoCustomizers()).contains(customizer);
|
||||
then(customizer).should().customize(any(DeploymentInfo.class));
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void undertowBuilderCustomizerRegisteredAsBeanAndViaFactoryIsOnlyCalledOnce() {
|
||||
this.serverRunner.withConfiguration(AutoConfigurations.of(UndertowServletWebServerAutoConfiguration.class))
|
||||
.withUserConfiguration(DoubleRegistrationUndertowBuilderCustomizerConfiguration.class)
|
||||
.run((context) -> {
|
||||
UndertowServletWebServerFactory factory = context.getBean(UndertowServletWebServerFactory.class);
|
||||
UndertowBuilderCustomizer customizer = context.getBean("builderCustomizer",
|
||||
UndertowBuilderCustomizer.class);
|
||||
assertThat(factory.getBuilderCustomizers()).contains(customizer);
|
||||
then(customizer).should().customize(any(Builder.class));
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void undertowBuilderCustomizerBeanIsAddedToFactory() {
|
||||
this.serverRunner.withUserConfiguration(UndertowBuilderCustomizerConfiguration.class).run((context) -> {
|
||||
UndertowServletWebServerFactory factory = context.getBean(UndertowServletWebServerFactory.class);
|
||||
assertThat(factory.getBuilderCustomizers())
|
||||
.contains(context.getBean("builderCustomizer", UndertowBuilderCustomizer.class));
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void undertowServletWebServerFactoryCustomizerIsAutoConfigured() {
|
||||
this.serverRunner.withUserConfiguration(UndertowBuilderCustomizerConfiguration.class)
|
||||
.run((context) -> assertThat(context).hasSingleBean(UndertowServletWebServerFactoryCustomizer.class));
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class UndertowBuilderCustomizerConfiguration {
|
||||
|
||||
@Bean
|
||||
UndertowBuilderCustomizer builderCustomizer() {
|
||||
return (builder) -> {
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class DoubleRegistrationUndertowBuilderCustomizerConfiguration {
|
||||
|
||||
private final UndertowBuilderCustomizer customizer = mock(UndertowBuilderCustomizer.class);
|
||||
|
||||
@Bean
|
||||
UndertowBuilderCustomizer builderCustomizer() {
|
||||
return this.customizer;
|
||||
}
|
||||
|
||||
@Bean
|
||||
WebServerFactoryCustomizer<UndertowServletWebServerFactory> undertowCustomizer() {
|
||||
return (undertow) -> undertow.addBuilderCustomizers(this.customizer);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class UndertowDeploymentInfoCustomizerConfiguration {
|
||||
|
||||
@Bean
|
||||
UndertowDeploymentInfoCustomizer deploymentInfoCustomizer() {
|
||||
return (deploymentInfo) -> {
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class DoubleRegistrationUndertowDeploymentInfoCustomizerConfiguration {
|
||||
|
||||
private final UndertowDeploymentInfoCustomizer customizer = mock(UndertowDeploymentInfoCustomizer.class);
|
||||
|
||||
@Bean
|
||||
UndertowDeploymentInfoCustomizer deploymentInfoCustomizer() {
|
||||
return this.customizer;
|
||||
}
|
||||
|
||||
@Bean
|
||||
WebServerFactoryCustomizer<UndertowServletWebServerFactory> undertowCustomizer() {
|
||||
return (undertow) -> undertow.addDeploymentInfoCustomizers(this.customizer);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,53 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.undertow.autoconfigure.servlet;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.undertow.autoconfigure.UndertowServerProperties;
|
||||
import org.springframework.boot.undertow.servlet.UndertowServletWebServerFactory;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link UndertowServletWebServerFactoryCustomizer}
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
class UndertowServletWebServerFactoryCustomizerTests {
|
||||
|
||||
@Test
|
||||
void eagerFilterInitCanBeDisabled() {
|
||||
UndertowServletWebServerFactory factory = new UndertowServletWebServerFactory(0);
|
||||
assertThat(factory.isEagerFilterInit()).isTrue();
|
||||
UndertowServerProperties undertowProperties = new UndertowServerProperties();
|
||||
undertowProperties.setEagerFilterInit(false);
|
||||
new UndertowServletWebServerFactoryCustomizer(undertowProperties).customize(factory);
|
||||
assertThat(factory.isEagerFilterInit()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void preservePathOnForwardCanBeEnabled() {
|
||||
UndertowServletWebServerFactory factory = new UndertowServletWebServerFactory(0);
|
||||
assertThat(factory.isPreservePathOnForward()).isFalse();
|
||||
UndertowServerProperties undertowProperties = new UndertowServerProperties();
|
||||
undertowProperties.setPreservePathOnForward(true);
|
||||
new UndertowServletWebServerFactoryCustomizer(undertowProperties).customize(factory);
|
||||
assertThat(factory.isPreservePathOnForward()).isTrue();
|
||||
}
|
||||
|
||||
}
|
|
@ -1,47 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.undertow.autoconfigure.servlet;
|
||||
|
||||
import jakarta.servlet.ServletContextListener;
|
||||
|
||||
import org.springframework.boot.undertow.servlet.UndertowServletWebServerFactory;
|
||||
import org.springframework.boot.web.server.servlet.AbstractServletWebServerServletContextListenerTests;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* Tests for Undertow driving {@link ServletContextListener}s correctly.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
class UndertowServletWebServerServletContextListenerTests extends AbstractServletWebServerServletContextListenerTests {
|
||||
|
||||
UndertowServletWebServerServletContextListenerTests() {
|
||||
super(UndertowConfiguration.class);
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class UndertowConfiguration {
|
||||
|
||||
@Bean
|
||||
UndertowServletWebServerFactory webServerFactory() {
|
||||
return new UndertowServletWebServerFactory(0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,173 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.undertow.reactive;
|
||||
|
||||
import java.io.File;
|
||||
import java.time.Duration;
|
||||
import java.util.Arrays;
|
||||
|
||||
import io.undertow.Undertow;
|
||||
import org.awaitility.Awaitility;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
import org.mockito.InOrder;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.boot.undertow.UndertowAccess;
|
||||
import org.springframework.boot.undertow.UndertowBuilderCustomizer;
|
||||
import org.springframework.boot.undertow.UndertowWebServer;
|
||||
import org.springframework.boot.web.server.Shutdown;
|
||||
import org.springframework.boot.web.server.reactive.AbstractReactiveWebServerFactoryTests;
|
||||
import org.springframework.boot.web.server.reactive.ConfigurableReactiveWebServerFactory;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.server.reactive.HttpHandler;
|
||||
import org.springframework.web.reactive.function.BodyInserters;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
import org.springframework.web.reactive.function.client.WebClientResponseException.ServiceUnavailable;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.inOrder;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Tests for {@link UndertowReactiveWebServerFactory} and {@link UndertowWebServer}.
|
||||
*
|
||||
* @author Brian Clozel
|
||||
* @author Madhura Bhave
|
||||
*/
|
||||
class UndertowReactiveWebServerFactoryTests extends AbstractReactiveWebServerFactoryTests {
|
||||
|
||||
@TempDir
|
||||
File tempDir;
|
||||
|
||||
@Override
|
||||
protected UndertowReactiveWebServerFactory getFactory() {
|
||||
return new UndertowReactiveWebServerFactory(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void setNullBuilderCustomizersShouldThrowException() {
|
||||
UndertowReactiveWebServerFactory factory = getFactory();
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> factory.setBuilderCustomizers(null))
|
||||
.withMessageContaining("'customizers' must not be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
void addNullBuilderCustomizersShouldThrowException() {
|
||||
UndertowReactiveWebServerFactory factory = getFactory();
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> factory.addBuilderCustomizers((UndertowBuilderCustomizer[]) null))
|
||||
.withMessageContaining("'customizers' must not be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
void builderCustomizersShouldBeInvoked() {
|
||||
UndertowReactiveWebServerFactory factory = getFactory();
|
||||
HttpHandler handler = mock(HttpHandler.class);
|
||||
UndertowBuilderCustomizer[] customizers = new UndertowBuilderCustomizer[4];
|
||||
Arrays.setAll(customizers, (i) -> mock(UndertowBuilderCustomizer.class));
|
||||
factory.setBuilderCustomizers(Arrays.asList(customizers[0], customizers[1]));
|
||||
factory.addBuilderCustomizers(customizers[2], customizers[3]);
|
||||
this.webServer = factory.getWebServer(handler);
|
||||
InOrder ordered = inOrder((Object[]) customizers);
|
||||
for (UndertowBuilderCustomizer customizer : customizers) {
|
||||
ordered.verify(customizer).customize(any(Undertow.Builder.class));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void useForwardedHeaders() {
|
||||
UndertowReactiveWebServerFactory factory = getFactory();
|
||||
factory.setUseForwardHeaders(true);
|
||||
assertForwardHeaderIsUsed(factory);
|
||||
}
|
||||
|
||||
@Test
|
||||
void accessLogCanBeEnabled() {
|
||||
testAccessLog(null, null, "access_log.log");
|
||||
}
|
||||
|
||||
@Test
|
||||
void accessLogCanBeCustomized() {
|
||||
testAccessLog("my_access.", "logz", "my_access.logz");
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenServerIsShuttingDownGracefullyThenNewConnectionsAreRejectedWithServiceUnavailable() {
|
||||
UndertowReactiveWebServerFactory factory = getFactory();
|
||||
factory.setShutdown(Shutdown.GRACEFUL);
|
||||
BlockingHandler blockingHandler = new BlockingHandler();
|
||||
this.webServer = factory.getWebServer(blockingHandler);
|
||||
this.webServer.start();
|
||||
this.webServer.shutDownGracefully((result) -> {
|
||||
});
|
||||
WebClient webClient = getWebClient(this.webServer.getPort()).build();
|
||||
Awaitility.await().atMost(Duration.ofSeconds(30)).until(() -> {
|
||||
blockingHandler.stopBlocking();
|
||||
try {
|
||||
webClient.get().retrieve().toBodilessEntity().block();
|
||||
return false;
|
||||
}
|
||||
catch (RuntimeException ex) {
|
||||
return ex instanceof ServiceUnavailable;
|
||||
}
|
||||
});
|
||||
this.webServer.stop();
|
||||
}
|
||||
|
||||
private void testAccessLog(String prefix, String suffix, String expectedFile) {
|
||||
UndertowReactiveWebServerFactory factory = getFactory();
|
||||
factory.setAccessLogEnabled(true);
|
||||
factory.setAccessLogPrefix(prefix);
|
||||
factory.setAccessLogSuffix(suffix);
|
||||
File accessLogDirectory = this.tempDir;
|
||||
factory.setAccessLogDirectory(accessLogDirectory);
|
||||
assertThat(accessLogDirectory).isEmptyDirectory();
|
||||
this.webServer = factory.getWebServer(new EchoHandler());
|
||||
this.webServer.start();
|
||||
WebClient client = getWebClient(this.webServer.getPort()).build();
|
||||
Mono<String> result = client.post()
|
||||
.uri("/test")
|
||||
.contentType(MediaType.TEXT_PLAIN)
|
||||
.body(BodyInserters.fromValue("Hello World"))
|
||||
.retrieve()
|
||||
.bodyToMono(String.class);
|
||||
assertThat(result.block(Duration.ofSeconds(30))).isEqualTo("Hello World");
|
||||
File accessLog = new File(accessLogDirectory, expectedFile);
|
||||
awaitFile(accessLog);
|
||||
assertThat(accessLogDirectory.listFiles()).contains(accessLog);
|
||||
}
|
||||
|
||||
private void awaitFile(File file) {
|
||||
Awaitility.waitAtMost(Duration.ofSeconds(10)).until(file::exists, is(true));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String startedLogMessage() {
|
||||
return UndertowAccess.getStartedLogMessage((UndertowWebServer) this.webServer);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addConnector(int port, ConfigurableReactiveWebServerFactory factory) {
|
||||
((UndertowReactiveWebServerFactory) factory)
|
||||
.addBuilderCustomizers((builder) -> builder.addHttpListener(port, "0.0.0.0"));
|
||||
}
|
||||
|
||||
}
|
|
@ -1,97 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.undertow.servlet;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Date;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import io.undertow.servlet.api.SessionPersistenceManager.PersistentSession;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link FileSessionPersistence}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class FileSessionPersistenceTests {
|
||||
|
||||
private File dir;
|
||||
|
||||
private FileSessionPersistence persistence;
|
||||
|
||||
private final ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
|
||||
|
||||
private final Date expiration = new Date(System.currentTimeMillis() + 10000);
|
||||
|
||||
@BeforeEach
|
||||
void setup(@TempDir File tempDir) {
|
||||
this.dir = tempDir;
|
||||
this.dir.mkdir();
|
||||
this.persistence = new FileSessionPersistence(this.dir);
|
||||
}
|
||||
|
||||
@Test
|
||||
void loadsNullForMissingFile() {
|
||||
Map<String, PersistentSession> attributes = this.persistence.loadSessionAttributes("test", this.classLoader);
|
||||
assertThat(attributes).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void persistAndLoad() {
|
||||
Map<String, PersistentSession> sessionData = new LinkedHashMap<>();
|
||||
Map<String, Object> data = new LinkedHashMap<>();
|
||||
data.put("spring", "boot");
|
||||
PersistentSession session = new PersistentSession(this.expiration, data);
|
||||
sessionData.put("abc", session);
|
||||
this.persistence.persistSessions("test", sessionData);
|
||||
Map<String, PersistentSession> restored = this.persistence.loadSessionAttributes("test", this.classLoader);
|
||||
assertThat(restored).isNotNull();
|
||||
assertThat(restored.get("abc").getExpiration()).isEqualTo(this.expiration);
|
||||
assertThat(restored.get("abc").getSessionData()).containsEntry("spring", "boot");
|
||||
}
|
||||
|
||||
@Test
|
||||
void dontRestoreExpired() {
|
||||
Date expired = new Date(System.currentTimeMillis() - 1000);
|
||||
Map<String, PersistentSession> sessionData = new LinkedHashMap<>();
|
||||
Map<String, Object> data = new LinkedHashMap<>();
|
||||
data.put("spring", "boot");
|
||||
PersistentSession session = new PersistentSession(expired, data);
|
||||
sessionData.put("abc", session);
|
||||
this.persistence.persistSessions("test", sessionData);
|
||||
Map<String, PersistentSession> restored = this.persistence.loadSessionAttributes("test", this.classLoader);
|
||||
assertThat(restored).isNotNull();
|
||||
assertThat(restored).doesNotContainKey("abc");
|
||||
}
|
||||
|
||||
@Test
|
||||
void deleteFileOnClear() {
|
||||
File sessionFile = new File(this.dir, "test.session");
|
||||
Map<String, PersistentSession> sessionData = new LinkedHashMap<>();
|
||||
this.persistence.persistSessions("test", sessionData);
|
||||
assertThat(sessionFile).exists();
|
||||
this.persistence.clear("test");
|
||||
assertThat(sessionFile).doesNotExist();
|
||||
}
|
||||
|
||||
}
|
|
@ -1,103 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.undertow.servlet;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.jar.JarOutputStream;
|
||||
import java.util.zip.ZipEntry;
|
||||
|
||||
import io.undertow.server.handlers.resource.Resource;
|
||||
import io.undertow.server.handlers.resource.ResourceManager;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
|
||||
import org.springframework.util.FileCopyUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link JarResourceManager}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
class JarResourceManagerTests {
|
||||
|
||||
@TempDir
|
||||
static File tempDir;
|
||||
|
||||
@ResourceManagersTest
|
||||
void emptyPathIsHandledCorrectly(String filename, ResourceManager resourceManager) throws IOException {
|
||||
Resource resource = resourceManager.getResource("");
|
||||
assertThat(resource).isNotNull();
|
||||
assertThat(resource.isDirectory()).isTrue();
|
||||
}
|
||||
|
||||
@ResourceManagersTest
|
||||
void rootPathIsHandledCorrectly(String filename, ResourceManager resourceManager) throws IOException {
|
||||
Resource resource = resourceManager.getResource("/");
|
||||
assertThat(resource).isNotNull();
|
||||
assertThat(resource.isDirectory()).isTrue();
|
||||
}
|
||||
|
||||
@ResourceManagersTest
|
||||
void resourceIsFoundInJarFile(String filename, ResourceManager resourceManager) throws IOException {
|
||||
Resource resource = resourceManager.getResource("/hello.txt");
|
||||
assertThat(resource).isNotNull();
|
||||
assertThat(resource.isDirectory()).isFalse();
|
||||
assertThat(resource.getContentLength()).isEqualTo(5);
|
||||
}
|
||||
|
||||
@ResourceManagersTest
|
||||
void resourceIsFoundInJarFileWithoutLeadingSlash(String filename, ResourceManager resourceManager)
|
||||
throws IOException {
|
||||
Resource resource = resourceManager.getResource("hello.txt");
|
||||
assertThat(resource).isNotNull();
|
||||
assertThat(resource.isDirectory()).isFalse();
|
||||
assertThat(resource.getContentLength()).isEqualTo(5);
|
||||
}
|
||||
|
||||
static List<Arguments> resourceManagers() throws IOException {
|
||||
File jar = new File(tempDir, "test.jar");
|
||||
try (JarOutputStream out = new JarOutputStream(new FileOutputStream(jar))) {
|
||||
out.putNextEntry(new ZipEntry("hello.txt"));
|
||||
out.write("hello".getBytes());
|
||||
}
|
||||
File troublesomeNameJar = new File(tempDir, "test##1.0.jar");
|
||||
FileCopyUtils.copy(jar, troublesomeNameJar);
|
||||
return Arrays.asList(Arguments.of(jar.getName(), new JarResourceManager(jar)),
|
||||
Arguments.of(troublesomeNameJar.getName(), new JarResourceManager(troublesomeNameJar)));
|
||||
}
|
||||
|
||||
@ParameterizedTest(name = "[{index}] {0}")
|
||||
@MethodSource("resourceManagers")
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
private @interface ResourceManagersTest {
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,399 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.undertow.servlet;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.SocketException;
|
||||
import java.net.URISyntaxException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.time.Duration;
|
||||
import java.util.Arrays;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import javax.net.ssl.SSLException;
|
||||
import javax.net.ssl.SSLHandshakeException;
|
||||
|
||||
import io.undertow.Undertow;
|
||||
import io.undertow.Undertow.Builder;
|
||||
import io.undertow.servlet.api.DeploymentInfo;
|
||||
import io.undertow.servlet.api.ServletContainer;
|
||||
import jakarta.servlet.ServletRegistration.Dynamic;
|
||||
import org.apache.hc.client5.http.classic.HttpClient;
|
||||
import org.apache.hc.client5.http.impl.classic.HttpClients;
|
||||
import org.apache.hc.core5.http.HttpResponse;
|
||||
import org.apache.jasper.servlet.JspServlet;
|
||||
import org.awaitility.Awaitility;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.condition.DisabledForJreRange;
|
||||
import org.junit.jupiter.api.condition.JRE;
|
||||
import org.mockito.InOrder;
|
||||
|
||||
import org.springframework.boot.testsupport.classpath.resources.WithPackageResources;
|
||||
import org.springframework.boot.testsupport.web.servlet.ExampleServlet;
|
||||
import org.springframework.boot.undertow.UndertowBuilderCustomizer;
|
||||
import org.springframework.boot.web.error.ErrorPage;
|
||||
import org.springframework.boot.web.server.GracefulShutdownResult;
|
||||
import org.springframework.boot.web.server.PortInUseException;
|
||||
import org.springframework.boot.web.server.Shutdown;
|
||||
import org.springframework.boot.web.server.servlet.AbstractServletWebServerFactoryTests;
|
||||
import org.springframework.boot.web.server.servlet.ConfigurableServletWebServerFactory;
|
||||
import org.springframework.boot.web.servlet.ServletRegistrationBean;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIOException;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.inOrder;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Tests for {@link UndertowServletWebServerFactory}.
|
||||
*
|
||||
* @author Ivan Sopov
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
class UndertowServletWebServerFactoryTests extends AbstractServletWebServerFactoryTests {
|
||||
|
||||
@Override
|
||||
protected UndertowServletWebServerFactory getFactory() {
|
||||
return new UndertowServletWebServerFactory(0);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void awaitClosureOfSslRelatedInputStreams() {
|
||||
// https://issues.redhat.com/browse/UNDERTOW-1705
|
||||
File resource = new File(this.tempDir, "test.txt");
|
||||
Awaitility.await().atMost(Duration.ofSeconds(30)).until(() -> (!resource.isFile()) || resource.delete());
|
||||
}
|
||||
|
||||
@Test
|
||||
void errorPage404() throws Exception {
|
||||
ConfigurableServletWebServerFactory factory = getFactory();
|
||||
factory.addErrorPages(new ErrorPage(HttpStatus.NOT_FOUND, "/hello"));
|
||||
this.webServer = factory.getWebServer(new ServletRegistrationBean<>(new ExampleServlet(), "/hello"));
|
||||
this.webServer.start();
|
||||
assertThat(getResponse(getLocalUrl("/hello"))).isEqualTo("Hello World");
|
||||
assertThat(getResponse(getLocalUrl("/not-found"))).isEqualTo("Hello World");
|
||||
}
|
||||
|
||||
@Test
|
||||
void setNullBuilderCustomizersThrows() {
|
||||
UndertowServletWebServerFactory factory = getFactory();
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> factory.setBuilderCustomizers(null))
|
||||
.withMessageContaining("'customizers' must not be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
void addNullAddBuilderCustomizersThrows() {
|
||||
UndertowServletWebServerFactory factory = getFactory();
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> factory.addBuilderCustomizers((UndertowBuilderCustomizer[]) null))
|
||||
.withMessageContaining("'customizers' must not be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
void builderCustomizers() {
|
||||
UndertowServletWebServerFactory factory = getFactory();
|
||||
UndertowBuilderCustomizer[] customizers = new UndertowBuilderCustomizer[4];
|
||||
Arrays.setAll(customizers, (i) -> mock(UndertowBuilderCustomizer.class));
|
||||
factory.setBuilderCustomizers(Arrays.asList(customizers[0], customizers[1]));
|
||||
factory.addBuilderCustomizers(customizers[2], customizers[3]);
|
||||
this.webServer = factory.getWebServer();
|
||||
InOrder ordered = inOrder((Object[]) customizers);
|
||||
for (UndertowBuilderCustomizer customizer : customizers) {
|
||||
ordered.verify(customizer).customize(any(Builder.class));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void setNullDeploymentInfoCustomizersThrows() {
|
||||
UndertowServletWebServerFactory factory = getFactory();
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> factory.setDeploymentInfoCustomizers(null))
|
||||
.withMessageContaining("'customizers' must not be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
void addNullAddDeploymentInfoCustomizersThrows() {
|
||||
UndertowServletWebServerFactory factory = getFactory();
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> factory.addDeploymentInfoCustomizers((UndertowDeploymentInfoCustomizer[]) null))
|
||||
.withMessageContaining("'customizers' must not be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
void deploymentInfo() {
|
||||
UndertowServletWebServerFactory factory = getFactory();
|
||||
UndertowDeploymentInfoCustomizer[] customizers = new UndertowDeploymentInfoCustomizer[4];
|
||||
Arrays.setAll(customizers, (i) -> mock(UndertowDeploymentInfoCustomizer.class));
|
||||
factory.setDeploymentInfoCustomizers(Arrays.asList(customizers[0], customizers[1]));
|
||||
factory.addDeploymentInfoCustomizers(customizers[2], customizers[3]);
|
||||
this.webServer = factory.getWebServer();
|
||||
InOrder ordered = inOrder((Object[]) customizers);
|
||||
for (UndertowDeploymentInfoCustomizer customizer : customizers) {
|
||||
ordered.verify(customizer).customize(any(DeploymentInfo.class));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithPackageResources("test.jks")
|
||||
void basicSslClasspathKeyStore() throws Exception {
|
||||
testBasicSslWithKeyStore("classpath:test.jks");
|
||||
}
|
||||
|
||||
@Test
|
||||
void defaultContextPath() {
|
||||
UndertowServletWebServerFactory factory = getFactory();
|
||||
final AtomicReference<String> contextPath = new AtomicReference<>();
|
||||
factory.addDeploymentInfoCustomizers((deploymentInfo) -> contextPath.set(deploymentInfo.getContextPath()));
|
||||
this.webServer = factory.getWebServer();
|
||||
assertThat(contextPath.get()).isEqualTo("/");
|
||||
}
|
||||
|
||||
@Test
|
||||
void useForwardHeaders() throws Exception {
|
||||
UndertowServletWebServerFactory factory = getFactory();
|
||||
factory.setUseForwardHeaders(true);
|
||||
assertForwardHeaderIsUsed(factory);
|
||||
}
|
||||
|
||||
@Test
|
||||
void eachFactoryUsesADiscreteServletContainer() {
|
||||
assertThat(getServletContainerFromNewFactory()).isNotEqualTo(getServletContainerFromNewFactory());
|
||||
}
|
||||
|
||||
@Test
|
||||
void accessLogCanBeEnabled() throws IOException, URISyntaxException {
|
||||
testAccessLog(null, null, "access_log.log");
|
||||
}
|
||||
|
||||
@Test
|
||||
void accessLogCanBeCustomized() throws IOException, URISyntaxException {
|
||||
testAccessLog("my_access.", "logz", "my_access.logz");
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenServerIsShuttingDownGracefullyThenRequestsAreRejectedWithServiceUnavailable() throws Exception {
|
||||
ConfigurableServletWebServerFactory factory = getFactory();
|
||||
factory.setShutdown(Shutdown.GRACEFUL);
|
||||
BlockingServlet blockingServlet = new BlockingServlet();
|
||||
this.webServer = factory.getWebServer((context) -> {
|
||||
Dynamic registration = context.addServlet("blockingServlet", blockingServlet);
|
||||
registration.addMapping("/blocking");
|
||||
registration.setAsyncSupported(true);
|
||||
});
|
||||
this.webServer.start();
|
||||
int port = this.webServer.getPort();
|
||||
Future<Object> request = initiateGetRequest(port, "/blocking");
|
||||
blockingServlet.awaitQueue();
|
||||
AtomicReference<GracefulShutdownResult> result = new AtomicReference<>();
|
||||
this.webServer.shutDownGracefully(result::set);
|
||||
assertThat(result.get()).isNull();
|
||||
blockingServlet.admitOne();
|
||||
assertThat(request.get()).isInstanceOf(HttpResponse.class);
|
||||
Object rejectedResult = initiateGetRequest(port, "/").get();
|
||||
assertThat(rejectedResult).isInstanceOf(HttpResponse.class);
|
||||
assertThat(((HttpResponse) rejectedResult).getCode()).isEqualTo(HttpStatus.SERVICE_UNAVAILABLE.value());
|
||||
this.webServer.stop();
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenServerIsShuttingDownARequestOnAnIdleConnectionAreRejectedWithServiceUnavailable() throws Exception {
|
||||
ConfigurableServletWebServerFactory factory = getFactory();
|
||||
factory.setShutdown(Shutdown.GRACEFUL);
|
||||
BlockingServlet blockingServlet = new BlockingServlet();
|
||||
this.webServer = factory.getWebServer((context) -> {
|
||||
Dynamic registration = context.addServlet("blockingServlet", blockingServlet);
|
||||
registration.addMapping("/blocking");
|
||||
registration.setAsyncSupported(true);
|
||||
});
|
||||
HttpClient httpClient = HttpClients.createMinimal();
|
||||
this.webServer.start();
|
||||
int port = this.webServer.getPort();
|
||||
Future<Object> keepAliveRequest = initiateGetRequest(httpClient, port, "/blocking");
|
||||
blockingServlet.awaitQueue();
|
||||
blockingServlet.admitOne();
|
||||
assertThat(keepAliveRequest.get()).isInstanceOf(HttpResponse.class);
|
||||
Future<Object> request = initiateGetRequest(port, "/blocking");
|
||||
blockingServlet.awaitQueue();
|
||||
this.webServer.shutDownGracefully((result) -> {
|
||||
});
|
||||
HttpResponse idleConnectionResponse = (HttpResponse) initiateGetRequest(httpClient, port, "/").get();
|
||||
assertThat(idleConnectionResponse.getCode()).isEqualTo(503);
|
||||
blockingServlet.admitOne();
|
||||
Object response = request.get();
|
||||
assertThat(response).isInstanceOf(HttpResponse.class);
|
||||
this.webServer.stop();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Override
|
||||
@Disabled("https://issues.redhat.com/browse/UNDERTOW-2420")
|
||||
protected void portClashOfSecondaryConnectorResultsInPortInUseException() throws Exception {
|
||||
super.portClashOfSecondaryConnectorResultsInPortInUseException();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Override
|
||||
@Disabled("Restart after stop is not supported with Undertow")
|
||||
protected void restartAfterStop() {
|
||||
}
|
||||
|
||||
@Test
|
||||
@Override
|
||||
@Disabled("Undertow's architecture prevents separating stop and destroy")
|
||||
protected void servletContextListenerContextDestroyedIsNotCalledWhenContainerIsStopped() {
|
||||
}
|
||||
|
||||
private void testAccessLog(String prefix, String suffix, String expectedFile)
|
||||
throws IOException, URISyntaxException {
|
||||
UndertowServletWebServerFactory factory = getFactory();
|
||||
factory.setAccessLogEnabled(true);
|
||||
factory.setAccessLogPrefix(prefix);
|
||||
factory.setAccessLogSuffix(suffix);
|
||||
File accessLogDirectory = this.tempDir;
|
||||
factory.setAccessLogDirectory(accessLogDirectory);
|
||||
assertThat(accessLogDirectory).isEmptyDirectory();
|
||||
this.webServer = factory.getWebServer(new ServletRegistrationBean<>(new ExampleServlet(), "/hello"));
|
||||
this.webServer.start();
|
||||
assertThat(getResponse(getLocalUrl("/hello"))).isEqualTo("Hello World");
|
||||
File accessLog = new File(accessLogDirectory, expectedFile);
|
||||
awaitFile(accessLog);
|
||||
assertThat(accessLogDirectory.listFiles()).contains(accessLog);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addConnector(int port, ConfigurableServletWebServerFactory factory) {
|
||||
((UndertowServletWebServerFactory) factory)
|
||||
.addBuilderCustomizers((builder) -> builder.addHttpListener(port, "0.0.0.0"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithPackageResources("restricted.jks")
|
||||
void sslRestrictedProtocolsEmptyCipherFailure() {
|
||||
assertThatIOException()
|
||||
.isThrownBy(() -> testRestrictedSSLProtocolsAndCipherSuites(new String[] { "TLSv1.2" },
|
||||
new String[] { "TLS_EMPTY_RENEGOTIATION_INFO_SCSV" }))
|
||||
.isInstanceOfAny(SSLException.class, SSLHandshakeException.class, SocketException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithPackageResources("restricted.jks")
|
||||
void sslRestrictedProtocolsECDHETLS1Failure() {
|
||||
assertThatIOException()
|
||||
.isThrownBy(() -> testRestrictedSSLProtocolsAndCipherSuites(new String[] { "TLSv1" },
|
||||
new String[] { "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256" }))
|
||||
.isInstanceOfAny(SSLException.class, SocketException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithPackageResources("restricted.jks")
|
||||
void sslRestrictedProtocolsECDHESuccess() throws Exception {
|
||||
testRestrictedSSLProtocolsAndCipherSuites(new String[] { "TLSv1.2" },
|
||||
new String[] { "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256" });
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithPackageResources("restricted.jks")
|
||||
@DisabledForJreRange(min = JRE.JAVA_24)
|
||||
void sslRestrictedProtocolsRSATLS12Success() throws Exception {
|
||||
testRestrictedSSLProtocolsAndCipherSuites(new String[] { "TLSv1.2" },
|
||||
new String[] { "TLS_RSA_WITH_AES_128_CBC_SHA256" });
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithPackageResources("restricted.jks")
|
||||
void sslRestrictedProtocolsRSATLS11Failure() {
|
||||
assertThatIOException()
|
||||
.isThrownBy(() -> testRestrictedSSLProtocolsAndCipherSuites(new String[] { "TLSv1.1" },
|
||||
new String[] { "TLS_RSA_WITH_AES_128_CBC_SHA256" }))
|
||||
.isInstanceOfAny(SSLException.class, SocketException.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JspServlet getJspServlet() {
|
||||
return null; // Undertow does not support JSPs
|
||||
}
|
||||
|
||||
private void awaitFile(File file) {
|
||||
Awaitility.waitAtMost(Duration.ofSeconds(10)).until(file::exists, is(true));
|
||||
}
|
||||
|
||||
private ServletContainer getServletContainerFromNewFactory() {
|
||||
UndertowServletWebServer container = (UndertowServletWebServer) getFactory().getWebServer();
|
||||
try {
|
||||
return container.getDeploymentManager().getDeployment().getServletContainer();
|
||||
}
|
||||
finally {
|
||||
container.stop();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Map<String, String> getActualMimeMappings() {
|
||||
return ((UndertowServletWebServer) this.webServer).getDeploymentManager()
|
||||
.getDeployment()
|
||||
.getMimeExtensionMappings();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Charset getCharset(Locale locale) {
|
||||
DeploymentInfo info = ((UndertowServletWebServer) this.webServer).getDeploymentManager()
|
||||
.getDeployment()
|
||||
.getDeploymentInfo();
|
||||
String charsetName = info.getLocaleCharsetMapping().get(locale.toString());
|
||||
return (charsetName != null) ? Charset.forName(charsetName) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleExceptionCausedByBlockedPortOnPrimaryConnector(RuntimeException ex, int blockedPort) {
|
||||
assertThat(ex).isInstanceOf(PortInUseException.class);
|
||||
assertThat(((PortInUseException) ex).getPort()).isEqualTo(blockedPort);
|
||||
Undertow undertow = (Undertow) ReflectionTestUtils.getField(this.webServer, "undertow");
|
||||
assertThat(undertow.getWorker()).isNull();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleExceptionCausedByBlockedPortOnSecondaryConnector(RuntimeException ex, int blockedPort) {
|
||||
handleExceptionCausedByBlockedPortOnPrimaryConnector(ex, blockedPort);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String startedLogMessage() {
|
||||
return ((UndertowServletWebServer) this.webServer).getStartedLogMessage();
|
||||
}
|
||||
|
||||
private void testRestrictedSSLProtocolsAndCipherSuites(String[] protocols, String[] ciphers) throws Exception {
|
||||
ConfigurableServletWebServerFactory factory = getFactory();
|
||||
factory.setSsl(getSsl(null, "password", "classpath:restricted.jks", null, protocols, ciphers));
|
||||
this.webServer = factory.getWebServer(new ServletRegistrationBean<>(new ExampleServlet(true, false), "/hello"));
|
||||
this.webServer.start();
|
||||
HttpComponentsClientHttpRequestFactory requestFactory = createHttpComponentsRequestFactory(
|
||||
createTrustSelfSignedTlsSocketStrategy());
|
||||
assertThat(getResponse(getLocalUrl("https", "/hello"), requestFactory)).contains("scheme=https");
|
||||
}
|
||||
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.undertow.servlet;
|
||||
|
||||
import org.springframework.boot.undertow.UndertowWebServer;
|
||||
import org.springframework.boot.web.server.servlet.context.ServletWebServerApplicationContext;
|
||||
import org.springframework.boot.web.servlet.context.AbstractServletWebServerMvcIntegrationTests;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* Integration tests for {@link ServletWebServerApplicationContext} and
|
||||
* {@link UndertowWebServer} running Spring MVC.
|
||||
*/
|
||||
class UndertowServletWebServerMvcIntegrationTests extends AbstractServletWebServerMvcIntegrationTests {
|
||||
|
||||
protected UndertowServletWebServerMvcIntegrationTests() {
|
||||
super(UndertowConfig.class);
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class UndertowConfig {
|
||||
|
||||
@Bean
|
||||
UndertowServletWebServerFactory webServerFactory() {
|
||||
return new UndertowServletWebServerFactory(0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -616,8 +616,8 @@ public abstract class AbstractReactiveWebServerFactoryTests {
|
|||
ConfigurableReactiveWebServerFactory factory = getFactory();
|
||||
this.webServer = factory.getWebServer(new EchoHandler());
|
||||
this.webServer.start();
|
||||
assertThat(startedLogMessage()).matches(
|
||||
"(Jetty|Netty|Tomcat|Undertow) started on port " + this.webServer.getPort() + " \\(http(/1.1)?\\)");
|
||||
assertThat(startedLogMessage())
|
||||
.matches("(Jetty|Netty|Tomcat) started on port " + this.webServer.getPort() + " \\(http(/1.1)?\\)");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -626,7 +626,7 @@ public abstract class AbstractReactiveWebServerFactoryTests {
|
|||
addConnector(0, factory);
|
||||
this.webServer = factory.getWebServer(new EchoHandler());
|
||||
this.webServer.start();
|
||||
assertThat(startedLogMessage()).matches("(Jetty|Tomcat|Undertow) started on ports " + this.webServer.getPort()
|
||||
assertThat(startedLogMessage()).matches("(Jetty|Tomcat) started on ports " + this.webServer.getPort()
|
||||
+ " \\(http(/1.1)?\\), [0-9]+ \\(http(/1.1)?\\)");
|
||||
}
|
||||
|
||||
|
|
|
@ -1372,7 +1372,7 @@ public abstract class AbstractServletWebServerFactoryTests {
|
|||
ConfigurableServletWebServerFactory factory = getFactory();
|
||||
this.webServer = factory.getWebServer();
|
||||
this.webServer.start();
|
||||
assertThat(startedLogMessage()).matches("(Jetty|Tomcat|Undertow) started on port " + this.webServer.getPort()
|
||||
assertThat(startedLogMessage()).matches("(Jetty|Tomcat) started on port " + this.webServer.getPort()
|
||||
+ " \\(http(/1.1)?\\) with context path '/'");
|
||||
}
|
||||
|
||||
|
@ -1382,7 +1382,7 @@ public abstract class AbstractServletWebServerFactoryTests {
|
|||
factory.setContextPath("/test");
|
||||
this.webServer = factory.getWebServer();
|
||||
this.webServer.start();
|
||||
assertThat(startedLogMessage()).matches("(Jetty|Tomcat|Undertow) started on port " + this.webServer.getPort()
|
||||
assertThat(startedLogMessage()).matches("(Jetty|Tomcat) started on port " + this.webServer.getPort()
|
||||
+ " \\(http(/1.1)?\\) with context path '/test'");
|
||||
}
|
||||
|
||||
|
@ -1392,7 +1392,7 @@ public abstract class AbstractServletWebServerFactoryTests {
|
|||
addConnector(0, factory);
|
||||
this.webServer = factory.getWebServer();
|
||||
this.webServer.start();
|
||||
assertThat(startedLogMessage()).matches("(Jetty|Tomcat|Undertow) started on ports " + this.webServer.getPort()
|
||||
assertThat(startedLogMessage()).matches("(Jetty|Tomcat) started on ports " + this.webServer.getPort()
|
||||
+ " \\(http(/1.1)?\\), [0-9]+ \\(http(/1.1)?\\) with context path '/'");
|
||||
}
|
||||
|
||||
|
|
|
@ -40,7 +40,6 @@ dependencies {
|
|||
optional(project(":module:spring-boot-metrics"))
|
||||
optional(project(":module:spring-boot-observation"))
|
||||
optional(project(":module:spring-boot-tomcat"))
|
||||
optional(project(":module:spring-boot-undertow"))
|
||||
optional(project(":module:spring-boot-validation"))
|
||||
optional(project(":module:spring-boot-web-server"))
|
||||
optional("com.fasterxml.jackson.core:jackson-databind")
|
||||
|
|
|
@ -21,14 +21,12 @@ import java.util.List;
|
|||
import java.util.Optional;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import io.undertow.servlet.api.DeploymentManager;
|
||||
import jakarta.servlet.ServletException;
|
||||
import org.apache.catalina.Container;
|
||||
import org.apache.catalina.Context;
|
||||
import org.apache.catalina.core.StandardWrapper;
|
||||
|
||||
import org.springframework.boot.tomcat.TomcatWebServer;
|
||||
import org.springframework.boot.undertow.servlet.UndertowServletWebServer;
|
||||
import org.springframework.boot.web.server.WebServer;
|
||||
import org.springframework.boot.web.server.servlet.context.ServletWebServerApplicationContext;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
@ -48,10 +46,6 @@ final class DispatcherServletHandlerMappings {
|
|||
private static final boolean TOMCAT_WEB_SERVER_PRESENT = ClassUtils.isPresent(
|
||||
"org.springframework.boot.tomcat.TomcatWebServer", DispatcherServletHandlerMappings.class.getClassLoader());
|
||||
|
||||
private static final boolean UNDERTOW_WEB_SERVER_PRESENT = ClassUtils.isPresent(
|
||||
"org.springframework.boot.undertow.UndertowWebServer",
|
||||
DispatcherServletHandlerMappings.class.getClassLoader());
|
||||
|
||||
private final String name;
|
||||
|
||||
private final DispatcherServlet dispatcherServlet;
|
||||
|
@ -79,10 +73,7 @@ final class DispatcherServletHandlerMappings {
|
|||
return;
|
||||
}
|
||||
WebServer webServer = webServerApplicationContext.getWebServer();
|
||||
if (UNDERTOW_WEB_SERVER_PRESENT && webServer instanceof UndertowServletWebServer undertowServletWebServer) {
|
||||
new UndertowServletInitializer(undertowServletWebServer).initializeServlet(this.name);
|
||||
}
|
||||
else if (TOMCAT_WEB_SERVER_PRESENT && webServer instanceof TomcatWebServer tomcatWebServer) {
|
||||
if (TOMCAT_WEB_SERVER_PRESENT && webServer instanceof TomcatWebServer tomcatWebServer) {
|
||||
new TomcatServletInitializer(tomcatWebServer).initializeServlet(this.name);
|
||||
}
|
||||
}
|
||||
|
@ -124,26 +115,4 @@ final class DispatcherServletHandlerMappings {
|
|||
|
||||
}
|
||||
|
||||
private static final class UndertowServletInitializer {
|
||||
|
||||
private final UndertowServletWebServer webServer;
|
||||
|
||||
private UndertowServletInitializer(UndertowServletWebServer webServer) {
|
||||
this.webServer = webServer;
|
||||
}
|
||||
|
||||
void initializeServlet(String name) {
|
||||
try {
|
||||
DeploymentManager deploymentManager = this.webServer.getDeploymentManager();
|
||||
if (deploymentManager != null) {
|
||||
deploymentManager.getDeployment().getServlets().getManagedServlet(name).forceInit();
|
||||
}
|
||||
}
|
||||
catch (ServletException ex) {
|
||||
// Continue
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -2153,7 +2153,6 @@ bom {
|
|||
"spring-boot-starter-test",
|
||||
"spring-boot-starter-thymeleaf",
|
||||
"spring-boot-starter-tomcat",
|
||||
"spring-boot-starter-undertow",
|
||||
"spring-boot-starter-validation",
|
||||
"spring-boot-starter-web",
|
||||
"spring-boot-starter-web-services",
|
||||
|
@ -2170,7 +2169,6 @@ bom {
|
|||
"spring-boot-tomcat",
|
||||
"spring-boot-tracing",
|
||||
"spring-boot-tx",
|
||||
"spring-boot-undertow",
|
||||
"spring-boot-validation",
|
||||
"spring-boot-webclient",
|
||||
"spring-boot-webflux",
|
||||
|
@ -2652,18 +2650,6 @@ bom {
|
|||
releaseNotes("https://github.com/pingidentity/ldapsdk/releases/tag/{version}")
|
||||
}
|
||||
}
|
||||
library("Undertow", "2.3.18.Final") {
|
||||
group("io.undertow") {
|
||||
modules = [
|
||||
"undertow-core",
|
||||
"undertow-servlet",
|
||||
"undertow-websockets-jsr"
|
||||
]
|
||||
}
|
||||
links {
|
||||
releaseNotes("https://github.com/undertow-io/undertow/releases/tag/{version}")
|
||||
}
|
||||
}
|
||||
library("Versions Maven Plugin", "2.18.0") {
|
||||
group("org.codehaus.mojo") {
|
||||
plugins = [
|
||||
|
|
|
@ -160,7 +160,6 @@ include "module:spring-boot-thymeleaf"
|
|||
include "module:spring-boot-tomcat"
|
||||
include "module:spring-boot-tracing"
|
||||
include "module:spring-boot-tx"
|
||||
include "module:spring-boot-undertow"
|
||||
include "module:spring-boot-validation"
|
||||
include "module:spring-boot-web-server" // FIXME move to core?
|
||||
include "module:spring-boot-web-server-test" // FIXME move to core?
|
||||
|
@ -252,7 +251,6 @@ include "starter:spring-boot-starter-sql"
|
|||
include "starter:spring-boot-starter-test"
|
||||
include "starter:spring-boot-starter-thymeleaf"
|
||||
include "starter:spring-boot-starter-tomcat"
|
||||
include "starter:spring-boot-starter-undertow"
|
||||
include "starter:spring-boot-starter-validation"
|
||||
include "starter:spring-boot-starter-web"
|
||||
include "starter:spring-boot-starter-web-services"
|
||||
|
@ -355,8 +353,6 @@ include ":smoke-test:spring-boot-smoke-test-tomcat-jsp"
|
|||
include ":smoke-test:spring-boot-smoke-test-tomcat-multi-connectors"
|
||||
include ":smoke-test:spring-boot-smoke-test-tomcat-ssl"
|
||||
include ":smoke-test:spring-boot-smoke-test-traditional"
|
||||
include ":smoke-test:spring-boot-smoke-test-undertow"
|
||||
include ":smoke-test:spring-boot-smoke-test-undertow-ssl"
|
||||
include ":smoke-test:spring-boot-smoke-test-war"
|
||||
include ":smoke-test:spring-boot-smoke-test-web-application-type"
|
||||
include ":smoke-test:spring-boot-smoke-test-web-freemarker"
|
||||
|
@ -374,7 +370,6 @@ include ":smoke-test:spring-boot-smoke-test-webflux-coroutines"
|
|||
include ":smoke-test:spring-boot-smoke-test-webservices"
|
||||
include ":smoke-test:spring-boot-smoke-test-websocket-jetty"
|
||||
include ":smoke-test:spring-boot-smoke-test-websocket-tomcat"
|
||||
include ":smoke-test:spring-boot-smoke-test-websocket-undertow"
|
||||
include ":smoke-test:spring-boot-smoke-test-xml"
|
||||
|
||||
include ":integration-test:spring-boot-actuator-integration-tests"
|
||||
|
|
|
@ -1,32 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the License);
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
plugins {
|
||||
id "java"
|
||||
}
|
||||
|
||||
description = "Spring Boot Undertow SSL smoke test"
|
||||
|
||||
dependencies {
|
||||
implementation(project(":starter:spring-boot-starter-webmvc")) {
|
||||
exclude module: "spring-boot-starter-tomcat"
|
||||
}
|
||||
implementation(project(":starter:spring-boot-starter-undertow"))
|
||||
|
||||
testImplementation(project(":starter:spring-boot-starter-restclient"))
|
||||
testImplementation(project(":starter:spring-boot-starter-test"))
|
||||
testImplementation("org.apache.httpcomponents.client5:httpclient5")
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package smoketest.undertow.ssl;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
public class SampleUndertowSslApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(SampleUndertowSslApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
@NullMarked
|
||||
package smoketest.undertow.ssl;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
|
@ -1,30 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package smoketest.undertow.ssl.web;
|
||||
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
public class SampleController {
|
||||
|
||||
@GetMapping("/")
|
||||
public String helloWorld() {
|
||||
return "Hello World";
|
||||
}
|
||||
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
@NullMarked
|
||||
package smoketest.undertow.ssl.web;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
|
@ -1,4 +0,0 @@
|
|||
server.port = 8443
|
||||
server.ssl.key-store = classpath:sample.jks
|
||||
server.ssl.key-store-password = secret
|
||||
server.ssl.key-password = password
|
Binary file not shown.
|
@ -1,57 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package smoketest.undertow.ssl;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
|
||||
import org.springframework.boot.web.server.AbstractConfigurableWebServerFactory;
|
||||
import org.springframework.boot.web.server.test.client.TestRestTemplate;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Basic integration tests for demo application.
|
||||
*
|
||||
* @author Ivan Sopov
|
||||
*/
|
||||
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
|
||||
class SampleUndertowSslApplicationTests {
|
||||
|
||||
@Autowired
|
||||
private TestRestTemplate restTemplate;
|
||||
|
||||
@Autowired
|
||||
private AbstractConfigurableWebServerFactory webServerFactory;
|
||||
|
||||
@Test
|
||||
void testSsl() {
|
||||
assertThat(this.webServerFactory.getSsl().isEnabled()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testHome() {
|
||||
ResponseEntity<String> entity = this.restTemplate.getForEntity("/", String.class);
|
||||
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
assertThat(entity.getBody()).isEqualTo("Hello World");
|
||||
}
|
||||
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the License);
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
plugins {
|
||||
id "java"
|
||||
}
|
||||
|
||||
description = "Spring Boot Undertow smoke test"
|
||||
|
||||
dependencies {
|
||||
implementation(project(":starter:spring-boot-starter-webmvc")) {
|
||||
exclude module: "spring-boot-starter-tomcat"
|
||||
}
|
||||
implementation(project(":starter:spring-boot-starter-undertow"))
|
||||
|
||||
testImplementation(project(":starter:spring-boot-starter-restclient"))
|
||||
testImplementation(project(":starter:spring-boot-starter-test"))
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue