Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

support management port in servlet #712

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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import io.micronaut.core.annotation.NonNull;
import io.micronaut.core.annotation.Nullable;
import io.micronaut.core.io.ResourceResolver;
import io.micronaut.core.util.CollectionUtils;
import io.micronaut.http.server.HttpServerConfiguration;
import io.micronaut.http.ssl.ClientAuthentication;
import io.micronaut.http.ssl.SslConfiguration;
Expand All @@ -35,15 +36,18 @@
import io.micronaut.servlet.engine.initializer.MicronautServletInitializer;
import io.micronaut.servlet.engine.server.ServletServerFactory;
import io.micronaut.servlet.engine.server.ServletStaticResourceConfiguration;
import io.micronaut.web.router.Router;
import jakarta.inject.Singleton;
import java.io.IOException;
import java.util.List;
import java.util.stream.Stream;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.stream.Stream;
import org.eclipse.jetty.alpn.server.ALPNServerConnectionFactory;
import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.http2.server.HTTP2CServerConnectionFactory;
import org.eclipse.jetty.http2.server.HTTP2ServerConnectionFactory;
import org.eclipse.jetty.server.ConnectionFactory;
import org.eclipse.jetty.server.HttpConfiguration;
import org.eclipse.jetty.server.HttpConnectionFactory;
import org.eclipse.jetty.server.Server;
Expand All @@ -70,6 +74,7 @@ public class JettyFactory extends ServletServerFactory {
public static final String RESOURCE_BASE = "resourceBase";

private final JettyConfiguration jettyConfiguration;
private final Router router;

/**
* Default constructor.
Expand All @@ -94,6 +99,7 @@ public JettyFactory(
staticResourceConfigurations
);
this.jettyConfiguration = serverConfiguration;
this.router = applicationContext.findBean(Router.class).orElse(null);
}

/**
Expand Down Expand Up @@ -269,7 +275,7 @@ protected Server jettyServer(
}

/**
* Configures the servlet initializer
* Configures the servlet initializer.
*
* @param server The server
* @param contextHandler The context handler
Expand Down Expand Up @@ -312,8 +318,29 @@ protected void configureConnectors(@NonNull Server server, @NonNull ServerConnec
if (serverConfiguration.isDualProtocol()) {
server.addConnector(http);
}
applyAdditionalPorts(server, https);
} else {
server.addConnector(http);
applyAdditionalPorts(server, http);
}
}

private void applyAdditionalPorts(Server server, ServerConnector serverConnector) {
if (router != null) {
Set<Integer> exposedPorts = router.getExposedPorts();
if (CollectionUtils.isNotEmpty(exposedPorts)) {
for (Integer exposedPort : exposedPorts) {
if (!exposedPort.equals(serverConnector.getLocalPort())) {
ServerConnector connector = new ServerConnector(
server,
serverConnector.getConnectionFactories().toArray(ConnectionFactory[]::new)
);
connector.setPort(exposedPort);
connector.setHost(getConfiguredHost());
server.addConnector(connector);
}
}
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,11 @@
import io.micronaut.http.server.exceptions.HttpServerException;
import io.micronaut.runtime.ApplicationConfiguration;
import io.micronaut.servlet.engine.server.AbstractServletServer;
import org.eclipse.jetty.server.Server;

import jakarta.inject.Singleton;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import org.eclipse.jetty.server.Server;

/**
* An implementation of the {@link io.micronaut.runtime.server.EmbeddedServer} interface for Jetty.
Expand All @@ -51,7 +50,8 @@ public JettyServer(

@Override
protected void startServer() throws Exception {
getServer().start();
Server server = getServer();
server.start();
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@ import io.micronaut.context.ApplicationContext
import io.micronaut.context.annotation.Requires
import io.micronaut.core.io.socket.SocketUtils
import io.micronaut.core.util.StringUtils
import io.micronaut.http.HttpStatus
import io.micronaut.http.annotation.Controller
import io.micronaut.http.annotation.Get
import io.micronaut.http.client.BlockingHttpClient
import io.micronaut.http.client.HttpClient
import io.micronaut.http.client.exceptions.HttpClientResponseException
import io.micronaut.runtime.server.EmbeddedServer
import io.netty.handler.ssl.util.SelfSignedCertificate
import spock.lang.Issue
Expand Down Expand Up @@ -59,7 +61,6 @@ class JettyManagementPortSpec extends Specification {
]
}

@PendingFeature
def 'management port can be configured different to main port'() {
given:
def port = SocketUtils.findAvailableTcpPort()
Expand All @@ -73,19 +74,26 @@ class JettyManagementPortSpec extends Specification {

when:
def mainResponse = mainClient.exchange('/management-port', String)
def healthResponse = mainClient.exchange('/health', String)
def healthResponse = managementClient.exchange('/health', String)

then:
mainResponse.body() == 'Hello world'
healthResponse.body() == '{"status":"UP"}'

when:
mainClient.exchange('/health', String)

then:
def e = thrown(HttpClientResponseException)
e.response.status() == HttpStatus.NOT_FOUND


cleanup:
mainClient.close()
managementClient.close()
server.stop()
}

@PendingFeature
def 'management port can be configured different to main port and uses ssl if also configured'() {
given:
def port = SocketUtils.findAvailableTcpPort()
Expand All @@ -99,12 +107,19 @@ class JettyManagementPortSpec extends Specification {

when:
def mainResponse = mainClient.exchange('/management-port', String)
def healthResponse = mainClient.exchange('/health', String)
def healthResponse = managementClient.exchange('/health', String)

then:
mainResponse.body() == 'Hello world'
healthResponse.body() == '{"status":"UP"}'

when:
mainClient.exchange('/health', String)

then:
def e = thrown(HttpClientResponseException)
e.response.status() == HttpStatus.NOT_FOUND

cleanup:
mainClient.close()
managementClient.close()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,11 @@
import io.micronaut.context.annotation.Requires;
import io.micronaut.core.annotation.NonNull;
import io.micronaut.core.annotation.Nullable;
import io.micronaut.core.util.CollectionUtils;
import io.micronaut.core.util.StringUtils;
import io.micronaut.http.HttpVersion;
import io.micronaut.inject.qualifiers.Qualifiers;
import io.micronaut.web.router.Router;
import jakarta.inject.Named;
import io.micronaut.servlet.engine.initializer.MicronautServletInitializer;
import java.io.File;
Expand All @@ -42,6 +44,7 @@
import org.apache.catalina.Context;
import org.apache.catalina.connector.Connector;
import org.apache.catalina.startup.Tomcat;
import org.apache.coyote.ProtocolHandler;
import org.apache.coyote.http2.Http2Protocol;
import org.apache.tomcat.util.net.SSLHostConfig;
import org.apache.tomcat.util.net.SSLHostConfigCertificate;
Expand All @@ -56,6 +59,8 @@
public class TomcatFactory extends ServletServerFactory {

private static final String HTTPS = "HTTPS";
private static final String CLIENT_AUTH = "clientAuth";
private final Router router;

/**
* Default constructor.
Expand All @@ -73,6 +78,7 @@ protected TomcatFactory(
ApplicationContext applicationContext,
List<ServletStaticResourceConfiguration> staticResourceConfigurations) {
super(resourceResolver, serverConfiguration, sslConfiguration, applicationContext, staticResourceConfigurations);
this.router = applicationContext.findBean(Router.class).orElse(null);
}

@Override
Expand Down Expand Up @@ -155,11 +161,58 @@ protected void configureConnectors(@NonNull Tomcat tomcat, @NonNull Connector ht
if (serverConfiguration.isDualProtocol()) {
tomcat.getService().addConnector(httpConnector);
}
applyAdditionalPorts(tomcat, httpsConnector);
} else {
tomcat.setConnector(httpConnector);
applyAdditionalPorts(tomcat, httpConnector);
}
}

private void applyAdditionalPorts(Tomcat server, Connector serverConnector) {
if (router != null) {
Set<Integer> exposedPorts = router.getExposedPorts();
if (CollectionUtils.isNotEmpty(exposedPorts)) {
for (Integer exposedPort : exposedPorts) {
if (!exposedPort.equals(serverConnector.getLocalPort())) {
Connector newConnector = cloneConnectorSettings(serverConnector);
newConnector.setPort(exposedPort);
server.getService().addConnector(newConnector);
}
}
}
}
}

private static Connector cloneConnectorSettings(Connector serverConnector) {
Connector newConnector = new Connector(serverConnector.getProtocol());
ProtocolHandler protocolHandler = serverConnector.getProtocolHandler();
SSLHostConfig[] sslHostConfigs = protocolHandler.findSslHostConfigs();
for (SSLHostConfig sslHostConfig : sslHostConfigs) {
newConnector.addSslHostConfig(sslHostConfig);
newConnector.setSecure(true);
newConnector.setScheme("https");
newConnector.setProperty(CLIENT_AUTH, "false");
newConnector.setProperty("sslProtocol", "TLS");
newConnector.setProperty("SSLEnabled", "true");
}
newConnector.setAllowBackslash(serverConnector.getAllowBackslash());
newConnector.setAllowTrace(serverConnector.getAllowTrace());
newConnector.setAsyncTimeout(serverConnector.getAsyncTimeout());
newConnector.setDiscardFacades(serverConnector.getDiscardFacades());
newConnector.setEnableLookups(serverConnector.getEnableLookups());
newConnector.setSecure(serverConnector.getSecure());
newConnector.setScheme(serverConnector.getScheme());
newConnector.setEnforceEncodingInGetWriter(serverConnector.getEnforceEncodingInGetWriter());
newConnector.setMaxCookieCount(serverConnector.getMaxCookieCount());
newConnector.setMaxPostSize(serverConnector.getMaxPostSize());
newConnector.setMaxParameterCount(serverConnector.getMaxParameterCount());
newConnector.setMaxSavePostSize(serverConnector.getMaxSavePostSize());
newConnector.setParseBodyMethods(serverConnector.getParseBodyMethods());
newConnector.setRejectSuspiciousURIs(serverConnector.getRejectSuspiciousURIs());
newConnector.setUseIPVHosts(serverConnector.getUseIPVHosts());
return newConnector;
}

/**
* Create a new context.
*
Expand Down Expand Up @@ -225,14 +278,14 @@ protected Connector sslConnector(SslConfiguration sslConfiguration) {
httpsConnector.setPort(sslPort);
httpsConnector.setSecure(true);
httpsConnector.setScheme("https");
httpsConnector.setProperty("clientAuth", "false");
httpsConnector.setProperty(CLIENT_AUTH, "false");
httpsConnector.setProperty("sslProtocol", protocol);
httpsConnector.setProperty("SSLEnabled", "true");
sslConfiguration.getCiphers().ifPresent(cyphers ->
sslHostConfig.setCiphers(String.join(",", cyphers))
);
sslConfiguration.getClientAuthentication().ifPresent(ca ->
httpsConnector.setProperty("clientAuth", ca == ClientAuthentication.WANT ? "want" : "true")
httpsConnector.setProperty(CLIENT_AUTH, ca == ClientAuthentication.WANT ? "want" : "true")
);


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@ public TomcatServer(
@Override
protected void startServer() throws Exception {
if (running.compareAndSet(false, true)) {
getServer().start();
Tomcat server = getServer();
server.start();
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@ import io.micronaut.context.ApplicationContext
import io.micronaut.context.annotation.Requires
import io.micronaut.core.io.socket.SocketUtils
import io.micronaut.core.util.StringUtils
import io.micronaut.http.HttpStatus
import io.micronaut.http.annotation.Controller
import io.micronaut.http.annotation.Get
import io.micronaut.http.client.BlockingHttpClient
import io.micronaut.http.client.HttpClient
import io.micronaut.http.client.exceptions.HttpClientResponseException
import io.micronaut.runtime.server.EmbeddedServer
import io.netty.handler.ssl.util.SelfSignedCertificate
import spock.lang.Issue
Expand Down Expand Up @@ -59,12 +61,11 @@ class TomcatManagementPortSpec extends Specification {
]
}

@PendingFeature
def 'management port can be configured different to main port'() {
given:
def port = SocketUtils.findAvailableTcpPort()
EmbeddedServer server = ApplicationContext.run(EmbeddedServer, [
'spec.name' : 'JettyManagementPortSpec',
'spec.name' : 'TomcatManagementPortSpec',
'endpoints.all.enabled': true,
'endpoints.all.port' : port,
])
Expand All @@ -73,24 +74,30 @@ class TomcatManagementPortSpec extends Specification {

when:
def mainResponse = mainClient.exchange('/management-port', String)
def healthResponse = mainClient.exchange('/health', String)
def healthResponse = managementClient.exchange('/health', String)

then:
mainResponse.body() == 'Hello world'
healthResponse.body() == '{"status":"UP"}'

when:
mainClient.exchange('/health', String)

then:
def e = thrown(HttpClientResponseException)
e.response.status() == HttpStatus.NOT_FOUND

cleanup:
mainClient.close()
managementClient.close()
server.stop()
}

@PendingFeature
def 'management port can be configured different to main port and uses ssl if also configured'() {
given:
def port = SocketUtils.findAvailableTcpPort()
EmbeddedServer server = ApplicationContext.run(EmbeddedServer, [
'spec.name' : 'JettyManagementPortSpec',
'spec.name' : 'TomcatManagementPortSpec',
'endpoints.all.enabled': true,
'endpoints.all.port' : port,
] + sslConfig())
Expand All @@ -99,20 +106,27 @@ class TomcatManagementPortSpec extends Specification {

when:
def mainResponse = mainClient.exchange('/management-port', String)
def healthResponse = mainClient.exchange('/health', String)
def healthResponse = managementClient.exchange('/health', String)

then:
mainResponse.body() == 'Hello world'
healthResponse.body() == '{"status":"UP"}'

when:
mainClient.exchange('/health', String)

then:
def e = thrown(HttpClientResponseException)
e.response.status() == HttpStatus.NOT_FOUND

cleanup:
mainClient.close()
managementClient.close()
server.stop()
}

@Controller("/management-port")
@Requires(property = "spec.name", value = "JettyManagementPortSpec")
@Requires(property = "spec.name", value = "TomcatManagementPortSpec")
static class TestController {

@Get
Expand Down
Loading
Loading