Fixes #10870 - How to set HttpConfiguration.securePort when the HTTPS port is dynamic?

Updated and expanded documentation.
Restored compilation of documentation Java sources.

Signed-off-by: Simone Bordet <simone.bordet@gmail.com>
This commit is contained in:
Simone Bordet 2023-12-19 19:22:47 +01:00
parent 80a04a554f
commit 231d8deb8a
3 changed files with 111 additions and 9 deletions

View File

@ -174,6 +174,19 @@
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<executions>
<execution>
<id>compile-documentation-sources</id>
<goals>
<goal>compile</goal>
</goals>
<phase>compile</phase>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>

View File

@ -51,21 +51,35 @@ You can use Unix-Domain sockets support only when you run your server with Java
include::../../{doc_code}/org/eclipse/jetty/docs/programming/server/http/HTTPServerDocs.java[tags=configureConnectorQuic]
----
The _acceptors_ are threads (typically only one) that compete to accept socket connections.
When a connection is accepted, `ServerConnector` wraps the accepted `SocketChannel` and passes it to the xref:pg-arch-io-selector-manager[`SelectorManager`].
[[pg-server-http-connector-acceptors]]
===== Acceptors
The _acceptors_ are threads (typically only one) that compete to accept TCP socket connections.
The connectors for the QUIC or HTTP/3 protocol, based on UDP, have no acceptors.
When a TCP connection is accepted, `ServerConnector` wraps the accepted `SocketChannel` and passes it to the xref:pg-arch-io-selector-manager[`SelectorManager`].
Therefore, there is a little moment where the acceptor thread is not accepting new connections because it is busy wrapping the just accepted connection to pass it to the `SelectorManager`.
Connections that are ready to be accepted but are not accepted yet are queued in a bounded queue (at the OS level) whose capacity can be configured with the `acceptQueueSize` parameter.
If your application must withstand a very high rate of connections opened, configuring more than one acceptor thread may be beneficial: when one acceptor thread accepts one connection, another acceptor thread can take over accepting connections.
The _selectors_ are components that manage a set of connected sockets, implemented by xref:pg-arch-io-selector-manager[`ManagedSelector`].
Each selector requires one thread and uses the Java NIO mechanism to efficiently handle a set of connected sockets.
As a rule of thumb, a single selector can easily manage up to 1000-5000 sockets, although the number may vary greatly depending on the application.
[[pg-server-http-connector-selectors]]
===== Selectors
For example, web applications for websites tend to use sockets for one or more HTTP requests to retrieve resources and then the socket is idle for most of the time.
In this case a single selector may be able to manage many sockets because chances are that they will be idle most of the time.
On the contrary, web messaging applications or REST applications tend to send many small messages at a very high frequency so that sockets are rarely idle.
In this case a single selector may be able to manage less sockets because chances are that many of them will be active at the same time, so you may need more than one.
The _selectors_ are components that manage a set of accepted TCP sockets, implemented by xref:pg-arch-io-selector-manager[`ManagedSelector`].
For QUIC or HTTP/3, there are no accepted TCP sockets, but only one `DatagramChannel` and therefore there is only 1 selector.
Each selector requires one thread and uses the Java NIO mechanism to efficiently handle a set of registered channels.
As a rule of thumb, a single selector can easily manage up to 1000-5000 TCP sockets, although the number may vary greatly depending on the application.
For example, web applications for websites tend to use TCP sockets for one or more HTTP requests to retrieve resources and then the TCP socket is idle for most of the time.
In this case a single selector may be able to manage many TCP sockets because chances are that they will be idle most of the time.
On the contrary, web messaging applications or REST applications tend to send many small messages at a very high frequency so that the TCP sockets are rarely idle.
In this case a single selector may be able to manage less TCP sockets because chances are that many of them will be active at the same time, so you may need more than one selector.
[[pg-server-http-connector-multiple]]
===== Multiple Connectors
It is possible to configure more than one `Connector` per `Server`.
Typical cases are a `ServerConnector` for clear-text HTTP, and another `ServerConnector` for secure HTTP.
@ -79,6 +93,19 @@ For example:
include::../../{doc_code}/org/eclipse/jetty/docs/programming/server/http/HTTPServerDocs.java[tags=configureConnectors]
----
If you do not specify the port the connector listens to explicitly, the OS will allocate one randomly when the connector starts.
You may need to use the randomly allocated port to configure other components, for example the port to use for secure redirects (when redirecting from a URI with the `http` scheme to the `https` scheme).
Another example is to bind both the HTTP/2 connector and the HTTP/3 connector to the same port.
This is possible since the HTTP/2 connector uses TCP, while the HTTP/3 connector uses UDP.
For example:
[source,java,indent=0]
----
include::../../{doc_code}/org/eclipse/jetty/docs/programming/server/http/HTTPServerDocs.java[tags=sameRandomPort]
----
[[pg-server-http-connector-protocol]]
===== Configuring Protocols

View File

@ -55,12 +55,14 @@ import org.eclipse.jetty.rewrite.handler.CompactPathRule;
import org.eclipse.jetty.rewrite.handler.RedirectRegexRule;
import org.eclipse.jetty.rewrite.handler.RewriteHandler;
import org.eclipse.jetty.rewrite.handler.RewriteRegexRule;
import org.eclipse.jetty.server.ConnectionFactory;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.CustomRequestLog;
import org.eclipse.jetty.server.FormFields;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.HttpConfiguration;
import org.eclipse.jetty.server.HttpConnectionFactory;
import org.eclipse.jetty.server.NetworkConnector;
import org.eclipse.jetty.server.ProxyConnectionFactory;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.RequestLogWriter;
@ -84,6 +86,7 @@ import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.Fields;
import org.eclipse.jetty.util.NanoTime;
import org.eclipse.jetty.util.Promise;
import org.eclipse.jetty.util.component.LifeCycle;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.util.resource.ResourceFactory;
import org.eclipse.jetty.util.ssl.SslContextFactory;
@ -257,6 +260,65 @@ public class HTTPServerDocs
// end::configureConnectors[]
}
public void sameRandomPort() throws Exception
{
// tag::sameRandomPort[]
SslContextFactory.Server sslContextFactory = new SslContextFactory.Server();
sslContextFactory.setKeyStorePath("/path/to/keystore");
sslContextFactory.setKeyStorePassword("secret");
Server server = new Server();
// The plain HTTP configuration.
HttpConfiguration plainConfig = new HttpConfiguration();
// The secure HTTP configuration.
HttpConfiguration secureConfig = new HttpConfiguration(plainConfig);
secureConfig.addCustomizer(new SecureRequestCustomizer());
// First, create the secure connector for HTTPS and HTTP/2.
HttpConnectionFactory https = new HttpConnectionFactory(secureConfig);
HTTP2ServerConnectionFactory http2 = new HTTP2ServerConnectionFactory(secureConfig);
ALPNServerConnectionFactory alpn = new ALPNServerConnectionFactory();
alpn.setDefaultProtocol(https.getProtocol());
ConnectionFactory ssl = new SslConnectionFactory(sslContextFactory, https.getProtocol());
ServerConnector secureConnector = new ServerConnector(server, 1, 1, ssl, alpn, http2, https);
server.addConnector(secureConnector);
// Second, create the plain connector for HTTP.
HttpConnectionFactory http = new HttpConnectionFactory(plainConfig);
ServerConnector plainConnector = new ServerConnector(server, 1, 1, http);
server.addConnector(plainConnector);
// Third, create the connector for HTTP/3.
HTTP3ServerConnectionFactory http3 = new HTTP3ServerConnectionFactory(secureConfig);
HTTP3ServerConnector http3Connector = new HTTP3ServerConnector(server, sslContextFactory, http3);
server.addConnector(http3Connector);
// Set up a listener so that when the secure connector starts,
// it configures the other connectors that have not started yet.
secureConnector.addEventListener(new LifeCycle.Listener()
{
@Override
public void lifeCycleStarted(LifeCycle lifeCycle)
{
if (lifeCycle instanceof NetworkConnector networkConnector)
{
int port = networkConnector.getLocalPort();
// Configure the plain connector for secure redirects from http to https.
plainConfig.setSecurePort(port);
// Configure the HTTP3 connector port to be the same as HTTPS/HTTP2.
http3Connector.setPort(port);
}
}
});
server.start();
// end::sameRandomPort[]
}
public void http11() throws Exception
{
// tag::http11[]